深度学习中优化器的Nesterov加速梯度(Nesterov Accelerated Gradient, NAG)算法原理与实现细节
1. 题目描述
Nesterov加速梯度(NAG)是一种基于动量(Momentum)的优化算法,由Yurii Nesterov在1983年提出。它在标准动量法的基础上进行改进,通过“前瞻一步”的方式更新梯度,从而在凸优化问题中达到更快的收敛速度。在深度学习中,NAG常被用于训练神经网络,尤其是在损失函数具有较高曲率或病态条件时表现优异。
核心问题:
- 标准动量法在更新时先计算当前梯度,再结合动量更新参数,可能导致“过冲”(overshooting)问题。
- NAG通过先根据动量方向“展望”未来参数位置,再计算该位置的梯度,从而更准确地调整更新方向。
2. 背景:标准动量法回顾
动量法(Momentum)的更新公式为:
- 计算当前梯度:
\[ g_t = \nabla f(\theta_{t-1}) \]
- 更新动量项(指数移动平均):
\[ v_t = \mu v_{t-1} + \eta g_t \]
- 更新参数:
\[ \theta_t = \theta_{t-1} - v_t \]
其中:
- \(\mu\) 是动量系数(通常设为0.9),
- \(\eta\) 是学习率,
- \(v_t\) 是累积的动量向量。
缺点:在梯度变化剧烈时,动量方向可能“滞后”,导致参数更新不够精准。
3. NAG的核心思想
NAG通过调整梯度计算顺序来解决上述问题:
- 先根据历史动量方向“跳跃”到一个临时位置(称为“前瞻位置”):
\[ \theta_{\text{lookahead}} = \theta_{t-1} - \mu v_{t-1} \]
- 然后在该位置计算梯度:
\[ g_t = \nabla f(\theta_{\text{lookahead}}) \]
- 最后用该梯度更新动量并调整参数。
直观理解:
想象一个小球从山坡滚下,标准动量法根据当前位置的坡度调整速度;而NAG先根据当前速度预测小球下一步的位置,再根据该位置的坡度调整速度,从而更早“感知”地形变化,减少振荡。
4. NAG的算法步骤(详细推导)
步骤1:初始化参数
- 初始参数 \(\theta_0\)(随机初始化),
- 初始动量 \(v_0 = 0\),
- 学习率 \(\eta\),动量系数 \(\mu \in [0,1)\)。
步骤2:循环更新(第 t 步)
- 计算“前瞻位置”:
\[ \theta_{\text{lookahead}} = \theta_{t-1} - \mu v_{t-1} \]
- 在前瞻位置计算梯度:
\[ g_t = \nabla f(\theta_{\text{lookahead}}) \]
- 更新动量项:
\[ v_t = \mu v_{t-1} + \eta g_t \]
- 更新参数:
\[ \theta_t = \theta_{t-1} - v_t \]
注意:第3-4步可合并为:
\[\theta_t = \theta_{\text{lookahead}} - \eta g_t \]
但标准实现通常保留动量项 \(v_t\) 用于下一步计算。
5. 与标准动量法的对比
| 方面 | 标准动量法 | NAG |
|---|---|---|
| 梯度计算位置 | 当前参数 \(\theta_{t-1}\) | 前瞻位置 \(\theta_{t-1} - \mu v_{t-1}\) |
| 物理意义 | 根据当前坡度加速 | 根据未来坡度提前调整 |
| 收敛性 | 在凸函数中收敛速度为 \(O(1/t)\) | 加速为 \(O(1/t^2)\)(理论保证) |
| 振荡抑制 | 可能过冲 | 更平滑,减少振荡 |
6. 代码实现示例(PyTorch风格)
import torch
def nesterov_momentum(parameters, lr=0.01, mu=0.9, num_iters=100):
params = list(parameters)
v = [torch.zeros_like(p) for p in params] # 动量向量
for t in range(num_iters):
# 1. 计算前瞻位置的临时参数
temp_params = [p - mu * v[i] for i, p in enumerate(params)]
# 2. 计算损失(伪代码,需根据实际任务定义损失函数)
loss = compute_loss(temp_params) # 假设 compute_loss 返回标量
loss.backward() # 对 temp_params 计算梯度
# 3. 更新动量和参数
for i, p in enumerate(params):
if p.grad is not None:
g = p.grad.data
v[i] = mu * v[i] + lr * g
p.data -= v[i]
p.grad.zero_()
关键点:
- 在
backward()前需将模型参数临时替换为前瞻位置(实际可通过torch.nn.utils.vector_to_parameters实现)。 - 更简洁的实现方式(常见于SGD with Nesterov Momentum):
\[ v_{t} = \mu v_{t-1} + \eta \nabla f(\theta_{t-1} - \mu v_{t-1}) \]
\[ \theta_{t} = \theta_{t-1} - v_{t} \]
7. 在深度学习中的实际应用
- 框架支持:PyTorch 和 TensorFlow 的优化器(如
torch.optim.SGD设置nesterov=True)已内置NAG。 - 超参数选择:
- 动量系数 \(\mu\) 通常取 0.9 或 0.99。
- 学习率 \(\eta\) 需随训练调整(可结合学习率调度器)。
- 适用场景:
- 损失函数曲面不规则、曲率较大时(如RNN、Transformer训练)。
- 与自适应学习率方法(如Adam)相比,NAG在理论上对凸问题有更好的收敛保证。
8. 总结
- NAG通过“前瞻梯度”修正动量方向,减少了标准动量法的更新滞后问题。
- 在深度学习中,NAG常作为SGD的增强版本,尤其适用于非光滑或病态条件优化问题。
- 实现时需注意梯度计算位置,现有深度学习框架已提供封装,可直接调用。
通过上述步骤,你可以理解NAG的原理、推导过程、实现细节及其与标准动量法的区别。