优化器详解:从SGD到Adam的原理与参数调优
选择合适的优化器以及理解其背后的参数,往往决定了模型是能够快速收敛到最佳状态,还是在训练过程中产生“梯度爆炸”甚至完全无法收敛。本文将通俗地解释这些概念,并深入剖析 PyTorch 中常用优化器的关键参数。
1. 通俗理解:蒙眼下山
想象你被蒙住双眼,放置在一座高山上。你的目标是下到山谷的最低点——在深度学习中,这代表着让模型的损失函数 (Loss) 最小化。
优化器,就是你的下山策略。
graph TD
Start("山顶 (高 Loss)") --> Strategy{选择优化器}
Strategy -- "SGD" --> Path1("固定步长,稳步试探")
Strategy -- "Adam" --> Path2("自适应速度,智能调整")
Path1 --> Goal("山谷 (低 Loss)")
Path2 --> Goal
🚶 基础选手:SGD (随机梯度下降)
策略:用脚探一下当前的地面,感觉哪个方向是向下的,就往那个方向迈出固定的一步。
特点:
- 老实:它完全依赖当前的梯度方向。你设定多大的步长(学习率),它就走多远。
- 稳健但缓慢:如果步长太小,下山速度极慢;如果步长太大,可能会在山谷震荡甚至跑偏。
- 优势:在计算机视觉(CV)任务中(如 ResNet 训练),SGD 往往能找到更平坦、泛化能力更强的极小值点。
🏎️ 进阶选手:Adam (自适应矩估计)
策略:它不仅看当前的坡度,还"记得"之前的速度(动量),并且会根据地形的复杂程度自动调整步子大小。
- 平坦路段:自动加速。
- 陡峭悬崖:自动减速,防止冲过头。
特点:
- 智能:结合了动量(Momentum)和自适应学习率(RMSprop 的思想)。
- 快速:收敛速度通常远快于 SGD。
- 劣势:对“初始推力”(学习率)非常敏感,且在某些 CV 任务上,最终的泛化性能可能不如精心调教的 SGD。
2. 深入剖析:关键参数与含义
在 PyTorch 中,我们通常通过 torch.optim 来调用这些优化器。理解每个参数的物理含义,是避免“炼丹”失败的关键。
SGD 的关键参数
1 | optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4) |
lr(Learning Rate, 学习率)- 含义:下山的“步长”。
- 通俗解释:决定了你每一次更新参数时变化有多大。
- 典型值:
0.1,0.01。SGD 需要较大的lr才能推动模型前进。
momentum(动量)- 含义:模拟物理中的“惯性”。
- 通俗解释:如果之前一直是下坡,那么这次不仅要看当前的坡度,还要加上之前的速度冲下去。这有助于冲过局部的平坦区域(鞍点)或小坑。
- 典型值:
0.9。这意味着当前的更新方向有 90% 来自之前的积累,10% 来自当前的梯度。
weight_decay(权重衰减)- 含义:L2 正则化。
- 通俗解释:一种“惩罚机制”。为了防止模型为了拟合数据而变得过于复杂(参数值过大),我们在损失函数里加了一项惩罚。它就像给模型加了“刹车”,防止过拟合。
- 典型值:
1e-4(0.0001) 或5e-4。
Adam 的关键参数
1 | optimizer = torch.optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999), eps=1e-8) |
lr(Learning Rate)- 注意:Adam 的学习率通常比 SGD 小得多。
- 典型值:
0.001(1e-3) 或3e-4。这是因为 Adam 内部会自动根据梯度的一阶矩和二阶矩放大更新幅度。
betas(Beta1, Beta2)- 含义:控制两个“记忆”的衰减率。
beta1(默认 0.9):对应 一阶矩估计(即动量 Momentum)。控制“惯性”保持多久。beta2(默认 0.999):对应 二阶矩估计(即梯度的平方)。用来衡量“路况”的平稳程度,从而实现自适应步长。- 调参建议:通常保持默认即可。
eps(Epsilon)- 含义:数值稳定性项。
- 作用:防止分母为零。在计算自适应步长时,分母是梯度的平方根,加一个极小数
eps避免除零错误。
3. 实战避坑:为什么会“梯度爆炸”?
在实际开发中,一个最常见的错误就是将 SGD 的经验直接套用到 Adam 上。
典型事故现场
假设你习惯了训练 ResNet,配置了 lr=0.1。然后你想尝试一下 Transformer,于是把优化器改成了 Adam,但忘记了修改学习率。
graph TD
Config["配置: Adam 优化器"] --> Input["输入: SGD 级别的学习率 (0.1)"]
Input -- "对于 Adam 来说" --> Mechanism["Adam 内部的自适应放大机制"]
Mechanism --> Result["实际更新步长过大 (相当于 SGD 的 100 倍)"]
Result --> Boom["参数变为 NaN / Infinity"]
Boom --> Loss["Loss 震荡或不收敛"]
原因分析: Adam 自带“助推器”(自适应调整)。对于 Adam 来说,0.001 已经是很快的速度了。如果你给它 0.1 的初始推力,相当于给一辆法拉利加了火箭燃料,瞬间就会因为步长过大冲出赛道,导致参数变成 NaN (Not a Number) 或无穷大。
修正方案: 一旦切换到 Adam,务必将学习率降低 1-2 个数量级(从 0.1 降到 0.001)。
4. 选型指南:我该用哪个?
没有绝对的“最好”,只有“最适合”。
✅ 选择 SGD 的场景
- 任务:计算机视觉标准任务(图像分类、物体检测、分割)。
- 代表模型:ResNet, VGG, YOLO。
- 理由:虽然收敛慢,需要配合学习率衰减策略(Learning Rate Scheduler),但往往能收敛到更优的解,测试集准确率更高。
- 推荐配置:
SGD(lr=0.1, momentum=0.9, weight_decay=5e-4)
✅ 选择 Adam 的场景
- 任务:自然语言处理(NLP)、强化学习、生成对抗网络(GAN)、或快速原型开发。
- 代表模型:Transformer, BERT, GPT, ViT。
- 理由:收敛速度极快,对超参数(Hyper-parameters)不那么敏感,开箱即用。
- 推荐配置:
Adam(lr=1e-3)或AdamW(lr=3e-4)
💡 小贴士:AdamW 是什么?
你可能会在现在的代码中经常看到 AdamW。它是 Adam 的修正版。简单来说,Adam 在处理 weight_decay(权重衰减)时存在一个理论上的 Bug,AdamW 修复了这个问题,使得它在训练 Transformer 等大模型时效果更好、更稳定。
总结
- SGD 像手动挡赛车,上限高,但需要精细的油门控制(调参)。
- Adam 像自动挡跑车,起步快,省心,但在某些赛道上可能不如手动挡精准。
- 切记:换车(优化器)的时候,千万别忘了换挡(调整学习率)!
5. 拓展:其他常见优化器一览
除了 SGD 和 Adam,深度学习工具箱中还有许多其他优化器。了解它们有助于在特定场景下做出更合适的选择。
📊 RMSprop (Root Mean Square Propagation)
诞生背景:为了解决 AdaGrad 学习率过早衰减的问题。
核心思想:使用梯度的移动平均平方(而非累积平方)来调整学习率,这样可以让学习率在训练后期仍然保持活力。
下山比喻:就像一个经验丰富的登山者,会根据最近一段路的情况(而不是整段路程)来判断下一步的步长。
1 | optimizer = torch.optim.RMSprop(model.parameters(), lr=0.01, alpha=0.99) |
关键参数: * lr:学习率,典型值 0.01 * alpha:平滑常数,控制历史梯度的衰减速度,默认 0.99
适用场景:RNN(循环神经网络)训练,是 Adam 出现之前的主流选择。
📈 AdaGrad (Adaptive Gradient)
核心思想:为每个参数维护一个累积的梯度平方和,频繁更新的参数会获得更小的学习率,稀疏参数会获得更大的学习率。
下山比喻:如果某个方向你经常走(梯度大),说明这个方向可能已经接近目标了,就放慢脚步;如果某个方向很少走(梯度小),就加快探索。
1 | optimizer = torch.optim.Adagrad(model.parameters(), lr=0.01) |
特点: * 优点:对稀疏梯度友好,适合处理稀疏数据。 * 缺点:学习率会单调递减,训练后期可能过小导致无法继续学习。
适用场景:自然语言处理中的稀疏特征学习,但现在已较少使用。
🔄 AdaDelta
诞生背景:AdaGrad 的改进版,解决了学习率过早衰减的问题。
核心创新:不需要手动设置学习率!它会根据参数更新的历史信息自动调整。
下山比喻:完全自动驾驶,不仅会根据路况调整速度,还会根据之前走过的路来预测下一步该走多远。
1 | optimizer = torch.optim.Adadelta(model.parameters(), rho=0.9) |
关键参数: * rho:衰减率,控制历史信息的权重,默认 0.9
特点: * 优点:无需设置学习率,对超参数不敏感。 * 缺点:收敛速度可能较慢,实际使用中不如 Adam 受欢迎。
⚡ AdamW (Adam with Weight Decay Fix)
核心改进:修复了 Adam 在处理权重衰减(Weight Decay)时的理论缺陷。
技术细节:在原始 Adam 中,weight_decay 被错误地应用到了梯度上,而不是直接应用到参数上。AdamW 将权重衰减与梯度更新解耦,使得正则化效果更符合 L2 正则化的原始意图。
下山比喻:Adam 在刹车(权重衰减)时,刹车片和油门混在一起了;AdamW 把刹车系统独立出来,刹车更精准。
1 | optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4, weight_decay=0.01) |
关键参数: * lr:学习率,典型值 3e-4(比 Adam 稍小) * weight_decay:权重衰减,典型值 0.01(可以比 Adam 中设置得更大)
适用场景: * Transformer 模型:BERT、GPT 等大模型的标准选择 * 计算机视觉:ViT(Vision Transformer)等 * 现代深度学习:几乎成为新项目的默认选择
为什么现在更推荐 AdamW? 1. 理论更严谨:权重衰减的实现方式更符合数学原理 2. 泛化性能更好:在大多数任务上测试集表现更优 3. 超参数更稳定:对 weight_decay 的敏感性更低
🎯 其他新兴优化器
NAdam (Nesterov-accelerated Adam)
结合了 Nesterov 动量和 Adam 的优点,在收敛速度和稳定性之间取得更好的平衡。
RAdam (Rectified Adam)
解决了 Adam 在训练初期可能不稳定的问题,通过动态调整学习率来保证训练的稳定性。
Lookahead
一种元优化器,可以包装在任何优化器(如 Adam、SGD)外面,通过"向前看"的策略来稳定训练过程。
📋 快速对比表
| 优化器 | 学习率需求 | 收敛速度 | 超参数敏感性 | 主要应用 |
|---|---|---|---|---|
| SGD | 较大 (0.1) | 慢 | 高 | CV 任务 (ResNet) |
| Adam | 较小 (0.001) | 快 | 中 | NLP、GAN、快速原型 |
| AdamW | 较小 (3e-4) | 快 | 低 | Transformer、现代大模型 |
| RMSprop | 中等 (0.01) | 中 | 中 | RNN(历史选择) |
| AdaGrad | 中等 (0.01) | 慢(后期) | 低 | 稀疏数据(较少使用) |
| AdaDelta | 无需设置 | 中 | 低 | 理论研究(较少使用) |
💡 实用建议
- 新手入门:直接使用
AdamW(lr=3e-4),这是 2024 年最稳妥的选择。 - CV 任务追求极致:尝试
SGD(lr=0.1, momentum=0.9)配合学习率调度器。 - 大模型训练:优先考虑
AdamW,配合weight_decay=0.1。 - 实验阶段:先用 AdamW 快速验证想法,再根据任务特点微调。
记住:没有万能的优化器,只有最适合你任务的优化器。理解原理比盲目选择更重要!