在 Keras 中训练多类图像分类器

Posted

技术标签:

【中文标题】在 Keras 中训练多类图像分类器【英文标题】:Train multi-class image classifier in Keras 【发布时间】:2017-06-08 22:47:35 【问题描述】:

我正在学习使用 Keras 训练分类器的教程

https://blog.keras.io/building-powerful-image-classification-models-using-very-little-data.html

具体来说,从作者给出的second script 中,我想将脚本转换为可以训练多类分类器的脚本(猫和狗的二进制)。我的火车文件夹中有 5 个课程,所以我做了以下更改:

train_top_model()的函数中:

我变了

model = Sequential()
model.add(Flatten(input_shape=train_data.shape[1:]))
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid'))

model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])

进入

model = Sequential()
model.add(Flatten(input_shape=train_data.shape[1:]))
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(5, activation='sigmoid'))

model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

train_labels = to_categorical(train_labels, 5)
validation_labels = to_categorical(validation_labels, 5)

完成训练后,模型达到了接近 99% 的训练准确率,但只有验证准确率的 70% 左右。因此我开始思考,将 2 类培训转换为 5 类可能并不那么简单。标记类时可能需要使用 one-hot 编码(但我不知道如何)

编辑:

我还附上了我的微调脚本。另一个问题:开始微调时准确率没有有效提高。

import os
import h5py
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
from keras.models import Sequential
from keras.layers import Convolution2D, MaxPooling2D, ZeroPadding2D
from keras.layers import Activation, Dropout, Flatten, Dense

# path to the model weights files.
weights_path = 'D:/Users/EJLTZ/Desktop/vgg16_weights.h5'
top_model_weights_path = 'bottleneck_weights_2.h5'
# dimensions of our images.
img_width, img_height = 150, 150

train_data_dir = 'D:/Users/EJLTZ/Desktop/BodyPart-full/train_new'
validation_data_dir = 'D:/Users/EJLTZ/Desktop/BodyPart-full/validation_new'
nb_train_samples = 500
nb_validation_samples = 972
nb_epoch = 50

# build the VGG16 network
model = Sequential()
model.add(ZeroPadding2D((1, 1), input_shape=(3, img_width, img_height)))

model.add(Convolution2D(64, 3, 3, activation='relu', name='conv1_1'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(64, 3, 3, activation='relu', name='conv1_2'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))

model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(128, 3, 3, activation='relu', name='conv2_1'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(128, 3, 3, activation='relu', name='conv2_2'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))

model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(256, 3, 3, activation='relu', name='conv3_1'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(256, 3, 3, activation='relu', name='conv3_2'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(256, 3, 3, activation='relu', name='conv3_3'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))

model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(512, 3, 3, activation='relu', name='conv4_1'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(512, 3, 3, activation='relu', name='conv4_2'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(512, 3, 3, activation='relu', name='conv4_3'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))

model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(512, 3, 3, activation='relu', name='conv5_1'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(512, 3, 3, activation='relu', name='conv5_2'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(512, 3, 3, activation='relu', name='conv5_3'))
model.add(MaxPooling2D((2, 2), strides=(2, 2)))

# load the weights of the VGG16 networks
# (trained on ImageNet, won the ILSVRC competition in 2014)
# note: when there is a complete match between your model definition
# and your weight savefile, you can simply call model.load_weights(filename)
assert os.path.exists(weights_path), 'Model weights not found (see "weights_path" variable in script).'
f = h5py.File(weights_path)
for k in range(f.attrs['nb_layers']):
    if k >= len(model.layers):
        # we don't look at the last (fully-connected) layers in the savefile
        break
    g = f['layer_'.format(k)]
    weights = [g['param_'.format(p)] for p in range(g.attrs['nb_params'])]
    model.layers[k].set_weights(weights)
f.close()
print('Model loaded.')

# build a classifier model to put on top of the convolutional model
top_model = Sequential()
top_model.add(Flatten(input_shape=model.output_shape[1:]))
top_model.add(Dense(256, activation='relu'))
top_model.add(Dropout(0.5))
top_model.add(Dense(5, activation='softmax'))

# note that it is necessary to start with a fully-trained
# classifier, including the top classifier,
# in order to successfully do fine-tuning
top_model.load_weights(top_model_weights_path)

# add the model on top of the convolutional base
model.add(top_model)

# set the first 25 layers (up to the last conv block)
# to non-trainable (weights will not be updated)
for layer in model.layers[:25]:
    layer.trainable = False

# compile the model with a SGD/momentum optimizer
# and a very slow learning rate.
model.compile(loss='categorical_crossentropy',
          optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
          metrics=['accuracy'])

# prepare data augmentation configuration
train_datagen = ImageDataGenerator(
    rescale=1./255,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True)

test_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
    train_data_dir,
    target_size=(img_height, img_width),
    batch_size=32,
    class_mode= 'categorical')

validation_generator = test_datagen.flow_from_directory(
    validation_data_dir,
    target_size=(img_height, img_width),
    batch_size=32,
    class_mode= 'categorical')

# fine-tune the model
model.fit_generator(
    train_generator,
    samples_per_epoch=nb_train_samples,
    nb_epoch=nb_epoch,
    validation_data=validation_generator,
    nb_val_samples=nb_validation_samples)

model.save_weights("fine-tune_weights.h5")
model.save("fine-tune_model.h5", True)

【问题讨论】:

您能说说您的训练和测试集是如何组织的吗?意思是,您提供的路径中不同文件夹中的不同类图像还是其他什么? 【参考方案1】:

    使用softmax作为输出层的激活函数,它是对多类情况的逻辑函数的推广。阅读更多关于它的信息here。

    如果验证误差远大于训练误差,如您的情况,则表明过度拟合。你应该做一些正则化,它被定义为学习算法的任何变化,旨在减少测试错误而不是训练错误。您可以尝试数据增强、提前停止、噪声注入、更激进的 dropout 等。

    如果您的设置与链接教程中的设置相同,请将 train_generatorvalidation_generatorclass_mode 更改为 categorical,它将一次性对您的课程进行编码。

【讨论】:

谢谢!我正在尝试您的解决方案。训练需要时间。我会告诉你进展如何。 这次我得到了80%的验证准确率。感人的!然而,当我按照第三个脚本(微调:训练网络的顶层)gist.github.com/fchollet/7eb39b44eb9e16e59632d25fb3119975 时,第一个时期的准确度从 20% 开始,并且没有增加。 IDK 为什么。(我设置了 class_mode='categorical') 在开始微调之前,您是否在卷积块之上训练了自己的全连接分类器? @AustinChen 我假设分类器是我从上一步得到的——***模型(微调前性能最好的模型),然后我需要加载这个模型并微调顶层对吗?我尝试了 2 类 cat-dog 示例,模型的准确率在微调之前约为 98%,并且在我开始微调后(缓慢)增加。但是,对于我有 5 个类的情况,微调从 20% 开始。我检查了我加载的模型是我刚刚在瓶颈特征之后训练的模型。 @Sergii Gryshkevych 以防万一:你冻结了除瓶颈层之外的所有卷积层吗?不看代码很难看出哪里出了问题。

以上是关于在 Keras 中训练多类图像分类器的主要内容,如果未能解决你的问题,请参考以下文章

Keras 多类图像分类和预测

多类图像分类中如何获取权重图

SVM 分类器和测试图像

使用 Keras 稀疏分类交叉熵进行像素级多类分类

如何找出概率输出中每列的哪个类对应于使用Keras进行多类分类?

Keras Multi-class 多标签图像分类:处理独立和从属标签和非二进制输出的混合