版本信息最后核对:2026-10-25
训练 loss 异常的定位:先分清指标算错、数值溢出和训练动力学问题。
训练日志中的 loss 突然变成 nan、inf 或极大的数值,梯度范数同步飙升,优化器开始跳过更新;也可能 loss 始终有限,但长期不降、反复尖峰、训练与验证背离。混合精度训练还常伴随 GradScaler 连续跳过 step、loss scale 一降再降。
分布式训练会放大误判:只打印 rank 0 时,其他 rank 可能早已出现非有限值;各 rank 用未加权平均时,日志 loss 与真实优化目标也不是一回事。被抢占后恢复还可能带来 step 回退、学习率重新 warmup 和曲线跳变。
需要区分三类问题:
先保住现场:记录第一个异常 step、最后一个正常 step、学习率、loss scale、梯度范数、出问题的 rank、全局 batch 配置、数据 shard 与随机种子。再把“第一次异常”与配置变更点对齐:
# 找第一个非有限值,以及它前后的学习率和 scale
python - <<'PY'
import re
bad = []
for i, line in enumerate(open("train.log", errors="ignore"), 1):
if re.search(r"\b(nan|inf|-inf)\b", line, re.I):
bad.append((i, line.strip()))
print("non-finite lines:", len(bad))
for item in bad[:20]:
print(item)
PY
# 对齐学习率、warmup、恢复点和扩卡时间
grep -nE 'lr|learning_rate|scale|grad_norm|resume|world_size' train.log | tail -200
再固定出问题的 batch,分别用训练态、评估态和 fp32 前向复算 loss:都正常则查混合精度、反向或优化器;评估态也异常则说明权重或输入已坏。
回答四个问题:
0/0、对负数取对数、mask 全空;inf 常来自溢出、除零和过大的学习率。| 线索 | 更可能的方向 |
|---|---|
| step 0 即 NaN,单卡也复现 | 数据、标签、loss 公式、初始化 |
| 学习率升高或 warmup 结束后开始异常 | 学习率过大、schedule 配置 |
| 混合精度开启后出现 inf,关闭后恢复 | loss scale、fp16 溢出、归约精度 |
| 扩卡或改梯度累积后曲线突变 | 全局 batch 语义、学习率未同步调整 |
| 只有部分 rank 异常 | 数据分片、设备、进程退出 |
| 恢复 checkpoint 后 step 回退或 loss 跳变 | 优化器、scheduler、RNG、sampler 状态缺失 |
| 评估正常、训练日志异常 | 指标聚合、日志采样、正则化差异 |
峰值学习率过高是最常见的发散来源。warmup 太短、按 step 与按 epoch 计数混用、恢复后 scheduler 从零开始,都会让实际学习率与预期不符。扩大全局 batch 后学习率调整不当,常表现为前期正常、随后爆炸;每个 micro-batch 都推进 scheduler,也会让梯度累积时的学习率被多推数倍。
输入含 NaN/Inf、解码失败、文本为空、标签越界、padding 未忽略、mask 全为负无穷,都会让 loss 变成非有限值。分片重复或遗漏、最后一个 batch 过小、动态 batch 只剩填充样本,会造成周期性尖峰;离线与在线预处理不一致则让训练和评估分布不同。
fp16 动态范围有限,未做 loss scale、scale 初始值过大、梯度裁剪放在 scaler.step 之后、loss 在低精度下归约,都可能产生 inf。bf16 范围更大但精度更低,关键归约仍建议用 fp32;自定义算子与归一化在低精度下计算也容易出问题。
没有梯度裁剪、阈值过大、残差增长、初始化不当、共享参数重复更新,都可能让梯度爆炸。适配器初始化错误、冻结参数仍参与优化,也会改变训练动力学。
全局 batch 通常等于单卡 batch × 数据并行度 × 梯度累积步数。扩卡后未同步调整学习率和 warmup,原有的步长、梯度噪声和 batch 语义不再等价,可能表现为发散、尖峰或收敛变慢。DDP 默认对梯度求平均,日志若按 micro-batch 简单平均就与优化目标不一致;各 rank 样本数不同又未加权,同样会让日志失真。
日志里的 loss 可能是滑动平均、裁剪值或只在部分 step 打印;各 rank 未加权聚合时,样本长度差异会带来明显偏差。训练 loss 含 dropout 与数据增强,评估 loss 不含,本就不应完全重合。重复消费样本或 step 从零计数,也常被误判为退化。
只恢复模型权重、缺少优化器动量、scheduler、loss scale、RNG 和 sampler,会让恢复后的前几步明显异常。step 与 epoch 对不上时 warmup 可能重跑,学习率跳到错误位置;数据顺序重置还会造成短期重复。
保存异常前后的日志、配置、随机种子、checkpoint 路径、全局 batch 算法和数据版本。明确“坏”的定义:非有限值、连续不降还是评估退化。没有统一标准,后续对比就失去意义。
不要只看最后一个报错。找出最后一个正常 step 和第一个异常 step,对齐学习率变化、warmup 结束、shard 切换、扩卡、恢复和代码变更。日志没有 step 时先补上再复现。
固化异常 batch,分别跑训练态、评估态、fp32 和关闭混合精度的前向,比较 loss 与中间激活。评估正常而训练异常,查 dropout、增强、AMP 和反向;fp32 正常而低精度异常,查溢出与归约;全都异常则检查输入和权重。
在测试环境用逐层 hook 记录输出和梯度的有限性,找到最早出现 NaN/Inf 的层。torch.autograd.set_detect_anomaly(True) 能定位反向,但会显著拖慢训练,只用于小 batch。逐层梯度范数比总范数更容易找到爆炸来源。
统计各 shard 的样本数、token 长度、空样本比例、标签范围和非有限值;检查 mask、padding、ignore index 与 reduction。再固定一个小 batch、关闭正则化做过拟合测试:loss 降不下去时,优先怀疑标签、mask 或学习率,而不是模型容量。
记录 loss scale、跳过 step 次数和梯度范数;确认 loss 在 fp32 中计算,裁剪在 scaler.unscale_ 之后、scaler.step 之前,scaler.update 每步一次。关闭混合精度后恢复,就保留 fp32 归约和关键归一化,再逐步放开低精度算子。
算出当前有效全局 batch,与改动前逐项对比;确认学习率缩放、warmup、累积边界、DDP 平均和指标权重。固定数据顺序与种子,用相同全局 batch 做单卡/多卡对照,前若干 step 的差异应在数值误差内。
检查 checkpoint 是否含模型、优化器、scheduler、loss scale、RNG、sampler 与 step。恢复后先用固定 batch 比较 loss、学习率和梯度范数,再连续跑若干 step 看曲线是否连续。缺优化器状态时应视为热启动,而不是无损续训。
每次只改一个变量:单卡单 batch、单卡多 batch、多卡,最后恢复完整配置。优先在测试环境验证学习率、精度和裁剪改动,生产任务保留原 checkpoint,避免排障变成不可回滚的实验。
按根因处理:
验证至少覆盖:
回滚和删除 checkpoint 前先备份;覆盖 best 或唯一可用 checkpoint 属于高风险操作,应只在测试环境或确认有副本时进行。
合上页面,先回答,再展开对照。
小程序版
微信扫码,随时查命令