本节摘要:优化器拿到梯度后决定"往哪走、走多快"。基础款 SGD 沿负梯度走固定步长;动量把近期梯度滑动平均,冲过小坑洼;Adam 再给每个参数配自适应步长,成为多数任务的默认起点。本节在一维抛物线上把三款的手感跑出来,并给出学习率的选择经验与排查套路。
参数更新的原始式只有一行:参数 = 参数 − 学习率 × 梯度。学习率是唯一的旋钮,太大一步迈过谷底来回震荡,太小半天挪不动。优化器的进化史就是在这行代码上做加法:动量给更新加惯性,Adam 给每个参数配各自的步长。理解它们的捷径不是背公式,而是在最简单的一维山谷里各跑几步——下面的账目全部可以手算复核。
阅读完本节,你应当能够:
目标函数 f(w) = (w−3)²,梯度 2(w−3),起点 w=0,学习率 0.1。纯 SGD 每步走 0.1×梯度:第一步 0 − 0.1×(−6) = 0.6,第二步 1.08,第三步 1.464——稳步逼近 3 但越走越慢。动量版给更新加一个 0.9 的惯性:每步位移 = 0.9×上次位移 − 0.1×梯度。手算三步:位移 0.6、1.02、1.194,位置 0.6、1.62、2.814——同样三步,动量版已经几乎抵达谷底。
def grad(w): # 目标:最小化 f(w) = (w − 3) 的平方 return 2 * (w - 3) w = 0.0 for i in range(3): w = w - 0.1 * grad(w) print(f"纯SGD 第{i+1}步 w={round(w, 3)}") # 0.6 1.08 1.464 w, v = 0.0, 0.0 for i in range(3): v = 0.9 * v - 0.1 * grad(w) # 位移是梯度的滑动平均 w = w + v print(f"动量SGD 第{i+1}步 w={round(w, 3)}") # 0.6 1.62 2.814
动量的含义读代码就懂:连续同向的梯度会累积位移(平缓的长坡上越滚越快),方向来回翻转的梯度互相抵消(震荡被滤平)。神经网络的损失面到处是平缓区与窄沟,这两个性质恰好对症——0.9 这个系数因此成了行业默认。
动量解决了"方向",没解决"步长的贫富差距":梯度常年很大的参数(如嵌入层)步子总是很大,梯度常年很小的参数(如深层权重)步子总是很小。Adam 的方案是给每个参数记两个滑动量:梯度的均值(动量)与梯度的平方均值(振幅),更新时用均值除以振幅的平方根——梯度大的参数自动缩步,梯度小的自动扩步。默认学习率 1e-3 是它广泛测试后的甜点值,前几百步还会有一个小的预热,防止初期估计不稳。
import torch torch.manual_seed(0) X = torch.linspace(0, 1, 64).unsqueeze(1) y = 3 * X + 0.5 + 0.05 * torch.randn_like(X) # 拟合目标:w≈3,b≈0.5 def train(name, lr, steps=300): w = torch.zeros(1, 1, requires_grad=True) b = torch.zeros(1, requires_grad=True) opt = {"sgd": torch.optim.SGD([w, b], lr=lr), "adam": torch.optim.Adam([w, b], lr=lr)}[name] for _ in range(steps): opt.zero_grad() loss = ((X @ w.T + b - y) ** 2).mean() loss.backward() opt.step() return round(w.item(), 2), round(b.item(), 2) print("SGD lr=0.1 :", train("sgd", 0.1)) # 逼近 (3.0, 0.5) print("Adam lr=1e-3:", train("adam", 1e-3)) # 同样逼近,前几十步明显更快
两个优化器最终都收敛到 w≈3、b≈0.5,差别在收敛路径:Adam 前期走得又稳又快,SGD 配合学习率调度在长期精度上常有微弱优势。经验法则由此而来:赶基线用 Adam(1e-3),打磨精度用 SGD 加动量(0.01 到 0.1)配余弦退火;4.6 节的调度正好接上后半句。
| 场景 | 首选 | 初始学习率 | 备注 |
|---|---|---|---|
| 视觉分类从零训练 | SGD 加动量 0.9 | 0.01 到 0.1 | 配合调度器 |
| 快速原型与微调头 | Adam 或 AdamW | 1e-3(头)到 1e-4(主干) | 微调另见 4.7 |
| 小批次(8 到 16) | Adam 系 | 1e-4 到 3e-4 | 批小噪音大,步子收小 |
| 损失爆炸 | 降学习率 + 梯度裁剪 | 减半到减十倍 | 先排除数据标签错误 |
排错口诀按顺序执行:损失不降先查学习率是否过大或过小;震荡查批大小与学习率;NaN 先查数据里有没有异常值、再上梯度裁剪;训练正常但验证不动,转去 4.6 节谈正则化。
问:AdamW 的权重衰减为什么默认 0.01,和 SGD 的 5e-4 差这么多?答:两者作用机制不同——AdamW 的衰减与自适应步长相乘后等效强度被放大,量级自然上调,两者不是同一刻度,别互相照抄。问:热身要多少步?答:经验上占前半个到一个轮次即可(几百到几千步),图小批小时短些,大模型大批时长些;判断标准是训练初期的损失曲线是否平稳下行、没有尖刺。
⚠️ 常见坑:换优化器时沿用旧学习率。Adam 的 1e-3 直接搬到 SGD 上几乎必然震荡(SGD 的常用值要大一到两个数量级),优化器与学习率是绑定选型,不能拆开换。
参数更新的节奏定了,还差原料的量与质。下一节看数据增强:如何在有限标注下把进料口撑宽。