我一直在尝试对VGG16进行微调,以对一个我自己创建的包含4个类别的数据集进行分类。虽然训练集和验证集的准确率都有显著提高,但无论我做什么,测试集的准确率始终保持在25%。因此,我决定首先使用Kaggle上的猫狗数据集对VGG16进行微调,并按照一些教程的步骤操作,这些教程都取得了不错的准确率结果。然而,我遇到了与第一次相同的问题。现在准确率是50%(因为只有两个类别)。我开始认为这是Keras VGG16的问题。我尝试了网上所有不同的建议,包括StackOverflow上类似问题的建议,但似乎没有任何效果。所有预处理、数据增强和层冻结似乎都已正确完成,经过几周的反复试错后,我不得不求助于你们的建议和意见。
这是我使用的完整代码:
from keras.models import Sequential, Model, load_modelfrom keras import applicationsfrom keras import optimizersfrom keras.layers import Dropout, Flatten, Densefrom keras.preprocessing.image import ImageDataGeneratorfrom sklearn.metrics import classification_report,confusion_matrixfrom keras.callbacks import ModelCheckpoint%matplotlib inlineimport matplotlib.pyplot as pltimport numpy as np
以下是我使用的混淆矩阵函数实现:
def plot_confusion_matrix_two(cm, target_names, title='Confusion matrix', cmap=None, normalize=True):import matplotlib.pyplot as pltimport numpy as npimport itertoolsaccuracy = np.trace(cm) / float(np.sum(cm))misclass = 1 - accuracyif cmap is None: cmap = plt.get_cmap('Blues')plt.figure(figsize=(8, 6))plt.imshow(cm, interpolation='nearest', cmap=cmap)plt.title(title)plt.colorbar()if target_names is not None: tick_marks = np.arange(len(target_names)) plt.xticks(tick_marks, target_names, rotation=45) plt.yticks(tick_marks, target_names)if normalize: cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]thresh = cm.max() / 1.5 if normalize else cm.max() / 2for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])): if normalize: plt.text(j, i, "{:0.4f}".format(cm[i, j]), horizontalalignment="center", color="white" if cm[i, j] > thresh else "black") else: plt.text(j, i, "{:,}".format(cm[i, j]), horizontalalignment="center", color="white" if cm[i, j] > thresh else "black")plt.tight_layout()plt.ylabel('True label')plt.xlabel('Predicted label\naccuracy={:0.4f}; misclass={:0.4f}'.format(accuracy, misclass))plt.show()
调用VGG16使用imagenet权重,不带顶层并冻结低层:
img_rows, img_cols, img_channel = 224, 224, 3base_model = applications.VGG16(weights='imagenet', include_top=False, input_shape=(img_rows, img_cols, img_channel))for layer in base_model.layers[:-4]: layer.trainable = False# check the trainable status of the individual layersfor layer in base_model.layers: print(layer, layer.trainable)
添加用于分类我们数据的最后几层并编译模型:
add_model = Sequential()add_model.add(Flatten(input_shape=base_model.output_shape[1:]))add_model.add(Dense(256, activation='relu'))add_model.add(Dropout(0.5))add_model.add(Dense(2, activation='softmax'))for layer in add_model.layers[:-3]: layer.trainable = Falsemodel = Model(inputs=base_model.input, outputs=add_model(base_model.output))model.compile(loss='categorical_crossentropy', optimizer=optimizers.SGD(lr=1e-4, momentum=0.9), metrics=['accuracy'])model.summary()
训练参数、路径等…
image_size = 224epochs = 500train_batch = 50valid_batch = 30test_batch = 20train_dir = 'D:/PetImages/train'valid_dir = 'D:/PetImages/valid'test_dir = 'D:/PetImages/test'
用于从不同数据集读取数据的数据生成器。在我的情况下,数据集位于不同的文件夹中,因此不需要将训练集和验证集分开。
train_datagen = ImageDataGenerator( rescale=1./255, rotation_range=20, width_shift_range=0.2, height_shift_range=0.2, horizontal_flip=True, #vertical_flip=True, fill_mode='nearest')validation_datagen = ImageDataGenerator(rescale=1./255)test_datagen = ImageDataGenerator(rescale=1./255)train_generator = train_datagen.flow_from_directory( train_dir, target_size=(image_size, image_size), batch_size=train_batch, class_mode='categorical', shuffle=True)validation_generator = validation_datagen.flow_from_directory( valid_dir, target_size=(image_size, image_size), batch_size=valid_batch, class_mode='categorical', shuffle=True)test_generator = test_datagen.flow_from_directory( test_dir, target_size=(image_size, image_size), batch_size=test_batch, class_mode='categorical', shuffle=True)
训练模型:
history = model.fit_generator( train_generator, steps_per_epoch=train_generator.samples // train_generator.batch_size, epochs=epochs, validation_data=validation_generator, validation_steps=validation_generator.samples // validation_generator.batch_size, #callbacks=[ModelCheckpoint('VGG16-transferlearning.model', monitor='val_acc', save_best_only=True)] verbose=1)
然后在测试集上进行预测,以与真实值进行比较并获取准确率等信息:
predictions = model.predict_generator(test_generator, steps=test_generator.samples//test_generator.batch_size, verbose=0)#Confution Matrix and Classification Reportpredictions = np.argmax(predictions, axis=1)print('Confusion Matrix')cm = confusion_matrix(test_generator.classes, predictions)#print(cm)target_names =['cats', 'dogs']#target_names =['Bark', 'Jump','Stand', 'Walk']plot_confusion_matrix_two(cm, target_names, title='Confusion Matrix',cmap=None,normalize=False)print('Classification Report')print(classification_report(test_generator.classes, predictions, target_names=target_names))print('Confusion Matrix')print(cm)
我真的尝试了所有不同的技巧来检查。我甚至尝试检查模型在训练数据上的表现,而不是测试数据,结果仍然是50%(考虑到训练准确率几乎达到99%,这非常奇怪)。我尝试调整超参数,不同的算法,但仍然没有任何变化。
系统:Windows 10, Anaconda, Keras 2.1.1 Tensorflow-gpu 1.4.0 Python 3.6.4
使用的数据集:https://files.fm/u/t6zdskc7
我现在已经卡在这里几个星期了,真的很沮丧。如果有人能帮助我,我将非常感激!!
编辑:
在四处询问后,有人指出模型实际上是能够学习的,我可以使用以下代码检查预测的准确率:
x, y = zip(*(test_generator[i] for i in range(len(test_generator))))x_test, y_test = np.vstack(x), np.vstack(y)loss, acc = model.evaluate(x_test, y_test, batch_size=64)print("Accuracy: ", acc)print("Loss: ",loss)
结果显示我确实得到了一个合理的结果(根据超参数调整,大约70%)。所以我现在猜测,当我尝试使用混淆矩阵和报告函数进行分析时,出了什么问题。不过,我仍然无法找到问题所在。
回答: