Fork me on GitHub

An overview of gradient descent optimization algorithms (论文解析)

原始论文:An overview of gradient descent optimization algorithms

梯度下降优化算法综述

摘要

虽然梯度下降优化算法越来越受欢迎,但通常作为黑盒优化器使用,因此很难对其优点和缺点的进行实际的解释。本文旨在让读者对不同的算法有直观的认识,以帮助读者使用这些算法。在本综述中,我们介绍梯度下降的不同变形形式,总结这些算法面临的挑战,介绍最常用的优化算法,回顾并行和分布式架构,以及调研用于优化梯度下降的其他的策略。

1 引言

梯度下降法是最著名的优化算法之一,也是迄今优化神经网络时最常用的方法。同时,在每一个最新的深度学习库中都包含了各种优化的梯度下降法的实现(例如:参见lasagne,caffe和keras的文档)。然而,这些算法通常是作为黑盒优化器使用,因此,很难对其优点和缺点的进行实际的解释。

本文旨在让读者对不同的优化梯度下降的算法有直观的认识,以帮助读者使用这些算法。在第2部分,我们首先介绍梯度下降的不同变形形式。在第3部分,我们将简要总结在训练的过程中所面临的挑战。随后,在第4部分,我们将介绍最常用的优化算法,包括这些算法在解决以上挑战时的动机以及如何得到更新规则的推导形式。在第5部分,我们将简单讨论在并行和分布式环境中优化梯度下降的算法和框架。最后,在第6部分,我们将思考对优化梯度下降有用的一些其他策略。

梯度下降法是最小化目标函数$J(\theta)$的一种方法,其中,$θ \in \mathbb R^d$为模型参数,梯度下降法利用目标函数关于参数的梯度$\triangledown_{\theta}J(\theta)$的反方向更新参数。学习率$\eta$决定达到最小值或者局部最小值过程中所采用的步长的大小。即,我们沿着目标函数的斜面下降的方向,直到到达谷底。如果你对梯度下降法不熟悉,你可以从此处资料找到介绍神经网络优化的材料。

2 梯度下降法的变形形式

梯度下降法有3中变形形式,它们之间的区别为我们在计算目标函数的梯度时使用到多少数据。根据数据量的不同,我们在参数更新的精度和更新过程中所需要的时间两个方面做出权衡。

2.1 批梯度下降法

Vanilla梯度下降法,又称为批梯度下降法(batch gradient descent),在整个训练数据集上计算损失函数关于参数$\theta$的梯度:

因为在执行每次更新时,我们需要在整个数据集上计算所有的梯度,所以批梯度下降法的速度会很慢,同时,批梯度下降法无法处理超出内存容量限制的数据集。批梯度下降法同样也不能在线更新模型,即在运行的过程中,不能增加新的样本。

批梯度下降法的代码如下所示:

1
2
3
for i in range(nb_epochs):
params_grad = evaluate_gradient(loss_function, data, params)
params = params - learning_rate * params_grad

对于给定的迭代次数,首先,我们利用全部数据集计算损失函数关于参数向量params的梯度向量params_grad。注意,最新的深度学习库中提供了自动求导的功能,可以有效地计算关于参数梯度。如果你自己求梯度,那么,梯度检查是一个不错的主意(关于如何正确检查梯度的一些技巧可以参见此处资料)。

然后,我们利用梯度的方向和学习率更新参数,学习率决定我们将以多大的步长更新参数。对于凸误差函数,批梯度下降法能够保证收敛到全局最小值,对于非凸函数,则收敛到一个局部最小值。

2.2 随机梯度下降法

相反,随机梯度下降法(stochastic gradient descent, SGD)根据每一条训练样本$x^{(i)}$和标签$y^{(i)}$更新参数:

对于大数据集,因为批梯度下降法在每一个参数更新之前,会对相似的样本计算梯度,所以在计算过程中会有冗余。而SGD在每一次更新中只执行一次,从而消除了冗余。因而,通常SGD的运行速度更快,同时,可以用于在线学习。SGD以高方差频繁地更新,导致目标函数出现如图1所示的剧烈波动。

