多类分类:验证集准确率高,测试集预测准确

Posted

技术标签:

【中文标题】多类分类:验证集准确率高,测试集预测准确【英文标题】:Multi-class classification: good accuracy on validation set but prediction on test set 【发布时间】:2019-08-20 08:42:52 【问题描述】:

我正在尝试对属于 16 个类别的图像进行分类。这些图像具有不同的几何形状 (see Fig. 2)。训练集包含 16 x 320 = 5120 张图片,验证集包含 16 x 160 = 2560 张图片,测试集包含 16 x 2 = 32 张图片。

我正在使用以下代码构建 CNN 并进行预测。

import numpy as np
np.random.seed(0)

import keras
from keras.models import Sequential,Input,Model
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.layers.normalization import BatchNormalization
from keras.layers.advanced_activations import LeakyReLU 
from keras import regularizers
from keras.layers import Activation

num_classes = 16
classifier = Sequential()
classifier.add(Conv2D(32, kernel_size=(3, 3),activation='relu',input_shape=(64, 64, 3),padding='same'))
classifier.add(MaxPooling2D((2, 2),padding='same'))

classifier.add(Dropout(0.2))

classifier.add(Conv2D(64, (3, 3), activation='relu',padding='same'))
#classifier.add(LeakyReLU(alpha=0.1))
classifier.add(MaxPooling2D(pool_size=(2, 2),padding='same'))

classifier.add(Dropout(0.2))

classifier.add(Conv2D(64, (3, 3), activation='relu',padding='same'))
classifier.add(MaxPooling2D(pool_size=(2, 2),padding='same'))

classifier.add(Dropout(0.25))

classifier.add(Conv2D(128, (3, 3), activation='relu',padding='same'))                 
classifier.add(MaxPooling2D(pool_size=(2, 2),padding='same'))

classifier.add(Dropout(0.25))

classifier.add(Flatten())
classifier.add(Dense(128, activation='relu'))    

classifier.add(Dropout(0.25))

classifier.add(Dense(num_classes, activation='softmax'))


# Compiling the CNN
classifier.compile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['accuracy'])

from keras.preprocessing.image import ImageDataGenerator
from IPython.display import display
from PIL import Image

train_datagen = ImageDataGenerator(rescale = 1./255,
                                   shear_range = 0.2,
                                   zoom_range = 0.2,
                                   width_shift_range=0.1, 
                                   height_shift_range=0.1)                              

test_datagen = ImageDataGenerator(rescale = 1./255)

training_set = train_datagen.flow_from_directory('dataset/training_set',
                                                 target_size = (64, 64),
                                                 batch_size = 32,
                                                 class_mode = 'categorical')

test_set = test_datagen.flow_from_directory('dataset/test_set',
                                            target_size = (64, 64),
                                            batch_size = 32,
                                            class_mode = 'categorical')

from keras.callbacks import ModelCheckpoint
from keras.callbacks import EarlyStopping

STEP_SIZE_TRAIN = training_set.n//training_set.batch_size
STEP_SIZE_TEST = test_set.n//test_set.batch_size

early_stopping_callback = EarlyStopping(monitor='val_loss', patience=3)
checkpoint_callback = ModelCheckpoint('model' + '.h5', monitor='val_loss', verbose=1, save_best_only=True, mode='min')

classifier.fit_generator(training_set,
                    steps_per_epoch = STEP_SIZE_TRAIN,
                    epochs = 10,
                    validation_data = test_set,
                    validation_steps = STEP_SIZE_TEST,
                    callbacks=[early_stopping_callback, checkpoint_callback],
                    workers = 32)

from keras.models import load_model
model = load_model('model.h5')


# Part 3 - making new predictions
import numpy as np
from keras.preprocessing import image
for i in range(1,33):
    test_image = image.load_img('dataset/single_prediction/Image ' + str(i) +'.bmp', target_size = (64, 64))
    test_image = image.img_to_array(test_image)
    test_image = np.expand_dims(test_image, axis = 0)
    #print(model.predict(test_image)[0])
    print(model.predict(test_image)[0].argmax()+1)

我得到以下训练和验证准确性和损失的结果。

Epoch 1/10
160/160 [==============================] - 29s 179ms/step - loss: 1.3693 - acc: 0.5299 - val_loss: 0.1681 - val_acc: 0.9297

Epoch 00001: val_loss improved from inf to 0.16809, saving model to model.h5
Epoch 2/10
160/160 [==============================] - 18s 112ms/step - loss: 0.2668 - acc: 0.8984 - val_loss: 0.0773 - val_acc: 0.9699

