详细介绍三种最具影响力且被广泛引用的梯度下降改进算法:Adam、RMSprop和Momentum。
Adam算法由Kingma和Ba在2014年提出,是目前深度学习中使用最广泛的优化算法之一,其原始论文被引用超过10万次。
Adam结合了动量法(Momentum)和自适应学习率方法(如AdaGrad、RMSprop)的优点,通过计算梯度的一阶矩估计和二阶矩估计来为每个参数动态调整学习率。
设目标函数为 $f(\theta)$,其中 $\theta$ 为模型参数,$g_t = \nabla_\theta f(\theta_t)$ 为时刻 $t$ 的梯度。
$m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t$
$v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2$
由于 $m_0 = 0$ 和 $v_0 = 0$,在训练初期会产生偏向零的偏差,需要进行修正:
$\hat{m}_t = \frac{m_t}{1 - \beta_1^t}$
$\hat{v}_t = \frac{v_t}{1 - \beta_2^t}$
$\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t$
RMSprop由Geoffrey Hinton在其Coursera课程中提出,虽然没有正式发表的论文,但被广泛采用并成为许多深度学习框架的标准优化器。
RMSprop解决了AdaGrad算法中学习率单调递减导致训练后期学习率过小的问题。通过使用指数移动平均来限制历史梯度平方的累积,使得学习率能够在训练过程中保持合理的水平。
$E[g^2]_t = \gamma E[g^2]_{t-1} + (1 - \gamma) g_t^2$
$\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{E[g^2]_t + \epsilon}} g_t$
其中:$\gamma$ 为衰减因子(通常为 0.9),$\eta$ 为学习率(通常为 0.001),$\epsilon$ 为防止除零的小常数(通常为 $10^{-8}$)
AdaGrad的更新公式为:$\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{G_t + \epsilon}} g_t$
其中 $G_t = \sum_{i=1}^{t} g_i^2$ 是所有历史梯度平方的累积和。
RMSprop通过指数移动平均 $E[g^2]_t$ 替代了累积和 $G_t$,避免了学习率的无限递减。
动量法最早由Polyak在1964年提出,后来Nesterov在1983年提出了加速版本。作为经典的优化方法,其相关工作累计被引用数万次。
动量法借鉴了物理学中的动量概念,通过累积历史梯度信息来加速收敛并减少震荡。就像一个球滚下山坡时会积累动量一样,参数更新也会在一致的方向上积累"动量"。
$v_t = \gamma v_{t-1} + \eta g_t$
$\theta_{t+1} = \theta_t - v_t$
其中:
等价形式:$\theta_{t+1} = \theta_t - (\gamma v_{t-1} + \eta g_t)$
Nesterov加速梯度是动量法的改进版本,具有"预见性"。
$v_t = \gamma v_{t-1} + \eta \nabla f(\theta_t - \gamma v_{t-1})$
$\theta_{t+1} = \theta_t - v_t$
关键思想:NAG首先根据当前动量 $\gamma v_{t-1}$ 进行一个"预测性"跳跃到 $\theta_t - \gamma v_{t-1}$,然后在该位置计算梯度,最后结合这个梯度和动量进行实际的参数更新。
在损失函数的等高线图中:
| 算法 | 收敛速度 | 稳定性 | 超参数敏感性 | 内存开销 |
|---|---|---|---|---|
| Momentum | 中等 | 中等 | 低 | 低 |
| RMSprop | 快 | 高 | 中等 | 中等 |
| Adam | 快 | 高 | 低 | 高 |
class Adam:
def __init__(self, lr=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8):
self.lr = lr
self.beta1 = beta1
self.beta2 = beta2
self.epsilon = epsilon
self.m = {} # 一阶矩估计
self.v = {} # 二阶矩估计
self.t = 0 # 时间步
def update(self, params, grads):
self.t += 1
for key in params:
if key not in self.m:
self.m[key] = np.zeros_like(params[key])
self.v[key] = np.zeros_like(params[key])
# 更新一阶和二阶矩估计
self.m[key] = self.beta1 * self.m[key] + (1 - self.beta1) * grads[key]
self.v[key] = self.beta2 * self.v[key] + (1 - self.beta2) * grads[key]**2
# 偏差修正
m_hat = self.m[key] / (1 - self.beta1**self.t)
v_hat = self.v[key] / (1 - self.beta2**self.t)
# 参数更新
params[key] -= self.lr * m_hat / (np.sqrt(v_hat) + self.epsilon)
class RMSprop:
def __init__(self, lr=0.001, gamma=0.9, epsilon=1e-8):
self.lr = lr
self.gamma = gamma
self.epsilon = epsilon
self.v = {} # 梯度平方的指数移动平均
def update(self, params, grads):
for key in params:
if key not in self.v:
self.v[key] = np.zeros_like(params[key])
# 更新梯度平方的指数移动平均
self.v[key] = self.gamma * self.v[key] + (1 - self.gamma) * grads[key]**2
# 参数更新
params[key] -= self.lr * grads[key] / (np.sqrt(self.v[key]) + self.epsilon)
class Momentum:
def __init__(self, lr=0.01, gamma=0.9):
self.lr = lr
self.gamma = gamma
self.v = {} # 速度(动量项)
def update(self, params, grads):
for key in params:
if key not in self.v:
self.v[key] = np.zeros_like(params[key])
# 更新速度
self.v[key] = self.gamma * self.v[key] + self.lr * grads[key]
# 参数更新
params[key] -= self.v[key]
本作业介绍了三种最具影响力的梯度下降改进算法: