本节摘要:优化器决定"参数怎么调",是训练的发动机。本节讲清 SGD、Momentum、Adam、AdamW 四代优化器的演进逻辑与差异,给出微调场景的选型经验,并解释"为什么大模型训练几乎都用 AdamW"。
阅读完本节,你应当能够:
同样是"沿着损失下降的方向调参数",不同优化器的聪明程度差很多。基础 SGD 像"蒙眼下坡",每一步都往最陡的方向走,但容易震荡、卡在坑里;Adam 像"带惯性的下坡者",既知道方向又知道每处的路况(自适应步长)。理解优化器的演进,就是理解"怎么把参数调得又快又稳"。
| 优化器 | 核心改进 | 解决的问题 |
|---|---|---|
| SGD | 梯度下降 | 基础 |
| Momentum | 累积历史方向 | 震荡、局部坑 |
| Adam | 自适应步长 | 不同参数尺度差异 |
| AdamW | 权重衰减解耦 | 正则与自适应冲突 |
Adam 给每个参数独立的步长:梯度大的参数少走点,梯度小的参数多走点。这让它在"参数尺度差异大"的场景(神经网络常见)收敛又快又稳。
💡 关键直觉:选优化器的口诀——大数据量快速训练用 SGD 系,通用深度学习用 Adam 系,大模型微调几乎总是 AdamW。别在优化器上花太多心思,AdamW 是安全默认。
| 场景 | 推荐优化器 | 理由 |
|---|---|---|
| 大模型微调 | AdamW | 稳定、效果好 |
| 简单任务 | Adam | 足够 |
| 大规模预训练 | SGD 系 | 泛化更好但难调 |
| LoRA 微调 | AdamW | 事实标准 |
AdamW 常用配置: learning_rate: 2e-5 ~ 5e-5(微调) weight_decay: 0.01(默认) betas: (0.9, 0.999)(默认)
⚠️ 常见坑:微调用预训练的学习率。预训练常用 1e-4 到 1e-3,微调要降到 1e-5 到 5e-5——起点不同,步幅必须不同。
除了优化器本身,学习率还常配"调度器":热身(warmup)阶段从小到大的学习率,训练后期逐步衰减。warmup 对微调很重要——刚换数据时用大学习率容易把已有参数冲坏。
观察训练曲线:损失平滑下降、无剧烈震荡 = 优化器配置合适。震荡剧烈 → 调小学习率;下降过慢 → 检查数据与模型配置。
纸上谈兵不如跑一次。下面用同一个二次函数分别用 SGD 与 Adam 优化,观察谁收敛更快:
import torch def train(opt_name): x = torch.tensor([5.0], requires_grad=True) if opt_name == "SGD": opt = torch.optim.SGD([x], lr=0.1) else: opt = torch.optim.Adam([x], lr=0.1) for _ in range(50): loss = (x - 1) ** 2 + 0.5 * x ** 2 # 简单二次函数 opt.zero_grad() loss.backward() opt.step() return x.item() print("SGD 结果:", train("SGD")) # 收敛慢,且对学习率敏感 print("Adam 结果:", train("Adam")) # 自适应步长,收敛快而稳
这个小实验背后是 Adam 的两个核心机制:一阶矩估计(类似动量,累积历史梯度方向)与二阶矩估计(按梯度平方归一化,自动调节每个参数的步长)。正是这两个机制让 Adam 对学习率不那么敏感、在参数尺度差异大的网络上表现稳定。实际微调时,把代码里的 optimizer 换成 transformers 库的 AdamW(Adam 的权重衰减解耦版)即可——训练循环完全不变,只换发动机,这正是优化器与训练框架解耦带来的便利。
到这一步,优化器的答案其实已经收敛:SGD 系适合大规模预训练与追求极致泛化的场景,Adam 系适合绝大多数深度学习任务,而大模型微调(无论全量还是 LoRA)几乎都是 AdamW。如果实在拿不准,就从 AdamW 加学习率 2e-5 开始,观察曲线再微调——优化器本身很少是微调失败的主因,主因几乎总在数据与配置上。
补充一个容易被忽略的细节:Adam 系优化器会为每个参数维护额外的动量与方差状态,参数量翻倍——对 7B 模型而言,这意味着优化器状态本身就要占掉十几 GB 显存。这也是 LoRA 省显存的另一个来源:可训练参数少,优化器状态也小。选型时可以把"优化器状态显存"一并算进预算。
优化器定了方向,下一节上安全网——正则化技术。