Keras 图像分类:显示的准确度很高,但测试图像上的准确度很低
Posted
技术标签:
【中文标题】Keras 图像分类:显示的准确度很高,但测试图像上的准确度很低【英文标题】:Keras image classification: High accuracy shown but low on test images 【发布时间】:2020-02-09 04:07:21 【问题描述】:我正在尝试对 Caltech101 数据集进行一些图像分类。我在 Keras 中使用了几个预训练模型。我在训练集上使用了一些增强:
train_datagen = keras.preprocessing.image.ImageDataGenerator(
rescale=1./255, rotation_range=15,
width_shift_range=0.1,
height_shift_range=0.1,
shear_range=0.01,
zoom_range=[0.9, 1.25],
horizontal_flip=False,
vertical_flip=False,
fill_mode='reflect',
data_format='channels_last',
brightness_range=[0.5, 1.5])
validation_datagen = keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
train1_dir, # Source directory for the training images
target_size=(image_size, image_size),
batch_size=batch_size)
validation_generator = validation_datagen.flow_from_directory(
validation_dir, # Source directory for the validation images
target_size=(image_size, image_size),
batch_size=batch_size)
我还使用了一些提前停止(在 100 个 epoch 后停止):
es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=100)
mc = ModelCheckpoint('best_model_%s_%s.h5' % (dataset_name, model_name), monitor='val_acc', mode='max', verbose=1, save_best_only=True)
callbacks = [es, mc]
首先我训练最后一层:
base_model.trainable = False
model = tf.keras.Sequential([
base_model,
keras.layers.GlobalAveragePooling2D(),
keras.layers.Dense(num_classes, activation='softmax')
])
model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=0.0001),
loss='categorical_crossentropy',
metrics=['accuracy'])
epochs = 10000
steps_per_epoch = train_generator.n // batch_size
validation_steps = validation_generator.n // batch_size
history = model.fit_generator(train_generator,
steps_per_epoch = steps_per_epoch,
epochs=epochs,
workers=4,
validation_data=validation_generator,
validation_steps=validation_steps,
callbacks=callbacks)
然后我按照 Keras 教程训练前面的层:
# After top classifier is trained, we finetune the layers of the network
base_model.trainable = True
# Let's take a look to see how many layers are in the base model
print("Number of layers in the base model: ", len(base_model.layers))
# Fine tune from this layer onwards
fine_tune_at = 1
# Freeze all the layers before the `fine_tune_at` layer
for layer in base_model.layers[:fine_tune_at]:
layer.trainable = False
model.compile(optimizer = tf.keras.optimizers.RMSprop(lr=2e-5),
loss='categorical_crossentropy',
metrics=['accuracy'])
epochs = 10000
history_fine = model.fit_generator(train_generator,
steps_per_epoch = steps_per_epoch,
epochs=epochs,
workers=4,
validation_data=validation_generator,
validation_steps=validation_steps,
callbacks=callbacks
)
最后,在模型完成训练后,我在单独的测试集上手动对其进行测试
label_list = train_generator.class_indices
numeric_to_class =
for key, val in label_list.items():
numeric_to_class[val] = key
total_num_images = 0
acc_num_images = 0
with open("%s_prediction_%s.txt" % (dataset_name, model_name), "wt") as fid:
fid.write("Label list:\n")
for label in label_list:
fid.write("%s," % label)
fid.write("\n")
fid.write("true_class,predicted_class\n")
fid.write("--------------------------\n")
for label in label_list:
testing_dir = os.path.join(test_dir, label)
for img_file in os.listdir(testing_dir):
img = cv2.imread(os.path.join(testing_dir, img_file))
img_resized = cv2.resize(img, (image_size, image_size), interpolation = cv2.INTER_AREA)
img1 = np.reshape(img_resized, (1, img_resized.shape[0], img_resized.shape[1], img_resized.shape[2]))
pred_class_num = model.predict_classes(img1)
pred_class_num = pred_class_num[0]
true_class_num = label_list[label]
predicted_label = numeric_to_class[pred_class_num]
fid.write("%s,%s\n" % (label, predicted_label))
if predicted_label == label:
acc_num_images += 1
total_num_images += 1
acc = acc_num_images / (total_num_images * 1.0)
我必须这样做,因为库不输出 F1 分数。但是我发现 val_acc 上升得非常高(大约 0.8),但在训练后的测试阶段,准确度非常低(我认为大约 0.1)。我不明白为什么会这样。请帮帮我,非常感谢。
2019 年 15 月 10 日更新:我尝试在网络顶部训练一个线性 svm,而不进行任何微调,我使用 VGG16(使用 RMSProp 优化器)在 Caltech101 上获得了 70% 的准确度。但是我不确定这是否是最佳选择。
更新 2:我在我的自定义数据集上使用了 Daniel Moller 建议的预处理部分(大约 450 张图像,283 个“打开”类,203 个“关闭”类,并且在使用耐心=100 的早期停止时获得了这种准确性和损失,只训练最后一层:
model = tf.keras.Sequential([
base_model,
keras.layers.GlobalAveragePooling2D(),
keras.layers.Dense(num_classes, activation='softmax')
])
更新 3:我也尝试使用 VGG16 中的最后一个全连接层,并在每个层之后添加 dropout 层,dropout 率(设置为 0 的比率) 60%,耐心=10(提前停止):
base_model = tf.keras.applications.VGG16(input_shape=IMG_SHAPE, \
include_top=True, \
weights='imagenet')
base_model.layers[-3].Trainable = True
base_model.layers[-2].Trainable = True
fc1 = base_model.layers[-3]
fc2 = base_model.layers[-2]
predictions = keras.layers.Dense(num_classes, activation='softmax')
dropout1 = Dropout(0.6)
dropout2 = Dropout(0.6)
x = dropout1(fc1.output)
x = fc2(x)
x = dropout2(x)
predictors = predictions(x)
model = Model(inputs=base_model.input, outputs=predictors)
我得到了最高的验证准确度 0.93750,测试准确度:0.966216。图表:
【问题讨论】:
epoch的数量是不是有点太多了?为什么使用 averageglobalpooling 而不是 maxpooling?你不是有一个极度过度拟合的网络(因为也有这么多的时代)?尝试使用 dropout 和 l2 等正则化技术。 @CeliusStingher 嗨,如果 100 个 epoch 后没有改善,我会使用提前停止来停止,所以 epoch 的数量只是最大数量。根据 Keras 上的初学者教程,我使用了 averageglobalpooling。我认为如果训练和验证的准确性都很高,那么就可以了? 是的,如果两者都很高,那没关系,但是您之间的差异很大,这可能应该通过正则化技术来解决。 @CeliusStingher 但是如果两者都很高(> = 90%),那么它们之间怎么会有很大的区别呢? :) 如果您可以发布指标/损失的历史记录,那将会很有帮助。尽管您在这里有很多信息,但没有任何迹象表明可能有问题。 【参考方案1】:主要问题似乎在这里:
您在打开图像进行预测时忘记重新调整1/255.
:
.....
img = cv2.imread(os.path.join(testing_dir, img_file))
img_resized = cv2.resize(img, (image_size, image_size), interpolation = cv2.INTER_AREA)
img1 = np.reshape(img_resized,
(1, img_resized.shape[0], img_resized.shape[1], img_resized.shape[2]))
#you will probably need:
img1 = img1/255.
pred_class_num = model.predict_classes(img1)
...........
另外,请注意cv2
将以BGR
格式打开图像,而 Keras 可能以RGB
格式打开它们。
例子:
keras_train = train_generator[0][0] #first image from first batch
keras_val = validation_generator[0][0]
img = cv2.imread(os.path.join(testing_dir, img_file))
img_resized = cv2.resize(img, (image_size, image_size), interpolation = cv2.INTER_AREA)
img1 = np.reshape(img_resized,
(1, img_resized.shape[0], img_resized.shape[1], img_resized.shape[2]))
your_image = img1[0]/255. #first image from your batch rescaled
用matplotlib
绘制这些图像。
还要确保它们具有相同的范围:
plt.imshow(keras_train)
plt.plot()
plt.imshow(keras_val)
plt.plot()
plt.imshow(your_image)
plt.plot()
print(keras_train.max(), keras_val.max(), img1.max())
您可能需要使用np.flip(images, axis=-1)
将 BGR 转换为 RGB。
keras 模型导入提示
如果你从 keras 导入基础模型,你应该从同一个模块导入预处理,以及使用 Keras 图像打开器。这将消除可能的不匹配:
from keras.applications.resnet50 import ResNet50
from keras.preprocessing import image #use this instead of cv2
from keras.applications.resnet50 import preprocess_input #use this in the generators
在两个生成器中,使用预处理功能:
#no rescale, only preprocessing function
train_datagen = keras.preprocessing.image.ImageDataGenerator(
rotation_range=15,
width_shift_range=0.1,
height_shift_range=0.1,
shear_range=0.01,
zoom_range=[0.9, 1.25],
horizontal_flip=False,
vertical_flip=False,
fill_mode='reflect',
data_format='channels_last',
brightness_range=[0.5, 1.5],
preprocessing_function=preprocess_input)
validation_datagen = keras.preprocessing.image.ImageDataGenerator(
preprocessing_function=preprocess_input)
加载并预处理您的图像以进行预测:
img = image.load_img(img_path, target_size=(image_size,image_size))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
更多内容:https://keras.io/applications/
其他可能性
但可能还有其他一些事情,例如:
非常强的过拟合,你对early stopper的耐心是100!通常这是一个非常高的数字。但确认这一点的唯一方法是检查 val_acc 与最佳值相比是否上升太多。 建议:在开始预测之前重新加载ModelCheckpoint
保存的最佳模型
建议:贴出acc历史,看看有没有坏处
基础模型有BatchNormalization
层吗? - 当你冻结一个批量标准化层时,它会保持moving_mean
和moving_variance
不变,但是这些值是用不同的数据训练的。如果您的数据没有相同的均值和方差,您的模型将训练得很好,但验证将是一场灾难
建议:看看validation loss/acc是不是从一开始就错得离谱
建议:不要冻结基础模型中的BatchNormalization
层(迭代层并仅冻结其他类型)
建议:如果您碰巧知道用于训练基本模型的数据库,请将您的数据均值和变异与该数据库的均值和变异进行比较(分别处理每个通道)
【讨论】:
嗨。我确实尝试过除以 255,但是值会从 0 到 1,所以它不再是整数,所以我得到了一个错误。 嗯,你正在为训练重新缩放,你肯定必须为预测重新缩放。 好的,我会尽量做到的。 理想情况下,如果基本模型是从 keras 导入的,您还应该从导入模型的同一模块加载preprocess_input
。您无需重新缩放,而是对该模型进行必要的精确预处理(您需要在生成器和图像中使用相同的预处理)
@DangManhTruong,我更新了导入 keras 模型时如何使用 Keras 预处理的问题。以上是关于Keras 图像分类:显示的准确度很高,但测试图像上的准确度很低的主要内容,如果未能解决你的问题,请参考以下文章
使用Python+OpenCV+Keras创建自己的图像分类模型