【d2l】Adam

Posted by ShawnD on September 8, 2021

在本节之前的讨论中,我们遇到了许多有效优化的技术。

  • 11.4 中的方法在解决优化问题时比梯度下降更有效
  • 11.5 中的方法通过向量化显著提高了效率, 在一个minibatch中使用更大的观测集合。 这是多机、多GPU、并行处理的关键
  • 11.6 增加了一个机制, 通过聚合历史梯度加速收敛
  • 11.7 对每个坐标进行缩放, 是一个计算效率高的preconditioner
  • 11.8 从学习率调整解耦每个坐标的缩放

Adam 是一个将所有这些技术结合到一起的学习算法。正如预期的那样,这是一种非常流行的算法,是用于深度学习的更健壮和有效的优化算法之一。它并非没有问题, 在有些情况下Adam会由于不好的方差控制发散。 后来的工作提出了一种 hotfix 的Adam, 叫做Yogi, 他解决了这些问题。

The Algorithm

Adam的一个关键部分是使用指数权重 moving averages(也叫做 leaky averaging) 来取得梯度当前的动量和二阶矩:

\[v_t = \beta_1 v_{t-1} + (1 - \beta_1) g_t\] \[s_t = \beta_2 s_{t-1} + (1 - \beta_2) g_t^2\]

这里 $\beta_1$ 和 $\beta_2$ 是非负的权重参数。 通常选择 $\beta_1 = 0.9$ 和 $\beta_2 = 0.999$。 方差估计比动量项移动得慢得多。注意, 如果我们初始化 $v_0 = s_0 = 0$,我们一开始倾向于更小的值。 这可以通过 $\sum_{i=0}^t \beta^i = \frac{1 - \beta^t}{1 - \beta}$ 重规范化项来解决。 相应的规范化状态变量为:

\[\hat v_t = \frac{v_t}{1 - \beta_1^t} \text{ and } \hat s_t = \frac{s_t}{1 - \beta_2^t}\]

有了正确的估计,我们现在可以写出更新方程了。首先,我们以一种非常类似于RMSProp的方式重新缩放梯度来获得:

\[g_t' = \frac{\eta \hat v_t}{\sqrt{\hat s_t} + \epsilon}\]

不和 RMSProp 一样, 我们的更新使用动量 $\hat v_t$ 而不是梯度本身。 此外,缩放使用 $\frac{1}{\sqrt{\hat s_t} + \epsilon}$ 取代 $\frac{1}{\sqrt{\hat s_t + \epsilon}}$。 前者在实践中可能稍微好一些,因此与RMSProp存在偏差。通常我们选择 $\epsilon = 10^{-6}$ 以在数值稳定性和保真度做权衡。

现在我们可以计算更新:

\[x_t \leftarrow x_{t-1} - g_t'\]

回顾Adam的设计非常清晰。 动量和缩放在状态变量中清晰可见。它们相当奇特的定义使我们 debias terms(这可以通过稍微不同的初始化和更新条件来修复)。

Yogi

Adam 的一个问题是当 $s_t$ 中的二阶矩 blow up时, 即便在凸设定下, 它也不能收敛。 我们重写Adam更新如下:

\[s_t \leftarrow s_{t+1} + (1 - \beta_2)(g_t^2 - s_{t-1})\]

当 $g_t^2$ 有高方差 或者 更新稀疏时, $s_t$ 可能快速地忘记过去的值。一个修复是使用 $g_t^2 \odot sgn(g_t^2 - s_{t-1})$ 替换 $g_t^2 - s_{t-1}$。 现在,更新的大小不再取决于偏差的大小。Yogi的更新如下:

\[s_t \leftarrow s_{t-1} + (1 - \beta_2) g_t^2 \odot sgn(g_t^2 - s_{t-1})\]

作者还建议在更大的初始批处理中初始化动量,而不仅仅是初始的点级别估计。