标签:ext 高维数据 极速 进一步 journal 生成 比较 分享 gen
本文翻译自Sebastian Ruder的“An overview of gradient descent optimization algoritms”,作者首先在其博客中发表了这篇文章,其博客地址为:An overview of gradient descent optimization algoritms,之后,作者将其整理完放在了arxiv中,其地址为:An overview of gradient descent optimization algoritms,在翻译的过程中以作者发布在Arxiv的论文为主,参考其在博客中的内容。
本文的翻译已经获得作者的同意。
虽然梯度下降优化算法越来越受欢迎,但通常作为黑盒优化器使用,因此很难对其优点和缺点的进行实际的解释。本文旨在让读者对不同的算法有直观的认识,以帮助读者使用这些算法。在本综述中,我们介绍梯度下降的不同变形形式,总结这些算法面临的挑战,介绍最常用的优化算法,回顾并行和分布式架构,以及调研用于优化梯度下降的其他的策略。
梯度下降法是最著名的优化算法之一,也是迄今优化神经网络时最常用的方法。同时,在每一个最新的深度学习库中都包含了各种优化的梯度下降法的实现(例如:参见lasagne,caffe和keras的文档)。然而,这些算法通常是作为黑盒优化器使用,因此,很难对其优点和缺点的进行实际的解释。
本文旨在让读者对不同的优化梯度下降的算法有直观的认识,以帮助读者使用这些算法。在第2部分,我们首先介绍梯度下降的不同变形形式。在第3部分,我们将简要总结在训练的过程中所面临的挑战。随后,在第4部分,我们将介绍最常用的优化算法,包括这些算法在解决以上挑战时的动机以及如何得到更新规则的推导形式。在第5部分,我们将简单讨论在并行和分布式环境中优化梯度下降的算法和框架。最后,在第6部分,我们将思考对优化梯度下降有用的一些其他策略。
梯度下降法是最小化目标函数
梯度下降法有3中变形形式,它们之间的区别为我们在计算目标函数的梯度时使用到多少数据。根据数据量的不同,我们在参数更新的精度和更新过程中所需要的时间两个方面做出权衡。
Vanilla梯度下降法,又称为批梯度下降法(batch gradient descent),在整个训练数据集上计算损失函数关于参数
因为在执行每次更新时,我们需要在整个数据集上计算所有的梯度,所以批梯度下降法的速度会很慢,同时,批梯度下降法无法处理超出内存容量限制的数据集。批梯度下降法同样也不能在线更新模型,即在运行的过程中,不能增加新的样本。
批梯度下降法的代码如下所示:
for i in range(nb_epochs):
params_grad = evaluate_gradient(loss_function, data, params)
params = params - learning_rate * params_grad
对于给定的迭代次数,首先,我们利用全部数据集计算损失函数关于参数向量params
的梯度向量params_grad
。注意,最新的深度学习库中提供了自动求导的功能,可以有效地计算关于参数梯度。如果你自己求梯度,那么,梯度检查是一个不错的主意(关于如何正确检查梯度的一些技巧可以参见http://cs231n.github.io/neural-networks-3/)。
然后,我们利用梯度的方向和学习率更新参数,学习率决定我们将以多大的步长更新参数。对于凸误差函数,批梯度下降法能够保证收敛到全局最小值,对于非凸函数,则收敛到一个局部最小值。
相反,随机梯度下降法(stochastic gradient descent, SGD)根据每一条训练样本
对于大数据集,因为批梯度下降法在每一个参数更新之前,会对相似的样本计算梯度,所以在计算过程中会有冗余。而SGD在每一次更新中只执行一次,从而消除了冗余。因而,通常SGD的运行速度更快,同时,可以用于在线学习。SGD以高方差频繁地更新,导致目标函数出现如图1所示的剧烈波动。
与批梯度下降法的收敛会使得损失函数陷入局部最小相比,由于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
小批量梯度下降法最终结合了上述两种方法的优点,在每次更新时使用
这种方法,a)减少参数更新的方差,这样可以得到更加稳定的收敛结果;b)可以利用最新的深度学习库中高度优化的矩阵优化方法,高效地求解每个小批量数据的梯度。通常,小批量数据的大小在50到256之间,也可以根据不同的应用有所变化。当训练神经网络模型时,小批量梯度下降法是典型的选择算法,当使用小批量梯度下降法时,也将其称为SGD。注意:在下文的改进的SGD中,为了简单,我们省略了参数
在代码中,不是在所有样本上做迭代,我们现在只是在大小为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
虽然Vanilla小批量梯度下降法并不能保证较好的收敛性,但是需要强调的是,这也给我们留下了如下的一些挑战:
下面,我们将列举一些算法,这些算法被深度学习社区广泛用来处理前面提到的挑战。我们不会讨论在实际中不适合在高维数据集中计算的算法,例如诸如牛顿法的二阶方法。
SGD很难通过陡谷,即在一个维度上的表面弯曲程度远大于其他维度的区域[19],这种情况通常出现在局部最优点附近。在这种情况下,SGD摇摆地通过陡谷的斜坡,同时,沿着底部到局部最优点的路径上只是缓慢地前进,这个过程如图2a所示。
如图2b所示,动量法[16]是一种帮助SGD在相关方向上加速并抑制摇摆的一种方法。动量法将历史步长的更新向量的一个分量
动量项
从本质上说,动量法,就像我们从山上推下一个球,球在滚下来的过程中累积动量,变得越来越快(直到达到终极速度,如果有空气阻力的存在,则
然而,球从山上滚下的时候,盲目地沿着斜率方向,往往并不能令人满意。我们希望有一个智能的球,这个球能够知道它将要去哪,以至于在重新遇到斜率上升时能够知道减速。
Nesterov加速梯度下降法(Nesterov accelerated gradient,NAG)[13]是一种能够给动量项这样的预知能力的方法。我们知道,我们利用动量项
同时,我们设置动量项
对于NAG的直观理解的另一种解释可以参见http://cs231n.github.io/neural-networks-3/,同时Ilya Sutskever在其博士论文[18]中给出更详细的综述。
既然我们能够使得我们的更新适应误差函数的斜率以相应地加速SGD,我们同样也想要使得我们的更新能够适应每一个单独参数,以根据每个参数的重要性决定大的或者小的更新。
Adagrad[7]是这样的一种基于梯度的优化算法:让学习率适应参数,对于出现次数较少的特征,我们对其采用更大的学习率,对于出现次数较多的特征,我们对其采用较小的学习率。因此,Adagrad非常适合处理稀疏数据。Dean等人[6]发现Adagrad能够极大提高了SGD的鲁棒性并将其应用于Google的大规模神经网络的训练,其中包含了YouTube视频中的猫的识别。此外,Pennington等人[15]利用Adagrad训练Glove词向量,因为低频词比高频词需要更大的步长。
前面,我们每次更新所有的参数
在
对于上述的更新规则,在
其中,
由于
Adagrad算法的一个主要优点是无需手动调整学习率。在大多数的应用场景中,通常采用常数
Adagrad的一个主要缺点是它在分母中累加梯度的平方:由于没增加一个正项,在整个训练过程中,累加的和会持续增长。这会导致学习率变小以至于最终变得无限小,在学习率无限小时,Adagrad算法将无法取得额外的信息。接下来的算法旨在解决这个不足。
Adadelta[21]是Adagrad的一种扩展算法,以处理Adagrad学习速率单调递减的问题。不是计算所有的梯度平方,Adadelta将计算计算历史梯度的窗口大小限制为一个固定值
在Adadelta中,无需存储先前的
我们将
我们先前得到的Adagrad参数更新向量变为:
现在,我们简单将对角矩阵
由于分母仅仅是梯度的均方根(root mean squared,RMS)误差,我们可以简写为:
作者指出上述更新公式中的每个部分(与SGD,动量法或者Adagrad)并不一致,即更新规则中必须与参数具有相同的假设单位。为了实现这个要求,作者首次定义了另一个指数衰减均值,这次不是梯度平方,而是参数的平方的更新:
因此,参数更新的均方根误差为:
由于
使用Adadelta算法,我们甚至都无需设置默认的学习率,因为更新规则中已经移除了学习率。
RMSprop是一个未被发表的自适应学习率的算法,该算法由Geoff Hinton在其Coursera课堂的课程6e中提出。
RMSprop和Adadelta在相同的时间里被独立的提出,都起源于对Adagrad的极速递减的学习率问题的求解。实际上,RMSprop是先前我们得到的Adadelta的第一个更新向量的特例:
同样,RMSprop将学习率分解成一个平方梯度的指数衰减的平均。Hinton建议将
自适应矩估计(Adaptive Moment Estimation,Adam)[9]是另一种自适应学习率的算法,Adam对每一个参数都计算自适应的学习率。除了像Adadelta和RMSprop一样存储一个指数衰减的历史平方梯度的平均
通过计算偏差校正的一阶矩和二阶矩估计来抵消偏差:
正如我们在Adadelta和RMSprop中看到的那样,他们利用上述的公式更新参数,由此生成了Adam的更新规则:
作者建议
下面两张图给出了上述优化算法的优化行为的直观理解。(还可以看看这里关于Karpathy对相同的图片的描述以及另一个简明关于算法讨论的概述)。
在图4a中,我们看到不同算法在损失曲面的等高线上走的不同路线。所有的算法都是从同一个点出发并选择不同路径到达最优点。注意:Adagrad,Adadelta和RMSprop能够立即转移到正确的移动方向上并以类似的速度收敛,而动量法和NAG会导致偏离,想像一下球从山上滚下的画面。然而,NAG能够在偏离之后快速修正其路线,因为NAG通过对最优点的预见增强其响应能力。
图4b中展示了不同算法在鞍点出的行为,鞍点即为一个点在一个维度上的斜率为正,而在其他维度上的斜率为负,正如我们前面提及的,鞍点对SGD的训练造成很大困难。这里注意,SGD,动量法和NAG在鞍点处很难打破对称性,尽管后面两个算法最终设法逃离了鞍点。而Adagrad,RMSprop和Adadelta能够快速想着梯度为负的方向移动,其中Adadelta走在最前面。
正如我们所看到的,自适应学习速率的方法,即 Adagrad、 Adadelta、 RMSprop 和Adam,最适合这些场景下最合适,并在这些场景下得到最好的收敛性。
那么,我们应该选择使用哪种优化算法呢?如果输入数据是稀疏的,选择任一自适应学习率算法可能会得到最好的结果。选用这类算法的另一个好处是无需调整学习率,选用默认值就可能达到最好的结果。
总的来说,RMSprop是Adagrad的扩展形式,用于处理在Adagrad中急速递减的学习率。RMSprop与Adadelta相同,所不同的是Adadelta在更新规则中使用参数的均方根进行更新。最后,Adam是将偏差校正和动量加入到RMSprop中。在这样的情况下,RMSprop、Adadelta和Adam是很相似的算法并且在相似的环境中性能都不错。Kingma等人[9]指出在优化后期由于梯度变得越来越稀疏,偏差校正能够帮助Adam微弱地胜过RMSprop。综合看来,Adam可能是最佳的选择。
有趣的是,最近许多论文中采用不带动量的SGD和一种简单的学习率的退火策略。已表明,通常SGD能够找到最小值点,但是比其他优化的SGD花费更多的时间,与其他算法相比,SGD更加依赖鲁棒的初始化和退火策略,同时,SGD可能会陷入鞍点,而不是局部极小值点。因此,如果你关心的是快速收敛和训练一个深层的或者复杂的神经网络,你应该选择一个自适应学习率的方法。
当存在大量的大规模数据和廉价的集群时,利用分布式SGD来加速是一个显然的选择。SGD本身有固有的顺序:一步一步,我们进一步进展到最小。SGD提供了良好的收敛性,但SGD的运行缓慢,特别是对于大型数据集。相反,SGD异步运行速度更快,但客户端之间非最理想的通信会导致差的收敛。此外,我们也可以在一台机器上并行SGD,这样就无需大的计算集群。以下是已经提出的优化的并行和分布式的SGD的算法和框架。
Niu等人[14]提出称为Hogwild!的更新机制,Hogwild!允许在多个CPU上并行执行SGD更新。在无需对参数加锁的情况下,处理器可以访问共享的内存。这种方法只适用于稀疏的输入数据,因为每一次更新只会修改一部分参数。在这种情况下,该更新策略几乎可以达到一个最优的收敛速率,因为CPU之间不可能重写有用的信息。
Downpour SGD是SGD的一种异步的变形形式,在Google,Dean等人[6]在他们的DistBelief框架(TensorFlow的前身)中使用了该方法。Downpour SGD在训练集的子集上并行运行多个模型的副本。这些模型将各自的更新发送给一个参数服务器,参数服务器跨越了多台机器。每一台机器负责存储和更新模型的一部分参数。然而,因为副本之间是彼此不互相通信的,即通过共享权重或者更新,因此可能会导致参数发散而不利于收敛。
通过容忍延迟算法的开发,McMahan和Streeter[11]将AdaGraad扩展成并行的模式,该方法不仅适应于历史梯度,同时适应于更新延迟。该方法已经在实践中被证实是有效的。
TensorFlow[1]是Google近期开源的框架,该框架用于实现和部署大规模机器学习模型。TensorFlow是基于DistBelief开发,同时TensorFlow已经在内部用来在大量移动设备和大规模分布式系统的执行计算。在2016年4月发布的分布式版本依赖于图计算,图计算即是对每一个设备将图划分成多个子图,同时,通过发送、接收节点对完成节点之间的通信。
Zhang等人[22]提出的弹性平均SGD(Elastic Averaging SGD,EASGD)连接了异步SGD的参数客户端和一个弹性力,即参数服务器存储的一个中心变量。EASGD使得局部变量能够从中心变量震荡得更远,这在理论上使得在参数空间中能够得到更多的探索。经验表明这种增强的探索能力通过发现新的局部最优点,能够提高整体的性能。
最后,我们介绍可以与前面提及到的任一算法配合使用的其他的一些策略,以进一步提高SGD的性能。对于其他的一些常用技巧的概述可以参见[10]。
总的来说,我们希望避免向我们的模型中以一定意义的顺序提供训练数据,因为这样会使得优化算法产生偏差。因此,在每一轮迭代后对训练数据洗牌是一个不错的主意。
另一方面,在很多情况下,我们是逐步解决问题的,而将训练集按照某个有意义的顺序排列会提高模型的性能和SGD的收敛性,如何将训练集建立一个有意义的排列被称为课程学习[3]。
Zaremba and Sutskever[20]只能使用课程学习训练LSTM来评估简单程序,并表明组合或混合策略比单一的策略更好,通过增加难度来排列示例。
为了便于学习,我们通常用0均值和单位方差初始化我们的参数的初始值来归一化。 随着不断训练,参数得到不同的程度的更新,我们失去了这种归一化,随着网络变得越来越深,这种现象会降低训练速度,且放大参数变化。
批量归一化[8]在每次小批量数据反向传播之后重新对参数进行0均值单位方差标准化。通过将模型架构的一部分归一化,我们能够使用更高的学习率,更少关注初始化参数。批量归一化还充当正则化的作用,减少(有时甚至消除)Dropout的必要性。
如Geoff Hinton所说:“Early Stopping是美丽好免费午餐”(NIPS 2015 Tutorial slides)。你因此必须在训练的过程中时常在验证集上监测误差,在验证集上如果损失函数不再显著地降低,那么应该提前结束训练。
Neelakantan等人[12]在每个梯度更新中增加满足高斯分布
高斯分布的方差需要根据如下的策略退火:
他们指出增加了噪音,使得网络对不好的初始化更加鲁棒,同时对深层的和复杂的网络的训练特别有益。他们猜测增加的噪音使得模型更优机会逃离当前的局部最优点,以发现新的局部最优点,这在更深层的模型中更加常见。
在这篇博客文章中,我们初步研究了梯度下降的三个变形形式,其中,小批量梯度下降是最受欢迎的。 然后我们研究了最常用于优化SGD的算法:动量法,Nesterov加速梯度,Adagrad,Adadelta,RMSprop,Adam以及不同的优化异步SGD的算法。 最后,我们已经考虑其他一些改善SGD的策略,如洗牌和课程学习,批量归一化和early stopping。
标签:ext 高维数据 极速 进一步 journal 生成 比较 分享 gen
原文地址:http://blog.csdn.net/google19890102/article/details/69942970