【问题标题】:vgg16 fine tuning dogs cats fails on testing datavgg16 微调狗猫在测试数据上失败
【发布时间】:2018-09-17 14:46:36
【问题描述】:

所以我一直在尝试微调 VGG16,以便对我创建的具有 4 个类别的数据集进行分类,但是虽然训练集和验证集的准确度提高得很好,但无论如何测试总是会产生 25% 的准确度我做了什么。我决定首先在 Kaggle 的猫狗数据集上微调 VGG16,然后我遵循了不同的教程,得到了很好的准确度结果。但是,这与我第一次遇到的问题相同。现在准确率是 50%(因为它是 2 个类别)。我开始认为这是一个 Keras VGG16 问题。我在网上尝试了所有不同的建议,包括 stackoverflow 中类似问题中的类似建议,但似乎没有任何效果。所有预处理、增强和层冻结似乎都进行得恰到好处,经过数周的试错,我发现自己不得不求助于您对此事的建议/建议。

这是我正在使用的全部代码:

from keras.models import Sequential, Model, load_model
from keras import applications
from keras import optimizers
from keras.layers import Dropout, Flatten, Dense
from keras.preprocessing.image import ImageDataGenerator
from sklearn.metrics import  classification_report,confusion_matrix
from keras.callbacks import ModelCheckpoint

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np

接下来是我使用的混淆矩阵函数实现。

def plot_confusion_matrix_two(cm,
                          target_names,
                          title='Confusion matrix',
                          cmap=None,
                          normalize=True):

import matplotlib.pyplot as plt
import numpy as np
import itertools

accuracy = np.trace(cm) / float(np.sum(cm))
misclass = 1 - accuracy

if 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() / 2
for 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()

使用 imagenet 权重调用 VGG16,没有顶层 + 冻结低层

img_rows, img_cols, img_channel = 224, 224, 3
base_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 layers

for 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 = False



model = 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 = 224

epochs = 500

train_batch = 50
valid_batch = 30
test_batch = 20

train_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 Report
predictions = 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% 取决于超参数调整)。所以我现在的猜测是,当我尝试使用混淆矩阵和报告函数进行分析时,出现了问题。不过我还是找不到问题。

【问题讨论】:

  • 请 1) 在 np.argmax() 操作 之前 发布您的 predictions 变量的样本 2) 确认您的标签确实是一次性编码的(如我的假设下面回答)
  • 看来问题与我正在使用的 scikit-learn 的混淆矩阵和分类报告有关。不过,我仍在努力弄清楚!也许我需要在函数调用中使用它们之前修改预测/标签

标签: machine-learning keras deep-learning neural-network computer-vision


【解决方案1】:

对我创建的具有 4 个类的数据集进行分类

对于初学者来说,对于 4 类分类问题,您的网络配置似乎很奇怪(至少可以这么说);第一个建议:

  1. 在您的模型编译中更改为loss='categorical_crossentropy'

  2. 将最后一层更改为

add_model.add(Dense(4, activation='softmax'))

由于您的predictions = np.argmax(predictions, axis=1) 行,我假设您已经在使用单热编码标签...

【讨论】:

  • 感谢您的建议!!事实上,我的真正目的是对 4 个类别进行分类,但由于微调失败,我尝试在 2 个类别上使用“狗和猫”数据集,但仍然失败。所以现在我正试图让它在 2 个类上工作(这解释了我的配置)。对不起,如果我的问题不清楚
  • @OsumanAAA 建议仍然有效;对于二进制分类,最后一层是Dense(2, activation='softmax'))(用于单热编码标签)或Dense(1, activation='sigmoid'))...
  • 谢谢!!我将按照您的建议使用“Dense(2, activation='softmax'))”再次运行,并按照您的建议获取预测样本。这需要一些时间,但我会在完成培训后发布。
  • @OsumanAAA 好的,对于二元分类,请保留您的 loss='binary_crossentropy' 原样;在更一般的层面上,由于您的问题已经涉及 很多 代码,请确保在代码、问题、问题等方面保持一致......
  • 由于我使用了 Dense(2, activation='softmax')),我认为使用 loss=''categorical_crossentropy'' 更有意义。那是错的吗?当然 !!一旦我尝试了您的建议,我将再次编辑我的问题!非常感谢!!
【解决方案2】:

model = Model(inputs=base_model.input, outputs=add_model(base_model.output)) 您未能正确添加新图层。只需使用函数式 API。

out = base_model.output
out = Flatten()(out)
out = Dense(256, activation='relu')(out)
out = Dropout(0.5)(out)
out = Dense(2, activation='softmax')(out)
for layer in add_model.layers[:-3]:
    layer.trainable = False
model = Model(inputs=base_model.input, outputs=out)

【讨论】:

  • 你确定吗?我打印了模型的摘要,在我看来并没有错。
  • 我刚刚使用了你上面的代码,添加新层的逻辑显然是错误的,你没有在修改后的模型中添加密集层、辍学层和softmax层。
猜你喜欢
  • 2020-03-15
  • 1970-01-01
  • 2017-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-30
  • 2017-10-13
  • 2018-08-20
相关资源
最近更新 更多