首页 机器学习 深度学习 LLM 推荐算法 Agent 项目 关于

优化器演进:SGD → Adam → AdamW → LAMB

优化器是深度学习训练的核心组件。从最简单的随机梯度下降(SGD),到带动量的 SGD,到 Adam、AdamW,再到 LAMB,每一次演进都围绕三个核心问题展开:怎么估计梯度方向?怎么自适应学习率?怎么处理大规模 batch? 本文系统梳理主流优化器的原理、代码实现和适用场景。


一、SGD 家族

1.1 朴素 SGD

\[\theta_{t+1} = \theta_t - \eta \cdot \nabla_\theta L(\theta_t)\]

朴素 SGD 收敛慢,且容易在高曲率方向震荡、在低曲率方向爬行。

1.2 SGD with Momentum

引入动量项,把过去梯度的指数移动平均作为更新方向:

\[v_t = \beta v_{t-1} + \nabla_\theta L(\theta_t)\] \[\theta_{t+1} = \theta_t - \eta \cdot v_t\]

其中 $\beta \in [0, 1)$ 通常取 0.9。Nesterov 动量进一步做前瞻修正:

\[v_t = \beta v_{t-1} + \nabla_\theta L(\theta_t - \eta \beta v_{t-1})\]

直观上,动量让优化器像一颗有惯性的球,在平坦方向加速、在陡峭方向减速。

1.3 PyTorch 实现

optimizer = torch.optim.SGD(
    model.parameters(),
    lr=0.01,
    momentum=0.9,
    weight_decay=1e-4,    # L2 正则化
    nesterov=True,
)

二、AdaGrad(Adaptive Gradient, 2011)

AdaGrad 让每个参数用自适应学习率:对更新频繁的参数降低学习率,对稀疏更新的参数保持较大学习率。

\[g_t = \nabla_\theta L(\theta_t)\] \[s_t = s_{t-1} + g_t^2\] \[\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{s_t + \epsilon}} \cdot g_t\]

问题:$s_t$ 单调递增,学习率会越来越小,最终无法继续学习。适合稀疏数据(NLP 词嵌入),但不适合深度网络。


三、RMSProp(Geoff Hinton, 未发表)

RMSProp 解决了 AdaGrad 学习率单调下降的问题,用指数移动平均代替累积和:

\[s_t = \beta s_{t-1} + (1 - \beta) g_t^2\] \[\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{s_t + \epsilon}} \cdot g_t\]

通常 $\beta = 0.99$。


四、Adam(Kingma & Ba, 2015)

Adam = Momentum(动量项)+ RMSProp(自适应学习率)。它同时维护两个移动平均:

一阶矩(梯度的均值)

\[m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t\]

二阶矩(梯度的方差)

\[v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2\]

偏差修正(因为 $m_0 = v_0 = 0$):

\[\hat{m}_t = \frac{m_t}{1 - \beta_1^t}, \quad \hat{v}_t = \frac{v_t}{1 - \beta_2^t}\]

更新参数

\[\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \cdot \hat{m}_t\]

默认超参:$\beta_1 = 0.9$,$\beta_2 = 0.999$,$\epsilon = 10^{-8}$。

PyTorch 使用

optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, betas=(0.9, 0.999))

Adam 的优势与问题

优势

  • 对学习率不敏感(默认 1e-3 在大多数任务上都能用)
  • 收敛快
  • 适合非平稳目标

问题

  • 泛化性差:在很多任务上 Adam 训练 loss 很低,但测试精度不如 SGD+Momentum
  • 原因是自适应学习率倾向于找到「尖锐极小值」,泛化不好

五、AdamW(Loshchilov & Hutter, 2019)

5.1 Adam + L2 正则化的陷阱

在 Adam 中直接加 L2 正则化(weight_decay)会让自适应学习率缩放权重衰减,导致正则化效果不均匀。

5.2 解耦权重衰减

AdamW 把权重衰减从梯度中解耦出来,直接作用于参数:

\[\theta_{t+1} = \theta_t - \eta \left( \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} + \lambda \theta_t \right)\]

其中 $\lambda$ 是权重衰减系数。注意这里 $\lambda$ 通常比 Adam 的 weight_decay 大 10–100 倍(典型值 0.01–0.1)。

5.3 PyTorch 使用

optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.01)

5.4 现代默认选择

Transformer / ViT / LLaMA / Stable Diffusion 等几乎都用 AdamW。它解决了 Adam 的两个问题:

  • 更好的泛化
  • 训练更稳定

六、LAMB(You et al., 2020)

6.1 动机

BERT 预训练中常用 batch size 几万(如 LAMB 用 65536)。Adam 直接用大学习率会发散;SGD 用大学习率太慢。LAMB 通过层级自适应学习率让大 batch 训练稳定。

6.2 算法

对每一层 $\theta^l$,先计算标准的 Adam 更新方向 $u^l$,再做层级归一化

