训练为什么会不稳定:学习率、梯度累积与数值精度

成长演进

具备清晰骨架、初步数学推导或工程验证,持续补充论据与实测。

ℹ数字花园

🌿 什么是数字花园与思想演进?

遵循 Maggie Appleton 与 Andy Matuschak 的常青笔记理念:这里不是按时间沉底的流水账,而是持续修剪打磨的知识生态。文章按成熟度经历:

  • 🌱 萌芽 (Seedling): 发散灵感、读书摘录、开放猜想
  • 🌿 演进 (Budding): 结构渐明、初步代码推导、积极修正
  • 🌳 常青 (Evergreen): 逻辑闭环、持久经得起检验的基石
  • 🔬 实验待证 (Speculative): 结合沙盒与仿真探针的理论探索
浏览全站数字花园与笔记全景 →

同一套数据和模型,换一个学习率,损失可能正常下降,也可能很快变成 NaN。把 batch 调小以后终于装进内存,训练又开始明显抖动。

这些现象不能都归结为“参数没调好”。有的来自目标或标签错误,有的来自一次更新使用的数据变了,还有的只是数字超出了计算格式能表达的范围。

本篇接着batch 与有效 token,从一次更新出发,解释常用控制项分别改变了什么。目标是遇到异常时能缩小原因范围,而不是记住一组似乎适用于所有模型的数值。

梯度给方向,学习率决定走多远#

先看最简单的梯度下降:

θt+1=θt−ηtgt\theta_{t+1}=\theta_t-\eta_t g_t

θ\theta 是参数,gg 是当前目标对参数的梯度,η\eta 是学习率。梯度描述在当前位置附近,参数改变怎样影响目标;学习率决定沿这个方向移动多少。

用一个单参数例子看得更清楚。设 L(θ)=θ2/2L(\theta)=\theta^2/2,梯度就是 θ\theta,于是更新后为:

θt+1=(1−η)θt\theta_{t+1}=(1-\eta)\theta_t

从 θ0=2\theta_0=2 出发:

学习率前几次参数现象
0.52 → 1 → 0.5 → 0.25幅度逐渐减小
1.52 → −1 → 0.5 → −0.25来回越过零,但幅度减小
2.52 → −3 → 4.5 → −6.75幅度越来越大

这个例子里,0<η<20<\eta<2 才收敛。真实神经网络没有这么简单的统一范围,AdamW 也不是这条裸梯度下降公式。例子只说明:方向正确,并不保证步子迈得大也安全。

反过来,学习率非常小可以让曲线看起来平静,却可能在有限预算里几乎没学到东西。所以稳定性要与进展速度一起看。

AdamW 还会利用过去的梯度#

Adam 对梯度及其平方维护指数移动平均,直观上分别记录近期方向与近期尺度。常见写法是:

mt=β1mt−1+(1−β1)gtm_t=\beta_1m_{t-1}+(1-\beta_1)g_tvt=β2vt−1+(1−β2)gt2v_t=\beta_2v_{t-1}+(1-\beta_2)g_t^2

这里的平方和后续运算按参数分量进行。初始化为零会使早期估计偏小,因此再进行偏差修正,得到 m^t\hat m_t 和 v^t\hat v_t。更新使用类似 m^t/(v^t+ϵ)\hat m_t/(\sqrt{\hat v_t}+\epsilon) 的量,而不是直接照搬原始梯度。Adam 论文

因此,“梯度范数翻倍”不能直接翻译成“Adam 参数更新距离翻倍”。历史状态、学习率和分量尺度都在参与计算。

AdamW 将权重衰减与梯度中的自适应缩放分开处理。概略地写为:

θt=(1−ηtλ)θt−1−ηtm^tv^t+ϵ\theta_t=(1-\eta_t\lambda)\theta_{t-1} -\eta_t\frac{\hat m_t}{\sqrt{\hat v_t}+\epsilon}

λ\lambda 是权重衰减系数。原论文指出,对这类自适应优化器,把 L2 项加入目标与这种解耦处理不等价。Decoupled Weight Decay Regularization

具体实现还要说明哪些参数使用衰减、参数组学习率是否相同、采用哪些数值选项。PyTorch 的 AdamW 文档给出了其更新过程和接口选项;实验应记录实际版本与参数,而非只记“用了 AdamW”。PyTorch 2.9 AdamW

梯度累积减少的是同时处理的数据量#

假设想让一次更新使用八条序列,但内存一次只能放两条,可以分四次前向和反向,累积梯度,最后更新一次参数。

在目标权重一致、累积期间参数不变等条件下,梯度求和能表达同一个大 batch 的梯度。但实际浮点顺序、随机 dropout 或跨样本操作可能使结果不完全一样;它也不等价于连续做四次参数更新。

