loss突变为inf或nan导致训练“假死”:梯度失效、参数不更新,主因是除零、log(0)、重复softmax等;需用torch.isfinite检查、清空optimizer状态或重建,并在amp中配合gradscaler监控。

Loss突然变成inf或nan,训练卡住不动
这是最典型的“假死循环”:模型没真卡死,但loss爆掉后,后续梯度全是nan,optimizer.step()不再更新参数,看起来像无限循环。常见于除零、log(0)、softmax输入过大、loss函数内部未防错。
- 立刻在训练循环里加检查:
if torch.isnan(loss) or torch.isinf(loss): print("Loss exploded at step", i); break - 用
torch.autograd.set_detect_anomaly(True)开启异常检测(只在调试时开,会拖慢训练) - 别依赖
loss.item()判断——它对nan返回nan,但对inf可能静默返回一个极大浮点数;优先用torch.isfinite(loss).all()
torch.nn.CrossEntropyLoss输入logits前忘了关softmax
这个错不会报错,但会让loss持续为inf或剧烈震荡。因为CrossEntropyLoss内部已包含log-softmax,若你手动加了torch.softmax(logits, dim=-1)再传入,就等于算两次softmax,数值极易溢出。
- 正确写法:
criterion = torch.nn.CrossEntropyLoss(); loss = criterion(logits, targets)(logits是未归一化的原始输出) - 错误写法:
probs = torch.softmax(logits, dim=-1); loss = criterion(probs, targets)→ 直接触发inf - 如果必须用概率输入(比如迁移旧代码),换用
torch.nn.NLLLoss+torch.log(probs),但要确保probs全>0
Adam优化器在loss异常时悄悄失效
Adam维护的exp_avg和exp_avg_sq缓存一旦混入nan,后续所有梯度更新都会被污染——即使loss后来恢复正常,参数也不再更新,表现就是loss平台期+acc不涨。
- 每次
loss不合法时,必须清空optimizer状态:optimizer.zero_grad(); optimizer.state.clear() - 更稳妥的做法是遇到
nan/inf后重建optimizer:optimizer = torch.optim.Adam(model.parameters(), lr=...) - 不要只靠
loss.backward()前加torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)——它拦不住loss本身爆炸,只能防梯度爆炸
混合精度训练(amp)下inf更隐蔽
用torch.cuda.amp.autocast时,某些层(如LayerNorm、Softmax)在FP16下更容易产出inf,但错误常延迟几轮才暴露,且loss.item()可能仍返回正常值,实际梯度已坏。
- 务必配合
GradScaler并检查其状态:if scaler.get_scale() == 0: print("Scaler scale hit zero") - 在
scaler.step(optimizer)后加scaler.update(),否则下次scale不会自适应调整 - 临时关闭
autocast做验证:把关键计算段用with torch.no_grad(): with torch.cuda.amp.disable_casts(): ...包起来,看是否还出inf
inf,而是它常藏在某次backward之后、step之前,而你只盯着loss曲线看。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











