事情本身不复杂,线索都在日志里。把精度改成 fp16 后,训练到几十步 loss 突然变成 NaN,梯度范数显示 inf,换回 fp32 一切正常。

第一反应是「损失里出现 log(0) 或除以零」,验证下来不成立。

这类报错背后常见的原因有几处,逐个排除就行。没有使用梯度缩放,小梯度在 fp16 下直接下溢。另一种情况是学习率沿用了 fp32 时的设定,更新幅度过大。

另外,scaler 没有参与反向与更新流程。

第一件事是改用 GradScaler 包住反向与优化器步骤

scaler = torch.cuda.amp.GradScaler(); scaler.scale(loss).backward(); scaler.step(opt); scaler.update()

接着对损失加上极小值避免 log 为零

loss = -(y * torch.log(p + 1e-8)).mean()

再往下开启梯度裁剪抑制异常更新

torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)

降低学习率后重跑观察是否复现

python train.py --lr 5e-6 --fp16

怎么算真的好了,看的是同一批现象。连续训练数千步 loss 保持稳定下降,不再出现 NaN 或 inf,验证集指标随之改善。

scaler 必须成对使用,scale 之后忘了 unscale 再做裁剪会让裁剪失效,看起来流程跑通但训练效果明显变差。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。

作者 9823189