使用预先训练网络和特征抽取大力提升图像识别率

图灵汇官网

神经网络在实际应用中常面临的一个挑战是数据量不足的问题。对于任何人工智能项目来说,缺乏足够的数据会限制模型性能,即便算法设计得再精细,如果数据量不够,最终效果也会大打折扣。我们当前正在进行的图像识别项目就是一个例子,要使网络能够准确识别猫和狗的图片,至少需要数万张图片的数据集。

尽管如此,我们仍可以通过借用外部资源来解决这个问题。有些机构利用大量的图片数据训练出成熟的模型,并将其共享出来。例如,如果有人已经使用了几万张猫狗图片训练了一个模型,我们可以直接使用这个模型来识别我们自己的猫狗图片,这无疑会大大提高效率和准确性。

许多机构使用ImageNet等大规模数据集来训练模型,从而获得高精度的识别效果,并愿意将这些成果共享。这使得我们能够便捷地利用现成的模型,而无需从零开始训练。我们将采用一个经过大量数据训练的大型卷积神经网络,特别是VGG16这样的网络,它经过了ImageNet上超过140万张图片的训练,涵盖日常生活中的各种物品及常见动物,其中包括多种猫和狗的图片。

VGG16这样的网络类似于程序开发中的开源库,可以直接拿来使用。然而,由于应用场景的不同,我们需要对这些网络进行适当的调整。常见的调整方法包括特征提取和参数微调。我们将首先探讨特征提取的方法。在构建卷积神经网络时,通常会有多层卷积层和最大池化层,之后通过Flatten()函数将多维向量压缩,再传入全连接层。我们即将使用的VGG16网络结构与此类似,只是层数更多。

在借用已训练好的网络时,通常会移除Flatten()后面的层,因为这些层与原应用场景紧密相关。我们真正需要的是卷积层和最大池化层输出的结果,这些结果包含了对训练图片本质的理解,这就是我们希望提取的特征。通过这种方式,我们可以用别人的网络提取特征,然后用这些特征训练自己的分类器。

VGG16网络已经集成在Keras框架中,可以很方便地引用。我们可以通过以下代码来初始化一个VGG16网络实例:

```python from keras.applications import VGG16

convbase = VGG16(weights='imagenet', includetop=False, input_shape=(150, 150, 3)) ```

这段代码将VGG16的卷积层和最大池化层的参数传递过来,并初始化成对应的网络结构。include_top=False表示不包括Flatten()之后的层,因为我们只关注图片的特征提取,而不是分类。

接下来,我们将读取自己的图片,并将它们输入到VGG16网络中,提取出图片的特征:

```python import os import numpy as np from keras.preprocessing.image import ImageDataGenerator

basedir = '/path/to/dataset' traindir = os.path.join(basedir, 'train') validationdir = os.path.join(base_dir, 'validation')

datagen = ImageDataGenerator(rescale=1./255) batch_size = 20

def extractfeatures(directory, samplecount): features = np.zeros(shape=(samplecount, 4, 4, 512)) labels = np.zeros(shape=(samplecount))

generator = datagen.flow_from_directory(directory, target_size=(150, 150),
                                         batch_size=batch_size, class_mode='binary')

i = 0
for inputs_batch, labels_batch in generator:
    features_batch = conv_base.predict(inputs_batch)
    features[i * batch_size : (i + 1) * batch_size] = features_batch
    labels[i * batch_size : (i + 1) * batch_size] = labels_batch
    i += 1
    if i * batch_size >= sample_count:
        break

return features, labels

trainfeatures, trainlabels = extractfeatures(traindir, 2000) validationfeatures, validationlabels = extractfeatures(validationdir, 1000) testfeatures, testlabels = extractfeatures(testdir, 1000) ```

提取出来的特征将被输入到我们自己的神经网络中进行分类:

```python from keras import models from keras import layers from keras import optimizers

model = models.Sequential() model.add(layers.Flatten(input_shape=(4, 4, 512))) model.add(layers.Dense(256, activation='relu')) model.add(layers.Dropout(0.5)) model.add(layers.Dense(1, activation='sigmoid'))

model.compile(optimizer=optimizers.RMSprop(lr=2e-5), loss='binary_crossentropy', metrics=['acc'])

history = model.fit(trainfeatures, trainlabels, epochs=30, batchsize=20, validationdata=(validationfeatures, validationlabels)) ```

除了特征提取,另一种方法是参数微调。在这种方法中,我们不仅提取特征,还会利用自己的数据对VGG16的部分卷积层进行微调。具体而言,我们会训练VGG16的最高两层,使其适应我们的数据集。

为了实现这一点,我们首先将VGG16的卷积层添加到我们的模型中,然后冻结所有层,只训练我们自己的网络层。接下来,我们解冻VGG16的最高两层,并一起训练这两层和我们自己的网络层:

```python convbase.trainable = True settrainable = False

for layer in convbase.layers: if layer.name == 'block5conv1': settrainable = True if settrainable: layer.trainable = True else: layer.trainable = False

model.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(lr=2e-5), metrics=['acc'])

history = model.fitgenerator(traingenerator, stepsperepoch=100, epochs=30, validationdata=validationgenerator, validation_steps=50) ```

这种方法能够在保持已有模型性能的同时,让模型更好地适应我们的特定任务。

通过上述方法,我们可以充分利用现有的优秀模型,显著提升我们项目的性能和效率。

本文来源: 图灵汇 文章作者: 颜如玉