6月5日 18:27

TensorFlow 优化器怎么选?Adam vs SGD 实战对比和选择指南

优化器决定了模型参数更新的方向和步长——选错了,再好的模型架构也训不出好结果。TensorFlow 提供了十几种优化器,但 90% 的场景你只需要在 Adam 和 SGD 之间做选择。这篇文章不罗列 API,而是讲清楚每个优化器的原理差异、什么时候用哪个、以及一些实战中容易踩的坑。

先搞懂优化器在做什么

优化器的核心工作就一件事:根据梯度更新参数。区别在于"怎么用梯度"——

  • SGD:梯度指向哪,就往那走一步,步长固定
  • Adam:记住历史梯度的方向和大小,自适应调整步长
  • 其他优化器:在这两个思路之间做各种变体

理解了这个本质,选优化器就不是背表格了。

SGD —— 简单但被低估

python
from tensorflow.keras.optimizers import SGD # 基本 SGD optimizer = SGD(learning_rate=0.01) # 带动量——实际使用时的标准配置 optimizer = SGD(learning_rate=0.01, momentum=0.9) # Nesterov 动量——更激进的变体 optimizer = SGD(learning_rate=0.01, momentum=0.9, nesterov=True)

为什么 SGD 值得重视

纯 SGD(无动量)确实慢,但加上 momentum 之后完全不同。动量的效果是:梯度方向一致时加速(积累动量),方向变化时减速(动量抵消),帮助逃出局部最优和鞍点。

SGD 最大的优势是泛化性能。大量研究表明,虽然 Adam 收敛更快,但 SGD(+momentum)最终往往能达到更好的泛化结果。原因在于 SGD 的更新路径更"曲折",更容易跳出尖锐的局部最优,找到更平坦的最优解——平坦的最优解泛化性更好。

什么时候用 SGD

  • 追求最终精度(打比赛、生产部署)
  • 数据量大(>100K 样本),有足够时间训练
  • 你愿意花时间调学习率

学习率调参是 SGD 的主要成本

SGD 需要手动设置学习率,而且不同阶段需要不同的学习率。典型做法是配合学习率衰减:

python
from tensorflow.keras.optimizers.schedules import CosineDecay lr_schedule = CosineDecay(initial_learning_rate=0.1, decay_steps=50000) optimizer = SGD(learning_rate=lr_schedule, momentum=0.9)

0.1 是 SGD 的经典初始学习率(配合 momentum),比 Adam 的 0.001 大很多——因为 SGD 没有自适应机制,需要更大的步长来补偿。

Adam —— 不用动脑的默认选择

python
from tensorflow.keras.optimizers import Adam optimizer = Adam(learning_rate=0.001)

Adam 为什么好用

Adam 维护了两个移动平均:一阶矩(梯度的指数平均,即方向)和二阶矩(梯度平方的指数平均,即大小)。然后用一阶矩除以二阶矩的平方根来更新参数——效果是梯度大时步长自动缩小,梯度小时步长自动放大。

这带来两个实际好处:

  1. 几乎不需要调学习率:0.001 对大多数任务都工作
  2. 每个参数有独立的学习率:稀疏特征也能得到合理的更新

Adam 的坑

  • 权重衰减实现有 bug:标准 Adam 把 L2 正则化加到了梯度里,而不是直接惩罚权重。这导致正则化效果被自适应学习率削弱。解决方案是用 AdamW
python
from tensorflow.keras.optimizers import AdamW optimizer = AdamW(learning_rate=0.001, weight_decay=0.01)

AdamW 在 Transformer 类模型(BERT、ViT 等)中几乎是标配。

  • 有时泛化不如 SGD:Adam 收敛快,但可能收敛到尖锐的最优解,测试集表现反而不如 SGD。

Adam vs SGD:到底选哪个

这是最常见的问题,直接给结论:

维度AdamSGD + Momentum
收敛速度快(通常快 2-5 倍)
调参难度低(lr=0.001 开箱即用)高(需调 lr + schedule)
最终精度一般通常更高
泛化性能稍差更好
稀疏数据
显存占用高(额外存储一阶/二阶矩)