optimizer-01.jpg

与批梯度下降法的收敛会使得损失函数陷入局部最小相比,由于SGD的波动性,一方面,波动性使得SGD可以跳到新的和潜在更好的局部最优。另一方面,这使得最终收敛到特定最小值的过程变得复杂,因为SGD会一直持续波动。然而,已经证明当我们缓慢减小学习率,SGD与批梯度下降法具有相同的收敛行为,对于非凸优化和凸优化,可以分别收敛到局部最小值和全局最小值。与批梯度下降的代码相比,SGD的代码片段仅仅是在对训练样本的遍历和利用每一条样本计算梯度的过程中增加一层循环。注意,如6.1节中的解释,在每一次循环中,我们打乱训练样本。

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 小批量梯度下降法

小批量梯度下降法最终结合了上述两种方法的优点,在每次更新时使用n个小批量训练样本:

这种方法,a)减少参数更新的方差,这样可以得到更加稳定的收敛结果;b)可以利用最新的深度学习库中高度优化的矩阵优化方法,高效地求解每个小批量数据的梯度。通常,小批量数据的大小在50到256之间,也可以根据不同的应用有所变化。当训练神经网络模型时,小批量梯度下降法是典型的选择算法,当使用小批量梯度下降法时,也将其称为SGD。注意:在下文的改进的SGD中,为了简单,我们省略了参数$x^{(i:i+n)};y^{(i:i+n)}$。

在代码中,不是在所有样本上做迭代,我们现在只是在大小为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 挑战

虽然Vanilla小批量梯度下降法并不能保证较好的收敛性,但是需要强调的是,这也给我们留下了如下的一些挑战:

选择一个合适的学习率可能是困难的。学习率太小会导致收敛的速度很慢,学习率太大会妨碍收敛,导致损失函数在最小值附近波动甚至偏离最小值。
学习率调整[17]试图在训练的过程中通过例如退火的方法调整学习率,即根据预定义的策略或者当相邻两代之间的下降值小于某个阈值时减小学习率。然而,策略和阈值需要预先设定好,因此无法适应数据集的特点[4]。
此外,对所有的参数更新使用同样的学习率。如果数据是稀疏的,同时,特征的频率差异很大时,我们也许不想以同样的学习率更新所有的参数,对于出现次数较少的特征,我们对其执行更大的学习率。
高度非凸误差函数普遍出现在神经网络中,在优化这类函数时,另一个关键的挑战是使函数避免陷入无数次优的局部最小值。Dauphin等人[5]指出出现这种困难实际上并不是来自局部最小值,而是来自鞍点,即那些在一个维度上是递增的,而在另一个维度上是递减的。这些鞍点通常被具有相同误差的点包围,因为在任意维度上的梯度都近似为0,所以SGD很难从这些鞍点中逃开。

4 梯度下降优化算法

下面,我们将列举一些算法,这些算法被深度学习社区广泛用来处理前面提到的挑战。我们不会讨论在实际中不适合在高维数据集中计算的算法,例如诸如牛顿法的二阶方法。

4.1 动量法

SGD很难通过陡谷,即在一个维度上的表面弯曲程度远大于其他维度的区域[19],这种情况通常出现在局部最优点附近。在这种情况下,SGD摇摆地通过陡谷的斜坡,同时,沿着底部到局部最优点的路径上只是缓慢地前进,这个过程如图2a所示。

optimizer-02.jpg

如图2b所示,动量法[16]是一种帮助SGD在相关方向上加速并抑制摇摆的一种方法。动量法将历史步长的更新向量的一个分量$\gamma$增加到当前的更新向量中(部分实现中交换了公式中的符号)

动量项$\gamma$通常设置为0.9或者类似的值。

