NMT Tutorial 2扩展b. 优化方法
基本优化方法:梯度下降
如正文 所提,梯度下降是一般机器学习中应用最多的优化算法,核心思想是让参数朝着梯度的反方向,也就是函数下降最快的方向移动。设定如下记号:
θ \boldsymbol{\theta} θ :模型参数
x ( i ) \boldsymbol{x}^{(i)} x ( i ) :第i i i 条数据
f f f :模型
y ( i ) \boldsymbol{y}^{(i)} y ( i ) :第i i i 条数据对应的标签
L L L :损失函数
n n n :样本总数量
η \eta η :学习率
梯度下降会根据样本求出一个梯度估计g ^ \hat{\boldsymbol{g}} g ^ ,然后根据梯度估计对原始的参数进行修正:θ ( t ) ← θ ( t − 1 ) − η g ^
\boldsymbol{\theta}^{(t)} \leftarrow \boldsymbol{\theta}^{(t-1)} - \eta \hat{\boldsymbol{g}}
θ ( t ) ← θ ( t − 1 ) − η g ^
批量梯度下降(batch gradient descent)会使用所有数据的平均梯度来估计g ^ \hat{\boldsymbol{g}} g ^ g ^ ← 1 n ∑ i = 1 n ∇ θ L ( f ( x ( i ) ; θ ) , y ( i ) )
\hat{\boldsymbol{g}} \leftarrow \frac{1}{n} \sum_{i=1}^n \nabla_\boldsymbol{\theta}L\left(f(\boldsymbol{x}^{(i)};\boldsymbol{\theta}), \boldsymbol{y}^{(i)}\right)
g ^ ← n 1 i = 1 ∑ n ∇ θ L ( f ( x ( i ) ; θ ) , y ( i ) )
这种更新方法的缺点是计算起来非常慢,而且如果数据集很大甚至会把内存爆掉。但是,对于凸函数,批量梯度下降会保证算法收敛到最值点;对于非凸函数,可以收敛到极值点
批量梯度下降对应的极端是随机梯度下降(stochastic gradient descent, SGD),每当该算法接收一条数据,都会计算当前数据的梯度,并使用其更新参数。即g ^ ← ∇ θ L ( f ( x ( i ) ; θ ) , y ( i ) )
\hat{\boldsymbol{g}} \leftarrow \nabla_{\boldsymbol{\theta}}L\left(f(\boldsymbol{x}^{(i)}; \boldsymbol{\theta}), \boldsymbol{y}^{(i)}\right)
g ^ ← ∇ θ L ( f ( x ( i ) ; θ ) , y ( i ) )
SGD的优点是计算比较快,而且可以使用新数据快速更新模型参数。但是,如果保持学习率不变,SGD会使得参数在极值点附近剧烈抖动。不过如果是学习率随着学习的过程缓慢减小,SGD可以达到和批量梯度下降近似的结果
对这两种方法的一个折中是小批量梯度下降(mini-batch gradient descent),即算法接收m ( m ≪ n ) m\ (m \ll n) m ( m ≪ n ) 条数据,根据这些数据的梯度信息更新参数g ^ ← 1 m ∑ i = 1 m ∇ θ L ( f ( x ( i ) ; θ ) , y ( i ) )
\hat{\boldsymbol{g}} \leftarrow \frac{1}{m} \sum_{i=1}^m \nabla_\boldsymbol{\theta}L\left(f(\boldsymbol{x}^{(i)};\boldsymbol{\theta}), \boldsymbol{y}^{(i)}\right)
g ^ ← m 1 i = 1 ∑ m ∇ θ L ( f ( x ( i ) ; θ ) , y ( i ) )
这种方法既可以减少每次更新量的方差进而达到一个稳定的收敛过程,也保持了计算的效率(因为现在有很多成熟的库对矩阵计算有很好的优化支持)
在其它一些文献中,也有些将小批量梯度下降称为SGD,而将这里的SGD称为“基于梯度下降的在线学习”(online learning)
使用梯度下降法来做优化可能会面临如下问题
很难选取一个合适的学习率。太小的学习率会导致收敛太慢,太大的学习率会难以收敛,损失函数在极值点附近震荡甚至发散。尽管可以使用诸如模拟退火这样的方法来调整学习率,但是这种做法需要额外的工作量,也难以很好地适配具体训练集的特点
经典的梯度下降法中,梯度的每一个分量所乘的学习率都相同。如果数据稀疏,特征的频率也不同,那么更好的方法是对不同的特征设计不同的学习率
相比较于局部最优点,非凸函数更大的问题是存在一些鞍点,在鞍点上某些方向的梯度会变大,某些则会变小。此外,鞍点周围会有大量的平原区域,使得梯度近似于0,权重更新极其缓慢
基于动量的梯度下降及其扩展
动量法
与传统的梯度下降法相比,动量法 (momentum)的形式是在每次更新参数时,除了加上m m m 个样本的平均梯度,还要加上一部分上一步的权重更新量。在第t t t 时刻,算法先求出梯度估计g ^ \hat{\boldsymbol{g}} g ^ ,然后使用上一步的权重更新量v ( t − 1 ) \boldsymbol{v}^{(t-1)} v ( t − 1 ) 和g ^ \hat{\boldsymbol{g}} g ^ 求出本步权重更新量v ( t ) \boldsymbol{v}^{(t)} v ( t ) ,最后更新权重g ^ ← 1 m ∑ i = 1 m ∇ θ L ( f ( x ( i ) ; θ ( t − 1 ) ) , y ( i ) ) v ( t ) ← α v ( t − 1 ) − η g ^ θ ( t ) ← θ ( t − 1 ) + v ( t )
\begin{aligned}
\hat{\boldsymbol{g}} &\leftarrow \frac{1}{m} \sum_{i=1}^m \nabla_\boldsymbol{\theta}L\left(f(\boldsymbol{x}^{(i)};\boldsymbol{\theta}^{(t-1)}), \boldsymbol{y}^{(i)}\right) \\
\boldsymbol{v}^{(t)} &\leftarrow \alpha\boldsymbol{v}^{(t-1)} - \eta\hat{\boldsymbol{g}} \\
\boldsymbol{\theta}^{(t)} &\leftarrow \boldsymbol{\theta}^{(t-1)} + \boldsymbol{v}^{(t)}
\end{aligned}
g ^ v ( t ) θ ( t ) ← m 1 i = 1 ∑ m ∇ θ L ( f ( x ( i ) ; θ ( t − 1 ) ) , y ( i ) ) ← α v ( t − 1 ) − η g ^ ← θ ( t − 1 ) + v ( t )
可以看出,使用动量法,每次更新权重的时候保留了一些上一次的更新信息,这里引入的新的超参数α \alpha α 就是控制保留多少上次的更新信息。考虑到在没有引入v \boldsymbol{v} v 时g ^ \hat{\boldsymbol{g}} g ^ 给出了每次参数前进的方向,因此动量法的核心是把上一步的前进方向和当前前进方向做一个加权平均。这样的做法在不同的情况下有两个效果
当每次梯度的方向大致相同时,上一步的前进方向可以看作是对参数变化的助推器,使得参数移动的步长越来越大。因此,随着训练的迭代次数增多,学习率η \eta η 应该逐渐变小
当每次梯度的方向很不相同时,实际上函数是病态条件的。如果这个代价函数是一个二次函数,画出等高线以后会发现椭圆的一个轴很长,另一个轴很短。如果使用传统的梯度下降,代价函数的值就会沿着椭圆的短轴方向来回振动,而每次振动时沿着长轴方向实际只迈出了微小的一步。在这种情况下,上一步的前进方向实际上起到了平滑的作用,尤其是当α \alpha α 比较大(事实上一般也的确不小)时,实际上更多的是保留原来的前进方向,新的梯度只是一种修正而已。这样就可以减少代价函数振动的程度,使其前进方向更指向极值点
下图给出了原始SGD与动量法加速的SGD损失函数优化轨迹的比较
假设每次迭代的梯度g ^ \hat{\boldsymbol{g}} g ^ 相同,那么可以对v ( t ) \boldsymbol{v}^{(t)} v ( t ) 展开:v ( t ) = α v ( t − 1 ) − η g ^ = α ( α v ( t − 2 ) − η g ^ ) − η g ^ = α 2 v ( t − 2 ) − α η g ^ − η g ^ = … = α t v ( 0 ) − α t − 1 η g ^ − ⋯ − η g ^
\begin{aligned}
\boldsymbol{v}^{(t)} &= \alpha\boldsymbol{v}^{(t-1)} - \eta\hat{\boldsymbol{g}} \\
&= \alpha(\alpha\boldsymbol{v}^{(t-2)}- \eta\hat{\boldsymbol{g}}) - \eta\hat{\boldsymbol{g}} \\
&= \alpha^2\boldsymbol{v}^{(t-2)} -\alpha\eta\hat{\boldsymbol{g}} - \eta\hat{\boldsymbol{g}} \\
&= \ldots \\
&= \alpha^t\boldsymbol{v}^{(0)} - \alpha^{t-1}\eta\hat{\boldsymbol{g}} - \cdots - \eta\hat{\boldsymbol{g}}
\end{aligned}
v ( t ) = α v ( t − 1 ) − η g ^ = α ( α v ( t − 2 ) − η g ^ ) − η g ^ = α 2 v ( t − 2 ) − α η g ^ − η g ^ = … = α t v ( 0 ) − α t − 1 η g ^ − ⋯ − η g ^
由于第0时刻没有速度,因此上式第一项为0。其余项通过等比数列求和可知v ( t ) = − η g ^ 1 − α
\boldsymbol{v}^{(t)} = -\frac{\eta\hat{\boldsymbol{g}}}{1-\alpha}
v ( t ) = − 1 − α η g ^
故而动量法新引入的超参数α \alpha α 可以看作是加速倍数。当α = 0.9 \alpha = 0.9 α = 0 . 9 时,可以看做最后步长是普通梯度下降法的1/(1-0.9) = 10倍。所以在使用动量法优化参数时,需要随着迭代次数的增多缩小学习率
假设把负梯度看作是让粒子在空间中移动的力,那么力的作用会让粒子的移动速度发生改变。如果将粒子看作是单位质量的,那么v = m v = p \boldsymbol{v} = m\boldsymbol{v} = {\boldsymbol{p}} v = m v = p 也可以看做是物体的动量。这就是这个算法名称的由来
distill上的这篇文章 给出了对动量法更深的讨论。如果对数学细节不感兴趣,至少可以玩玩文章开头的demo来感受一下动量法超参数的意义和如何选取:)
Nesterov动量法
动量法加速了参数优化的速度,但是仍然不够智能。如果算法可以在看到陡峭的损失平面时提前减速,效果应该会更好。Sutskever在2013年受Nesterov加速梯度算法启发提出了一种方法,使用如下规则更新参数θ ~ ( t − 1 ) ← θ + α v ( t − 1 ) g ^ ← 1 m ∑ i = 1 m ∇ θ ~ L ( f ( x ( i ) ; θ ~ ( t − 1 ) ) , y ( i ) ) v ( t ) ← α v ( t − 1 ) − η g ^ θ ( t ) ← θ ( t − 1 ) + v ( t )
\begin{aligned}
\tilde{\boldsymbol{\theta}}^{(t-1)} &\leftarrow \boldsymbol{\theta} + \alpha\boldsymbol{v}^{(t-1)} \\
\hat{\boldsymbol{g}} &\leftarrow \frac{1}{m}\sum_{i=1}^m\nabla_{\tilde{\boldsymbol{\theta}}}L\left(f(\boldsymbol{x}^{(i)};\tilde{\boldsymbol{\theta}}^{(t-1)}), \boldsymbol{y}^{(i)}\right) \\
\boldsymbol{v}^{(t)} &\leftarrow \alpha\boldsymbol{v}^{(t-1)} - \eta\hat{\boldsymbol{g}} \\
\boldsymbol{\theta}^{(t)} &\leftarrow \boldsymbol{\theta}^{(t-1)} + \boldsymbol{v}^{(t)}
\end{aligned}
θ ~ ( t − 1 ) g ^ v ( t ) θ ( t ) ← θ + α v ( t − 1 ) ← m 1 i = 1 ∑ m ∇ θ ~ L ( f ( x ( i ) ; θ ~ ( t − 1 ) ) , y ( i ) ) ← α v ( t − 1 ) − η g ^ ← θ ( t − 1 ) + v ( t )
这种算法称为Nesterov动量法,也称为Nesterov加速梯度法(Nesterov Accelarated Gradient, NAG)。相比于传统的动量法,NAG对当前点梯度∇ θ \nabla_{\boldsymbol{\theta}} ∇ θ 的信赖程度没有那么高,而是去看按照速度向量走出一步以后的梯度值∇ θ ~ \nabla_{\tilde{\boldsymbol{\theta}}} ∇ θ ~ 。如果∇ θ \nabla_{\boldsymbol{\theta}} ∇ θ 和∇ θ ~ \nabla_{\tilde{\boldsymbol{\theta}}} ∇ θ ~ 差别不大,那么两者的更新量近似;然而如果两者相差比较大,那么说明目标函数表面可能存在一些弯曲,用之前的梯度可能会走向不希望的方向。这样,NAG收敛更快,也更稳定。下图给出了NAG和原始动量法的比较
《比Momentum更快:揭开Nesterov Accelerated Gradient的真面目 》一文对NAG的原理给出了更进一步的分析,得出结论是
在原始形式中,Nesterov Accelerated Gradient(NAG)算法相对于Momentum的改进在于,以“向前看”看到的梯度而不是当前位置梯度去更新。经过变换之后的等效形式中,NAG算法相对于Momentum多了一个本次梯度相对上次梯度的变化量,这个变化量本质上是对目标函数二阶导的近似。由于利用了二阶导的信息,NAG算法才会比Momentum具有更快的收敛速度。
[Ruder2016]提到NAG可以显著提升RNN的性能。然而,花书指出,在随机梯度的情况下,NAG没有改进收敛率
自适应学习率算法
基于动量的梯度下降法主要调整所有 参数收敛的方向和速度,而更多的优化算法致力于使每个 参数的学习率都能各自动态自适应
AdaGrad
定义⊙ \odot ⊙ 为两个向量的逐元素乘积,AdaGrad的算法的参数更新规则为g ^ ← 1 m ∑ i = 1 m ∇ θ L ( f ( x ( i ) ; θ ( t − 1 ) ) , y ( i ) ) r ( t ) ← r ( t − 1 ) + g ^ ⊙ g ^ Δ θ ( t ) ← − η ϵ + r ( t ) ⊙ g ^ θ ( t ) ← θ ( t − 1 ) + Δ θ ( t )
\begin{aligned}
\hat{\boldsymbol{g}} &\leftarrow \frac{1}{m} \sum_{i=1}^m \nabla_\boldsymbol{\theta}L\left(f(\boldsymbol{x}^{(i)};\boldsymbol{\theta}^{(t-1)}), \boldsymbol{y}^{(i)}\right) \\
\boldsymbol{r}^{(t)} &\leftarrow \boldsymbol{r}^{(t-1)} + \hat{\boldsymbol{g}}\odot\hat{\boldsymbol{g}} \\
\Delta\boldsymbol{\theta}^{(t)} &\leftarrow -\frac{\eta}{\epsilon +\sqrt{\boldsymbol{r}^{(t)}}}\odot \hat{\boldsymbol{g}} \\
\boldsymbol{\theta}^{(t)} &\leftarrow \boldsymbol{\theta}^{(t-1)} + \Delta\boldsymbol{\theta}^{(t)}
\end{aligned}
g ^ r ( t ) Δ θ ( t ) θ ( t ) ← m 1 i = 1 ∑ m ∇ θ L ( f ( x ( i ) ; θ ( t − 1 ) ) , y ( i ) ) ← r ( t − 1 ) + g ^ ⊙ g ^ ← − ϵ + r ( t ) η ⊙ g ^ ← θ ( t − 1 ) + Δ θ ( t )
其中第三步计算权重更新量的时候使用的是逐元素开方、加和和被常数除。从这部更新量的计算方法来看,更新量与前面梯度平方的累积量r \boldsymbol{r} r 成反比。因此如果梯度在某个维度上的分量一直很大,就会使得参数在该分量上的学习率下降更快。但是由于r \boldsymbol{r} r 单调递增,因此每个元素的学习率一直不会提高,可能会快速收敛到一个不好的解
使用AdaGrad优化器时,通常把ϵ \epsilon ϵ 设为0.01,ϵ \epsilon ϵ 设为1 0 − 8 10^{-8} 1 0 − 8 。这里ϵ \epsilon ϵ 是一个为了让分母不为0的稳定值
AdaDelta
针对AdaGrad会使学习率单调下降(确切说是不增)的情况,AdaDelta做出了修改,不再单纯累加梯度的平方,而是将过往梯度的平方与当前梯度的平方一起求一个加权平均值g ^ ← 1 m ∑ i = 1 m ∇ θ L ( f ( x ( i ) ; θ ( t − 1 ) ) , y ( i ) ) r ( t ) ← ρ r ( t − 1 ) + ( 1 − ρ ) g ^ ⊙ g ^
\begin{aligned}
\hat{\boldsymbol{g}} &\leftarrow \frac{1}{m} \sum_{i=1}^m \nabla_\boldsymbol{\theta}L\left(f(\boldsymbol{x}^{(i)};\boldsymbol{\theta}^{(t-1)}), \boldsymbol{y}^{(i)}\right) \\
\boldsymbol{r}^{(t)} &\leftarrow \rho\ \boldsymbol{r}^{(t-1)} + (1-\rho)\ \hat{\boldsymbol{g}}\odot\hat{\boldsymbol{g}}
\end{aligned}
g ^ r ( t ) ← m 1 i = 1 ∑ m ∇ θ L ( f ( x ( i ) ; θ ( t − 1 ) ) , y ( i ) ) ← ρ r ( t − 1 ) + ( 1 − ρ ) g ^ ⊙ g ^ Δ θ ( t ) \Delta \boldsymbol{\theta}^{(t)} Δ θ ( t ) 与之前的计算方式类似,只不过稳定值ϵ \epsilon ϵ 放在根号内Δ θ ( t ) ← − η r ( t ) + ϵ ⊙ g ^
\Delta \boldsymbol{\theta}^{(t)} \leftarrow -\frac{\eta}{\sqrt{\boldsymbol{r}^{(t)} + \epsilon}}\odot \hat{\boldsymbol{g}}
Δ θ ( t ) ← − r ( t ) + ϵ η ⊙ g ^ 原始文献 指出参数变化量Δ θ \Delta \boldsymbol{\theta} Δ θ 应该与参数θ \boldsymbol{\theta} θ 的“单位”匹配。如果仅使用梯度,那么不能满足这个条件,要使用牛顿法才可以。然而若要计算牛顿法,需要计算Hessian矩阵H \boldsymbol{H} H 的逆,时间复杂度太高。而由于u n i t s o f Δ θ ∝ u n i t s o f H − 1 g ∝ ∂ L ∂ θ ∂ 2 L ∂ θ 2 ∝ u n i t s o f θ
{\rm units\ of\ }\Delta \boldsymbol{\theta} \propto {\rm units\ of\ }\boldsymbol{H}^{-1}\boldsymbol{g} \propto \frac{\frac{\partial L}{\partial \theta}}{\frac{\partial^2 L}{\partial \theta^2}} \propto {\rm units\ of\ }\boldsymbol{\theta}
u n i t s o f Δ θ ∝ u n i t s o f H − 1 g ∝ ∂ θ 2 ∂ 2 L ∂ θ ∂ L ∝ u n i t s o f θ
因此要计算H − 1 \boldsymbol{H}^{-1} H − 1 的单位,只要算出Δ θ g \frac{\Delta \boldsymbol{\theta}}{\boldsymbol{g}} g Δ θ 就可以。类似上面的做法,引入Δ θ ( t ) \Delta \boldsymbol{\theta}^{(t)} Δ θ ( t ) 的平方并求加权平均值s ( t ) ← ρ s ( t − 1 ) + ( 1 − ρ ) Δ θ ( t ) ⊙ Δ θ ( t )
\boldsymbol{s}^{(t)} \leftarrow \rho\ \boldsymbol{s}^{(t-1)} + (1-\rho)\ \Delta\boldsymbol{\theta}^{(t)} \odot \Delta\boldsymbol{\theta}^{(t)}
s ( t ) ← ρ s ( t − 1 ) + ( 1 − ρ ) Δ θ ( t ) ⊙ Δ θ ( t )
则更新规则为θ ( t ) ← θ ( t − 1 ) − s ( t − 2 ) + ϵ r ( t − 1 ) + ϵ ⊙ g ^
\boldsymbol{\theta}^{(t)} \leftarrow \boldsymbol{\theta}^{(t-1)} - \frac{\sqrt{\boldsymbol{s}^{(t-2)}+\epsilon}}{\sqrt{\boldsymbol{r}^{(t-1)}+\epsilon}} \odot \hat{\boldsymbol{g}}
θ ( t ) ← θ ( t − 1 ) − r ( t − 1 ) + ϵ s ( t − 2 ) + ϵ ⊙ g ^
记E ( x ( t ) ) = ρ E ( x ( t − 1 ) ) + ( 1 − ρ ) E ( x ( t ) ) R M S ( x ( t ) ) = E ( x ( t ) ) + ϵ
\begin{aligned}
E\left(x^{(t)}\right) &= \rho E\left(x^{(t-1)}\right) + (1-\rho)E\left(x^{(t)}\right) \\
{\rm RMS}\left(x^{(t)}\right) &= \sqrt{E\left(x^{(t)}\right) + \epsilon}
\end{aligned}
E ( x ( t ) ) R M S ( x ( t ) ) = ρ E ( x ( t − 1 ) ) + ( 1 − ρ ) E ( x ( t ) ) = E ( x ( t ) ) + ϵ
则上述更新规则也可以写为θ ( t ) ← θ ( t − 1 ) − R M S ( θ ( t − 1 ) ) R M S ( g ^ ) ⊙ g ^
\boldsymbol{\theta}^{(t)} \leftarrow \boldsymbol{\theta}^{(t-1)} - \frac{ {\rm RMS}\left(\boldsymbol{\theta}^{(t-1)}\right)}{ {\rm RMS}\left(\hat{\boldsymbol{g}}\right)} \odot \hat{\boldsymbol{g}}
θ ( t ) ← θ ( t − 1 ) − R M S ( g ^ ) R M S ( θ ( t − 1 ) ) ⊙ g ^
可以看到AdaDelta的更新规则里已经没有学习率这一项了
RMSProp
去掉AdaDelta更新规则中保持参数更新量单位不变的部分,就是RMSProp算法。不过RMSProp和AdaDelta两个算法是独立提出的g ^ ← 1 m ∑ i = 1 m ∇ θ L ( f ( x ( i ) ; θ ( t − 1 ) ) , y ( i ) ) r ( t ) ← ρ r ( t − 1 ) + ( 1 − ρ ) g ^ ⊙ g ^ Δ θ ( t ) ← − η r ( t ) + ϵ ⊙ g ^ θ ( t ) ← θ ( t − 1 ) + Δ θ ( t )
\begin{aligned}
\hat{\boldsymbol{g}} &\leftarrow \frac{1}{m} \sum_{i=1}^m \nabla_\boldsymbol{\theta}L\left(f(\boldsymbol{x}^{(i)};\boldsymbol{\theta}^{(t-1)}), \boldsymbol{y}^{(i)}\right) \\
\boldsymbol{r}^{(t)} &\leftarrow \rho\ \boldsymbol{r}^{(t-1)} + (1-\rho)\ \hat{\boldsymbol{g}}\odot\hat{\boldsymbol{g}} \\
\Delta \boldsymbol{\theta}^{(t)} &\leftarrow -\frac{\eta}{\sqrt{\boldsymbol{r}^{(t)} + \epsilon}}\odot \hat{\boldsymbol{g}} \\
\boldsymbol{\theta}^{(t)} &\leftarrow \boldsymbol{\theta}^{(t-1)} + \Delta\boldsymbol{\theta}^{(t)}
\end{aligned}
g ^ r ( t ) Δ θ ( t ) θ ( t ) ← m 1 i = 1 ∑ m ∇ θ L ( f ( x ( i ) ; θ ( t − 1 ) ) , y ( i ) ) ← ρ r ( t − 1 ) + ( 1 − ρ ) g ^ ⊙ g ^ ← − r ( t ) + ϵ η ⊙ g ^ ← θ ( t − 1 ) + Δ θ ( t )
通常ρ \rho ρ 被设置为0.9,η \eta η 设为0.001。RMSProp对RNN效果很好,不过仍然需要设置学习率
Adam
Adam的全称是自适应动量估计法(Adaptive Moment Estimation),可以看作是动量法与RMSprop的结合:一方面,Adam像动量法一样计算梯度的指数衰减平均值(也是梯度的有偏一阶矩估计)s \boldsymbol{s} s ;另一方面,像RMSprop一样计算梯度平方的指数衰减平均值(有偏二阶矩估计)r \boldsymbol{r} r g ^ ← 1 m ∑ i = 1 m ∇ θ L ( f ( x ( i ) ; θ ( t − 1 ) ) , y ( i ) ) s ( t ) ← ρ 1 s ( t − 1 ) + ( 1 − ρ 1 ) g ^ r ( t ) ← ρ 2 r ( t − 1 ) + ( 1 − ρ 2 ) g ^ ⊙ g ^
\begin{aligned}
\hat{\boldsymbol{g}} &\leftarrow \frac{1}{m} \sum_{i=1}^m \nabla_\boldsymbol{\theta}L\left(f(\boldsymbol{x}^{(i)};\boldsymbol{\theta}^{(t-1)}), \boldsymbol{y}^{(i)}\right) \\
\boldsymbol{s}^{(t)} &\leftarrow \rho_1\boldsymbol{s}^{(t-1)} + (1-\rho_1)\hat{\boldsymbol{g}} \\
\boldsymbol{r}^{(t)} &\leftarrow \rho_2\boldsymbol{r}^{(t-1)} + (1-\rho_2)\hat{\boldsymbol{g}} \odot\hat{\boldsymbol{g}}
\end{aligned}
g ^ s ( t ) r ( t ) ← m 1 i = 1 ∑ m ∇ θ L ( f ( x ( i ) ; θ ( t − 1 ) ) , y ( i ) ) ← ρ 1 s ( t − 1 ) + ( 1 − ρ 1 ) g ^ ← ρ 2 r ( t − 1 ) + ( 1 − ρ 2 ) g ^ ⊙ g ^
由于s \boldsymbol{s} s 和r \boldsymbol{r} r 的初始值都是0向量,因此在最初几步当ρ 1 , ρ 2 \rho_1, \rho_2 ρ 1 , ρ 2 都接近1时,会导致两个矩估计都偏向0。因此每步之后需要做如下修正s ^ ( t ) ← s ( t ) 1 − ρ 1 t r ^ ( t ) ← r ( t ) 1 − ρ 2 t
\begin{aligned}
\hat{\boldsymbol{s}}^{(t)} &\leftarrow \frac{\boldsymbol{s}^{(t)}}{1-\rho_1^t} \\
\hat{\boldsymbol{r}}^{(t)} &\leftarrow \frac{\boldsymbol{r}^{(t)}}{1-\rho_2^t}
\end{aligned}
s ^ ( t ) r ^ ( t ) ← 1 − ρ 1 t s ( t ) ← 1 − ρ 2 t r ( t )
修正项被用来计算参数更新Δ θ ( t ) ← − η r ^ ( t ) + ϵ ⊙ s ^ ( t ) θ ( t ) ← θ ( t − 1 ) + Δ θ ( t )
\begin{aligned}
\Delta\boldsymbol{\theta}^{(t)} &\leftarrow -\frac{\eta}{\sqrt{\hat{\boldsymbol{r}}^{(t)}} + \epsilon} \odot \hat{\boldsymbol{s}}^{(t)} \\
\boldsymbol{\theta}^{(t)} &\leftarrow \boldsymbol{\theta}^{(t-1)} + \Delta \boldsymbol{\theta}^{(t)}
\end{aligned}
Δ θ ( t ) θ ( t ) ← − r ^ ( t ) + ϵ η ⊙ s ^ ( t ) ← θ ( t − 1 ) + Δ θ ( t )
通常令ρ 1 = 0.9 , ρ 2 = 0.999 , ϵ = 1 0 − 8 \rho_1 = 0.9, \rho_2 = 0.999, \epsilon=10^{-8} ρ 1 = 0 . 9 , ρ 2 = 0 . 9 9 9 , ϵ = 1 0 − 8
Adam算法还有两种其它扩展。一种是将Adam进一步和Nesterov动量法结合,得到Nadam算法 ;另一种是在更新r \boldsymbol{r} r 时不再使用梯度的二次幂,而是使用无穷次幂r ( t ) ← ρ 2 ∞ r ( t − 1 ) + ( 1 − ρ 2 ∞ ) ∣ g ^ ∣ ∞ = max ( ρ 2 r ( t − 1 ) , g ^ )
\begin{aligned}
\boldsymbol{r}^{(t)} &\leftarrow \rho_2^\infty \boldsymbol{r}^{(t-1)} + (1-\rho_2^\infty)|\hat{\boldsymbol{g}}|^\infty \\
&= \max(\rho_2\boldsymbol{r}^{(t-1)}, \hat{\boldsymbol{g}})
\end{aligned}
r ( t ) ← ρ 2 ∞ r ( t − 1 ) + ( 1 − ρ 2 ∞ ) ∣ g ^ ∣ ∞ = max ( ρ 2 r ( t − 1 ) , g ^ )
更新规则也修改为Δ θ ( t ) ← − η r ( t ) ⊙ s ^ ( t )
\Delta\boldsymbol{\theta}^{(t)} \leftarrow -\frac{\eta}{ \boldsymbol{r}^{(t)}} \odot \hat{\boldsymbol{s}}^{(t)}
Δ θ ( t ) ← − r ( t ) η ⊙ s ^ ( t )
这种算法称为AdaMax算法
(一般的文章,包括本文,都将Adam看作是动量法和RMSProp算法的结合。不过按照原始论文的说法,Adam的直接来源是AdaGrad和RMSProp,其中AdaGrad的贡献在于对稀疏梯度的处理)
Adam算法刚被提出的时候因为其快速收敛的能力得到了人们的广泛关注,但是[Wilson2017]指出Adam(以及其它自适应学习率算法)得到的解虽然在训练集上效果不错,但是泛化能力都比SGD要差(而且经常是明显得差)。[Reddi2018]进一步通过实验指出对某些简单的凸优化问题Adam都不能收敛到最优解,因为某些batch给出的梯度信息可能很重要,但是会被指数衰减的计算方法很快淹没。文章给出了一个解决方案:不再对梯度做有偏二阶矩估计,而是只保留所有二阶矩的最大值。文章将这种新的改进方法称为AMSGrad,并称算法能够取得很好的收敛结果。但是奥地利一名研究人员的博客 通过一些实验表明AMSGrad的效果可能没有[Reddi2018]中提到的这么出色
结语
上图对上文所提到的优化方法之间的关系做了一个梳理。大部分优化器都包含在tf.train
这个包中,小部分出现在tf.contrib.opt
包。后者包下其它优化器大部分都是由第三方的开发人员提供,这里就不介绍了;前面tf.train
包中有一些优化器没有包含在图里,这些主要是针对海量稀疏数据使用,例如tf.train.FtrlOptimizer
在计算广告点击率(CTR)时非常常用。[Reddi2018]则是给出了一个更抽象的公式来涵盖上面提出各种算法的计算流程g ( t ) ← ∇ L ( x ( t ) ) s ( t ) ← ϕ t ( g ( 1 ) , … , g ( t ) ) r ( t ) ← ψ t ( g ( 1 ) , … , g ( t ) ) Δ θ ( t ) ← − α ( t ) r ( t ) ⊙ s ( t ) θ ( t + 1 ) ← θ ( t ) + Δ θ ( t )
\begin{aligned}
{\boldsymbol{g}^{(t)}} &\leftarrow \nabla L(\boldsymbol{x}^{(t)}) \\
\boldsymbol{s}^{(t)} &\leftarrow \phi_t(\boldsymbol{g}^{(1)}, \ldots, \boldsymbol{g}^{(t)}) \\
\boldsymbol{r}^{(t)} &\leftarrow \psi_t(\boldsymbol{g}^{(1)}, \ldots, \boldsymbol{g}^{(t)}) \\
\Delta \boldsymbol{\theta}^{(t)} &\leftarrow -\frac{\alpha^{(t)}}{\sqrt{\boldsymbol{r}^{(t)}}} \odot \boldsymbol{s}^{(t)} \\
\boldsymbol{\theta}^{(t+1)} &\leftarrow \boldsymbol{\theta}^{(t)} + \Delta \boldsymbol{\theta}^{(t)}
\end{aligned}
g ( t ) s ( t ) r ( t ) Δ θ ( t ) θ ( t + 1 ) ← ∇ L ( x ( t ) ) ← ϕ t ( g ( 1 ) , … , g ( t ) ) ← ψ t ( g ( 1 ) , … , g ( t ) ) ← − r ( t ) α ( t ) ⊙ s ( t ) ← θ ( t ) + Δ θ ( t )
(其中符号有一些修改)上式中ϕ t \phi_t ϕ t 函数可以看做是对动量(梯度历史)的计算,而ψ t \psi_t ψ t 则是梯度二阶矩估计相关的函数
所谓“世界上没有免费的午餐”,正如世界上没有一种机器学习模型能够解决所有问题一样,也没有哪个优化器可以在所有问题上能同时满足易于使用、收敛快、能加速通过平原、保证落到最值点这些要求。[Schaul2013]设计了一些类似单元测试的实验,并指出精心调参过的SGD在大多数实验上的效果表现最好,同时自适应学习率系列的算法不太受超参数的影响。[Ruder2016]的实验显示对比较简单的有鞍点的损失函数,SGD和单纯带动量的优化器比较难以找到正确方向,而AdaGrad、RMSProp和AdaDelta可以,其中AdaDelta跑得最快。不过SGD通常可以收敛(尽管需要更长的时间,以及比较小心的初始化+衰减机制),因此大部分论文还是倾向于使用最基础的,不带动量的SGD,同时加一些启发式的衰减,通过一些策略让学习率逐渐变小。[Keskar2017]认为先使用Adam,然后切换到SGD可以达到更好的效果,而[Bahar2017]的实验则是倾向于先使用Adam,再使用学习率不断衰减的Adam
参考文献
除已在正文中标明的来源外,本文参考了以下文章/书籍片段
综述
知乎专栏:一个框架看懂优化算法
Slinuxer: SGD算法比较
[Ruder2016] Ruder, Sebastian. “An overview of gradient descent optimization algorithms.” arXiv preprint arXiv:1609.04747 (2016).
[Bahar2017] Bahar, Parnia, et al. “Empirical investigation of optimization algorithms in neural machine translation.” The Prague Bulletin of Mathematical Linguistics 108.1 (2017): 13-25.
花书第八章,8.3、8.5节
[Schaul2013] Schaul, Tom, Ioannis Antonoglou, and David Silver. “Unit tests for stochastic optimization.” arXiv preprint arXiv:1312.6055 (2013).
[Keskar2017] Keskar, Nitish Shirish, and Richard Socher. “Improving generalization performance by switching from adam to sgd.” arXiv preprint arXiv:1712.07628 (2017).
具体算法
动量法
Nesterov动量法
原始论文 : Sutskever, Ilya, et al. “On the importance of initialization and momentum in deep learning.” International conference on machine learning (2013): 1139-1147
斯坦福大学Panos Achlioptas的资格考试报告
AdaGrad算法
AdaDelta算法
Adam算法
原始论文 : Kingma, Diederik P., and Jimmy Ba. “Adam: A method for stochastic optimization.” arXiv preprint arXiv:1412.6980 (2014).
[Wilson2017] Wilson, Ashia C., et al. “The marginal value of adaptive gradient methods in machine learning.” Advances in Neural Information Processing Systems (2017): 4148-4158
[Reddi2018] Reddi, Sashank J., Satyen Kale, and Sanjiv Kumar. “On the convergence of adam and beyond.” (2018).