使用Keras函数式API从零训练VGG16时所有测试图被预测为同一类怎么办
问题排查与修正方案
核心错误点
- 损失函数与分类配置不匹配:你使用
class_mode='categorical'加载数据,模型输出层是2神经元+softmax,对应应该使用categorical_crossentropy作为损失函数,当前用的binary_crossentropy仅适用于二分类单神经元sigmoid输出、class_mode='binary'的场景,损失函数错误直接导致模型梯度更新逻辑错误,无法正常学习分类边界。 - 预测调用模型名称错误:你定义的模型变量名为
model1,但预测时调用的是model.predict(),如果没有额外定义过model变量,该调用要么报错要么返回无关模型的输出,是所有样本输出同一类的直接原因。 - 类别标签映射错误:
flow_from_directory默认按文件夹名称的字典序分配标签,比如如果你的数据集文件夹命名为car、plane,那么标签0对应car、1对应plane,你当前判断pos==0输出plane的逻辑完全颠倒,也会导致分类结果全错。 - 训练迭代轮数不足:VGG16参数量超过1.3亿,你仅训练5轮,模型完全没有收敛,从损失曲线也能看出训练/验证损失还处于持续下降阶段,没有进入稳定区间。
- 步长计算逻辑错误:
steps_per_epoch的计算逻辑为math.ceil(traindata.samples//batch_size),先做整数除法再取整等于直接丢弃了最后不足一个batch的样本,你1000张训练图batch=32的话,完整步长应该是32,按你的写法只会取31步,每次训练少用8张图,验证集同理也会丢失样本。 - 小数据集训练策略不合理:你从零开始训练VGG16,仅用1000张训练样本远不足以支撑这么大的模型收敛,很容易出现过拟合或者欠拟合。
修正方案
1. 修正模型编译逻辑
将损失函数替换为匹配的categorical交叉熵,可替换更稳定的Adam优化器降低调参难度:
opt = optimizers.Adam(learning_rate=1e-4) model1.compile(loss='categorical_crossentropy', optimizer=opt, metrics=['accuracy'])
2. 修正训练步长计算
直接使用生成器自动计算步长,或者修正计算逻辑:
history = model1.fit_generator( traindata, steps_per_epoch=traindata.samples // batch_size, epochs=30, # 至少训练30轮保证收敛 verbose=1, validation_data = validdata, validation_steps=validdata.samples // batch_size)
3. 修正预测逻辑
首先确认标签映射,再修正模型调用变量名:
# 先打印确认标签对应关系 print(traindata.class_indices) # 假设输出为{'car':0, 'plane':1},则预测逻辑调整为: uploaded = files.upload() for fn in uploaded.keys(): path = '/content/' + fn img = image.load_img(path, target_size=(224, 224)) img = np.asarray(img) plt.imshow(img) img = np.expand_dims(img, axis=0) classes = model1.predict(img/255) # 修正为model1 pos=np.argmax(classes[0]) if pos==0: print("car") elif pos==1: print("plane")
4. 可选优化(大幅提升效果)
使用预训练VGG16权重做迁移学习,冻住卷积层只训练全连接层,适配小数据集场景:
from keras.applications import VGG16 from keras.layers import Dropout # 加载预训练权重,去掉顶部全连接层 base_model = VGG16(weights='imagenet', include_top=False, input_shape=(224,224,3)) # 冻住卷积层不训练 for layer in base_model.layers: layer.trainable = False # 拼接自定义全连接层 x = Flatten()(base_model.output) x = Dense(4096, activation='relu')(x) x = Dropout(0.5)(x) # 加Dropout防止过拟合 x = Dense(4096, activation='relu')(x) x = Dropout(0.5)(x) output = Dense(2, activation='softmax')(x) model1 = Model(inputs=base_model.input, outputs=output)
内容的提问来源于stack exchange,提问作者kumar siddharth
相关产品推荐
相关产品推荐