Epoch 00002: val_loss improved from 0.16809 to 0.07725, saving model to model.h5
Epoch 3/10
160/160 [==============================] - 18s 111ms/step - loss: 0.1469 - acc: 0.9482 - val_loss: 0.0133 - val_acc: 1.0000

Epoch 00003: val_loss improved from 0.07725 to 0.01327, saving model to model.h5
Epoch 4/10
160/160 [==============================] - 18s 111ms/step - loss: 0.0990 - acc: 0.9650 - val_loss: 0.0147 - val_acc: 1.0000

Epoch 00004: val_loss did not improve from 0.01327
Epoch 5/10
160/160 [==============================] - 18s 113ms/step - loss: 0.0700 - acc: 0.9740 - val_loss: 7.3014e-04 - val_acc: 1.0000

Epoch 00005: val_loss improved from 0.01327 to 0.00073, saving model to model.h5
Epoch 6/10
160/160 [==============================] - 18s 114ms/step - loss: 0.0545 - acc: 0.9809 - val_loss: 0.0012 - val_acc: 1.0000

Epoch 00006: val_loss did not improve from 0.00073
Epoch 7/10
160/160 [==============================] - 18s 111ms/step - loss: 0.0374 - acc: 0.9865 - val_loss: 0.0101 - val_acc: 1.0000

Epoch 00007: val_loss did not improve from 0.00073
Epoch 8/10
160/160 [==============================] - 18s 111ms/step - loss: 0.0489 - acc: 0.9832 - val_loss: 0.0200 - val_acc: 0.9992

当试图在测试集的 32 张图像上测试模型时,我只得到了 3 个正确的预测。所以我的问题是:

1) 为什么我的验证准确度很高,但模型在测试集上却失败了?

2) 我如何显示验证集的随机样本(例如 10 张图像)及其预测类别,以了解 CNN 在验证集上的表现?

3) 关于如何提高测试集准确率的一般提示?

感谢任何帮助! 非常感谢:)

【问题讨论】:

validation acc 在某些时期为 1。数据从火车泄漏到有效的可能性。 @Sreeram TP :你对如何解决这个问题有想法吗? 这 3 个数据集是独立的。我的意思是,图像是完全随机分配给每个图像的?如果是,另一种可能性可能是验证集上的过度拟合。尝试在验证集中使用很少的图像,看看它是如何工作的。然后逐渐增加它的大小,看看你是否达到了测试集准确度降低而验证准确度增加的地步。另外,您可以尝试提前停止。 @DavideVisentin:我在我的代码中使用了提前停止。问题是我有点新手,我不知道样本数量是否足以用于训练和验证。 “图像完全随机分配给每个图像”是什么意思?我有三个文件夹,我将图像随机放入每个文件夹中。这有意义吗? 对第二个问题有帮助吗?谢谢:) 【参考方案1】:

发生这种情况的原因有很多,但我将解决一个问题,即训练集、验证集和测试集之间的数据分布差异。

在理想情况下,您的训练集、验证集和测试集应该来自同一个分布。虽然这是一个概念,但在实践中,它不仅意味着每个类的数据点数量相同,而且还意味着许多其他维度。这样的维度可能是每次分割的图像质量,即你应该避免在你的训练集和有效集中有高质量的图像,而在测试集中有低质量的图像。而且还有更多这样的维度,理想情况下分布应该是相同的。

因此,这是一场机会游戏,并且您希望避免由于运气不佳而发生这样的事件,无论这种事件的可能性有多大,最终得到一个与其他测试集不同的测试集的分裂。

要克服这个问题:

1) 在拆分前对数据进行洗牌时选择不同的种子

2) 为您的测试集和验证集选择相等的分割大小

3) 使用 k 折交叉验证

您可以单独或组合执行上述一个或任何步骤。您可以在此处阅读更多信息:

https://cs230-stanford.github.io/train-dev-test-split.html

希望对你有帮助

【讨论】:

以上是关于多类分类:验证集准确率高,测试集预测准确的主要内容,如果未能解决你的问题,请参考以下文章

Keras 函数模型验证准确率高,但预测不正确

Keras 图像分类 - 验证数据集的预测准确性与 val_acc 不匹配

为多类分类留一出准确率

分类预测算法评价(初识)

为啥交叉验证结果显示高准确率而存在过度拟合?

评价分类与预测算法的指标