从本质上说,动量法,就像我们从山上推下一个球,球在滚下来的过程中累积动量,变得越来越快(直到达到终极速度,如果有空气阻力的存在,则$\gamma<1$)。同样的事情也发生在参数的更新过程中:对于在梯度点处具有相同的方向的维度,其动量项增大,对于在梯度点处改变方向的维度,其动量项减小。因此,我们可以得到更快的收敛速度,同时可以减少摇摆。

4.2 Nesterov加速梯度下降法

然而,球从山上滚下的时候,盲目地沿着斜率方向,往往并不能令人满意。我们希望有一个智能的球,这个球能够知道它将要去哪,以至于在重新遇到斜率上升时能够知道减速。

Nesterov加速梯度下降法(Nesterov accelerated gradient,NAG)[13]是一种能够给动量项这样的预知能力的方法。我们知道,我们利用动量项$\gamma v_{t-1}$来更新参数θ。通过计算$\theta - \gamma v_{t-1}$能够告诉我们参数未来位置的一个近似值(梯度并不是完全更新),这也就是告诉我们参数大致将变为多少。通过计算关于参数未来的近似位置的梯度,而不是关于当前的参数$\theta$的梯度,我们可以高效的求解 :

同时,我们设置动量项$\gamma$大约为0.9。动量法首先计算当前的梯度值(图3中的小的蓝色向量),然后在更新的累积梯度(大的蓝色向量)方向上前进一大步,Nesterov加速梯度下降法NAG首先在先前累积梯度(棕色的向量)方向上前进一大步,计算梯度值,然后做一个修正(绿色的向量)。这个具有预见性的更新防止我们前进得太快,同时增强了算法的响应能力,这一点在很多的任务中对于RNN的性能提升有着重要的意义[2]。

optimizer-03.jpg

对于NAG的直观理解的另一种解释可以参见此处资料,同时Ilya Sutskever在其博士论文[18]中给出更详细的综述。

既然我们能够使得我们的更新适应误差函数的斜率以相应地加速SGD,我们同样也想要使得我们的更新能够适应每一个单独参数,以根据每个参数的重要性决定大的或者小的更新。

4.3 Adagrad

Adagrad[7]是这样的一种基于梯度的优化算法:让学习率适应参数,对于出现次数较少的特征,我们对其采用更大的学习率,对于出现次数较多的特征,我们对其采用较小的学习率。因此,Adagrad非常适合处理稀疏数据。Dean等人[6]发现Adagrad能够极大提高了SGD的鲁棒性并将其应用于Google的大规模神经网络的训练,其中包含了YouTube视频中的猫的识别。此外,Pennington等人[15]利用Adagrad训练Glove词向量,因为低频词比高频词需要更大的步长。

前面,我们每次更新所有的参数$\theta$时,每一个参数$\theta_i$都使用的是相同的学习率$\eta$。由于Adagrad在t时刻对每一个参数$\theta_i$使用了不同的学习率,我们首先介绍Adagrad对每一个参数的更新,然后我们对其向量化。为了简洁,令$g_{t,i}$为在t时刻目标函数关于参数$\theta_i$的梯度:

在t时刻,对每个参数$\theta_i$的更新过程变为:

对于上述的更新规则,在t时刻,基于对$\theta_i$计算过的历史梯度,Adagrad修正了对每一个参数$\theta_i$的学习率:

其中,$G_t \in \mathbb R^{d \times d}$是一个对角矩阵,对角线上的元素i,i是直到t时刻为止,所有关于$\theta_i$的梯度的平方和(Duchi等人[7]将该矩阵作为包含所有先前梯度的外积的完整矩阵的替代,因为即使是对于中等数量的参数d,矩阵的均方根的计算都是不切实际的。),$\epsilon$是平滑项,用于防止除数为0(通常大约设置为1e−8)。比较有意思的是,如果没有平方根的操作,算法的效果会变得很差。

由于$G_t$的对角线上包含了关于所有参数θ的历史梯度的平方和,现在,我们可以通过$G_t$和$g_t$之间的元素向量乘法$\odot$向量化上述的操作:

Adagrad算法的一个主要优点是无需手动调整学习率。在大多数的应用场景中,通常采用常数0.01。

Adagrad的一个主要缺点是它在分母中累加梯度的平方:由于没增加一个正项,在整个训练过程中,累加的和会持续增长。这会导致学习率变小以至于最终变得无限小,在学习率无限小时,Adagrad算法将无法取得额外的信息。接下来的算法旨在解决这个不足。

4.4 Adadelta

Adadelta[21]是Adagrad的一种扩展算法,以处理Adagrad学习速率单调递减的问题。不是计算所有的梯度平方,Adadelta将计算计算历史梯度的窗口大小限制为一个固定值w。

在Adadelta中,无需存储先前的w个平方梯度,而是将梯度的平方递归地表示成所有历史梯度平方的均值。在t时刻的均值$E[g^2]_t$只取决于先前的均值和当前的梯度(分量$\gamma$类似于动量项):

我们将$\gamma$设置成与动量项相似的值,即0.9左右。为了简单起见,我们利用参数更新向量$\Delta \theta_t$重新表示SGD的更新过程:

我们先前得到的Adagrad参数更新向量变为:

现在,我们简单将对角矩阵$G_t$替换成历史梯度的均值$E[g^2]_t$:

由于分母仅仅是梯度的均方根(root mean squared,RMS)误差,我们可以简写为:

作者指出上述更新公式中的每个部分(与SGD,动量法或者Adagrad)并不一致,即更新规则中必须与参数具有相同的假设单位。为了实现这个要求,作者首次定义了另一个指数衰减均值,这次不是梯度平方,而是参数的平方的更新:

因此,参数更新的均方根误差为:

由于$RMS[\Delta \theta]_t$是未知的,我们利用参数的均方根误差来近似更新。利用$RMS[\Delta \theta]_{t−1}$替换先前的更新规则中的学习率$\eta$,最终得到Adadelta的更新规则:

使用Adadelta算法,我们甚至都无需设置默认的学习率,因为更新规则中已经移除了学习率。

4.5 RMSprop

RMSprop是一个未被发表的自适应学习率的算法,该算法由Geoff Hinton在其Coursera课堂的课程6e中提出。

RMSprop和Adadelta在相同的时间里被独立的提出,都起源于对Adagrad的极速递减的学习率问题的求解。实际上,RMSprop是先前我们得到的Adadelta的第一个更新向量的特例:

同样,RMSprop将学习率分解成一个平方梯度的指数衰减的平均。Hinton建议将$\gamma$设置为0.9,对于学习率$\eta$,一个好的固定值为0.001。

4.6 Adam

自适应矩估计(Adaptive Moment Estimation,Adam)[9]是另一种自适应学习率的算法,Adam对每一个参数都计算自适应的学习率。除了像Adadelta和RMSprop一样存储一个指数衰减的历史平方梯度的平均$v_t$,Adam同时还保存一个历史梯度的指数衰减均值$m_t$,类似于动量:

$m_t$和$v_t$分别是对梯度的一阶矩(均值)和二阶矩(非确定的方差)的估计,正如该算法的名称。当$m_t$和$v_t$初始化为0向量时,Adam的作者发现它们都偏向于0,尤其是在初始化的步骤和当衰减率很小的时候(例如$\beta_1$和$\beta_2$趋向于1)。

通过计算偏差校正的一阶矩和二阶矩估计来抵消偏差:

正如我们在Adadelta和RMSprop中看到的那样,他们利用上述的公式更新参数,由此生成了Adam的更新规则:

作者建议$\beta_1$取默认值为0.9,$\beta_2$为0.999,$\epsilon$为10−8。他们从经验上表明Adam在实际中表现很好,同时,与其他的自适应学习算法相比,其更有优势。

4.7 算法可视化

下面两张图给出了上述优化算法的优化行为的直观理解。(还可以看看这里关于Karpathy对相同的图片的描述以及另一个简明关于算法讨论的概述)。

