论文阅读--An overview of gradient descent optimization algorithms
An overview of gradient descent optimization algorithms∗
文章目录
- An overview of gradient descent optimization algorithms∗
-
-
Abstract
-
1 Introduction
-
2 Gradient descent variants
-
- 2.1 Batch gradient descent
- 2.2 Stochastic gradient descent
- 2.3 Mini-batch gradient descent
-
3 Challenges
-
4 Challenges
-
- 4.1 Momentum
- 4.2 Nesterov accelerated gradient
- 4.3 Adagrad
- 4.4 Adadelta
- 4.5 RMSprop
- 4.6 Adam
- 4.7 AdaMax
- 4.8 Nadam
- 4.9 Visualization of algorithms
- 4.10 Which optimizer to use?
-
Abstract
梯度下降法(Gradient Descent)优化算法,越来越受欢迎,通常用作black-box优化器,但是实践证明他们的优点和缺点都很难克服。本文旨在提供不同算法的表现,主要是梯度下降法的变体,总结了解决问题的挑战,介绍了常见的优化算法,而且调查了优化梯度下降方法的策略。
1 Introduction
梯度下降法是处理优化最受欢迎的算法,而且是目前优化神经网络常见的方法。同时,每个最新的深度学习包都包含了优化梯度下降的不同算法的实现(lasagne,caffe,Keras),这些算法通常作为black-box优化器。
本文主要对不同优化梯度下降的算法进行介绍:
section 2:了解梯度下降的不同变体;
section 3:总结训练时的挑战;
section 4:介绍最常见的优化算法和如何处理这些挑战及算法的流程,更新的规则;
section 5:并行和分布式的方法如何优化梯度下降法;
section 6:介绍优化梯度下降的其他策略。
梯度下降法最下化目标函数J(\theta),参数是\theta \in {\Bbb R}^d,通过目标函数 {\nabla}_{\theta}J(\theta)。学习速率\eta决定到达(局部)最小值的步数,换句话说,在目标函数的图上,随着表面坡度一直下降,直到山谷。
2 Gradient descent variants
有三种梯度下降法的变体,差别在于用多少数据来计算目标函数的梯度。根据数据的大小,在参数更新的准确率和进行更新所需的时间上进行trade-off。
2.1 Batch gradient descent
Vanilla gradient descent 和 aka batch gradient descent计算关于整个数据集的参数\theta的损失函数的梯度:
\theta = \theta-\eta\cdot{\nabla}_{\theta}J(\theta) \tag{1}
一次更新就要计算整个数据集的梯度,batch gradient descent非常慢,而且当数据无法全部加载到内存时无法处理。批梯度下降法无法在线更新模型,比如如果有新的样本。
for i in range ( nb_epochs ):
params_grad = evaluate_gradient ( loss_function , data , params )
params = params - learning_rate * params_grad
对于预先定义好的迭代次数,首先计算整个数据集的损失函数的梯度向量params_grad,现在一些深度学习包提供了自动微分的算法计算梯度。根据梯度的方向计算参数,然后决定学习速率的大小。batch gradient descent在凸优化问题上可以收敛到全局最优,非凸问题可以收敛到局部最优。
2.2 Stochastic gradient descent
Stochastic gradient descent (SGD)对每个训练样本(x^{(i)},y^{(i)})进行参数更新:
\theta = \theta-\eta\cdot{\nabla}_{\theta}J(\theta;x^{(i)}, y^{(i)}) \tag{2}
Batch gradient descent对于大数据集进行的多余的计算,因为在进行参数更新时,相似的样本也要重新计算。SGD通过更新一次计算一次处理这个问题。SGD通常比较快,而且可以处理在线问题,由于SGD频繁更新导致高方差,这就导致目标函数振荡严重,如图所示。

