第四讲:优化器配置——SGD、Adam、AdamW的选择标准与超参数调优

优化器这东西,说白了就是告诉模型「往哪个方向走、走多大步」。我见过太多同学在优化器上翻车——要么模型死活不收敛,要么loss炸到天上去。今天咱们就把SGD、Adam、AdamW这三个主流优化器掰开揉碎讲清楚。

4.1 三大优化器的核心差异

先给个直观对比,你心里有个底:

优化器 核心思想 适用场景 我个人的使用频率
SGD + Momentum 梯度下降 + 动量累积 CV、小批量数据、需要精细调参 30%
Adam 自适应学习率 + 动量 NLP、GAN、快速原型验证 50%
AdamW Adam + 解耦权重衰减 Transformer、大模型、需要正则化 20%

嗯,这里要注意:AdamW不是Adam的简单升级版,它修正了Adam里weight_decay的实现方式。我在项目中遇到过用Adam训BERT,loss降不下去,换成AdamW立马见效——原因就是权重衰减被正确解耦了。

4.2 SGD:老派但可靠

SGD + Momentum,我习惯叫它「手动挡」。为什么?因为所有超参数都得你亲手调,调好了性能上限很高,调不好就原地踏步。

关键超参数:

  • lr(学习率):典型值0.01~0.1。我一般从0.1开始试,如果loss震荡就降到0.01。
  • momentum:通常取0.9。别乱改,0.9是经过大量验证的黄金值。
  • weight_decay:1e-4 ~ 5e-4。太大模型欠拟合,太小过拟合。
# 我常用的SGD配置
optimizer = torch.optim.SGD(
    model.parameters(),
    lr=0.1,
    momentum=0.9,
    weight_decay=1e-4,
    nesterov=True  # 我习惯开Nesterov,收敛略快
)

为什么会有人坚持用SGD?因为它的泛化能力确实好。Adam有时候会「记住」训练集上的噪声,SGD反而更鲁棒。不过代价就是——你得有耐心,SGD收敛慢,经常要跑几百个epoch。

我的小技巧:用SGD时,配合余弦退火学习率调度(CosineAnnealingLR),效果比StepLR好很多。我曾经在ResNet50上对比过,余弦退火让top-1准确率提升了0.8%。

4.3 Adam:快速上手首选

Adam就像自动挡汽车——大部分情况你踩油门就走。它自适应调整每个参数的学习率,对lr不那么敏感。

但注意,这不代表你可以乱设lr。我见过有人把lr设成0.1,结果loss直接NaN。Adam的lr通常比SGD小一个数量级。

关键超参数:

  • lr:典型值1e-4 ~ 3e-4。Transformer用5e-5都常见。
  • betas:默认(0.9, 0.999)。第一个是动量衰减,第二个是梯度平方衰减。
  • eps:1e-8。防止除零,一般不动。
# Adam的标准配置
optimizer = torch.optim.Adam(
    model.parameters(),
    lr=1e-4,
    betas=(0.9, 0.999),
    eps=1e-8,
    weight_decay=0  # Adam里我通常不开weight_decay
)

这里有个坑:Adam的weight_decay实现有问题。它把权重衰减加到了梯度里,而不是直接更新参数。这会导致正则化效果变差。所以——

警告:如果你用Adam,又想加权重衰减,请直接用AdamW。别在Adam里设weight_decay,那是伪正则化。

4.4 AdamW:大模型时代的标配

AdamW解决了Adam的权重衰减问题。说白了,就是把「权重衰减」和「梯度更新」解耦了。你想想看,Adam本来就在自适应调整学习率,再把权重衰减混进去,两个机制互相打架,效果能好吗?

AdamW的做法很简单:先做Adam的梯度更新,再单独对参数做权重衰减。这样互不干扰。

# AdamW的正确用法
optimizer = torch.optim.AdamW(
    model.parameters(),
    lr=1e-4,
    betas=(0.9, 0.999),
    weight_decay=0.01  # 这里可以放心设大一点
)

我个人的经验:训练GPT、BERT这类大模型时,AdamW的weight_decay设0.01~0.1效果都不错。太小了过拟合,太大了模型学不动。

4.5 超参数调优实战指南

调优这事儿,说白了就是试错。但我们可以让试错更有章法。

4.5.1 学习率:最重要的超参数

学习率决定了模型能不能收敛。我有个「三板斧」方法:

  1. 先跑学习率范围测试:从1e-6到1,指数增长,看loss曲线。找到loss下降最快的区间。
  2. 选中间值:比如测试发现1e-4到1e-3效果最好,那就取3e-4。
  3. 配合warmup:前5%~10%的step线性增加lr,防止一开始就震荡。

避坑指南:我曾经在训练一个文本分类模型时,lr设成1e-3,loss一直不降。后来用学习率范围测试才发现,最佳lr是5e-5——差了20倍!所以别凭感觉设lr,跑个测试花不了几分钟。

4.5.2 betas:Adam家族的专属调节

betas控制着动量和平滑程度。默认(0.9, 0.999)对大多数任务都够用,但有些场景需要调整:

场景 建议betas 原因
稀疏梯度(如NLP) (0.9, 0.999) 默认就很好
密集梯度(如CV) (0.9, 0.99) 减少二阶动量平滑,加速收敛
训练不稳定 (0.95, 0.999) 增大一阶动量,让更新更平滑

嗯,这里要注意:beta1越大,动量越强,模型越不容易震荡,但也可能错过最优解。我一般只在loss震荡时才调高beta1。

4.5.3 weight_decay:正则化的艺术

权重衰减说白了就是L2正则化。但不同优化器的最佳值差很多:

  • SGD:1e-4 ~ 5e-4。太大模型欠拟合。
  • Adam:不建议用,用AdamW代替。
  • AdamW:0.01 ~ 0.1。大模型可以到0.1。

注意:weight_decay不是越大越好。我见过有人设0.5,结果模型直接学成常数——所有参数都趋近于0。一般从0.01开始试,如果验证集loss下降就继续增大,直到验证集loss开始上升为止。

4.6 我的选择策略总结

说了这么多,到底怎么选?我有个简单的决策树:

  1. 数据量小(<1万):用SGD + Momentum。Adam容易过拟合。
  2. 数据量大,任务简单:用Adam。省心,收敛快。
  3. 大模型、Transformer:用AdamW。别无他选。
  4. 不确定:先用Adam跑10个epoch看趋势,再决定要不要换。

最后说一句:没有万能优化器。我见过用SGD训出SOTA的,也见过用AdamW训崩的。关键是你得理解每个超参数在干什么,然后根据loss曲线去调。嗯,多试几次就有感觉了。