大模型本地部署 · 收录于 2026-08-30 · 适用 1 工具 ✓ 实测验证

训练时 loss 突然尖峰(spike)/ 数值发散(NaN、inf)怎么办

已验证 2026-08-30训练 loss 尖峰/数值发散——兜底梯度裁剪(clip_grad_norm_ max_norm=1.0),从最后健康 checkpoint 恢复并把学习率降到 1/3~1/10。

快速修复

# 1. 兜底:梯度裁剪(PyTorch 标准 API,max_norm=1.0 是常见起点)
torch.nn.utils.clip_grad_norm_(model.parameters, max_norm=1.0)

# 2. 从最后一个健康 checkpoint 恢复 + 学习率降到原来的 1/3~1/10
#    (恢复前用异常 batch 和普通 batch 各重放一次,确认根因)

机制实测:学习率过大 → loss 在约 100 步内从 0.05 级爆炸到 inf(不恢复);同样学习率加梯度裁剪 max_norm=1.0 → 全程有限稳定;注入单步尖峰后从健康 checkpoint 降学习率恢复 → 正常收敛。

你会遇到什么

训练(大模型微调/从头训练)跑到一半,loss 曲线突然跳出尖峰:可能单步跳高后自己回落,可能连续多个 spike,也可能 spike 之后永远回不到原来的水平,甚至直接变 NaN / inf。网上最常见的建议是"降学习率",但现象不等于原因——先分清是数据、超参还是链路问题,再动手。

本文路径:快速修复(30 秒一段命令)→ 一步步排查(可跳读)→ 原理与变体(症状/原因/证据)。赶时间直接跳到「快速修复」。

为什么会这样

一次参数更新 = 学习率 × 梯度。loss 尖峰本质是更新量失控,但同一现象背后有三类常见根因:

  1. 数据:异常 batch(损坏样本、序列过长、label 非法)——通常表现为单步 spike 后恢复;
  2. 超参/链路:学习率过大、缺梯度裁剪、AMP 精度问题——通常表现为连续 spike 或永久不恢复;
  3. 目标/实现:loss mask、label、多卡归约错误——通常表现为"loss 平滑但验证不改善"或只有多卡异常。

按尖峰类型分流(原文诊断表 + 机制实测):

现象先看什么最小验证确认后才处理
单步 spike 后恢复batch 上下文(token/序列长度/来源/label)、grad norm重放相同 batch修复损坏数据;合法难样本不丢弃
连续多个 spikegrad norm、clip 系数、相对更新量、学习率对齐这些指标调整已确认的超参/结构问题
spike 后永久不恢复相对更新量、optimizer state健康 checkpoint 对照修复根因后从健康 checkpoint 恢复
第一步 NaN输入、activation、loss单 batch FP32 重放修复第一个非有限运算或精度配置

训练循环里,参数更新的本质是 参数 += 学习率 × 梯度,loss 尖峰就是一次(或连续几次)更新量过大。更新量由两个因子相乘:学习率(超参,恒定或按 schedule 变化)和梯度(由当前 batch 的损失反向传播算出)。

两个因子各自爆炸都会让乘积失控:学习率过大 = 每次更新都太大,即使梯度正常也会发散;梯度异常 = batch 里有损坏样本/序列过长/label 非法时,这一批算出的梯度方向或量级异常,单步更新就巨大。

这就是“现象 ≠ 原因”的结构:尖峰是结果,要从“哪个因子失控”反推根因。两个排障工具对应两个因子:重放相同 batch(如果同一批数据稳定复现尖峰,说明是数据因子;不能复现,说明是随机性/并行/状态因子);看 grad norm(裁剪前梯度范数持续抬升 = 梯度因子风险;grad norm 正常但更新仍大 = 学习率因子)。

梯度裁剪兜底的机制也由此而来:它直接把梯度范数限制在阈值内,等于给“梯度因子”上了保险丝,即使数据或计算偶发异常,单步更新量也被钳制在安全范围。

一步步来

1. 先分类:什么类型的 spike — 必做

单步后回落 / 连续多个 / 永久不恢复 / 直接 NaN。类型决定排查方向(见上表)。

