TensorFlow脑肿瘤CNN模型与Kaggle同功能模型表现差异排查求助
脑肿瘤分类CNN模型问题排查:数据管道差异分析
核心问题
使用tf.keras.utils.image_dataset_from_directory替代原Kaggle自定义数据管道后,模型出现异常:所有样本预测同一值,但评估显示90%准确率,与原模型表现差距极大,核心问题集中在数据管道的多个关键差异上。
关键差异与问题点
1. 验证/测试集的使用逻辑错误
- 你的代码:将独立的
Testing目录直接作为验证集使用,且训练阶段未传入任何验证数据(model.fit无validation_data参数);同时从Training目录拆分出的20%验证集被完全闲置。 - 原代码:用
Training全量数据训练,Testing仅作为最终测试集,训练过程无额外验证集,但自定义生成器仅对训练数据做增强。 - 影响:所谓的“90%验证准确率”实际是测试集准确率,若数据集存在类别不平衡(某类样本占比90%),模型只需全预测该类就能得到高准确率,但完全丧失分类能力。
2. 数据增强的时机与实现错误
- 你的代码:将增强层嵌入模型,导致验证/测试阶段也会应用随机增强(如
RandomBrightness),且Rescaling(1./255)放在增强之后——原始图像为0-255范围,增强后像素值可能超出范围,再做归一化会破坏数据分布。 - 原代码:仅在训练阶段的自定义生成器中做增强,验证/测试时无增强;且基于0-255图像做增强后再归一化到0-1。
- 影响:测试数据的随机变换会导致评估结果不可靠,数据分布异常则会让模型无法学习有效特征,最终只能偏向占比最高的类别。
3. 预训练模型的预处理缺失
- 你的代码:仅用
Rescaling(1./255)做归一化,未遵循VGG16预训练时的输入要求(需将像素值转换为符合ImageNet数据集的分布,通常是减去均值并缩放)。 - 原代码:虽未显式使用VGG16的预处理函数,但自定义的归一化逻辑(除以255)与原模型训练时的输入更匹配。
- 影响:VGG16的预训练特征无法有效复用,模型只能从头学习,且数据分布不匹配会导致特征提取失效,最终出现随机预测或偏向多数类的情况。
4. 类别编码顺序不一致
- 你的代码:
image_dataset_from_directory默认按字母顺序编码类别。 - 原代码:按
os.listdir(train_dir)的系统遍历顺序编码类别。 - 影响:若两类编码顺序不同,模型预测的索引会对应错误的类别,进一步干扰准确率的判断,甚至让你误以为模型有效。
修复建议
1. 修正验证集与测试集的使用
从Training目录拆分出验证集用于训练监控,Testing目录仅作为最终测试:
# 训练集(80%) train_ds = tf.keras.preprocessing.image_dataset_from_directory( "C:\\data\\MRI Images\\Training", labels="inferred", color_mode="rgb", batch_size=BATCH_SIZE, seed=123, image_size=(IMG_SIZE, IMG_SIZE), subset="training", validation_split=0.2 ) # 验证集(20%,来自Training目录) val_ds = tf.keras.preprocessing.image_dataset_from_directory( "C:\\data\\MRI Images\\Training", labels="inferred", color_mode="rgb", batch_size=BATCH_SIZE, seed=123, image_size=(IMG_SIZE, IMG_SIZE), subset="validation", validation_split=0.2 ) # 独立测试集 test_ds = tf.keras.preprocessing.image_dataset_from_directory( "C:\\data\\MRI Images\\Testing", labels="inferred", color_mode="rgb", batch_size=BATCH_SIZE, seed=123, image_size=(IMG_SIZE, IMG_SIZE) )
2. 调整数据增强的应用时机
将增强移出模型,仅在训练阶段应用,同时使用VGG16的官方预处理:
# 定义增强层(仅训练时使用) data_augmentation = Sequential([ layers.RandomBrightness(0.2), layers.RandomContrast(0.2), ]) # 训练数据:预处理+增强 def train_preprocess(image, label): image = tf.keras.applications.vgg16.preprocess_input(image) image = data_augmentation(image, training=True) return image, label # 验证/测试数据:仅预处理 def val_test_preprocess(image, label): image = tf.keras.applications.vgg16.preprocess_input(image) return image, label train_ds = train_ds.map(train_preprocess, num_parallel_calls=tf.data.AUTOTUNE) val_ds = val_ds.map(val_test_preprocess, num_parallel_calls=tf.data.AUTOTUNE) test_ds = test_ds.map(val_test_preprocess, num_parallel_calls=tf.data.AUTOTUNE)
3. 修正模型结构
移除模型内的增强层,确保输入符合VGG16要求:
base_model = VGG16(input_shape=(IMG_SIZE, IMG_SIZE, 3), weights='imagenet', include_top=False) for layer in base_model.layers: layer.trainable = False # 解冻最后3层 base_model.layers[-2].trainable = True base_model.layers[-3].trainable = True base_model.layers[-4].trainable = True model = Sequential([ base_model, layers.Flatten(), layers.Dropout(0.3), layers.Dense(128, activation='relu'), layers.Dropout(0.2), layers.Dense(4, activation='softmax') ]) model.compile( loss='sparse_categorical_crossentropy', metrics=['sparse_categorical_accuracy'], optimizer=Adam(learning_rate=0.0001) )
4. 监控训练过程并处理类别不平衡
- 训练时传入验证集,观察损失与准确率变化:
epochs=10 # 适当增加轮次 history = model.fit( train_ds, epochs=epochs, validation_data=val_ds )
- 检查数据集类别分布,若不平衡,在
fit中加入class_weight参数:
# 统计各类样本数,计算权重 class_counts = [len(os.listdir(f"C:\\data\\MRI Images\\Training\\{cls}")) for cls in os.listdir("C:\\data\\MRI Images\\Training")] total_samples = sum(class_counts) class_weight = {i: total_samples / count for i, count in enumerate(class_counts)} history = model.fit( train_ds, epochs=epochs, validation_data=val_ds, class_weight=class_weight )
最终评估
训练完成后,用独立测试集评估模型性能:
print("测试集评估结果:", model.evaluate(test_ds))
内容的提问来源于stack exchange,提问作者Jomithy
相关产品推荐
相关产品推荐