最常见的误操作有两个:每个小批次后就更新了参数,或者每次反向前把此前累积的梯度清空了。前者改变更新频率,后者只留下最后一个小批次。

如果各小批次有效目标数不同,平均损失还要按有效数加权。上一篇已经推导过,不能始终机械地除以累积次数。

累积通常降低同时保留的激活需求,但模型参数、梯度和优化器状态仍然需要存放。一个模型若仅这些状态就放不下,继续增加累积次数并不会让它突然可行。

batch 变大,为什么不能机械放大学习率#

更大的有效 batch 通常改变梯度估计的波动程度,也改变固定 token 预算下更新多少次。

假设总预算为一百万个有效目标。每次更新用一千个,就有约一千次更新;改成四千个,只有约二百五十次。若学习率调度仍按旧的“第几步”设计,整个训练过程的节奏也变了。

所以比较时至少同时报告:每次更新的有效目标、累计有效目标、更新次数,以及学习率随哪个计数变化。

某些设置下可以采用学习率随 batch 调整的经验规则,但它不是任意模型和预算下的定理。合理做法是在已稳定的基线附近做有限比较,并看固定预算下的验证结果,而不是把“线性放大”当成无需检验的配置公式。

warmup 解决早期节奏,不修复错误目标#

warmup 是在训练初期逐渐把学习率升到预定水平。它可以缓和初期较激进的更新;之后还可以按既定规则降低学习率。

在教学设计里,假设前一百次更新做线性 warmup,最大值为 ηmax⁡\eta_{\max},可以定义第 tt 次更新使用:

ηt=ηmax⁡min⁡(t/100,1),t≥1.\begin{aligned}\eta_t&=\eta_{\max}\min(t/100,1),\\&\qquad t\ge1.\end{aligned}

这样第一次是最大值的百分之一,第一百次达到最大值。别忽略步数从零还是一开始,以及调度器是在更新前还是后推进,这些细节足以造成一位偏移。

如果改了有效 batch,同样一百次更新会消耗不同的 token 数。报告 warmup 只写百分比,也要说明百分比的分母是预定步数还是 token 预算。

warmup 不会修复错位标签、错误遮罩或大量重复验证样本。遇到曲线异常,应先确认任务和数值,再调整调度策略。

低精度节省资源,也改变数值边界#

训练中的数字需要用有限位数表示。FP32、FP16、BF16 对可表示范围和精细程度作了不同取舍。BF16 的指数范围比 FP16 更宽,但不是所有数都表示得更精细。

混合精度也不等于把所有张量一律改成半精度。它通常根据算子安排计算类型,某些状态或归约仍使用更高精度;具体行为取决于设备、框架和实现。

FP16 训练中,较小梯度可能难以保留。loss scaling 的思路是先放大损失,让反向得到相应放大的梯度,再在更新前还原。它不是让实际学习率永久增大。

若采用梯度缩放,累积期间需要保持缩放约定一致,等累积完成再还原、检查和更新。PyTorch 的 AMP 示例明确说明了这一顺序,以及出现非有限梯度时可能跳过参数更新的行为。PyTorch AMP 示例

因此,日志里应区分“读过多少批次”和“真正执行多少次优化器更新”。若大量更新被跳过,却照常推进调度和预算,曲线解释就会混乱。

本文不预设某台设备上的最佳精度。首先建立可运行且数值有限的基线,再用相同样本与预算比较精度方案;不能把 FP32 也当作绝对正确的数学答案,它同样存在舍入误差。

梯度裁剪限制异常幅度,但不是万能保险#

常见的全局范数裁剪可写成:

g′=gmin⁡(1,c∥g∥2)g'=g\min\left(1,\frac{c}{\lVert g\rVert_2}\right)

若范数不超过阈值 cc,不改变梯度;超过时,把整组梯度按同一比例缩小。零梯度保持不变,实际代码要避免除零。

例如 g=(3,4)g=(3,4),范数为 5,阈值为 2,裁剪后就是 (1.2,1.6)(1.2,1.6)。方向不变,范数变成 2。这里说的是对所有被选参数合在一起计算的全局范数,不是每个分量单独截断。PyTorch 范数裁剪接口

若用了梯度缩放,应先还原再按真实阈值裁剪;否则阈值作用在被放大的数上。裁剪一般放在一次有效 batch 累积完成后。每个小批次先各自裁剪再相加,是另一个更新规则。

持续频繁裁剪值得检查:可能学习率或数据分布发生问题,也可能阈值本来就设得低。它不是自动证明存在梯度爆炸。已经出现 NaN 的梯度,也不能靠一个范数裁剪公式保证修好。

遇到异常,先保存能定位问题的证据#

只记录一个平均 loss,通常不够。每次更新至少关联数据批次标识、有效目标数、学习率、损失、梯度范数和更新是否执行。范数最好说明记录于还原后、裁剪前还是裁剪后。