\[g^l = \frac{\hat{m}_t^l}{\sqrt{\hat{v}_t^l} + \epsilon}\] \[\phi^l = \frac{\|\theta^l\|}{\|g^l\| + \epsilon}\] \[\theta^l \leftarrow \theta^l - \eta \cdot \phi^l \cdot (g^l + \lambda \theta^l)\]

直觉:每层的更新幅度被自动缩放到与该层参数范数相当,避免深层网络累积过大更新。

6.3 PyTorch 实现

from torch.optim import Optimizer

class LAMB(Optimizer):
    def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-6, weight_decay=0.01):
        defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay)
        super().__init__(params, defaults)

    @torch.no_grad()
    def step(self, closure=None):
        loss = closure() if closure else None
        for group in self.param_groups:
            for p in group['params']:
                if p.grad is None:
                    continue
                grad = p.grad
                state = self.state[p]
                if len(state) == 0:
                    state['step'] = 0
                    state['m'] = torch.zeros_like(p)
                    state['v'] = torch.zeros_like(p)

                m, v = state['m'], state['v']
                beta1, beta2 = group['betas']
                state['step'] += 1
                step = state['step']

                # Adam update direction
                m.mul_(beta1).add_(grad, alpha=1 - beta1)
                v.mul_(beta2).addcmul_(grad, grad, value=1 - beta2)
                m_hat = m / (1 - beta1 ** step)
                v_hat = v / (1 - beta2 ** step)
                update = m_hat / (v_hat.sqrt() + group['eps'])

                # Weight decay
                if group['weight_decay'] != 0:
                    update = update + group['weight_decay'] * p

                # Layer-wise adaptive scaling
                weight_norm = p.norm().clamp(min=0)
                update_norm = update.norm()
                if weight_norm == 0 or update_norm == 0:
                    continue
                trust_ratio = weight_norm / update_norm

                p.add_(update, alpha=-group['lr'] * trust_ratio.item())
        return loss

6.4 适用场景

  • BERT/RoBERTa 预训练:batch ≥ 8K 时基本必用
  • 大模型分布式训练

七、Lion(Chen et al., 2023)

7.1 动机

Google 在 2023 年提出的新型优化器,只用动量(一阶矩),不维护二阶矩,显存占用减半

7.2 算法

\[c_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t\] \[\theta_{t+1} = \theta_t - \eta \cdot \left( \text{sign}(c_t) + \lambda \theta_t \right)\] \[m_t = \beta_2 m_{t-1} + (1 - \beta_2) g_t\]

注意 sign(c) 把动量量化到 ${-1, +1}$,所有维度用相同步长。

7.3 优势与局限

  • 优势:训练速度快、显存占用小、对小 batch 友好
  • 局限:泛化性需要仔细调参,目前主要在 LLM 上验证

八、八大优化器对比

优化器 自适应 LR 动量 权重衰减 显存/参数 主要场景
SGD 可选 L2 CNN 分类(需要更好泛化)
SGD+M L2 ResNet 系列
AdaGrad 稀疏特征
RMSProp RNN
Adam 通用、GAN
AdamW 强(解耦) Transformer 默认
LAMB 大 batch 训练
Lion LLM 显存优化

九、工程实践指南

任务 推荐优化器 典型学习率 关键超参
ResNet 图像分类 SGD + Momentum + nesterov 0.1 momentum=0.9, wd=1e-4
Transformer 预训练 AdamW 1e-4 ~ 3e-4 betas=(0.9, 0.95), wd=0.1
BERT 大 batch 预训练 LAMB 1e-3 ~ 2e-3 warmup + linear decay
GAN Adam (不带 WD) 1e-4 ~ 2e-4 betas=(0.0, 0.9)
扩散模型(Stable Diffusion) AdamW 1e-5 ~ 1e-4 wd=0.01
LLaMA 微调 AdamW 2e-5 ~ 2e-4 wd=0.1, betas=(0.9, 0.95)

十、AdamW 完整训练循环示例

import torch
import torch.nn as nn

model = nn.Linear(10, 2)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.01)

# 注意:AdamW 的 weight_decay 通常 0.01 ~ 0.1
# 不要和 Adam 的 weight_decay 数值混淆

for epoch in range(num_epochs):
    for x, y in dataloader:
        # 1. 前向传播
        pred = model(x)
        loss = criterion(pred, y)

        # 2. 反向传播
        optimizer.zero_grad()
        loss.backward()

        # 3. 梯度裁剪(Transformer 必加)
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

        # 4. 优化器更新
        optimizer.step()

十一、一句话总结

SGD 适合泛化,Adam/AdamW 适合快速收敛,LAMB 适合大 batch 训练,Lion 适合显存受限的 LLMTransformer 时代 AdamW 是默认选择,但别忘了:在图像分类任务上仔细调过的 SGD+Momentum 仍然可能打败 Adam。