CNN验证准确率不提升求助:EyePACs视网膜图像分类任务
眼部视网膜图像分类问题分析与优化
问题背景
尝试构建简易CNN区分不同严重程度的眼部视网膜扫描图像,初始为5类多分类任务,采用Kaggle EyePACs缩放后数据集。训练时预期训练与验证准确率同步上升,但实际验证准确率毫无提升;转为0和4两类二分类任务后,曲线仍无明显改善。未做图像预处理,原计划先实现基础学习再用CLAHE等优化,但模型初始准确率达0.75且后续无提升,验证集完全无学习迹象。
当前代码与输出
数据集构建代码
import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers train_ds = tf.keras.preprocessing.image_dataset_from_directory( "/content/drive/MyDrive/Colab Notebooks/resized train 15/Binary 0-4", labels="inferred", label_mode="binary", validation_split=0.2, seed=1337, subset="training", ) val_ds = tf.keras.preprocessing.image_dataset_from_directory( "/content/drive/MyDrive/Colab Notebooks/resized train 15/Binary 0-4", labels="inferred", label_mode="binary", validation_split=0.2, seed=1337, subset="validation", ) test_ds = tf.keras.preprocessing.image_dataset_from_directory( "/content/drive/MyDrive/Colab Notebooks/resized test 15/0-4/", labels="inferred", label_mode="binary", )
数据集输出
Found 26518 files belonging to 2 classes.
Using 21215 files for training.
Found 26518 files belonging to 2 classes.
Using 5303 files for validation.
Found 36759 files belonging to 2 classes.
模型与训练代码
# 加速处理 train_ds = train_ds.prefetch(buffer_size=32) val_ds = val_ds.prefetch(buffer_size=32) test_ds = test_ds.prefetch(buffer_size=32) # 模型架构: from keras.models import Sequential from keras.layers import Dense, Rescaling, Conv2D, MaxPool2D, Flatten model = Sequential() model.add(Rescaling(1.0 / 255)) model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(256,256,3))) model.add(MaxPool2D(pool_size=(2, 2), strides=2)) model.add(Conv2D(64, kernel_size=(3, 3), activation='relu')) model.add(MaxPool2D(pool_size=(2, 2), strides=2)) model.add(Flatten()) model.add(Dense(units=2, activation='sigmoid')) # 编译模型: from keras import optimizers model.compile(optimizer=keras.optimizers.Adam(1e-3), loss='sparse_categorical_crossentropy', metrics=['accuracy']) # 训练: history = model.fit( x=train_ds, epochs=30, validation_data=val_ds, ) # 测试评估: loss, acc = model.evaluate(test_ds) print("Accuracy", acc)
训练输出
1149/1149 [==============================] - 278s 238ms/step - loss: 0.1408 - accuracy: 0.9672
Accuracy 0.9671916961669922
核心问题分析
- 损失函数与输出层不匹配:二分类任务中
label_mode="binary"对应单值标签(0/1),但当前输出层设为2个单元+sigmoid激活,搭配sparse_categorical_crossentropy损失,完全不符合二分类配置逻辑,导致模型学习信号混乱,无法有效更新权重。 - 类别不平衡嫌疑:初始准确率0.75大概率是某类样本占比75%,模型仅靠输出占比高的类别就能达到该准确率,后续无提升说明模型未学到有效特征。
- 模型容量不足:仅两层卷积的简易CNN,无法捕捉视网膜图像中区分病理特征(如血管、渗出物)的精细信息,特征提取能力太弱。
- 缺乏图像预处理:视网膜图像普遍存在光照不均、对比度低的问题,直接输入原始图像会让模型难以聚焦于有效病理特征,阻碍基础学习。
优化方向与修正代码
关键优化点
- 修正模型与损失函数的二分类适配配置
- 处理类别不平衡问题
- 增强模型特征提取能力
- 添加图像预处理与数据增强
修正后的核心代码
# 修正模型架构 model = Sequential() model.add(Rescaling(1.0 / 255)) # 添加数据增强 model.add(layers.RandomFlip("horizontal")) model.add(layers.RandomRotation(0.1)) # 增加卷积层并加入BatchNormalization model.add(Conv2D(32, kernel_size=(3, 3), activation='relu', input_shape=(256,256,3))) model.add(layers.BatchNormalization()) model.add(MaxPool2D(pool_size=(2, 2), strides=2)) model.add(Conv2D(64, kernel_size=(3, 3), activation='relu')) model.add(layers.BatchNormalization()) model.add(MaxPool2D(pool_size=(2, 2), strides=2)) model.add(Conv2D(128, kernel_size=(3, 3), activation='relu')) model.add(layers.BatchNormalization()) model.add(MaxPool2D(pool_size=(2, 2), strides=2)) model.add(Flatten()) # 添加Dropout防止过拟合 model.add(layers.Dropout(0.5)) # 二分类正确输出层:1个单元+ sigmoid model.add(Dense(units=1, activation='sigmoid')) # 修正编译配置 model.compile(optimizer=keras.optimizers.Adam(1e-4), # 降低学习率提升稳定性 loss='binary_crossentropy', metrics=['accuracy']) # 计算类别权重(假设类别0占比75%,类别4占比25%,权重反比于类别占比) class_weights = {0: 1.0, 4: 3.0} # 训练时加入类别权重 history = model.fit( x=train_ds, epochs=30, validation_data=val_ds, class_weight=class_weights )
额外建议
- 先统计数据集各类别样本数量,确认是否存在类别不平衡问题
- 可视化训练/验证的损失与准确率曲线,直观观察模型学习状态
- 尝试预训练模型(如ResNet50)做迁移学习,这是医学图像分类任务中快速提效的常用方法
内容的提问来源于stack exchange,提问作者brosefzai
相关产品推荐
相关产品推荐

