Appearance
动量法
动量法是梯度下降(及 SGD、小批量 SGD)的 “加速升级包”—— 核心是给登山者加 “惯性”,让顺坡方向冲得更快,震荡方向稳得更牢,专门解决梯度下降在 “狭窄峡谷”(目标函数凹凸不平、不同方向梯度差异大)里的收敛慢、震荡大的问题,是深度学习里常用的优化算法之一。
一、核心逻辑:为啥需要 “惯性登山”?
1. 梯度下降的痛点
梯度下降(包括 SGD、小批量 SGD)在遇到 “狭窄峡谷” 地形时(比如目标函数 , 方向平缓、 方向陡峭),会陷入两难:
学习率太小: 方向(平缓坡)走得极慢,20 轮迭代还在半山腰;
学习率太大: 方向(陡峭坡)会来回震荡甚至发散,根本下不了山。
这就像登山者在 “窄峡谷” 里:一侧坡缓、一侧坡陡,每步都要重新判断方向,要么慢得离谱,要么晃得摔倒。
2. 动量法的解决方案
动量法给登山者加了 “惯性”——记住过去的下山方向,积累冲劲:
顺坡方向(比如 的平缓坡):过去的梯度方向一致,惯性会不断积累,像顺坡冲下去,速度越来越快;
震荡方向(比如 的陡峭坡):过去的梯度方向来回相反,惯性会抵消反向的梯度,像冲坡时稳住重心,不会来回晃。
简单说:动量法让 “对的方向更顺,错的方向更稳”。
二、动量法的核心原理
1. 核心公式
动量法不直接用当前梯度更新参数,而是先计算 “动量”(过去梯度的加权平均),再用动量更新参数:
其中:
:动量,是过去所有梯度的泄漏平均值(越近的梯度权重越大);
:动量系数,控制惯性大小, , 越大,惯性越强,能记住的过去梯度越多;
:当前步的梯度;
:学习率,控制步长大小。
2. 公式解读
把动量公式展开可以看到,动量是过去梯度的加权和:
当 时, ,相当于记住最近 10 步的梯度;
当 时,动量法退化为普通梯度下降。
3. 惯性的效果
以 “狭窄峡谷” 地形为例:
方向(平缓坡):每次梯度方向一致,动量 会不断积累,20 轮就能冲到山脚;
方向(陡峭坡):梯度方向来回震荡,动量 会抵消反向梯度,最终平稳下降。
三、关键参数:怎么调才不 “冲过头”?
1. 动量系数
常用值: (默认最优选择),兼顾惯性和灵活性;
小 (如 ):惯性不足,解决震荡的效果差,收敛慢;
大 (如 ):惯性太大,容易 “冲过山脚”(参数超调),需要配合更小的学习率。
2. 学习率
动量法的有效步长是 ,比普通梯度下降大,所以 要比梯度下降时略小:
比如普通梯度下降用 ,动量法( )可设 ;
若 太大,即使有动量,也可能在陡峭方向发散。
3. 参数搭配口诀
“ 选 0.9, 往小了调;峡谷越窄, 越大、 越小”。
四、PyTorch 实战:带惯性的登山流程
1. 从零开始实现
手动实现动量法的参数更新逻辑,适合理解原理:
python
import torch
from torch import nn
from d2l import torch as d2l
# 1. 初始化动量状态(和参数同形状的零向量)
def init_momentum_states(feature_dim):
v_w = torch.zeros((feature_dim, 1)) # 权重的动量
v_b = torch.zeros(1) # 偏置的动量
return (v_w, v_b)
# 2. 动量法更新函数
def sgd_momentum(params, states, hyperparams):
for p, v in zip(params, states):
with torch.no_grad():
# 计算动量:过去动量*β + 当前梯度
v[:] = hyperparams['momentum'] * v + p.grad
# 用动量更新参数
p[:] -= hyperparams['lr'] * v
p.grad.data.zero_() # 清空梯度
# 3. 训练函数
def train_momentum(lr, momentum, num_epochs=2):
# 加载数据
data_iter, feature_dim = d2l.get_data_ch11(batch_size=10)
# 定义模型
net = nn.Sequential(nn.Linear(feature_dim, 1))
nn.init.normal_(net[0].weight, std=0.01)
# 训练
d2l.train_ch11(
sgd_momentum,
init_momentum_states(feature_dim),
{'lr': lr, 'momentum': momentum},
data_iter, feature_dim, num_epochs
)
# 启动训练(lr=0.005,momentum=0.9,经典搭配)
train_momentum(lr=0.005, momentum=0.9)2. 框架简洁实现
用 PyTorch 内置的optim.SGD直接调用动量法,更简洁:
python
# 直接使用框架内置的动量SGD
trainer = torch.optim.SGD
d2l.train_concise_ch11(
trainer,
{'lr': 0.005, 'momentum': 0.9}, # 配置动量参数
data_iter
)五、理论分析:动量法的收敛性
在凸二次问题中,动量法的收敛性可以通过特征值分解分析:
目标函数可以分解为多个独立的一维优化问题,动量法在每个维度上的收敛是独立的;
动量法的收敛范围是 ( 是目标函数的特征值),比梯度下降的 更大,说明动量法的参数选择更灵活;
有效梯度数为 , 越大,能利用的过去梯度越多,收敛越快。
六、常见误区:避开这 3 个坑
只调学习率,不调 : 时有效步长更大,若 和普通梯度下降一样大,会冲过山脚;
设得太大(如 ):惯性太强,参数更新 “刹不住车”,后期震荡;
忽略动量的状态存储:动量 需要和参数同形状,初始化必须为 0,不能随意设值。
七、核心总结
动量法本质:带惯性的梯度下降,用过去梯度的加权平均(动量)替代当前梯度,顺坡加速、震荡减速;
关键参数: (惯性大小,默认 0.9)、 (步长,配合 减小);
适用场景:所有深度学习任务,尤其适合目标函数 “凹凸不平”(如 CNN、Transformer)的场景;
优势:收敛更快,震荡更小,能解决梯度下降在狭窄峡谷里的痛点;
一句话记忆:登山带惯性,顺坡冲得快,晃坡稳得住,下山更高效~
(注:文档部分内容可能由 AI 生成)