2. 重放相同 batch — 必做

  • 能稳定复现 → 数据问题优先(损坏样本、label、mask);
  • 不能复现 → 检查随机算子、并行执行、前一状态差异。

3. 看 grad norm,而不是只看 loss — 必做

裁剪前 grad norm 持续抬升 → 更新链路风险;用相对更新量(参数更新幅度/参数幅值)判断训练是否还在有效学习。

4. 按根因选处理手段 — 必做

  • 数据损坏 → 修数据;合法难样本 → 不丢弃;
  • 更新量失控 → 梯度裁剪 / 降学习率(一次只改一个变量,做单变量对照);
  • 已 NaN / 不恢复 → 从健康 checkpoint 恢复,不要只修模型参数后继续用可疑 optimizer state。

5. 防再犯:pilot run — 必做

正式训练前小规模试跑:覆盖完整 warmup、峰值学习率一段、代表性数据、至少一次 validation、固定随机种子。监控分三档:每步(loss/梯度是否有限)、窗口(趋势偏离)、周期(验证指标)。

怎么确认修好了

loss 恢复下降趋势且不再出尖峰;grad norm 回到健康区间;从健康 checkpoint 恢复后训练能持续推进;validation 指标同步改善。

其它环境的表现

环境说明
单卡正常、多卡异常先查归约与控制流(有效 token 数、loss 归约、各 rank 是否一致 backward),不先调超参
FP16/AMP先 unscale 再统计和裁剪梯度(GradScaler 判断思路见原文)
框架差异梯度裁剪各框架都有对应实现(PyTorch clip_grad_norm_ / TF clip_by_global_norm 等),机制相同
未实测本条目为等价最小机制复现(numpy 2 层 MLP),未在完整 LLM 训练框架复现;LLaMA-Factory 微调相关见 combo-local-llm-finetune

症状

loss 曲线突然尖峰:单步 spike 后恢复、连续 spike、spike 后永久不恢复,或直接 NaN/inf;grad norm 同步异常抬升。

原因

更新量 = 学习率 × 梯度,尖峰本质是更新量失控。根因分三类:数据(异常 batch)、超参/链路(学习率过大、缺梯度裁剪、AMP 精度)、目标/实现(mask、label、多卡归约错误)。现象 ≠ 原因,先证据后处理。

解法

  1. 按尖峰类型定位(单步/连续/不恢复/NaN);
  2. 重放相同 batch + 看 grad norm / 相对更新量;
  3. 按根因处理:修数据 / 梯度裁剪 / 降学习率 / 从健康 checkpoint 恢复(一次只改一个变量);
  4. 训练前跑 pilot run + 分档监控防再犯。
验证证据
  • 本机实测(2026-08-30,Python 3.14 + numpy 2.5.2,venv+清华镜像):2 层 MLP 回归 y=sin(x) 机制复现——lr=0.01 收敛(loss 0.065→0.026);lr=1.0 发散(25 步 loss 5.4e51,98 步 inf,不恢复);lr=1.0+clip=1.0 全程有限(gradnorm 裁剪后=1.0);注入单步尖峰(loss 0.048→0.224)后从健康 checkpoint 降 lr 恢复收敛(0.028)。证据:实测复现日志(2026-08-30)。
  • 原文:石头开会《模型训练稳定性:从训练链路到排障手册》(2026-08-28,博客园)。
  • 未实测:完整 LLM 训练框架(PyTorch/LLaMA-Factory)上的实况复现(环境重,标注"机制已复现、环境未复现",SM-076)。

适用工具 (1)

本问题方案已挂到下列工具的方案页——从工具页也能回到这里,双向可达。

python · 外部工具

成套方案 (组合包)

这个问题包含在下列成套安装方案中。

来源与证据

收录 验证 ✓ 实测验证适用 1 工具
方案含本地实测或来源可复核的解法;未实测项已在文中如实标注。

素材来源:https://www.cnblogs.com/GlenTt/p/22742530

← 大模型本地部署 问题域 | 问题货架 | 遇到其它问题?去 问题空间 提交求助卡