You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Keras与SKLearn计算的图像分类模型准确率存在差异?

二分类模型评估准确率不一致问题分析

问题背景

训练了一个图像二分类线性模型,使用model.evaluate在测试集上得到73.33%的准确率,但手动用sklearn计算混淆矩阵得到的准确率仅为47.33%,两者差异极大。

模型编译参数:

model.compile(
    optimizer=keras.optimizers.SGD(learning_rate=0.1),
    loss=keras.losses.CategoricalCrossentropy(),
    metrics=['accuracy']
)

测试集加载代码:

dataset = keras.utils.image_dataset_from_directory(
      '/content/test-dataset-2',
      image_size=(512, 384),
      labels='inferred',
      label_mode='categorical',
).map(preprocess)

手动计算准确率的代码:

from tensorflow.keras.utils import to_categorical
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay, classification_report
import numpy as np

def get_labels(arr):
  return np.argmax(arr, axis=1)

# 冗余操作
predictions = to_categorical(np.argmax(model.predict(dataset), axis=1), 2)
y = np.concatenate([y for x, y in dataset], axis=0)

cm = confusion_matrix(get_labels(y), get_labels(predictions))
cm_display = ConfusionMatrixDisplay(cm).plot()

错误原因排查

1. 类别索引错位(最可能)

keras.utils.image_dataset_from_directory会按文件夹名称的字典序自动分配类别索引(例如文件夹class_a和class_b会分别对应索引0和1)。如果训练集与测试集的文件夹名称/顺序不一致,会直接导致模型预测的类别索引与测试集的标签索引完全错位:

  • 例:训练集文件夹为['cat', 'dog'](cat=0、dog=1),测试集文件夹为['dog', 'cat'](dog=0、cat=1)
  • 此时模型预测的0对应cat,但测试集标签的0对应dog,混淆矩阵会把所有正确预测判定为错误,准确率直接反转。

2. 冗余操作增加出错风险

代码中to_categorical(np.argmax(model.predict(dataset), axis=1), 2)属于完全冗余的操作:

  • model.predict(dataset)已输出二分类概率分布(shape为(样本数, 2))
  • np.argmax(..., axis=1)已得到类别索引(0或1),可直接传入confusion_matrix,无需转独热编码后再转回索引。这一步虽不直接导致错误,但会增加代码复杂度,隐藏潜在问题。

3. 预处理函数不一致

如果preprocess函数在训练和测试阶段行为不同(例如训练时用了随机翻转、特定归一化逻辑,测试时未执行或参数不符),会导致模型预测结果失真,若预处理差异引发标签与预测的对应错位,也会出现准确率不一致的情况。

4. 数据集批次处理差异

model.evaluate与model.predict处理tf.data.Dataset的默认逻辑一致,但如果测试集加载时设置了shuffle=True(用户代码中未设置,默认False),会导致标签与预测结果顺序不匹配,不过该可能性较低。

验证与修正方案

步骤1:对齐训练集与测试集的类别索引

打印训练集和测试集的类别名称,确认顺序是否一致:

# 打印训练集类别
train_dataset = keras.utils.image_dataset_from_directory("训练集路径")
print("训练集类别:", train_dataset.class_names)
# 打印测试集类别
print("测试集类别:", dataset.class_names)

若顺序不一致,加载测试集时强制指定类别顺序:

dataset = keras.utils.image_dataset_from_directory(
      '/content/test-dataset-2',
      image_size=(512, 384),
      labels='inferred',
      label_mode='categorical',
      class_names=train_dataset.class_names  # 与训练集类别顺序对齐
).map(preprocess)

步骤2:简化手动计算代码

去掉冗余的独热编码转换,直接使用类别索引计算:

import numpy as np
from sklearn.metrics import confusion_matrix, classification_report

# 获取模型预测的类别索引
y_pred = np.argmax(model.predict(dataset), axis=1)
# 获取测试集真实类别索引
y_true = np.concatenate([np.argmax(y, axis=1) for x, y in dataset], axis=0)

# 计算混淆矩阵和分类报告
cm = confusion_matrix(y_true, y_pred)
print(classification_report(y_true, y_pred))
ConfusionMatrixDisplay(cm).plot()

步骤3:确保预处理函数一致

测试集的preprocess函数必须与训练集完全相同,例如训练时使用了特定的归一化逻辑,测试时不能省略或修改参数。

步骤4:检查模型输出合理性

损失值高达56明显异常,二分类任务的CategoricalCrossentropy损失正常范围通常在0~10之间,建议检查:

  • 模型输出层是否使用了softmax激活(配合CategoricalCrossentropy需要输出概率分布)
  • 训练时的标签格式是否与测试集一致(均为categorical格式)

结论

最可能的原因是训练集与测试集的类别索引顺序不匹配,导致手动计算准确率时标签与预测结果错位。修正类别索引对齐后,手动计算的准确率应与model.evaluate的结果一致。

内容的提问来源于stack exchange,提问作者Allan Juan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 23:27:45