loss 变成 nan 通常源于数值溢出或未定义操作,需从数据、损失函数、优化器三方面排查:检查输入是否含 nan/inf,拆解损失函数避免 log(0) 或除零,启用梯度裁剪和合理学习率,并警惕 amp 下的隐性陷阱。

Loss 变成 NaN 通常不是模型“坏了”,而是某个数值计算环节溢出或未定义——最常见的是除零、log(0)、梯度爆炸、输入含 NaN 或 Inf,修复要从数据、损失函数、优化器三处顺次排查。
检查输入数据是否含 NaN / Inf
PyTorch 不会主动拦截含 NaN 的张量进入计算,一旦 loss 依赖了这样的输入,后续所有梯度都会污染。尤其注意归一化、加载的标签、自定义数据增强后的输出。
- 在
dataloader返回 batch 后立刻加断言:assert not torch.isnan(x).any() and not torch.isinf(x).any() - 对 label 同样检查:
assert not torch.isnan(y).any(),分类任务中常见 label 被误设为 -1 或超出类别数导致 one-hot 时生成全零向量,再进log_softmax就出 NaN - 若用
torchvision.transforms.Normalize,确认mean和std没传空列表或含零值(如std=[0,0,0])
定位 loss 计算中哪一步崩了
不要只看最终 loss.item(),要拆开损失函数内部:比如 nn.CrossEntropyLoss 实际是 log_softmax + nll_loss,而 log_softmax 对极大 logit 值会产出 -inf,再进 nll_loss(负对数似然)就可能触发 -inf * 0 这类未定义行为。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 手动替换损失计算:
logits = model(x)<br>probs = torch.softmax(logits, dim=1)<br>assert not torch.isnan(probs).any(), "softmax output has NaN"<br>loss = -torch.log(probs[range(len(y)), y] + 1e-8).mean()
- 对
nn.MSELoss类,检查 target 是否被意外缩放到远超 pred 的范围(如 target 是 1e6 级别而 pred 是 0.1 级别),平方后直接 overflow - 若用了自定义 loss(如带
torch.log、torch.sqrt、除法),务必在分母加小常数:1e-6,且对入参做 clamp:torch.clamp(x, min=1e-6)
梯度爆炸与学习率不匹配
NaN 往往出现在训练中期而非开头,说明前向没问题,反向传播时梯度爆炸导致权重更新后产生非法值,下一轮 forward 就崩。典型表现是 loss 先飞速下降,然后某 step 突然跳成 NaN。
- 启用梯度裁剪:
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0),先试1.0,再按需调高 - 降低初始学习率:尤其用 Adam 时,
lr=3e-4在某些大模型上仍偏高,可降到1e-4或更低;若用了lr_scheduler,确认 warmup 阶段没让 lr 瞬间冲太高 - 检查 weight decay 是否过大(如设成
1e-2),它会在 optimizer.step 中对参数做减法,若参数本身极小(如 BatchNorm 的 gamma 接近 0),就容易下溢成 0 再参与后续除法
混合精度训练(AMP)下的隐性陷阱
用 torch.cuda.amp.autocast + GradScaler 时,NaN 更难定位,因为前向可能正常,但 scaler 在 unscale 梯度时发现 inf/NaN 就静默跳过 step,下轮用旧权重继续训,很快雪球效应爆发。
- 务必开启 scaler 的异常检测:
scaler = torch.cuda.amp.GradScaler(enabled=True, growth_interval=1000),并定期检查:if scaler.get_scale() == float('inf'):<br> print("Scaler hit inf scale — likely NaN in gradients") - 在 autocast 块内避免手动 cast:比如不要写
loss.float().backward(),这会绕过 scaler 管理,导致 half 梯度和 float 权重混算 - BatchNorm 层在 AMP 下对极小 batch size(如 1)不稳定,可临时换用
nn.InstanceNorm2d或关掉 BN 的 track_running_stats
真正棘手的 NaN 往往藏在数据 pipeline 最深处:比如一个被忽略的 .npy 文件里存了 np.nan,或 HDF5 数据集某个 slice 的压缩参数损坏。与其花时间猜,不如在第一个 epoch 的第 0 个 batch 就插断点,逐 tensor 打印 .isfinite().all()——数值问题从来不是玄学,只是日志没打够。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










