Keras CNN二分类模型准确率恒为0.5,小样本下如何优化?
问题描述
我的Keras CNN二分类模型训练及验证准确率始终为0.5。目前该CNN面临的首要问题是图片数量过少(总计980张,其中180张划分为测试集),且我无法获取更多图片来训练模型。
我想了解可以对模型进行哪些调整来提升验证准确率;若样本量是核心问题,哪些模型适用于该数据集训练?
classifier=Sequential() from google.colab import drive drive.mount('/content/gdrive') train_set = '/content/gdrive/MyDrive/Extra/ICs/ML/Martinez/train_set' test_set = '/content/gdrive/MyDrive/Extra/ICs/ML/Martinez/test_set' train_datagen = ImageDataGenerator( rescale=1./255, rotation_range = 50, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0, horizontal_flip=True, fill_mode='nearest') test_datagen = ImageDataGenerator(rescale=1./255) train_gen = train_datagen.flow_from_directory( train_set, target_size = (224,224), batch_size=100, class_mode='categorical') test_gen = test_datagen.flow_from_directory( test_set, target_size=(224,224), batch_size=10, shuffle=False, class_mode='categorical') model = tf.keras.Sequential([ Conv2D(32, (3,3), activation='relu', input_shape=(224, 224, 3)), MaxPool2D(2,2), Conv2D(32, (3,3), activation='relu'), MaxPool2D(2,2), Conv2D(64, (3,3), activation='relu'), MaxPool2D(2,2), Flatten(), Dense(780, activation='relu'), Dense(1, activation='sigmoid') ]) print(len(train_gen)) model.summary() model.compile( loss=tf.keras.losses.BinaryCrossentropy(), optimizer=tf.keras.optimizers.Adam(), metrics='accuracy' ) model.fit(train_gen, steps_per_epoch=6, epochs=6, validation_data=test_gen, validation_steps=10)
解决方案
一、先修正代码中的核心矛盾
你的代码存在标签格式与输出层不匹配的问题:
flow_from_directory设置了class_mode='categorical',会输出one-hot编码的2维标签;- 但模型输出层是
Dense(1, activation='sigmoid'),损失用的BinaryCrossentropy,期望的是1维的二分类标签。
这会导致模型无法正确学习,直接造成准确率固定0.5。必须二选一修正:
- 保持输出层和损失不变,将
train_gen、test_gen的class_mode改为'binary'; - 保持
class_mode='categorical',将输出层改为Dense(2, activation='softmax'),损失替换为CategoricalCrossentropy。
二、针对小样本的模型调整策略
1. 强化数据增强
- 将
zoom_range=0改为0.2,增加随机缩放的多样性; - 若图片上下翻转不影响类别判断,添加
vertical_flip=True; - 增加
brightness_range=[0.8,1.2],引入随机亮度调整; - 注意:数据增强仅作用于训练集,测试集保持仅归一化即可(你当前的做法正确)。
2. 简化模型避免过拟合
- 全连接层
Dense(780)参数过多,对800张训练图来说极易过拟合,建议改为Dense(128)或Dense(64); - 在卷积层后添加
Dropout层,比如每个MaxPool2D后加Dropout(0.2),全连接层后加Dropout(0.5); - 可减少卷积层数量或滤波器数量,比如将第三个
Conv2D的64改为32,或直接去掉一层卷积。
3. 优化训练参数
epochs=6太少,建议增加到20-30轮,同时配合早停:early_stopping = tf.keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True) model.fit(..., callbacks=[early_stopping])- 减小
batch_size至16或32,小batch能让模型接触更多样本组合,提升泛化性; - 调整学习率至0.0001,Adam默认的0.001对小样本来说可能过大;
- 修正
steps_per_epoch:训练集共800张,batch_size=100时应设为len(train_gen)(即8),当前设置6会浪费200张训练数据。
4. 添加正则化
在卷积层和全连接层加入L2正则化,限制权重大小:
Conv2D(32, (3,3), activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.001))
三、适合小样本的模型选择
1. 迁移学习模型(首选)
利用预训练CNN的通用特征,仅微调顶层即可快速适配小样本任务:
base_model = tf.keras.applications.MobileNetV2(input_shape=(224,224,3), include_top=False, weights='imagenet') base_model.trainable = False # 冻结预训练层 model = tf.keras.Sequential([ base_model, tf.keras.layers.GlobalAveragePooling2D(), # 替代Flatten,减少参数 tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dropout(0.5), tf.keras.layers.Dense(1, activation='sigmoid') ])
常用预训练模型:MobileNetV2、ResNet50、VGG16,其中MobileNetV2参数少,适合小样本。
2. 轻量级CNN模型
选择参数更少的模型如MobileNet、SqueezeNet,这类模型本身设计就考虑了低数据量场景,不易过拟合。
3. 传统机器学习结合特征提取
先用简单CNN提取图片特征,再用SVM或随机森林做分类:
- 训练一个简单CNN,输出中间层特征;
- 将训练集和测试集的特征保存为数组;
- 用SVM训练分类器,传统模型对小样本的鲁棒性有时优于CNN。
内容的提问来源于stack exchange,提问作者Art
相关产品推荐
相关产品推荐

