中PyTorch训练中出现死循环 检查Loss是否异常导致梯度无穷深度解析|Duuu笔记
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不涨。
独响
一个轻笔记+角色扮演的app
下载
每次
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()
可能仍返回正常值,实际梯度已坏。
“
Python免费学习笔记(深入)
”;
务必配合
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曲线看。