在图4a中,我们看到不同算法在损失曲面的等高线上走的不同路线。所有的算法都是从同一个点出发并选择不同路径到达最优点。注意:Adagrad,Adadelta和RMSprop能够立即转移到正确的移动方向上并以类似的速度收敛,而动量法和NAG会导致偏离,想像一下球从山上滚下的画面。然而,NAG能够在偏离之后快速修正其路线,因为NAG通过对最优点的预见增强其响应能力。

图4b中展示了不同算法在鞍点出的行为,鞍点即为一个点在一个维度上的斜率为正,而在其他维度上的斜率为负,正如我们前面提及的,鞍点对SGD的训练造成很大困难。这里注意,SGD,动量法和NAG在鞍点处很难打破对称性,尽管后面两个算法最终设法逃离了鞍点。而Adagrad,RMSprop和Adadelta能够快速想着梯度为负的方向移动,其中Adadelta走在最前面。

optimizers-04.gif

optimizers-05.gif

正如我们所看到的,自适应学习速率的方法,即 Adagrad、 Adadelta、 RMSprop 和Adam,最适合这些场景下最合适,并在这些场景下得到最好的收敛性。

4.8 选择使用哪种优化算法?

那么,我们应该选择使用哪种优化算法呢?如果输入数据是稀疏的,选择任一自适应学习率算法可能会得到最好的结果。选用这类算法的另一个好处是无需调整学习率,选用默认值就可能达到最好的结果。

总的来说,RMSprop是Adagrad的扩展形式,用于处理在Adagrad中急速递减的学习率。RMSprop与Adadelta相同,所不同的是Adadelta在更新规则中使用参数的均方根进行更新。最后,Adam是将偏差校正和动量加入到RMSprop中。在这样的情况下,RMSprop、Adadelta和Adam是很相似的算法并且在相似的环境中性能都不错。Kingma等人[9]指出在优化后期由于梯度变得越来越稀疏,偏差校正能够帮助Adam微弱地胜过RMSprop。综合看来,Adam可能是最佳的选择。

有趣的是,最近许多论文中采用不带动量的SGD和一种简单的学习率的退火策略。已表明,通常SGD能够找到最小值点,但是比其他优化的SGD花费更多的时间,与其他算法相比,SGD更加依赖鲁棒的初始化和退火策略,同时,SGD可能会陷入鞍点,而不是局部极小值点。因此,如果你关心的是快速收敛和训练一个深层的或者复杂的神经网络,你应该选择一个自适应学习率的方法。

5 并行和分布式SGD

当存在大量的大规模数据和廉价的集群时,利用分布式SGD来加速是一个显然的选择。SGD本身有固有的顺序:一步一步,我们进一步进展到最小。SGD提供了良好的收敛性,但SGD的运行缓慢,特别是对于大型数据集。相反,SGD异步运行速度更快,但客户端之间非最理想的通信会导致差的收敛。此外,我们也可以在一台机器上并行SGD,这样就无需大的计算集群。以下是已经提出的优化的并行和分布式的SGD的算法和框架。

5.1 Hogwild!

Niu等人[14]提出称为Hogwild!的更新机制,Hogwild!允许在多个CPU上并行执行SGD更新。在无需对参数加锁的情况下,处理器可以访问共享的内存。这种方法只适用于稀疏的输入数据,因为每一次更新只会修改一部分参数。在这种情况下,该更新策略几乎可以达到一个最优的收敛速率,因为CPU之间不可能重写有用的信息。

5.2 Downpour SGD

Downpour SGD是SGD的一种异步的变形形式,在Google,Dean等人[6]在他们的DistBelief框架(TensorFlow的前身)中使用了该方法。Downpour SGD在训练集的子集上并行运行多个模型的副本。这些模型将各自的更新发送给一个参数服务器,参数服务器跨越了多台机器。每一台机器负责存储和更新模型的一部分参数。然而,因为副本之间是彼此不互相通信的,即通过共享权重或者更新,因此可能会导致参数发散而不利于收敛。

5.3 延迟容忍SGD

