AdamW 原理 从自适应步长到解耦权重衰减
沿定义、计算与实验边界阅读,分开模型原理、教学验证和实际系统效果。
章节速览8 节 · 展开选择
反向传播与优化器已经区分了“求梯度”和“使用梯度”。这篇继续追踪梯度交给 Adam 以后经历的几次变换:为什么要保存两份状态,为什么除以偏差修正项,以及权重衰减究竟在哪一步参与。
Dropout在训练前向里引入随机性,LayerNorm改变特征的归一化。AdamW 则直接规定参数如何更新。这几件事可以同时出现,却不能彼此替代,也不能仅凭某次参数变小就认定泛化改善。
m 平滑梯度,v 平滑平方梯度;v 不是中心化方差。
epsilon 在根号外;学习率也乘在衰减项上。
L2 进入梯度和矩估计;解耦衰减直接作用于旧参数。
固定这一篇的更新约定
讨论实数、稠密的一组参数,最小化可微数据损失 fₜ;gₜ=∇fₜ(θₜ₋₁) 在更新前的参数上计算。各坐标独立进行下面的算术,共享时间步 t 与超参数。初始 m₀=v₀=0;0≤β₁,β₂<1、学习率 αₜ≥0、衰减系数 λ≥0、epsilon ε>0。
采用 PyTorch 2.14 AdamW 的非 AMSGrad、非 maximize 公式:
mₜ = β₁mₜ₋₁ + (1−β₁)gₜ;vₜ = β₂vₜ₋₁ + (1−β₂)gₜ²。
m̂ₜ = mₜ/(1−β₁ᵗ);v̂ₜ = vₜ/(1−β₂ᵗ)。
θₜ = (1−αₜλ)θₜ₋₁ − αₜm̂ₜ/(√v̂ₜ+ε)。
平方、开根号、除法都按坐标执行。这里的 weight_decay=λ 与学习率相乘;不能把别处记号中“每一步直接减去 λθ”的 λ 原样搬来。AdamW 原论文的 Algorithm 2 使用调度倍数 ηₜ,把更新写成 −ηₜ(α·自适应方向+λ_paper·θ)。当 αₜ=ηₜα 时,匹配本文约定需要 λ_paper=α·λ,而非假定同名符号总有相同数值。
本文不实现混合精度、稀疏梯度、AMSGrad、参数组或框架内核,也不声称这些公式验证了具体训练任务。生产实现的状态保存、跳步与调度顺序仍需单独处理。
零初始化修正了什么偏差
把一阶递推展开,可得 mₜ=(1−β₁)Σₖ₌₁ᵗ β₁ᵗ⁻ᵏgₖ。这些系数的和为 1−β₁ᵗ,初期小于 1;除以它,就把已有梯度的指数权重归一化为和为 1。
若各步梯度的期望均为同一个 μ,则 E[mₜ]=(1−β₁ᵗ)μ,于是 E[m̂ₜ]=μ。同理,若平方梯度的期望恒为 ν,E[v̂ₜ]=ν。这说明Adam 原论文 Algorithm 1中修正项的来历;不表示真实训练中变化的梯度分布也被变成了平稳分布。
v 保存的是二阶原始矩,即平方梯度的平滑值,不是 E[g²]−E[g]²。即使每步梯度恒为 c,修正后仍有 m̂=c、v̂=c²,因此方向为 c/(|c|+ε)。ε 很小时,不同幅值的恒定梯度可能给出相近的单步位移,但全局学习率仍然决定尺度。
flowchart LR A[旧参数计算数据梯度] --> B[平滑梯度与平方梯度] B --> C[用时间步修正零初始化偏差] C --> D[自适应位移] E[旧参数乘衰减因子] --> F[相减得到新参数] D --> F F --> G[下一步重新计算数据梯度] G --> A
epsilon 不是可以随手移动的括号
本文分母是 √v̂+ε,而非 √(v̂+ε)。例如 v̂=4、ε=0.5 时,前者为 2.5,后者约为 2.1213,两者产生不同更新。手算故意用较大的 ε 展示差异,它不是默认超参数建议。
若把偏差修正合进学习率,也要连同 epsilon 一起变换。令 c₁=1−β₁ᵗ、c₂=1−β₂ᵗ,则同一个自适应位移可以写成:
αₜmₜ/[c₁(√(vₜ/c₂)+ε)]
= [αₜ√c₂/c₁] · mₜ/[√vₜ+ε√c₂]。
在后一式中继续使用未缩放的 ε,一般不与前一式完全相同。阅读不同库或论文时,应把完整分母和偏差项对齐,再判断只是重排还是改变了算法。
L2 为什么没有自然变成解耦衰减
对没有动量、没有自适应缩放的普通 SGD,把 λ‖θ‖²/2 加入损失后,梯度变为 g+λθ;一步为 θ−α(g+λθ)=(1−αλ)θ−αg。在这个特定约定下,它与同因子的直接衰减相同。
Adam 加 L2 则把 ĝₜ=gₜ+λθₜ₋₁ 放入 m、v 两个递推,L2 的影响同时进入方向、尺度和历史状态。AdamW 只把数据梯度 g 放入这两个状态,额外从旧参数扣除 αλθ。自适应缩放和状态递推使普通 SGD 的那一步代数拆分无法直接照搬。
固定 θ₀=(2,2)、数据梯度 g₁=(1,4)、β₁=β₂=0.5、α=0.1、λ=0.2、ε=0.5,两种算法的第一步可以完整算出:
| 路径 | 进入矩估计的梯度 | 第一对修正矩 m̂、v̂ | 更新后参数 |
|---|---|---|---|
| AdamW | (1,4) | m̂=(1,4),v̂=(1,16) | (1.8933333, 1.8711111) |
| Adam 加 L2 | (1.4,4.4) | m̂=(1.4,4.4),v̂=(1.96,19.36) | (1.9263158, 1.9102041) |
AdamW 的两个坐标都先乘 0.98,再减去各自的自适应位移;Adam 加 L2 没有再额外乘一次 0.98。两者在相同初值和同名 λ 下已经不同,这个反例足以说明它们不普遍等价,并不是比较某个任务的好坏。
flowchart TD A[同一旧参数与数据梯度] --> B[Adam 加 L2] A --> C[AdamW] B --> D[先加 lambda 乘旧参数] D --> E[增广梯度进入两个矩状态] C --> F[仅数据梯度进入两个矩状态] C --> G[旧参数独立乘衰减因子] E --> H[自适应更新] F --> I[自适应位移与衰减合并] G --> I
两步手算:零梯度还会继续移动
现在只看一个坐标 θ₀=2,超参数仍为 β₁=β₂=0.5、α=0.1、λ=0.2、ε=0.5,给定数据梯度序列 [2,0]。这是用来审查更新公式的外部输入,不是声称某个模型恰好产生这些梯度。
| t 与 gₜ | mₜ,vₜ | 修正除数 c₁=c₂ | m̂ₜ,v̂ₜ | 衰减后的旧参数 | 自适应位移 | 新参数 θₜ |
|---|---|---|---|---|---|---|
| 1;2 | 1,2 | 1/2 | 2,4 | 2×0.98=1.96 | 0.1×2/(2+0.5)=0.08 | 1.88 |
| 2;0 | 1/2,1 | 3/4 | 2/3,4/3 | 1.88×0.98=1.8424 | 0.1×(2/3)/(√(4/3)+0.5)≈0.0402893 | ≈1.8021107 |
第二步 g₂=0,历史 m₂ 仍为正,因此自适应位移没有立刻消失。只要本步修正一阶矩 m̂ₜ 为零,自适应位移就为零;全部历史梯度均为零是一种充分情形,历史正负贡献抵消也可能得到同样结果。例如 β₁=β₂=0.5、梯度为 [2,−1] 时,第二步 m₂=0、v₂=1.5,仍然只有衰减贡献,并不要求 v₂ 为零。
自检:AdamW 解耦后,改变 λ 会永远不影响 m、v 吗?
不成立。若外部固定给定相同梯度序列,m、v 的公式确实不含 λ;但实际训练的参数路径会随 λ 改变,下一步重新计算的梯度也可能不同,继而改变后续状态。
自检:当前数据梯度为零,能断定本步只有权重衰减吗?
不能。还要看一阶矩是否保留了过去的梯度。两步手算的第二步仍扣除约 0.0402893 的自适应位移;零梯度与缺失梯度也不能混为一谈。
一个可直接运行的标准库实现
代码用不可变状态返回新值,不原地改参数或旧状态;同时提供 Adam 加 L2 的对照入口。状态是同一组参数连续更新的状态,β 应沿该序列保持不变。若中途更换 β,简单使用当前 βᵗ 不再代表此前全部权重的修正,因此应重新定义状态协议。
1 | from dataclasses import dataclass |
第一段轨迹的参数应依次为 1.88、约 1.8021107;二维对照与前表一致。实现拒绝非有限数值及会产生非有限中间值的输入,不承诺任意极端 float 都能稳定更新。返回的额外信息用于核对手算,不是训练监控系统。
调度、参数分组与恢复状态的边界
| 需要明确的事 | 本文约定或应检查的结果 |
|---|---|
| 学习率变化 | 每步的 αₜ 同时乘自适应项与 λ;λ 固定不表示每步收缩比例固定 |
| 想把它叫作收缩 | 需有 0≤αₜλ≤1;大于 1 时因子为负,不能继续描述为非负比例缩小 |
| λ=0 | 退化为本文 epsilon 约定下的 Adam |
| αₜ=0 | 本实现参数不变,但仍更新矩状态与步数,不等于整步跳过 |
| 某些坐标不衰减 | 可按模型目标划分参数组,不把“排除所有偏置或归一化参数”当作无条件定理 |
| 没有梯度与梯度为零 | 本实现只接受完整数值向量;不模拟框架对 grad=None 的跳过行为 |
| 暂停后恢复 | 参数、m、v、步数、超参数与调度位置应一起恢复;只存 θ 不足以续接相同轨迹 |
PyTorch 2.14 Adam 还提供 decoupled_weight_decay=True。因此阅读当前配置不能只看类名,应连同这个开关与参数组一起核对。不同实现的浮点执行顺序可能产生微小差异,本文没有声称已经逐内核验证 PyTorch。
验证范围与继续阅读
tests/verify-adamw-article.cjs 提取上面的原样 Python 并实际执行。专门参考在高精度 Decimal 中把历史梯度展开为有限加权和,不沿用待测 m、v 的递推;另检查增广二次损失的数值梯度、epsilon 重排、零梯度历史和状态恢复。原图中的两组参数与手算表同时接受数值核验。
600 组预定种子的随机轨迹共 5020 步、17245 个坐标通过 70 位 Decimal 参考,并核对坐标置换、固定梯度序列下的解耦状态和 600 次断点恢复。另有 100 组恒定梯度的 2000 步、1406 个数值导数、500 次 epsilon 等价重排、历史梯度抵消反例和 33 类非法输入。两图共 33 个文字节点均位于画布内,原图已逐张查看。
这些检查验证更新算术和输入约定,没有训练真实神经网络,没有测量速度、泛化、正则化收益或证明一般非凸问题的收敛。继续阅读可回到反向传播检查数据梯度的来源,或比较Dropout 的期望损失与LayerNorm 的特征梯度,把前向随机性、归一化与优化器状态放在各自的位置。