CNN模型训练准确率高但验证表现差的原因及优化咨询
巴迪克图案分类CNN模型过拟合优化方案
针对训练集准确率高、验证集表现差的过拟合问题,结合你的数据规模(256个训练样本、65个验证样本)和现有尝试,给出以下具体优化建议:
1. 修正数据生成器的迭代步数设置
你当前设置stepsPerEpochVal=200,但实际训练样本仅256个、batch size为2,每个epoch只需迭代256//2=128次即可遍历完训练数据。设置200步会导致每个epoch重复使用训练数据,大幅加剧过拟合。
修改代码:
stepsPerEpochVal = len(X_train) // batchSizeVal
2. 调整图像预处理策略,保留关键特征
巴迪克图案的色彩信息是核心分类特征,你当前将彩色图转为灰度图的操作会丢失大量区分度信息。建议修改预处理流程:
def PreProcessing(img): # 保留RGB通道,仅做归一化 img = img / 255.0 return img
同时调整模型输入维度:
# 匹配RGB输入,修改Conv2D的input_shape model.add((Conv2D(NumberOfFilters1, sizeOfFilter1, input_shape=(32,32,3), activation='relu')))
另外,equalizeHist可能过度破坏图案纹理,保留RGB时建议移除该操作。
3. 缩小模型容量,匹配小数据集规模
你当前使用128、256个卷积核的模型,对仅256个样本的数据集来说容量过大,极易过拟合。建议大幅缩减模型参数:
def myModel(): NumberOfFilters1 = 32 # 从128降至32 NumberOfFilters2 = 64 # 从256降至64 sizeOfFilter1=(3,3) # 缩小卷积核尺寸 sizeOfFilter2=(3,3) sizeOfPool=(2,2) NumberOfNode=64 # 全连接层从128降至64 model = Sequential() model.add((Conv2D(NumberOfFilters1, sizeOfFilter1, input_shape=(32,32,3), activation='relu'))) model.add(MaxPooling2D(pool_size=sizeOfPool)) model.add(Dropout(0.3)) # 降低Dropout比例,避免欠拟合 model.add((Conv2D(NumberOfFilters2, sizeOfFilter2, activation='relu'))) model.add(MaxPooling2D(pool_size=sizeOfPool)) model.add(Dropout(0.3)) model.add(Flatten()) model.add(Dense(NumberOfNode, activation='relu')) model.add(Dropout(0.3)) model.add(Dense(NumberOfClasses, activation='softmax')) model.compile(Adam(lr=0.001), loss='categorical_crossentropy', metrics=['accuracy']) return model
更高效的方案是使用迁移学习,基于预训练模型(如MobileNetV2)微调,利用预训练的图像特征适配小数据集:
from tensorflow.keras.applications import MobileNetV2 base_model = MobileNetV2(input_shape=(32,32,3), include_top=False, weights='imagenet') base_model.trainable = False # 冻结预训练层 model = Sequential([ base_model, tf.keras.layers.GlobalAveragePooling2D(), Dense(64, activation='relu'), Dropout(0.3), Dense(NumberOfClasses, activation='softmax') ]) model.compile(Adam(lr=0.001), loss='categorical_crossentropy', metrics=['accuracy'])
4. 强化数据增强,扩充有效样本多样性
当前数据增强参数保守,可进一步增加变换强度,生成更多差异化样本:
dataGen = ImageDataGenerator( width_shift_range=0.2, height_shift_range=0.2, zoom_range=0.3, shear_range=0.2, rotation_range=30, horizontal_flip=True, # 加入水平翻转 brightness_range=[0.8, 1.2] # 亮度调整 )
注意:验证集和测试集绝对不能使用数据增强,你当前的处理是正确的,需保持。
5. 加入正则化与早停机制
- L2正则化:在卷积层和全连接层加入权重约束,限制参数规模:
from tensorflow.keras.regularizers import l2 model.add((Conv2D(NumberOfFilters1, sizeOfFilter1, input_shape=(32,32,3), activation='relu', kernel_regularizer=l2(0.001))))
- 早停(Early Stopping):当验证集损失连续多轮不下降时停止训练,避免过度拟合,同时保留最优模型:
from tensorflow.keras.callbacks import EarlyStopping early_stop = EarlyStopping(patience=10, restore_best_weights=True) # 修改训练代码,加入回调 history = model.fit_generator( dataGen.flow(X_train,Y_train,batch_size=batchSizeVal), steps_per_epoch=stepsPerEpochVal, epochs=epochsVal, validation_data=(X_validation,Y_validation), shuffle=1, callbacks=[early_stop] )
6. 优化数据集划分与类别平衡
- 分层抽样:使用
train_test_split的stratify参数,保证训练、验证、测试集的类别分布与原始数据一致:
X_train,X_test,Y_train,Y_test = train_test_split(images,classNumber,test_size=testRatio, stratify=classNumber) X_train,X_validation,Y_train,Y_validation= train_test_split(X_train,Y_train,test_size=validationRatio, stratify=Y_train)
- 类别平衡:如果某类样本数量过少(从你输出的
NumberOfSamples可查看),可针对性对少样本类别做数据增强,或使用加权交叉熵损失:
# 计算类别权重 class_weight = {} total_samples = len(Y_train) for i in range(NumberOfClasses): count = len(np.where(Y_train == i)[0]) class_weight[i] = total_samples / (NumberOfClasses * count) # 训练时传入class_weight history = model.fit_generator(..., class_weight=class_weight)
7. 学习率动态调整
使用学习率衰减策略,当验证集性能停滞时降低学习率,帮助模型跳出局部最优:
from tensorflow.keras.callbacks import ReduceLROnPlateau lr_scheduler = ReduceLROnPlateau(factor=0.5, patience=5, min_lr=1e-6) # 加入训练回调 history = model.fit_generator(..., callbacks=[early_stop, lr_scheduler])
内容的提问来源于stack exchange,提问作者Bhetrand Dwilangga
相关产品推荐
相关产品推荐