现象优先检查不宜立即下的结论
第一次就出现非有限值输入、标签、空目标、logits 和损失计算模型太大所以必然不稳定
某一批次突然尖峰该批次长度、来源、目标数和异常文本所有数据都需要重新清洗
训练损失下降,验证变差过拟合、分布差异、评价口径优化器失效
损失很平但几乎不降梯度是否存在、参数是否更新、学习率数据已经学完
换精度后频繁跳过更新数值范围、缩放状态、相关算子低精度一定不适合该任务

在发现异常的位置保存最近的有效检查点和批次信息,再用短范围重现。不要在同一轮里同时换数据、学习率、batch 和精度,否则即使好了,也不知道哪一项起作用。

一组可解释的比较怎样安排#

先固定分词器、数据顺序、模型初始化、目标归一化和有效 batch,选择能稳定运行的基线。围绕一个问题比较,例如“当前学习率是否过大”。

可以选基线的一半、原值和两倍,在相同有效 token 预算下运行,提前约定停止条件:非有限数值、持续无更新,或资源超限。保存失败设置,不能只留下成功曲线。

学习率比较结束后,再单独研究精度或 batch。观察验证损失时,同时看达到该结果消耗的有效 token 和实际时间;单个随机种子的细微差异不足以证明稳定收益。

这是一份实验设计,不意味着这些配置已在本站运行。真正执行后的结论需要带上环境、日志和重复情况。

两个检查理解的问题#

第一,把一个大 batch 拆成四个小批次,为什么应该只更新一次参数?如果各部分有效目标数不同,损失怎样加权?

第二,某次梯度范数从 2 变成 20,裁剪后仍为 1,损失随后恢复正常。仅凭这些日志,能否判断是数据异常、学习率过大,还是阈值太低?还缺哪些证据?

最后一篇将这些检查整理成可解释的小模型预训练实验,把“跑起来”逐步推进到“知道结果说明了什么”。

文章概念网络

从章节进入正文概念。实线表示所属层级,虚线连接跨章节重复出现的概念,不代表因果关系。

50 / 50 个节点 · 正文标记术语与关键词概念

训练为什么会不稳定:学习率、梯度累积与数值精度同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept同一概念 / Shared concept训练为什么会不稳定:学习率、梯度累积与数值精度训练为什么会不稳定:学习率、梯度累积与数值精度NaNNaNbatch 与有效 tokenbatch 与有效 token梯度给方向,学习率决定走多远梯度给方向,学习率决定走多远学习率学习率AdamW 还会利用过去的梯度AdamW 还会利用过去的梯度AdamAdamAdamWAdamW权重衰减权重衰减PyTorch 2.9 AdamWPyTorch 2.9 AdamW梯度累积减少的是同时处理的数据量梯度累积减少的是同时处理的数据量batch 变大,为什么不能机械放大学习率batch 变大,为什么不能机械放大学习率warmup 解决早期节奏,不修复错误目标warmup 解决早期节奏,不修复错误目标warmupwarmup低精度节省资源,也改变数值边界低精度节省资源,也改变数值边界loss scalingloss scalingPyTorch AMP 示例PyTorch AMP 示例梯度裁剪限制异常幅度,但不是万能保险梯度裁剪限制异常幅度,但不是万能保险全局范数裁剪全局范数裁剪PyTorch 范数裁剪接口PyTorch 范数裁剪接口遇到异常,先保存能定位问题的证据遇到异常,先保存能定位问题的证据一组可解释的比较怎样安排一组可解释的比较怎样安排两个检查理解的问题两个检查理解的问题可解释的小模型预训练实验可解释的小模型预训练实验tokentoken梯度下降梯度下降PyTorchPyTorch状态状态优化优化实验实验状态状态机械机械优化优化tokentokentokentoken策略策略PyTorchPyTorch状态状态优化优化PyTorchPyTorch状态状态优化优化tokentoken归一化归一化分词分词环境环境选择选择实验实验预训练预训练实验实验
训练为什么会不稳定:学习率、梯度累积与数值精度

训练为什么会不稳定:学习率、梯度累积与数值精度

回到原文 ↗
BIBTEX

引用本文

@article{2026-10-20-llm-training-pretrain-05-optimization,
  author  = {枫语},
  title   = {训练为什么会不稳定:学习率、梯度累积与数值精度},
  journal = {枫语},
  year    = {2026},
  month   = {oct},
  url     = {https://meiluosi.github.io/posts/2026-10-20-llm-training-pretrain-05-optimization/}
}
🌐

本篇知识星丛与滑动侧栈

训练为什么会不稳定:学习率、梯度累积与数值精度 当前主轴

搜索

正在载入索引…