4. 动量优化:让梯度下降跑得更稳更快
梯度下降法有个老毛病——震荡。尤其是在峡谷地形里,参数来回摆动,收敛慢得像蜗牛。我早年调一个图像分类模型,SGD跑了200轮还没稳定,后来换成动量法,80轮就搞定了。说白了,动量就是给梯度下降加了个“惯性”。
4.1 动量法的原理
先看标准SGD的更新公式:
θ = θ - η * ∇L(θ)
每一步只依赖当前梯度。如果梯度方向来回变,参数也跟着来回跳。
动量法引入了速度项v:
v = β * v - η * ∇L(θ)
θ = θ + v
β是动量系数,通常取0.9。v累积了历史梯度的加权平均。当前梯度与历史方向一致时,速度叠加,加速前进;方向相反时,速度抵消,减少震荡。
核心直觉:就像推一个重物。每次推一下,物体会累积动量。你松手它还会继续滑一段。梯度下降加上动量,就能平滑地穿过局部震荡,直奔谷底。
我在项目中遇到过最典型的场景:训练一个深层ResNet,损失曲线像心电图一样上下跳。加上动量(β=0.9)后,曲线立刻变得平滑,收敛速度提升了近一倍。
4.2 Nesterov加速梯度(NAG)
Nesterov是动量法的改良版。它聪明在哪?先“往前看一步”,再计算梯度。
标准动量:先算当前位置的梯度,再更新速度。
Nesterov:先按当前速度“预跳”一步,在预跳位置算梯度,再更新速度。
公式对比:
| 方法 | 速度更新 | 参数更新 |
|---|---|---|
| 标准动量 | v = βv - η∇L(θ) | θ = θ + v |
| Nesterov | v = βv - η∇L(θ + βv) | θ = θ + v |
差别就在梯度计算的位置。Nesterov在θ + βv处算梯度,相当于提前“刹车”或“转向”。
我的经验:Nesterov在接近最优点时更稳。标准动量容易冲过头,Nesterov会提前感知到坡度变化,及时减速。我调一个NLP模型时,NAG比标准动量少用了15%的迭代次数。
为什么会这样?你想想看,标准动量像闭着眼往前冲,Nesterov像睁着眼先瞄一下。瞄完再决定怎么走,自然更精准。
4.3 动量参数的选择技巧
动量系数β是最关键的参数。选错了,要么震荡依旧,要么发散。
| β值 | 行为 | 适用场景 |
|---|---|---|
| 0.0 | 退化为标准SGD | 几乎不用 |
| 0.5~0.8 | 弱动量,轻微平滑 | 梯度噪声小的任务 |
| 0.9 | 标准动量,平衡性好 | 大多数深度学习任务 |
| 0.95~0.99 | 强动量,惯性大 | 梯度噪声大、地形平坦 |
我个人习惯从β=0.9开始调。如果损失曲线震荡明显,适当降低到0.8;如果收敛太慢,可以试试0.95。
我曾经踩过的坑:β设到0.99,结果模型直接发散。强动量在陡峭地形上容易冲过头,收不住。后来加了学习率预热,才稳住。记住:β越大,学习率要越小,或者加warmup。
学习率η和β需要配合。我常用的经验法则:
- β=0.9时,η可以保持SGD的默认值(如0.01)
- β=0.99时,η要降到原来的1/2到1/3
- β=0.5时,η可以适当增大
还有一个技巧:动量预热。前几个epoch让β从0线性增加到目标值。这样模型一开始不会“冲太猛”,等梯度方向稳定后再加速。
4.4 动量法的变体与选择
实际工程中,我们很少手写动量。PyTorch和TensorFlow都内置了。但了解底层实现,能帮你更好地调参。
PyTorch的SGD优化器:
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9, nesterov=True)
设置nesterov=True就启用了NAG。我建议默认开启,几乎没有额外计算成本,收益却很明显。
Adam优化器内部也用了动量思想。它同时维护一阶动量(梯度均值)和二阶动量(梯度方差)。但Adam的动量系数是自适应的,不需要手动调β。
| 优化器 | 动量类型 | 调参难度 | 推荐场景 |
|---|---|---|---|
| SGD + Momentum | 手动β | 中等 | CV、需要精细调参 |
| SGD + Nesterov | 手动β | 中等 | 需要更快收敛 |
| Adam | 自适应β | 低 | NLP、快速原型 |
嗯,这里要注意:Adam虽然省心,但有时泛化性不如SGD+Momentum。我做过对比实验,在CIFAR-10上,SGD+动量(β=0.9)最终准确率比Adam高0.5~1%。
我的建议:
- 快速验证用Adam
- 追求极致性能用SGD+Nesterov
- β从0.9开始,观察损失曲线再微调
- 学习率与β联动调整,不要单独改一个
4.5 动量优化核心流程
下面这张图总结了动量优化的完整流程,从初始化到参数更新:
从流程图可以看出,Nesterov只是在标准动量基础上多了一步“预跳”。这个改动很小,但效果显著。我建议所有新项目都默认用Nesterov。
一句话总结:动量让梯度下降有了“记忆”,Nesterov让这个记忆有了“远见”。两者结合,收敛又快又稳。