长跑的预训练里,loss 曲线出现尖峰(spike)是常态而非事故:LLaMA、OPT 等公开训练 loss 曲线都带着几十个肉眼可见的尖峰,多数自己消化了。但每个尖峰都值得记录和归因,因为一小部分尖峰是系统性问题的早期症状,拖下去就是发散。这篇按工程师的现场视角整理:先判断是不是真 spike,再按命中率排查成因,最后按决策树处置。
第一步:量化,把"感觉不对"变成数字
肉眼判 spike 不可靠——batch 间 loss 方差本来就大,单点跳高 3 倍可能只是采样运气。可靠的信号是相对滑动基线的偏离:维护 loss 的 EMA($\beta\approx0.98$,窗口约 50–100 步),当 $L_t / \mathrm{EMA}_t > 1.5$ 且连续 $k$ 步($k\ge3$)成立时记为候选 spike。配套看三个伴随指标,基本能预判成因:
- 梯度范数:spike 同步跳高一个数量级 → 数据/前向异常;梯度范数反而塌缩 → 优化器状态污染(常见!);
- update/param 比率(RMS 口径):正常训练稳定在 $10^{-3}$ 量级,spike 前后若它先异常(突然大或小),说明优化器通道先坏;
- 激活/权重范数:某层权重范数缓慢爬升数日后 spike,是慢性的尺度失控,不是急性事件。
成因排查:按命中率从高到低
1. 数值溢出(bf16/fp16)。bf16 指数位宽(8 bit)让它几乎不溢出,但尾数只有 7 位有效,两个相近大数相减的灾难性抵消、以及激活累加的长链舍入,会在前向悄悄埋雷;一旦某 batch 的激活跨过敏感区,梯度出现 nan/inf,一步就废。fp16 更直接——$6\times10^4$ 的上限,大的 attention logits 或过深的残差累加直接 inf。鉴别方法:开 anomaly detection(torch.autograd.set_detect_anomaly)或在 loss 计算处显式 torch.isnan/isinf 检查,命中即停步。注意溢出引发的 spike 常常不是孤立的——inf 梯度写进 Adam 的 $m, v$ 后,即使下一步梯度恢复正常,污染的状态会让后续几十步都带着病态步长,这正是"spike 自愈了但 loss 平台抬高"的常见解释。
2. 优化器状态污染。Adam 的 $v_t$ 是指数平均,记忆约 $1/(1-\beta_2)\approx1000$ 步。单步 inf/巨大梯度写入 $v$ 后,$\sqrt{\hat v}$ 被永久垫高,受影响坐标的有效学习率掉到接近零——loss 不炸,但之后上千步这些参数实质冻结。鉴别信号就是上面说的"梯度范数塌缩 + loss 缓坡"。处置只有一条:从 spike 前的 checkpoint 恢复,并重置优化器状态。
3. 脏数据段。预训练语料里混着异常文档:超长 token 重复串(aaaa…)、罕见的乱码编码页、模板垃圾,这些样本的 loss 和梯度天然离群。鉴别要靠数据侧日志——spike 时刻的数据 loader 偏移量(step × batch size 反推),把该窗口的样本捞出来人工/程序检查。如果同一段数据重放必现 spike,实锤脏数据。
4. 学习率配置事故。warmup 没走完就进入峰值段、续训时忘记重置 lr 计划、多阶段训练的第二阶段沿用峰值 lr,都会制造系统性 spike(规律性出现而非随机)。这类 spike 的特征是周期性——每隔固定间隔来一下,和 lr 计划的结构对齐。
处置:回滚、降学习率、换数据段的顺序
决策树的核心原则是状态的不可逆性:参数和优化器状态一旦污染,继续训练的每一步都在扩大损失,所以处置动作按"止损 → 修复 → 复跑"排序:
- 止损:出现连续 spike 或伴随 nan,立即暂停。检查最近 checkpoint 的 step 与 spike 时刻的先后——optimizer state 的污染窗口是 1000 步($\beta_2$ 记忆),checkpoint 要回滚到 spike 前至少一个窗口长度,否则恢复后病态 $v$ 还在。
- 修复:恢复时做三件事——lr 临时减半(消除触发条件中的学习率因素)、重置 $m, v$(连同 warmup 短程重跑)、排除已定位的脏数据段。三者按定位结果取舍:确认纯数据问题就不必降 lr,确认数值溢出则优先排查精度配置(如检查是否漏了 loss scale)。
- 复跑:恢复后的头几百步密切盯 loss 斜率——恢复点的 loss 应略低于原曲线同位置(少走了污染步)。若恢复点 loss 反而更高,说明回滚不够远或脏数据不止一段,回到第一步。
有一条纪律值得写下来:宁可回滚到一天前的 checkpoint,也不要对着 spike 后的状态调参硬救。公开的 loss 曲线(OPT-175B 是个著名例子)里,硬扛 spike 的后果是平台期抬高数倍、用大量步数才追平,账面上比回滚亏得多。
预防配置
排查成本远高于预防,标准配置里该有的是:动态 loss scale(fp16 必开,bf16 场景也有框架提供)、inf/nan 每步检查 + 跳步(跳过该 batch 不更新)、梯度范数与 update/param 比率的持续打点、数据 loader 的偏移量日志。spike 复盘时最值钱的信息永远是"当时吃的是哪段数据",没有这个偏移量,脏数据归因就是猜。