TensorFlow CNN图像分类器所有图像均被判定为同一类别问题求助
问题排查与解决方案
先帮你定位几个最可能导致所有图像都被判定为Cubic的核心问题,按优先级逐一解决:
1. 测试阶段未做与训练一致的图像归一化(最关键)
你的模型在训练时,第一个层就是Rescaling(1./255),把输入图像的像素值从[0,255]缩放到了[0,1]。但在测试代码里,你加载图像后直接转成数组就输入模型,完全没做归一化处理!这会导致模型接收到的输入分布和训练时完全不一致,自然输出毫无意义的固定结果。
修复代码:在测试时给图像数组加入归一化步骤:
# 测试图像加载后添加这行 img_array = keras.preprocessing.image.img_to_array(img) img_array = img_array / 255.0 # 新增:和训练时保持一致的归一化 img_array = tf.expand_dims(img_array, 0)
2. 训练轮次严重不足
只训练3个epochs对于CNN来说远远不够,模型还没来得及学习到三类正弦曲线图像的特征,处于严重欠拟合状态,权重几乎没有有效更新,自然无法区分不同类别。
修复方案:
- 先把epochs提升到20-30,观察训练/验证的准确率变化:
epochs = 25 history = model.fit( train_ds, validation_data=val_ds, epochs=epochs )
- 可以加入早停机制,防止过拟合并保留最优权重:
from tensorflow.keras.callbacks import EarlyStopping early_stop = EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True) history = model.fit( train_ds, validation_data=val_ds, epochs=epochs, callbacks=[early_stop] )
3. 清理无关代码,避免数据路径干扰
你的代码里先加载了cifar10和flower_photos数据集,虽然后面覆盖了data_dir,但这些冗余代码可能导致混淆,建议直接删除:
# 删除以下无用代码块: # from keras.datasets import cifar10 # (x_train, y_train), (x_test, y_test) = cifar10.load_data() # dataset_url = "https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz" # data_dir = tf.keras.utils.get_file(origin = dataset_url, fname = "flower_photos", untar = True) # data_dir = pathlib.Path(data_dir)
4. 验证数据集的类别平衡性
如果你的数据集里Cubic类别的图像数量远多于Linear和Quadratic,模型会倾向于输出占比最高的类别(这也是固定输出的常见原因之一)。可以打印训练/验证集的类别分布确认:
import collections # 统计训练集类别数量 train_counts = collections.defaultdict(int) for _, labels in train_ds: for label in labels: train_counts[class_names[label.numpy()]] += 1 print("训练集类别分布:", dict(train_counts)) # 统计验证集类别数量 val_counts = collections.defaultdict(int) for _, labels in val_ds: for label in labels: val_counts[class_names[label.numpy()]] += 1 print("验证集类别分布:", dict(val_counts))
如果存在严重不平衡,可以通过过采样少数类、欠采样多数类或者数据增强来平衡数据集。
5. 可选:增强模型泛化能力
如果上述步骤后效果仍不理想,可以尝试:
- 增加卷积层的滤波器数量(比如把
Conv2D(32, 3)改成Conv2D(64, 3)) - 在Dense层前加入Dropout层防止过拟合:
model = tf.keras.Sequential([ tf.keras.layers.experimental.preprocessing.Rescaling(1./255), tf.keras.layers.Conv2D(64, 3, activation='relu'), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Conv2D(64, 3, activation='relu'), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Conv2D(64, 3, activation='relu'), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Flatten(), tf.keras.layers.Dropout(0.5), # 新增Dropout层 tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(num_classes) ])
- 加入数据增强,提升模型对不同图像的适应能力:
data_augmentation = tf.keras.Sequential([ tf.keras.layers.experimental.preprocessing.RandomFlip("horizontal"), tf.keras.layers.experimental.preprocessing.RandomRotation(0.1), tf.keras.layers.experimental.preprocessing.RandomZoom(0.1), ]) # 把数据增强加入模型开头 model = tf.keras.Sequential([ data_augmentation, tf.keras.layers.experimental.preprocessing.Rescaling(1./255), # 后续卷积、池化层保持不变... ])
先从归一化修复和增加训练轮次这两个步骤开始,这两个是最可能解决你当前问题的核心措施。
内容的提问来源于stack exchange,提问作者rb3652




