我知道这个问题之前已经被问过,但我已经尝试了所有他们的解决方案,但对我来说都没有效果。
我的问题:
我正在运行一个卷积神经网络(CNN)来对一些图像进行分类,这是一个典型的任务,没有什么特别的。我的模型编译如下:
model.compile(optimizer = keras.optimizers.Adam(learning_rate = exp_learning_rate), loss = tf.keras.losses.SparseCategoricalCrossentropy(), metrics = ['accuracy'])
我用训练数据集进行拟合,并在验证数据集上进行评估,如下所示:
history = model.fit(train_dataset, validation_data = validation_dataset, epochs = 5)
然后我在一个单独的测试集上进行评估,如下所示:
model.evaluate(test_dataset)
结果如下:
4/4 [==============================] – 30s 7s/step – loss: 1.7180 – accuracy: 0.8627
然而,当我运行以下代码时:
model.predict(test_dataset)
我得到了如下的混淆矩阵输出:
这显然不是像 .evaluate 方法告诉我的86%的准确率。实际上,它的准确率只有35.39%。为了确保不是测试数据集的问题,我让模型在我的训练和验证数据集上进行预测,结果仍然得到了与这里相似的百分比(约30%),尽管我在拟合过程中训练和验证的准确率分别达到了96%和87%。
问题:
我不知道为什么 .predict 和 .evaluate 会输出不同的结果?这是怎么回事?看起来当我调用 .predict 时,它并没有使用我在拟合过程中训练的任何权重?(事实上,考虑到有3个类别,这个输出还不如盲目猜测每个标签好)。我的拟合权重没有转移到我的预测中吗?我的损失函数是正确的(我对数据进行了标签编码,正如tensorflow希望与sparse_categorical_crossentropy一起使用),并且当我传递’accuracy’时,它只会取与我的损失函数对应的准确率。所有这些都应该是一致的。但为什么 .evaluate 和 .predict 的结果会有这么大的差异?我应该相信哪一个?
我尝试解决我的问题:
我以为可能是稀疏分类交叉熵不对,所以我对目标标签进行了独热编码,并使用了categorical_crossentropy损失函数。我仍然遇到了上面完全相同的问题。
担忧:
如果 .evaluate 不正确,那么这不意味着我的训练准确率和验证准确率在拟合过程中也是不准确的吗?那些不也使用 .evaluate 方法吗?如果是这种情况,那么我能相信什么?损失并不是衡量我的模型表现好的好指标,因为众所周知,最小损失并不意味着好的准确率(尽管反过来通常是真的,这取决于我们使用的“好”的标准)。如果我的准确率指标不正确,我如何评估我的模型的有效性?我真的不知道该看什么了,因为我没有其他方法来判断我的模型是否在学习,如果有人能帮我理解这是怎么回事,我会非常感激的。我感到非常沮丧。
编辑:(2021-10-28:00:26)
好的,所以我将提供更多的代码来真正排查这个问题。
我最初对数据进行预处理如下:
image_size = (256, 256)batch_size = 16train_ds = keras.preprocessing.image_dataset_from_directory( directory = image_directory, label_mode = 'categorical', shuffle = True, validation_split = 0.2, subset = 'training', seed = 24, batch_size = batch_size)val_ds = keras.preprocessing.image_dataset_from_directory( directory = image_directory, label_mode = 'categorical', shuffle = True, validation_split = 0.2, subset = 'validation', seed = 24, batch_size = batch_size)
其中 image_directory 是一个包含我的图像的路径的字符串。现在你可能已经阅读了文档,但 image_dataset_from_directory 方法实际上返回一个包含一堆批次的相应(训练、验证)数据的 tf.data.Dataset 对象。
我导入了 VGG16 架构来进行我的分类,所以我调用了 VGG16 的相应预处理函数如下:
preprocess_input = tf.keras.applications.vgg16.preprocess_inputtrain_ds = train_ds.map(lambda x, y: (preprocess_input(x), y))val_ds = val_ds.map(lambda x, y: (preprocess_input(x), y))
这将图像转换成了适合作为 VGG16 输入的格式。然后,在我的最后一步处理中,我进行了以下验证/测试分割:
val_batches = tf.data.experimental.cardinality(val_ds)test_dataset = val_ds.take(val_batches // 5)validation_dataset = val_ds.skip(val_batches // 5)
然后我继续缓存并预取我的数据:
AUTOTUNE = tf.data.AUTOTUNEtrain_dataset = train_ds.cache().prefetch(buffer_size=AUTOTUNE)validation_dataset = validation_dataset.cache().prefetch(buffer_size=AUTOTUNE)test_dataset = test_dataset.cache().prefetch(buffer_size=AUTOTUNE)
问题:
问题出现在上面的方法中。我仍然不确定 .evaluate 是否是我的模型准确性的真实指标。但是我发现当我的神经网络是 keras.Sequential() 模型时,.evaluate 和 .predict 总是吻合的。然而,(如果我错了请纠正我)我怀疑的是,从 keras.applications API 导入的 VGG16 实际上不是一个 keras.Sequential() 模型。因此,我认为当我直接将数据输入我的模型时,.predict 和 .evaluate 的结果实际上并不吻合(我本来想把这个作为答案发布,但我没有足够的知识和研究来确认我说的任何一点是正确的,请有人加入讨论,因为我喜欢学习我几乎一无所知的东西,这是一个临时的编辑)。
最后,我通过调用 ImageDataGenerator() 而不是 image_dataset_from_directory() 来绕过我的问题,如下所示:
train_datagen = ImageDataGenerator( preprocessing_function=preprocess_input, rescale=1./255, rotation_range=20, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)test_datagen = ImageDataGenerator(preprocessing_function=preprocess_input, rescale=1./255)train_generator = train_datagen.flow_from_directory( train_dir, target_size=(img_height, img_width), batch_size=batch_size, class_mode='categorical')validation_generator = test_datagen.flow_from_directory( validation_dir, target_size=(img_height, img_width), batch_size=batch_size, class_mode='categorical')
(注意:我根据以下链接从 tensorflow 的文档中得到了这个:https://www.tensorflow.org/api_docs/python/tf/keras/preprocessing/image/ImageDataGenerator)
这完成了我所有的预处理。然后,当我调用 model.evaluate(test_generator) 时,它返回的结果与我调用 model.predict(test_generator) 时完全相同。经过一些对预测输出的轻微处理后,我使用以下代码来计算我的混淆矩阵:
Y_pred = model.predict(test_generator)y_pred = np.argmax(Y_pred, axis=1)from sklearn.metrics import confusion_matrixcm = confusion_matrix(test_generator.classes, y_pred)print(cm)
这消除了混淆矩阵和 model.evaluate(test_generator) 结果之间的差异。
结论:
如果你正在将图像加载到分类模型中,并且你的损失和准确率匹配,但你在预测和损失、准确率之间存在差异,请尝试以各种可能的方式进行预处理。我通常使用 image_dataset_from_directory() 方法来预处理我所有的 keras.sequential() 模型的图像,然而,对于 VGG16 模型,我怀疑它不是一个 sequential() 模型,使用 ImageDataGenerator(…).flow_from_directory(…) 可以为模型生成与性能指标一致的预测结果。
TLDR 我没有回答我最初的任何问题,但我找到了一个解决方法。如果这在任何方面是垃圾信息,我深感抱歉。正如大多数 Stack Overflow 帖子一样,我希望我在过去几个小时的挣扎能在未来帮助某人。
回答:
我遇到了同样的问题。即使使用了 ImageDataGenerator,这种奇怪的行为仍然存在。
但我认为问题出在验证集的 shuffle 标志上。
你从这里改变了它:
val_ds = keras.preprocessing.image_dataset_from_directory( directory=image_directory, label_mode='categorical', shuffle=True, validation_split=0.2, subset='validation', seed=24, batch_size=batch_size)
到这里:
test_ds = val_datagen.flow_from_directory( test_image_directory, target_size=(224, 224), batch_size=16, seed=24, shuffle=False, classes=['class1', 'class2', 'class3'], class_mode='categorical')