机器学习数学基础专题【左扬精讲】—— 大模型的优化器详解:从 SGD 到 AdamW,从动量到自适应学习率
机器学习数学基础专题【左扬精讲】—— 大模型的优化器详解:从 SGD 到 AdamW,从动量到自适应学习率
优化器(Optimizer)是训练循环中唯一负责 "修改模型参数" 的组件。 前向传播产生预测,损失函数衡量错误,反向传播计算梯度——但真正把梯度转化为参数更新的,是优化器。
本文从最基础的 SGD 出发,逐步引入动量、自适应学习率和权重衰减,完整梳理从 SGD 到 AdamW 的设计演进,并给出大模型场景下的选择指南。
本文所有代码片段均为通用 PyTorch 2.x 写法,可直接在 PyTorch 2.0+ 环境中运行验证 。
本文涉及的核心 PyTorch 模块(PyTorch 2.x):
torch.optim.SGD ← 随机梯度下降 + 动量
torch.optim.Adam ← 自适应矩估计
torch.optim.AdamW ← 解耦权重衰减的 Adam
torch.optim.Adagrad ← 自适应梯度算法
torch.optim.RMSprop ← 均方根传播
torch.optim.lr_scheduler ← 学习率调度器(本文侧重优化器本身)
torch.nn.utils.clip_grad_norm_ ← 梯度裁剪
优化器 SGD 动量 AdamW 自适应学习率 权重衰减 梯度裁剪 二阶矩 偏置修正 学习率
学习重点提示
★ 必须掌握
- 必须掌握:SGD 的更新公式 — theta = theta - lr * grad
- 必须掌握:动量(Momentum)解决的是 SGD 在峡谷地形中的震荡问题
- 必须掌握:AdamW 的"W"代表 Weight Decay 解耦,这是它与 Adam 的核心区别
- 必须掌握:大模型训练默认选 AdamW + 梯度裁剪
★ 建议掌握
- 建议掌握:AdaGrad / RMSprop 的设计动机与局限性
- 建议掌握:Adam 的偏置修正(bias correction)原理
- 建议掌握:为什么 SGD + 动量在大数据集上依然有竞争力
本文目录
- 一、优化器在训练中的角色 — What & Why
- 二、SGD:最基础的梯度下降
- 三、Momentum:给 SGD 加上"惯性"
- 四、AdaGrad / RMSprop:自适应学习率的探索
- 五、Adam:动量 + 自适应学习率的结合
- 六、AdamW:解耦权重衰减的终极形态
- 七、优化器选择指南
- 八、常见问题 FAQ(20 组)
- 九、Roadmap 后续预告
一、优化器在训练中的角色 — What & Why
What — 优化器在训练循环中扮演什么角色?
优化器是训练循环中唯一的"决策者"。它接收反向传播计算出的梯度,决定每个参数往哪个方向走、走多远。没有优化器,梯度只是数字,永远不会变成参数更新。
Why — 为什么不能手动写 w = w - lr * grad?
问题一:不同参数需要不同的步长
稀疏特征和稠密特征的梯度量级差异巨大。统一学习率会导致稀疏参数更新不足、稠密参数更新过度。
问题二:优化器维护了训练状态
Adam 等优化器维护了动量和二阶矩估计。这些状态需要在多步之间累积,手动管理极其复杂。
没有优化器会发生什么?
- 梯度被计算但从未使用,模型参数永远不变。
- 无法利用动量等加速收敛的机制。
- 不同参数的步长无法自适应,调参变成纯经验艺术。
所有 PyTorch 优化器共享同一套接口契约:
# 1. 创建优化器,传入模型参数
optimizer = torch.optim.AdamW(model.parameters(), lr=3e-5)
# 2. 每个 iteration: 清零梯度
optimizer.zero_grad()
# 3. ... 前向传播 + 反向传播 ...
# 4. 每个 iteration: 更新参数
optimizer.step()
# 5. 可选:调整学习率(由 scheduler 驱动)
scheduler.step()
无论 SGD、Adam 还是 AdamW,接口完全一致。替换优化器只需要改一行创建代码。
核心结论
优化器的核心任务是把梯度转化为参数更新。不同优化器的区别在于:① 是否对每个参数使用自适应步长,② 是否维护历史梯度状态,③ 如何处理权重衰减。
二、SGD:最基础的梯度下降
What — SGD 是什么?
随机梯度下降(Stochastic Gradient Descent)是最基础的优化算法。每次只用一个 batch 的梯度估计整体梯度,然后按固定学习率更新参数。
θ = θ - η · ∇θ L(θ)
其中 η 是学习率,∇θ L 是损失函数对参数的梯度。
Why — 为什么 SGD 虽然简单,却至今仍有价值?
问题一:全批量梯度下降不可行
对于百万级数据集,计算完整梯度需要遍历全部样本,每次更新的计算量是 batch 版的几千倍。
问题二:SGD 的噪声有正则效果
batch 梯度的随机噪声相当于给训练过程注入了隐式正则,有助于跳出 sharp minimum,提升泛化能力。
没有 SGD 会发生什么?
- 大模型训练的时间成本将不可接受。
- 无法享受 batch 噪声带来的正则效应。
纯 SGD 不带动量,通常用于教学或简单任务:
# 纯 SGD(无动量)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# SGD + 动量(工业界最常用)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
注意:PyTorch 的 SGD 默认 momentum=0,即纯 SGD。实际项目中几乎不会用纯 SGD,都会配上动量 。
三、Momentum:给 SGD 加上"惯性"
What — 动量(Momentum)是什么?
动量是 SGD 的改进版本。它在每次更新时引入一个"速度"概念,当前步的更新方向是历史梯度的指数移动平均,而不是仅看当前梯度。
v = μ · v + (1 - μ) · g
θ = θ - η · v
PyTorch 中的实现略有不同:v = momentum * v + grad,然后 param -= lr * v。
Why — 为什么动量能加速收敛?
问题一:SGD 在峡谷地形中震荡
损失曲面在某个方向上非常陡峭、另一个方向平缓时,SGD 会在陡峭方向上来回震荡,前进效率极低。
问题二:动量抑制了震荡方向
震荡方向的梯度方向交替变化,动量通过指数移动平均把它们抵消;而前进方向的梯度方向一致,被放大。
没有动量会发生什么?
- SGD 在陡峭方向来回震荡,收敛速度极慢。
- 容易卡在局部极小值或鞍点附近。
- 大批量训练时噪声被平均掉,SGD 失去正则效应。
PyTorch 的 SGD 还支持 Nesterov 动量,它在计算梯度前先"预览"动量方向的位置:
optimizer = torch.optim.SGD(
model.parameters(),
lr=0.01,
momentum=0.9,
nesterov=True,
)
Nesterov 的核心思想是"先预测,再修正":先用动量方向更新一步,再在该位置计算梯度。这相当于给 SGD 装上了"导航系统",在凸优化中有理论保证的加速效果 。
四、AdaGrad / RMSprop:自适应学习率的探索
What — AdaGrad 和 RMSprop 分别是什么?
AdaGrad为每个参数维护一个历史梯度平方的累积和,频繁更新的参数步长自动变小,稀疏参数步长保持较大。RMSprop是 AdaGrad 的改进版,用指数移动平均替代累积和,避免步长过早归零。
AdaGrad: G = G + g ⊙ g, θ = θ - (η / (√G + ε)) ⊙ g
RMSprop: E[g²] = ρ · E[g²] + (1-ρ) · g⊙g, θ = θ - (η / (√E[g²] + ε)) ⊙ g
Why — 为什么需要自适应学习率?
问题一:统一学习率的困境
同一个模型里,词嵌入矩阵的梯度可能很大,而注意力 bias 的梯度可能很小。统一学习率无法同时满足两者。
问题二:AdaGrad 的步长单调递减
AdaGrad 的历史梯度累积会导致步长持续变小,在非凸优化中可能过早停止。RMSprop 通过指数移动平均解决了这个问题。
没有自适应学习率会发生什么?
- 稀疏特征(如 rare words)更新不足,模型学不到低频模式。
- 稠密特征步长过大,参数在最优解附近震荡。
RMSprop 在 PyTorch 中的默认参数 ρ=0.99,ε=1e-8:
optimizer = torch.optim.RMSprop(
model.parameters(),
lr=1e-3,
alpha=0.99, # rho: 移动平均衰减率
eps=1e-8, # epsilon: 数值稳定项
weight_decay=0.0,
)
RMSprop 在 RNN 训练中曾有良好表现,但在 Transformer 大模型场景中已被 AdamW 取代。了解它的设计有助于理解 Adam 的演进脉络 。
五、Adam:动量 + 自适应学习率的结合
What — Adam 是什么?
Adam(Adaptive Moment Estimation) combines 一阶动量(梯度均值)和二阶动量(梯度平方均值),对每个参数自适应地调整学习率。它是当前最流行的优化器之一。
m = β1 · m + (1-β1) · g
v = β2 · v + (1-β2) · g⊙g
&hat;m = m / (1 - β1t)
&hat;v = v / (1 - β2t)
θ = θ - η · &hat;m / (√&hat;v + ε)
Why — 为什么 Adam 如此受欢迎?
问题一:动量 + 自适应是互补的
一阶动量加速收敛(沿一致方向加快),二阶动量自适应步长(稀疏参数大步、稠密参数小步)。两者结合效果叠加。
问题二:偏置修正保证早期稳定性
初始时 m=0, v=0,前几步的估计会有偏。偏置修正 m_hat = m / (1 - beta1^t) 在 t 较小时放大估计值,保证早期更新方向正确。
没有 Adam 会发生什么?
- 需要手动在 SGD 和 RMSprop 之间做选择,且两者各有短板。
- 非凸优化中自适应方法通常收敛更快、超参更鲁棒。
PyTorch 的 torch.optim.Adam 默认参数:lr=1e-3,betas=(0.9, 0.999),eps=1e-8:
optimizer = torch.optim.Adam(
model.parameters(),
lr=1e-3, # 默认学习率比 SGD 大
betas=(0.9, 0.999), # 一阶/二阶动量衰减率
eps=1e-8, # 数值稳定项
weight_decay=0, # 注意:Adam 默认 weight_decay=0
)
关键注意点:PyTorch 的 Adam 默认 weight_decay=0,L2 正则实际上是加到梯度上的(而非直接对参数做衰减),这在实践中会导致权重衰减与自适应学习率耦合 。
六、AdamW:解耦权重衰减的终极形态
What — AdamW 是什么?
AdamW(Adam with decoupled Weight Decay)是 Adam 的改进版本。它将权重衰减与梯度更新解耦:权重衰减直接作用于参数,L2 正则不再混入梯度中。
AdamW 两步更新:
1. 梯度更新:θ = θ - η · &hat;m / (√&hat;v + ε)
2. 权重衰减:θ = θ - η · λ · θ
Why — 为什么解耦如此重要?
问题一:原始 Adam 的 L2 正则实现有缺陷
原始 Adam 将 L2 正则直接加到梯度中:grad = grad + lambda * param。这意味着权重衰减的步长也被自适应学习率缩放,当自适应学习率对某个参数很大时,权重衰减也被过度放大。
问题二:AdamW 的权重衰减是固定比例的
AdamW 先做自适应梯度更新,再独立做权重衰减 param *= (1 - lr * weight_decay)。衰减步长与学习率自适应无关,行为更可预测。
没有解耦会发生什么?
- 大学习率时权重衰减被过度放大,参数被压缩过度。
- 小学习率时权重衰减几乎不起作用,正则效果丢失。
- 不同论文报告的"weight_decay=0.01"效果不一致,因为实现不同。
大模型训练几乎都使用 AdamW,配合梯度裁剪和 warmup:
optimizer = torch.optim.AdamW(
model.parameters(),
lr=3e-5, # 大模型典型学习率
weight_decay=0.01, # 标准权重衰减
betas=(0.9, 0.95), # 二阶动量常用 0.95(vs Adam 默认 0.999)
eps=1e-8,
)
注意:大模型通常把 betas[1] 从 0.999 降到 0.95,因为大 batch 下二阶矩估计更稳定,不需要那么长的窗口 。
优化器演进速记
- SGD:基础,固定步长,噪声有正则效应
- SGD + Momentum:加惯性,抑制震荡
- AdaGrad:自适应步长,但步长单调递减
- RMSprop:指数平均替代累积,解决 AdaGrad 过早衰减
- Adam:动量 + 自适应 + 偏置修正
- AdamW:解耦权重衰减,大模型事实标准
七、优化器选择指南
What — 如何为不同场景选择优化器?
没有万能优化器。选择取决于任务类型、数据规模、模型架构和训练资源。
Why — 为什么同样的模型换优化器效果差异巨大?
问题一:不同优化器的隐式正则强度不同
SGD + 动量 + 大 batch 的隐式正则很强,可能在小数据集上就欠拟合;AdamW 的隐式正则较弱,需要显式 weight decay 配合。
问题二:学习率的最优范围因优化器而异
SGD 的推荐学习率通常为 0.1~1.0,Adam 为 1e-3~1e-4,AdamW 为 1e-5~3e-5。直接套用会导致训练失败。
# 场景 1: CNN 图像分类(ResNet 等)
optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4)
# 场景 2: Transformer 大模型预训练(BERT/GPT 类)
optimizer = torch.optim.AdamW(model.parameters(), lr=3e-5, weight_decay=0.01, betas=(0.9, 0.95))
# 场景 3: 小数据集 fine-tuning
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-5, weight_decay=0.01)
# 场景 4: RNN / 序列模型
optimizer = torch.optim.RMSprop(model.parameters(), lr=1e-3, alpha=0.99)
核心原则:大模型用 AdamW,CNN 用 SGD+Momentum,小模型 fine-tuning 用更低学习率的 AdamW。
八、常见问题 FAQ(20 组)
FAQ 常见问题解答
Q1. SGD 和 Adam 的根本区别是什么?
SGD 对所有参数使用相同的学习率,Adam 为每个参数自适应调整学习率。SGD 的更新只依赖当前梯度,Adam 还维护了一阶和二阶动量的历史估计。
Q2. 为什么大模型不用纯 SGD?
纯 SGD 在大模型上收敛极慢且不稳定。大模型参数量大、条件数差,需要自适应学习率来平衡不同参数组的更新幅度。SGD 需要极精细的超参 tuning 才能工作 。
Q3. 动量的物理意义是什么?
动量模拟了物理中的惯性。在损失曲面的峡谷中,梯度方向交替变化;动量通过对历史梯度做指数移动平均,抑制震荡方向、加速一致方向。
Q4. Adam 和 RMSprop 的区别是什么?
Adam 在 RMSprop 的基础上增加了一阶动量。RMSprop 只有二阶自适应学习率,Adam 同时使用一阶动量和二阶动量,配合偏置修正。
Q5. 为什么 AdamW 比 Adam 更适合大模型?
AdamW 将权重衰减与梯度更新解耦。原始 Adam 的 weight decay 实现有缺陷,会与自适应学习率耦合;AdamW 的权重衰减是独立操作,行为更可预测 。
Q6. AdamW 的 weight_decay 和 L2 正则一样吗?
数学形式相似但实现不同。L2 正则加到损失函数中,通过反向传播影响梯度;AdamW 的 weight decay 直接作用于参数,不影响梯度计算。这就是"解耦"的含义。
Q7. Adam 的 betas 参数怎么理解?
beta1 控制一阶动量的平滑度,beta2 控制二阶动量的平滑度。beta1=0.9 表示当前梯度占 10%,历史占 90%;beta2=0.999 表示二阶矩的窗口极长,适合稀疏梯度场景。
Q8. 什么是偏置修正(bias correction)?
训练初期 m 和 v 初始化为 0,前几步的估计会偏小。偏置修正用 m/(1-beta1^t) 和 v/(1-beta2^t) 放大早期估计,t 越大修正越弱。
Q9. 为什么 Adam 的学习率通常比 SGD 小很多?
因为 Adam 的更新幅度受动量累积和自适应缩放影响。相同学习率下,Adam 的实际步长通常比 SGD 大得多。使用相同学习率会导致 Adam 训练不稳定。
Q10. 梯度裁剪和优化器是什么关系?
梯度裁剪发生在反向传播之后、optimizer.step() 之前。它是一个独立的操作,不依赖特定优化器。所有优化器都可以配合梯度裁剪使用。
Q11. 为什么 SGD 配合动量在 CNN 上表现很好?
CNN 的损失曲面相对平滑,SGD 的噪声有助于泛化。ImageNet 竞赛中,SGD + Momentum + 大 batch + 学习率调度长期占据主导地位 。
Q12. AdaGrad 为什么不再流行?
因为它的步长单调递减。历史梯度平方的累积使得步长持续变小,在非凸优化的后期几乎停止更新。RMSprop 和 Adam 都通过指数移动平均解决了这个问题。
Q13. 优化器的 weight_decay 应该多大?
视模型和数据集而定。大模型常用 0.01,CNN 常用 1e-4,小模型可能用 0.1。weight_decay 过大导致欠拟合,过小导致过拟合 。
Q14. 学习率和 weight_decay 有交互吗?
有。在 SGD 中,weight decay 加到梯度上,受学习率缩放。在 AdamW 中,weight decay 是独立步长,但仍受学习率影响。两者需要协同 tuning。
Q15. 多组参数能否用不同优化器?
可以,使用 param_groups。PyTorch 优化器支持传入多个 param_group,每组可以有不同的 lr、weight_decay 等。常见做法是对 bias 和 LayerNorm 参数禁用 weight decay。
Q16. 为什么 BERT 训练用 Adam 而不是 SGD?
因为 BERT 需要快速收敛且对超参鲁棒。Adam 的自适应学习率减少了手动 tuning 的工作量,配合 warmup 可以在约 1M 步内完成预训练 。
Q17. 优化器状态占用多少显存?
Adam/AdamW 需要存储两倍参数量的动量状态。对于 7B 参数模型,AdamW 的优化器状态约占用 14B 浮点数的显存(fp32 约 56GB)。这就是 FSDP 等参数分片方案要解决的问题。
Q18. 训练到一半可以换优化器吗?
技术上可以,但不推荐。因为新优化器没有历史动量状态,相当于从头开始积累。如果必须换,建议保存 checkpoint 后重新初始化优化器。
Q19. 什么是 Lookahead 优化器?
Lookahead 是一种优化器包装器,在基础优化器外额外维护"慢权重"。它让基础优化器(如 Adam)快速探索,同时用慢权重跟踪更稳定的方向,提升泛化能力 。
Q20. 优化器中的 amsgrad 是什么?
AMSGrad 是 Adam 的变体,使用 v 的累积最大值而非当前值。论文声称它解决了 Adam 在某些问题上泛化差的问题,但后续研究表明差异不大。PyTorch 支持 amsgrad=True 参数。
全篇总纲
- 1 个核心接口:optimizer.zero_grad() / step() — 所有优化器一致
- 1 条演进主线:SGD → SGD+Momentum → AdaGrad → RMSprop → Adam → AdamW
- 2 个关键设计:动量(一阶自适应)+ 二阶自适应学习率
- 1 个终极选择:大模型默认 AdamW + 梯度裁剪 + warmup
- 3 个避坑:不用纯 SGD、不用原始 Adam(用 AdamW)、不用统一学习率
九、Roadmap 后续预告
后续预告
- 第 8 篇:学习率调度器全解 — Warmup、Cosine、Linear、Polynomial、ReduceLROnPlateau
- 第 9 篇:混合精度与分布式训练 — 从单机单卡到 DDP、FSDP、DeepSpeed
- 第 10 篇:大模型推理与部署 — 从 PyTorch 到 ONNX / TensorRT / vLLM

浙公网安备 33010602011771号