当batch gradient descent收敛到参数空间的最优点时,SGD的振荡却可以跳到新的可能局部最优点,另外,SGD的过度调整使最小点变得复杂。但是,当逐渐降低学习速率,SGD和batch gradient descent有相同的收敛方式,通常肯定会收敛到局部最优(non-convex)或全局最优(convex),代码只是在训练样本上添加一个循环,并且分别估计每个样本的梯度,代码如下:
for i in range ( nb_epochs ):
np. random . shuffle ( data )
for example in data :
params_grad = evaluate_gradient ( loss_function , example , params )
params = params - learning_rate * params_grad
2.3 Mini-batch gradient descent
Mini-batch gradient descent 有以上两个的优点,对一个mini-batch的n个训练样本进行更新,公式如下:
\theta=\theta-\eta\cdot{\nabla}_{\theta}J(\theta;x^{(i:i+n)}, y^{(i:i+n)}) \tag{3}
这种方法,a)降低了参数更新的方差,可以获得更稳定的收敛;b)可以充分使用优化矩阵算法,和关于mini-batch的梯度的深度学习包相似。和大小在50-256之间的mini-batch相似,但不同的应用不同。mini-batch梯度下降法只是训练神经网络时的一种选择,使用mini-batch时使用SGD。
在代码中,不是循环训练样本,假设mini-batches=50
for i in range ( nb_epochs ):
np. random . shuffle ( data )
for batch in get_batches (data , batch_size =50):
params_grad = evaluate_gradient ( loss_function , batch , params )
params = params - learning_rate * params_grad
3 Challenges
Vanilla mini-batch gradient descent不能保证很好的收敛,但提供了一些需要处理的挑战:
- 选择恰当的学习速率很难。小的学习速率导致慢的收敛速度,大的学习速率会阻碍收敛,导致损失函数在最小值附近动荡,甚至发散。
- Learning rate schedules通过退火算法调整学习速率,比如通过预先定义好的日程降低学习速率或是两个epoch之间的降低低于某个阈值。这些日程和阈值,必须先定义好并且无法根据数据的特性进行调整。
- 另外,所有的参数更新使用相同的学习速率。如果数据集是稀疏的,并且特征有不同的频率,我们可能不想使用相同的程度更新他们,但对于较少出现的特征更新更大。
- 最小化神经网络的高维非凸误差函数的主要挑战是如何避免陷入大量的局部最优点中。Dauphin et al认为导致困难的不是局部最优点而是鞍点,比如点的某一维斜率上升而另一维下降。这些鞍点通常由同一错误包围,SGD很难处理这些,因为每一维的梯度都接近零。
4 Challenges
接下来介绍处理之前的挑战的算法,不介绍无法处理高维数据的算法,比如二阶方法,牛顿方法。
4.1 Momentum
SGD有navigating ravines(沟壑),比如在表面曲线更陡的维度,而另一维不会,通常在局部最优点上。这种情况下,SGD在沟壑的陡峭处振荡,导致往低处的速度很慢。

Momentum在相关方向帮助SGD和降低动荡的危害。通过增加一个摩擦系数 \gamma来更新向量
v_t = {\gamma}v_{t-1}+ {\eta}{\nabla}_{\theta}J(\theta)
\theta=\theta-v_t
momentum项gamma通常设为0.9或相似的值。
使用momentum时,相当于在斜坡推一个球,这个球在往下滚时积累了momentum,变得越来越快(直到到达最终的速度)。参数更新也是相似的:momentum项在梯度相同的方向增加,在梯度改变方向时减少。因此,获得更快的收敛速度和降低振荡。
4.2 Nesterov accelerated gradient
球在斜坡下滚时是没有目的的,我们需要一个更聪明的球,知道如何走。
Nesterov accelerated gradient (NAG)使momentum有这个认知。我们要把momentum项{\gamma}v_{t-1}移到参数\theta处,计算\theta-{\gamma}v_{t-1}可得参数的下个大概位置,大体上我们的参数如何修改。现在我们可以有效计算梯度,不是关于当前的参数\theta,而是参数未来的大概位置:
v_t={\gamma}v_{t-1}+\eta\nabla_{\theta}J(\theta-{\gamma}v_{t-1})
\theta=\theta-v_t