通过容忍延迟算法的开发,McMahan和Streeter[11]将AdaGraad扩展成并行的模式,该方法不仅适应于历史梯度,同时适应于更新延迟。该方法已经在实践中被证实是有效的。

5.4 TensorFlow

TensorFlow[1]是Google近期开源的框架,该框架用于实现和部署大规模机器学习模型。TensorFlow是基于DistBelief开发,同时TensorFlow已经在内部用来在大量移动设备和大规模分布式系统的执行计算。在2016年4月发布的分布式版本依赖于图计算,图计算即是对每一个设备将图划分成多个子图,同时,通过发送、接收节点对完成节点之间的通信。

5.5 弹性平均SGD

Zhang等人[22]提出的弹性平均SGD(Elastic Averaging SGD,EASGD)连接了异步SGD的参数客户端和一个弹性力,即参数服务器存储的一个中心变量。EASGD使得局部变量能够从中心变量震荡得更远,这在理论上使得在参数空间中能够得到更多的探索。经验表明这种增强的探索能力通过发现新的局部最优点,能够提高整体的性能。

6 优化SGD的其他策略

最后,我们介绍可以与前面提及到的任一算法配合使用的其他的一些策略,以进一步提高SGD的性能。对于其他的一些常用技巧的概述可以参见[10]。

6.1 数据集的洗牌和课程学习

总的来说,我们希望避免向我们的模型中以一定意义的顺序提供训练数据,因为这样会使得优化算法产生偏差。因此,在每一轮迭代后对训练数据洗牌是一个不错的主意。

另一方面,在很多情况下,我们是逐步解决问题的,而将训练集按照某个有意义的顺序排列会提高模型的性能和SGD的收敛性,如何将训练集建立一个有意义的排列被称为课程学习[3]。

Zaremba and Sutskever[20]只能使用课程学习训练LSTM来评估简单程序,并表明组合或混合策略比单一的策略更好,通过增加难度来排列示例。

6.2 批量归一化

为了便于学习,我们通常用0均值和单位方差初始化我们的参数的初始值来归一化。 随着不断训练,参数得到不同的程度的更新,我们失去了这种归一化,随着网络变得越来越深,这种现象会降低训练速度,且放大参数变化。

批量归一化[8]在每次小批量数据反向传播之后重新对参数进行0均值单位方差标准化。通过将模型架构的一部分归一化,我们能够使用更高的学习率,更少关注初始化参数。批量归一化还充当正则化的作用,减少(有时甚至消除)Dropout的必要性。

6.3 Early stopping

如Geoff Hinton所说:“Early Stopping是美丽好免费午餐”(NIPS 2015 Tutorial slides)。你因此必须在训练的过程中时常在验证集上监测误差,在验证集上如果损失函数不再显著地降低,那么应该提前结束训练。

6.4 梯度噪音

Neelakantan等人[12]在每个梯度更新中增加满足高斯分布$N(0,\sigma^2_t)$的噪音:

高斯分布的方差需要根据如下的策略退火:

他们指出增加了噪音,使得网络对不好的初始化更加鲁棒,同时对深层的和复杂的网络的训练特别有益。他们猜测增加的噪音使得模型更优机会逃离当前的局部最优点,以发现新的局部最优点,这在更深层的模型中更加常见。

7 总结

在这篇博客文章中,我们初步研究了梯度下降的三个变形形式,其中,小批量梯度下降是最受欢迎的。 然后我们研究了最常用于优化SGD的算法:动量法,Nesterov加速梯度,Adagrad,Adadelta,RMSprop,Adam以及不同的优化异步SGD的算法。 最后,我们已经考虑其他一些改善SGD的策略,如洗牌和课程学习,批量归一化和early stopping。

参考博文:梯度下降优化算法综述-zhiyong_will


-------------本文结束感谢您的阅读-------------

本文标题:An overview of gradient descent optimization algorithms (论文解析)

文章作者:小火箭

原始链接:https://www.xiemingzhao.com/posts/eafa0d01.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。