实战建议

  • 项目初期 / 快速验证:用 Adam,快速跑出基线结果
  • 追求最佳精度:先 Adam 预训练,再切换 SGD 精调
  • 数据稀疏(NLP、推荐):Adam 或 Adagrad
  • 显存紧张:SGD

Adam 预训练 + SGD 精调的混合策略

这是竞赛和工业界常用的套路:

python
# 阶段 1:Adam 快速收敛 model.compile(optimizer=Adam(learning_rate=0.001), loss="...") model.fit(x_train, y_train, epochs=20) # 阶段 2:切换 SGD 精调 model.compile(optimizer=SGD(learning_rate=0.001, momentum=0.9), loss="...") model.fit(x_train, y_train, epochs=30)

切换时学习率通常设为 Adam 最终学习率的 1/10 到 1/100,让 SGD 在 Adam 找到的最优解附近精细搜索。

其他优化器:什么时候才需要

RMSprop —— RNN 的老搭档

python
from tensorflow.keras.optimizers import RMSprop optimizer = RMSprop(learning_rate=0.001)

RMSprop 是 Adam 的前身之一,只维护二阶矩(不做一阶矩的指数平均)。在 RNN/LSTM 训练中曾有不错的效果,但现在基本被 Adam 替代了。如果你没有特别理由,不需要选 RMSprop。

Adagrad —— 稀疏特征的经典选择

python
from tensorflow.keras.optimizers import Adagrad optimizer = Adagrad(learning_rate=0.01)

Adagrad 对频繁出现的特征用小学习率,对罕见特征用大学习率。适合处理极度稀疏的数据(比如广告点击率预测,特征空间几百万维但每条样本只激活几十个)。

缺点是学习率只减不增,训练后期可能过早衰减到接近 0。Adadelta 是 Adagrad 的改进版,限制了累积历史的影响,但实际效果不如 Adam。

Nadam —— Adam 的 Nesterov 版本

python
from tensorflow.keras.optimizers import Nadam optimizer = Nadam(learning_rate=0.001)

Nadam 把 Nesterov 动量的思路融入 Adam——在计算梯度时先用当前动量"往前看一步"。理论上收敛更快,但实际差异很小。如果你对 Adam 的收敛速度不满意,Nadam 可以试一下,但别期望质变。

Ftrl —— 大规模稀疏场景专用

python
from tensorflow.keras.optimizers import Ftrl optimizer = Ftrl(learning_rate=0.01, l1_regularization_strength=0.01)

Ftrl(Follow-the-Regularized-Leader)是 Google 为点击率预测设计的优化器,天生支持 L1/L2 正则化,适合在线学习场景。只在推荐系统/广告的工业级部署中才会用到。

学习率调参实战

优化器选对了,学习率没调好还是白搭。几个实用经验:

学习率太大 vs 太小的信号

  • 太大:Loss 震荡不下降,或直接 NaN
  • 太小:Loss 下降极慢,几百个 epoch 还在慢慢爬
  • 刚好:Loss 在前几个 epoch 快速下降,然后稳定收敛

学习率预热(Warmup)

大模型训练的标准操作——前 N 步用很小的学习率,线性增加到目标值:

python
warmup_steps = 1000 total_steps = 50000 def warmup_cosine_schedule(step): lr = 0.001 if step < warmup_steps: return lr * (step / warmup_steps) # 之后余弦衰减 progress = (step - warmup_steps) / (total_steps - warmup_steps) return lr * 0.5 * (1 + tf.cos(3.14159 * progress)) optimizer = Adam(learning_rate=warmup_cosine_schedule)

预热避免训练初期参数还很随机时,大梯度导致的不稳定更新。Transformer 类模型几乎必用。

快速决策参考

你的情况推荐优化器学习率
刚开始,不确定Adam0.001
追求最高精度SGD + Momentum0.1 + 余弦衰减
大模型微调AdamW0.001, weight_decay=0.01
NLP/稀疏特征Adam0.001
推荐系统/广告Ftrl0.01
显存不够SGD + Momentum0.1
想两全其美Adam → SGDAdam: 0.001, SGD: 0.001

别在优化器上纠结太久——先选 Adam 跑出结果,再根据需要切换。大部分性能提升来自数据和模型架构,不是优化器。

标签:Tensorflow