You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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难度极高,可采用分步训练降低收敛难度:

  1. 先冻结所有卷积层,只训练顶层全连接层(和迁移学习流程一致),等待精度收敛;
  2. 解冻最后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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 07:25:40