动手学深度学习 7.8 Adam算法

前言

从零开始学习ai文章系列计划是个人在《动手学深度学习》和《磨菇书》两本书的学习中的个人笔记,文章也会以课本中的章节分开,即每个章节一片笔记。我会尽量的把主要内容以及遇到的难点进行记录与解决,如果哪里有错误的欢迎指正。或者不清晰的可以直接查看原文部分。

《动手学深度学习》原文(课本):https://tangshusen.me/Dive-into-DL-PyTorch/#/

《动手学深度学习》代码:https://github.com/ShusenTang/Dive-into-DL-PyTorch

(由于有时候公式太多,可能会直接贴图片)


Adam算法在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均 [1]。下面我们来介绍这个算法。

所以Adam算法可以看做是RMSProp算法与动量法的结合。

1. 算法

Adam算法使用了动量变量\(v_t\) 和RMSProp算法中小批量随机梯度按元素平方的指数加权移动平均变量\(s_t\) ,并在时间步0将它们中每个元素初始化为0。

给定超参数 \(0≤β_1<1\)(算法作者建议设为0.9),时间步t 的动量变量\(v_t\) 即小批量随机梯度\(g_t\) 的指数加权移动平均:

和RMSProp算法中一样,给定超参数 \(0≤β_2<1\)(算法作者建议设为0.999), 将小批量随机梯度按元素平方后的项 \(g_t⊙g_t\) 做指数加权移动平均得到 \(s_t\)

由于我们将 \(v_0\)\(s_0\) 中的元素都初始化为0, 在时间步t 我们得到 \(v_t=(1-\beta_1)\sum_{i=1}^{t} {\beta^{t-i}_1g_i}\)

将过去各时间步小批量随机梯度的权值相加,得到 \((1-\beta_1)\sum_{i=1}^{n} {\beta^{t-i}_1} = 1-\beta^t_1\) (等比公式求和)

需要注意的是,当t较小时,过去各时间步小批量随机梯度权值之和会较小。例如,当β1=0.9时,\(v_1=0.1g_1\) 。为了消除这样的影响,对于任意时间步t,我们可以将vt再除以 \(1-\beta^t_1\) 从而使过去各时间步小批量随机梯度权值之和为1。这也叫作偏差修正。

\(v_t\) 是“加权和”。因为权重总和等于 \(1-\beta^t_1\) ,而不是 1,所以必须除以它,才是 unbiased 的平均。

在Adam算法中,我们对变量 \(v_t\)\(s_t\) 均作偏差修正: \[ \begin{align*} \hat{v_t} \leftarrow \frac{v_t}{1-\beta_1^t} \\ \hat{s_t} \leftarrow \frac{s_t}{1-\beta_2^t} \end{align*} \] 接下来,Adam算法使用以上偏差修正后的变量 \(\hat{v_t}\)\(\hat{s_t}\) ,将模型参数中每个元素的学习率通过按元素运算重新调整: \[ g'_t \leftarrow \frac {\eta \hat{v_t}}{\sqrt{\hat{s_t}}+\epsilon} \] 其中η是学习率,ϵ是为了维持数值稳定性而添加的常数,如 \(10^{-8}\) 。和AdaGrad算法、RMSProp算法以及AdaDelta算法一样,目标函数自变量中每个元素都分别拥有自己的学习率。最后,使用 \(g'_t\) 迭代自变量: \[ x_t \leftarrow x_{t-1}-g'_t \]

Adam 通常默认: \[ \begin{align*} \\ \eta &=0.001 \\ 𝛽_1&=0.9 \\ 𝛽_2&=0.999 \\ \end{align*} \]

2. 从零开始实现

我们按照Adam算法中的公式实现该算法。其中时间步t通过hyperparams参数传入adam函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
features, labels = d2l.get_data_ch7()

def init_adam_states():
v_w, v_b = torch.zeros((features.shape[1], 1), dtype=torch.float32), torch.zeros(1, dtype=torch.float32)
s_w, s_b = torch.zeros((features.shape[1], 1), dtype=torch.float32), torch.zeros(1, dtype=torch.float32)
return ((v_w, s_w), (v_b, s_b))

def adam(params, states, hyperparams):
beta1, beta2, eps = 0.9, 0.999, 1e-6
for p, (v, s) in zip(params, states):
v[:] = beta1 * v + (1 - beta1) * p.grad.data
s[:] = beta2 * s + (1 - beta2) * p.grad.data**2
v_bias_corr = v / (1 - beta1 ** hyperparams['t'])
s_bias_corr = s / (1 - beta2 ** hyperparams['t'])
p.data -= hyperparams['lr'] * v_bias_corr / (torch.sqrt(s_bias_corr) + eps)
hyperparams['t'] += 1

使用学习率为0.01的Adam算法来训练模型。

1
d2l.train_ch7(adam, init_adam_states(), {'lr': 0.01, 't': 1}, features, labels)

3. 简洁实现

通过名称为“Adam”的优化器实例,我们便可使用PyTorch提供的Adam算法。

1
d2l.train_pytorch_ch7(torch.optim.Adam, {'lr': 0.01}, features, labels)