问题 在Keras训练多级图像分类器


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

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

具体来说,来自 第二个脚本 作者给出的,我希望将脚本转换为可以训练多类分类器的脚本(是猫和狗的二进制文件)。我的火车文件夹中有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课程并不是那么简单。也许我在标记类时需要使用单热编码(但我不知道如何)

编辑:

我也附上了我的精细编曲脚本。另一个问题:当精细调谐开始时,准确性没有有效增加。

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)

9091
2018-01-24 08:12


起源

你能提一下你的训练和测试集是如何组织的吗?意思是,您提供的路径中的不同文件夹中的不同类图像是什么? - Mohit Motwani


答案:


  1. 使用 softmax 作为输出层的激活函数,它是多类情形的逻辑函数的推广。阅读更多相关信息 这里

  2. 如果验证错误远远大于训练误差,就像您的情况一样,它是过度拟合的指标。你应该做一些正则化,它被定义为学习算法的任何变化,旨在减少测试错误而不是训练错误。你可以尝试一些事情,如数据增加,早期停止,噪音注入,更积极的辍学等。

  3. 如果您具有与链接教程中相同的设置,请更改 class_modetrain_generatorvalidation_generator 至 categorical 它会对你的课程进行一次热门编码。


13
2018-01-24 09:28



谢谢!我正在尝试你的解决方案。培训需要时间。我会告诉你它是怎么回事。 - Austin Chen
这次我的验证准确率达到了80%。令人印象深刻!但是,当我按照第3个脚本(微调:训练网络的顶层)时 gist.github.com/fchollet/7eb39b44eb9e16e59632d25fb3119975  准确度从第1纪元的20%开始,并没有增加。 IDK为什么。(我设置了class_mode ='分类') - Austin Chen
在开始微调之前,您是否已经在卷积块上训练了自己的全连接分类器? @AustinChen - Sergii Gryshkevych
我假设分类器是我从最后一步得到的 - 顶级模型(微调之前表现最好的模型),然后我需要加载这个模型并调整顶层对吗?我尝试了2级cat-dog示例,模型的精确度在微调之前为98%,并且一旦我开始微调,就会增加(慢慢地)。但是,对于我的5个班级的案例,微调从20%开始。我检查了我加载的模型是我刚刚训练后的瓶颈功能。 @Sergii Gryshkevych - Austin Chen
以防万一:除了瓶颈以外,你是否冻结了所有转换层?没有看到代码就很难看出出了什么问题。 - Sergii Gryshkevych