另外momentum项设置为\gamma设置为0.9,第一个momentum计算当前的梯度,然后在更新了的积累梯度的方向上跳一大步,NAG在首先在之前积累的梯度上跳一步,测量梯度,然后获得正确的。这个预想更新可以防止更新太快,并且快反应,在RNNs的许多任务上提高了性能。
现在我们可以根据误差函数的斜率更新参数,并且加速SGD,而且可以根据每个参数进行更新,就是根据参数的重要性进行小的或大的更新。
4.3 Adagrad
Adagrad是基于梯度的算法,使学习速率的更新是根据参数的,出现频繁的参数更新小,不频繁的参数更新大。基于这个原因,处理稀疏数据比较好。Dean et al发现adagrad可以提高SGD的鲁棒性,并且用来训练大的神经网络,比如识别YouTube的猫的视频。另外,Pennington 使用Adagrad来训练GloVe词向量,因为频率少的词比其他词需要更大的更新系数。
之前,所有参数\theta的更新使用相同的学习速率\eta,Adagrad中,不同的参数\theta_i使用不同的学习速率,在每个time step t,首先是adagrad的每个参数的更新,为了简化,假设g_(t,i)是关于参数\theta_i在time step t时的目标函数的梯度:
g_{t, i}=\nabla_{\theta_t}J(\theta_{t, i})
SGD在每个time stept上更新每个参数\theta_i:
\theta_{t+1, i}=\theta_{t, i}-\eta{\cdot}g_{t, i}
在更新规则中,adagrad在每个time stept修改每个参数\theta_i的学习速率\eta:
\theta_{t+1, i}=\theta_{t, i}- \frac{\eta}{\sqrt{G_(t, ii)+\varepsilon}} \cdot g_{t, i} \tag{8}
G_t \in {\Bbb R}^{d \times d} 是一个对角矩阵,每个对角元素i, i,在time step t时,关于参数\theta_i的梯度的平方和。\varepsilon是平滑项,避免除数为零,通常是\varepsilon=1e-8。若是没有求根操作,算法的性能更差。
由于G_t包含了关于所有参数\theta的过去所有梯度的平方和,通过element-wise \odot乘法向量化G_t和g_t,即:
\theta_{t+1}=\theta-\frac{\eta}{\sqrt{G_t+\varepsilon}}\odot{g_t}
adagrad的一个好处是避免手工调整学习速率,默认值大多采用0.01。
adagrad的缺点是在分母积累梯度平方。这导致学习速率降低,最终变得无限小,这时算法无法再获得更多的知识。
4.4 Adadelta
adadelta是adagrad的扩展,避免学习速率不断单一下降,不要积累过去所有梯度的平方和,adadelta把过去积累梯度的个数都限制在w内。
不是无效率地存储前w个梯度的平方,梯度的和递归的定义为所有过去梯度的平方和的衰减平均值。每个time step t的平均E{[g^2]}_t取决于之前的平均和当前的梯度:
E{[g^2]}_t=\gamma E{[g^2]}_{t-1}+(1-\gamma)g^2_t \tag{10}
\gamma的设置和momentum相似,大概0.9。为了简化,在参数更新向量\Delta \theta_t重写vanilla SGD的更新,如下:
\Delta \theta_t = -\eta\cdot g_{t, i} \\ \theta_{t+1} = \theta_t +\Delta\theta_t \tag{11}
adagrad的参数更新向量的形式如下:
\Delta \theta_t=-\frac{\eta}{\sqrt{G_t+\varepsilon}}\odot g_t
现在把对角矩阵G_t用衰减平均值替换,即过去梯度平方E{[g^2]}_t:
\Delta \theta_t=-\frac{\eta}{\sqrt{E{[g^2]}_t+\varepsilon}}\odot g_t \tag{13}
分母是梯度的平方和的平均的根(RMS),公式换为:
\Delta \theta_t=-\frac{\eta}{RMS{g}_t}g_t
作者发现这个更新并不匹配,为了处理这个,首先定义另一个exponentially decaying average,不是梯度平方而是参数的平方:
E{[\Delta\theta^2]}_t = \gamma E{[\Delta\theta^2]}_{t-1} + (1-\gamma)\Delta{\theta}^2_t
参数的root mean squared error的更新如下:
RMS{[\Delta\theta]}_t=\sqrt{E{[\Delta\theta^2]_t+\varepsilon}}
RMS{[\Delta\theta]}_t是未知的,根据RMS的参数估计。用RMS{[\Delta\theta]}_t替换学习速率\eta,获得adadelta的更细腻公式:
\Delta\theta_t = -\frac{RMS{[\Delta\theta]}_{t-1}}{RMS{[g]}_t} g_t\\ \theta_{t+1} = \theta_t+\Delta\theta_t
有了adadelta,连设置学习速率的默认值都不需要,因为从更新规则去除了。
4.5 RMSprop
RMSprop没有发表出来,是Hiton在Coursera上的课提出来。
RMSprop 和 Adadelta几乎同时提出来,处理adagrad的学习速率下降问题。RMSprop的第一个向量的更新和adadelta相似,即:
E{[g^2]}_t = 0.9E{[g^2]}_{t-1} + 0.1g^2_t \\ \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{E{[g^2]}_t+\varepsilon}}g_t
RMSprop把学习概率除以梯度的平方的exponentially decaying average。Hinton表示\gamma设为0.9,learning rate的默认值为0.001。
4.6 Adam
Adaptive Moment Estimation (Adam)给每个参数计算自适应的学习概率,除了和adadelta和RMSprop一样,存储过去梯度的平方v_t的exponentially decaying average,Adam还保存了过去梯度m_t的exponentially decaying average,和momentum相似:
m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t \\ v_t = \beta_2 v_{t-1} + (1-\beta_2) g^2_t
m_t和v_t分别是梯度的一阶矩量(均值)和二阶矩量(方差),m_t和v_t通常由零向量初始化,作者发现他们偏向于零,特别是初始时的time step和decay rates比较小(\beta_1和\beta_2接近1)。
通过计算第一和第二矩量的误差修正修改这些偏差:
\hat{m}_t=\frac{m_t}{1-\beta^t_1} \\ \hat{v}_t=\frac{v_t}{1-\beta^t_2}
然后用来更新参数,Adam的更新规则如下:
\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t}+\varepsilon}{\hat{m}_t}
作者建议\beta_1=0.9,\beta_2=0.999,\varepsilon=10^{-8},实验表示Adam表现比其他自适应的学习算法好。
4.7 AdaMax
在Adam更新规则中的因子v_t缩放梯度,是v_{t-1}和当前梯度{g_t}^2的倍数,即:
v_t = \beta_2 v_{t-1} + (1-\beta_2){|g_t|}^p
将这个更新规则泛化到{ l}_p范数,把\beta_2参数化为:
v_t=\beta^p_2 v_{t-1} + (1-\beta^p_2){|g_t|}^p
p的大范数通常不稳定,这是为什么{ l}_1和{ l}_2最常用。然而{ l}{\infty}通常表现稳定。由于这个原因,作者提出adamax,实验表示使用{ l}{\infty}的v_t收敛到更稳定的值,为了避免和Adam混淆,用u_t表示无限的范数约束v_t:
u_t = \beta^{\infty}_{2} v_{t-1} + (1-\beta^{\infty}_2){|g_t|}^{\infty} \\ =max(\beta_2\cdot v_{t-1}, |g_t|)
把这个加入Adam更新规则,用\sqrt{\hat{v}_t}+\varepsilon替换u_t,adamax的更新规则如下:
\theta_{t+1} = \theta_t-\frac{\eta}{u_t}\hat{m}_t
u_t依赖于max操作,默认值是\eta=0.002,\beta_1=0.9,\beta_2=0.999。
4.8 Nadam
如之前所示,Adam可以看做RMSprop和momentum的结合:RMSprop贡献了过去梯度的平方的exponentially decaying average v_t,momentum计算了过去梯度的 exponentially decaying average m_t,而且 Nesterov accelerated gradient (NAG)也比vanilla momentum好。
Nadam (Nesterov-accelerated Adaptive Moment Estimation)结合了Adam和NAG,为了把NAG融合到Adam,需要修改momentum 项m_t。
首先,momentum的更新规则如下:
g_t = \nabla_{\theta_t}J(\theta_t) \\ m_t = \gamma m_{t-1} + \eta g_t \\ \theta_{t+1} = \theta - m_t
J是目标函数,\gamma是momentum decay项,\eta是步骤大小,第三个公式扩展为
\theta_{t+1} = \theta_t - (\gamma m_{t-1} + \eta g_t)
这个重新证明了momentum引进了之前momentum向量的方向和当前梯度的向量。
NAG可以获得更精确的梯度方向,在计算梯度之前用momentum step来更新参数,修改NAG的梯度g_t可以获得:
g_t = \nabla_{\theta_t}J(\theta_t-\gamma m_{t-1}) \\ m_t = \gamma m_{t-1} + \eta g_t \\ \theta_{t+1} = \theta_t + m_t
Dozat 提出使用以下方法修改NAG,不是每步使用momentum两次,而是一次更新梯度g_t,第二次更新参数\theta_{t+1},使用之前的momentum向量更新当前的参数:
g_t = \nabla_{\theta_t}J(\theta_t) \\ m_t = \gamma m_{t-1} + \eta g_t \\ \theta_{t+1} = \theta_1 - (\gamma m_t + \eta g_t)
不是随机初始化之前公式27的momentum向量m_{t-1},而是使用当前momentum向量m_t,为了把Nesterov momentum加到Adam中,用当前的momentum向量替换之前的momentum向量。首先,Adam的更新规则如下:
m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t \\ \hat{m}_t = \frac{m_t}{1-\beta^t_1} \\ \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t+\varepsilon}}\hat{m}_t \tag{30}
扩展第二个公式,用\hat{m}_t和m_t的定义:
\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t}+\varepsilon}(\frac{\beta_1 m_{t-1}}{1-\beta^t_1} + \frac{(1-\beta_1)g_t}{1-\beta^t_1})
\frac{\beta_1 m_{t-1}}{1-\beta^t_1}是前一个time step的momentum向量的偏差修正估计,用\hat{m}_{t-1}代替:
\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t}_\varepsilon}(\beta_1 \hat{m}_{t-1} + \frac{(1-\beta_1)g_t}{1-\beta^t_1})
这个公式和公式27相似,和公式27相似,把前一个time step\hat{m}_{t-1}的momentum向量的修正偏差估计和当前的momentum向量\hat{m}_t的偏差修正估计替换,获得Nadam的更新的公式:
\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{\hat{v}_t}+\varepsilon}(\beta_1 \hat{m}_t + \frac{(1-\beta_1)g_t}{1-\beta^t_1})
4.9 Visualization of algorithms
下图表示了优化器的直观行为。

4.10 Which optimizer to use?
如何使用优化器?如果数据是稀疏,那么选择自适应学习速率的方法是最好的。另一个好处是不需要手动调参,使用默认值就可以获得最好的结果。
总之,RMSprop是adagrad的扩展,处理的问题是学习速率不断下降。和adadelta相似,除了adadelta使用RMS更新参数。最后,Adam增加了偏差修正和RMSprop增加了momentum。到目前为止,RMSprop、adadelta和Adam都是相似的算法,在相似的环境都可以获得很好的结果。Kingma et al表示偏差修正使Adam的性能比RMSprop好,就是在梯度变得稀疏时优化效果更好。目前为止,Adam是最好的选择。


