Keras中从零训练VGG16无法提升精度问题求助
我在Keras中分别通过迁移学习和从零训练两种方式训练VGG16模型,数据集包含4个类别,每个类别有7000张图片。迁移学习约10个epoch即可收敛,但从零训练至20个epoch时,训练精度和验证精度始终卡在0.2637(接近4类任务的随机猜测精度0.25),还出现loss为nan的情况。训练设备为NVIDIA GeForce RTX 3060 Laptop GPU。
迁移学习模型构建代码
base_model = apps.VGG16( include_top=False, # 不包含顶层全连接层 weights="imagenet", input_shape=input_shape, classifier_activation="softmax", pooling=pooling, ) # 冻结基础模型 for layer in base_model.layers: layer.trainable = False # 将基础模型输出转为一维向量 x = Flatten()(base_model.output) # 添加全连接层 x = Dense(units=4096, activation='relu')(x) x = Dense(units=4096, activation='relu')(x) # 分类输出层 prediction = Dense(4, activation='softmax')(x) model = Model(inputs=base_model.input, outputs=prediction) model.compile(loss='categorical_crossentropy', optimizer=optimizers.Adam(learning_rate=0.001), metrics=['accuracy'])
从零训练模型构建代码
model = apps.VGG16( include_top=True, # 包含顶层全连接层 weights=None, input_shape=input_shape, classifier_activation="softmax", pooling=pooling, classes=4 # 指定输出类别数 ) model.compile(loss='categorical_crossentropy', optimizer=optimizers.Adam(learning_rate=0.1), # 尝试过0.001等不同值 metrics=['accuracy']) model.summary()
通用训练代码
history = model.fit(train_images, validation_data=val_images, epochs=epochs, verbose=1, callbacks=callbacks)
针对从零训练时的精度停滞和loss=nan问题,可按以下步骤排查和修复:
1. 大幅降低初始学习率
从零训练VGG16这类百万级参数的大型模型时,高学习率是导致梯度爆炸(loss=nan)和模型参数混乱的核心原因。你当前设置的learning_rate=0.1远高于Adam默认的0.001,即使降到0.001,对于全模型训练来说仍然偏大。
建议直接将学习率调整为1e-4或1e-5:
optimizer=optimizers.Adam(learning_rate=1e-4)
迁移学习时用0.001有效,是因为只训练少量顶层全连接层参数,而全模型训练需要更小的步长来稳定更新。
2. 确保数据预处理符合VGG16要求
VGG16在ImageNet上训练时依赖特定的输入标准化,如果你从零训练时跳过了正确的预处理,模型将难以收敛。必须对训练/验证数据执行以下操作之一:
- 使用VGG16内置的预处理函数:
from tensorflow.keras.applications.vgg16 import preprocess_input train_images = preprocess_input(train_images) val_images = preprocess_input(val_images) - 手动将像素值归一化到
[0,1]区间:train_images = train_images / 255.0 val_images = val_images / 255.0
注意:预处理方式必须统一应用于训练和验证集。
3. 添加梯度裁剪抑制梯度爆炸
loss出现nan本质是梯度值过大导致参数更新溢出,除了降低学习率,还可以通过梯度裁剪限制梯度的最大范数:
optimizer=optimizers.Adam(learning_rate=1e-4, clipnorm=1.0)
clipnorm=1.0表示将所有梯度的L2范数限制在1.0以内,避免参数更新幅度过大。
4. 检查标签格式与损失函数匹配
使用categorical_crossentropy时,标签必须是独热编码格式(形状为(样本数, 4))。如果你的标签是整数形式(形状为(样本数,)),需要二选一:
- 改用
sparse_categorical_crossentropy损失函数:model.compile(loss='sparse_categorical_crossentropy', ...) - 将标签转换为独热编码:
from tensorflow.keras.utils import to_categorical train_labels = to_categorical(train_labels, num_classes=4) val_labels = to_categorical(val_labels, num_classes=4)
标签格式不匹配会导致损失计算异常,直接引发训练停滞或loss异常。
5. 调整训练策略(分步训练)
直接从零训练整个VGG16难度极高,可采用分步训练降低收敛难度:
- 先冻结所有卷积层,只训练顶层全连接层(和迁移学习流程一致),等待精度收敛;
- 解冻最后3-5层卷积层,将学习率降至
1e-5,继续训练整个模型。
这种方式既利用了卷积层的特征提取能力,又避免了全模型初始训练的不稳定性。
6. 优化batch size
RTX3060 Laptop的显存为6G,VGG16输入为224x224x3时,建议将batch size设置为16或32:
- batch size过小会导致梯度噪声过大,训练不稳定;
- batch size过大则会超出显存限制,引发OOM错误。
内容的提问来源于stack exchange,提问作者Tomas Premoli Muniagurria

