梯度下降优化算法综述(翻译)


原文题目:An overview of gradient descent optimization algorithms

原文链接:http://sebastianruder.com/optimizing-gradient-descent

博文地址:http://blog.csdn.net/wangxinginnlp/article/details/50974594


梯度下降是最流行的优化算法之中的一个而且眼下为止是优化神经网络最常见的算法。

与此同一时候,每个先进的深度学习库都包括各种算法实现的梯度下降(比方lasagne'scaffe's, 和 keras'的文档)。然而,这些算法常常作为黑盒优化程序使用。所以难以感受到各种算法的好处和不足。


此博文旨在为你提供对不同梯度算法的直观感受,以期会帮助你更好地使用不同的梯度下降算法。

首先,我们会罗列各种梯度下降算法的变种并简单地总结算法训练阶段的挑战。

然后。我们会通过展示解决这个问题的动机和根据这些动机来推导更新法则。以介绍最常见的优化算法。我们也顺带罗列下并行分布式环境下的算法和体系结构。最后,我们会讨论其它有利于梯度下降优化算法的策略。


梯度下降是一种以通过在目标函数梯度 的反向上更新模型參数,来最小化模型參数的目标函数的方法。学习速率 决定了我们前往(局部)极小值的步长。

换言之,我们沿着目标函数所构造曲面的斜面按向下的方向走动。直到我们到达山谷。假设你对梯度下降不熟悉。你能够点击此处点击打开链接去了解一篇关于优化神经网络的介绍。


梯度下降算法变种

存在三种梯度下降的变种,他们不同之处在于我们在计算目标函数梯度时所用数据量的多少。

根据数据的规模,我们在更新參数的准确性和运行一次更新所用时间之间进行一种折中。


批量梯度下降

普通的梯度下降,也称批量梯度下降,利用全部的训练数据计算目标函数的梯度。

                                                                                             

因为我们每进行一次參数更新须要计算总体训练数据的梯度,批量梯度下降会变得非常慢而且一遇到内存吃不下数据就挂了。同一时候批量梯度下降也无法支持模型的在线更新,比如,新的样本不停的到来。


代码中。批量梯度下降大概长这样:

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

对于一个预先定义迭代轮数。我们首先以总体数据计算损失函数的梯度向weights_grad关于參数向量params值得注意的是先进的深度学习库提供对一些參数进行自己主动求导能够有效地计算梯度。假设你是自己来推梯度。梯度检查是一个不错的注意。(点击点击打开链接查看关于怎样正确地检查梯度的建议)


我们随后以梯度的反方向更新我们的參数。此时学习速率决定着我们每次运行多大的更新。批量梯度下降能够保证在convex error surfaces 条件下取得全局最小值,在non-convex surfaces条件下取得局部极小值。


随机梯度下降

随机梯度下降(SGD)以一个训练例子和标签进行一次參数更新。

                                                                                  

因为在每次參数更新前对相似的例子进行梯度反复计算, 批量梯度下降会在大数据集上进行冗余计算。SGD通过每次计算一个例子的方式避开这样的冗余。

因此。SGD速度会更快并支持在线学习。

SGD频繁的运行更新所伴随的高方差(a high variance)会导致目标函数如图1所看到的的剧烈波动。

 图1. SGD波动


批量梯度下降收敛到盆面的极小值,SGD的波动一方面可以使(损失函数)跳到一个全新而且可鞥呢更优的局部极小值,还有一方面这样的波动因为一直overshooting终究会非常难收敛到确切的极小值。

然而,(实验)表明当我们慢慢地减小学习速率时SGD表现出和批量梯度下降相同的收敛行为。差点儿确定地在non-convex and convex optimization中各自收敛到一个局部或者全局极小值在


SGD的代码片段简单在训练实例上套一个循环并评估每个训练例子的梯度。值得注意的是我们在每轮迭代时候会打乱训练数据。相关解释见点击打开链接


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

Mini-batch gradient descent

Mini-batch gradient descent 吸收了上述两个算法的好处。利用小批量训练例子运行一次更新。

                                                                                     

以这样的方式。它能够 a) 减小參数更新的方差,导致更平稳的收敛。b) 利用先进深度学习库中常见的高度优化矩阵操作来高效地计算小批量的梯度。普通小批量的规模在50到256之间。但在不同的应用中会变化。Mini-batch gradient desent 是训练神经网络的经典选择。採用mini-bathes时经常也会称为SGD。注意在后文中SGD改进中,为简单起见。我们省略參数


代码中,我们每轮迭代的mini-bathes规模设置为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

挑战

然而,普通的mini-batch gradient descent不能保证较好的收敛性,这一点引出了下述挑战:

  1. 选择一个合适的学习速率是非常难的。学习速率太小会导致收敛慢,太大会阻碍收敛并导致损失函数在极小值周围波动甚至背离。
  2. 尝试通过设置调度在训练时候调整训练速率,比方,模拟退火依照预先定义好的调度算法或者当相邻的迭代中目标变化小于一个阈值时候减小学习速率。可是这些调度和阈值须要预先设置,无法对数据集特征进行自适应。
  3. 除此之外。对全部的參数更新都依照同一个学习速率也是一个问题。假设我们的数据非常稀疏而且我们的特征出现的次数不同。我们可能不会希望全部的參数以某种同样的幅度进行更新。而是针对非常少出现的特征进行一次大幅度更新。

  4. 在神经网络中常见的极小化highly non-convex error functions的一个关键挑战是避免步入大量的suboptimal local minima。

    Dauphin等人觉得实践中的困难来自saddle points而非local minima。

    所谓saddle points是指那些维度梯度不一致的点。

    这些saddle points常常被一个相等误差的平原包围。导致SGD非常难摆脱,由于梯度在全部方向都近似于0。

注意:关于saddle points讨论就后面附加博客3

梯度下降优化算法

以下我们会概述一些深度学习社区广泛採用的以解决上述挑战的算法。

我们不会讨论那些实践中对高维数据集合计算上不可行的算法,比方二阶方法(比方牛顿法)。



Momentum


SGD不那么easy越过ravines,所谓ravines也就是那些surface curves在某个维度比其它维度梯度大得多的地方,这些地方常常在局部极小值周围出现。在这样的情况下。SGD会像图2一般沿着slopes of the ravine振荡中前进。在底部磨磨蹭蹭地朝局部最优走。


图2. 不带Momentum的SGD


图3. 带有Momentum的SGD

Momentum是一种帮助SGD在相关方向进行加速并抑制振荡的方法,如图3所看到的。它通过向当前更新向量中增加上一时刻的更新向量的部分实现上述功能。
                                                                                 
                                                                                  

注意,有些实现中会对等式中的符号进行变动。momentum term  一般设置为0.9或类似值。

我们向山下丢一个小球就会涉及使用到momentum。小球在向山下滚动时候会积累动量滚动地越来越快(假设存在空气阻力,会一直加速到终端速度terminal velocity)。同样的事情会发生在參数更新上:momentum term会在更新方向同样的维度加速。会在更新方向不同的维度上减速。终于。我们更快地收敛并降低振荡。


Nesterov accelerated gradient


然而,我们并不惬意于滚动的小球只不过盲目地沿着斜面slope滚动。我们希望有一个更加聪明的小球,一个知道自己是否能认识到自己选择道路的小球,这样的小球会在斜面slope再次上倾的时候放慢自己的速度。


Nesterov accelerated gradient (NAG) 是一种可以给我们momentum term带来这样的先见之明的方法。

我们清楚我们会使用momentum term 来更新參数。计算 会让我们看到更新后參数的近似值(完整的更新还须要考虑梯度),让我们大致知道參数朝那地方更新。我们如今可以通过计算下一个位置參数的梯度(而不是当前位置的參数) 进行提前准备: 


                                                                                   
                                                                                    

我们再次将momentum term  设置在0.9的附近。不同于Momentum方法先计算当面的梯度(图4中蓝色小向量)后在更新过的累积梯度方向上进行一个大跨越(蓝色大向量)。NAG先在上一个累积梯度方向进行跳跃(棕色向量)。測量下梯度然后进行一个修正(绿色向量)。这样的预期式的更新防止我们走的太快并添加反应能力,显著提高了RNN在一些任务上的性能。

图4. Nesterov更新


点击点击打开链接參考对于NAG背后直觉的第二种解释,同一时候Ilya Sutskever在他博士论文中给出更具体的综述。


如今我们能够让我们的更新适应于损失函数所构造的斜面slope的同一时候加快SGD的速度。

我们也希望我们的更新适应于单独的參数。依照參数自身的重要性来进行大幅度或者小幅度的更新。



Adagrad

Adagrad是一个基于梯度优化的算法:它让学习速率自适应于參数,对低频參数进行大幅度更新而对高频參数进行小幅度更新。由于这一点,它很适合于处理稀疏数据。Dean等人发现Adagrad大大地提高了SGD的鲁棒性并在谷歌的大规模神经网络训练中採用了它进行參数更新,当中包括了在Youtube视频中进行猫脸识别。此外,由于低频词(參数)须要更大幅度的更新,Pennington等人在GloVe word embeddings的训练中也採用了Adagrad。

之前,我们每次更新都涉及全部的參数且每一个參数  採用同样的速率。因为Adagrad在时刻 对每一个參数  採用了不同的学习速率,我们先展示Adagrad的per-parameter 更新然后对他们进行向量化。简答起见, 表示损失函数中參数  在时刻  的梯度。
                                                                                                                                     
SDG算法时刻  对參数  进行更新则表示为:
 
                                                                                                   

在这个更新规则中,Adagrad利用过去时刻算好了的梯度对不同一时候刻  不同參数 的学习速率  进行调整:

                                                                                                    
是一个对角矩阵,当中每一个对角元素是參数  到时刻  为止全部时刻梯度的平方之和, 是平滑项以避免分母为0。

有趣的是,假设不用平方根操作。算法性能会变差非常多。



因为  的对角包括着全部參数过去时刻的平方之和。我们能够通过在  和   运行element-wise matrix-vector multiplication来向量化我们的操作:

                                                                                                     

Adagrad的一个长处就是不用进行人工调整学习速率。很多实现中都是使用缺省的0.01进行赋值。

Adagrad的一个主要弱点就是它在分母中累加梯度的平方:因为每次添加的是一个正数。累加的和在训练阶段一直添加。这会导致学习速率一直变小终于变得无限小,在学习速率无限小的地方Adagrad算法无法取得额外的知识。以下这个算法就是为了克服这个缺陷而产生的。

Adadelta

Adadelta是Adagrad的一种扩展。以缓解Adagrad学习速率单调递减问题的算法。Adadelta不是对过去全部时刻的梯度平庸进行累加,而是将累加时刻限制在窗体大小为的  区间。


梯度累加没有採用简单的存储前个时刻的梯度平方。而是递归使的定义为过去全部时刻梯度平方的decaying average。

时刻  的running average  只依赖于之前average和当前的梯度:

                                    

类似momentum term,我们将  取值在0.9附近。

 简洁起见,我们从參数更新向量 角度重写普通SGD的參数更新:

                                                               
                                                                                            

                                                                                             

Adagrad中我们推导的參数更新向量如今就下面述形式出现:
                                                                                              

如今我们简单地将对角矩阵  替换为过去时刻梯度平方的decaying average 
                                                                                              
因为分母是root mean squared (RMS) error criterion of the gradient, 则上面公式能够替换为:
                                                                                               

[中间有些没搞懂] 


作者发现(和SGD,Momentum或者Adagrad一样)上述更新中的单元不匹配(我的个人理解是:仅仅有部分參数进行更新)。也就是參数和更新应该有着同样的hypothetical units。

为了实现这个目的,他们首先定义了另外一个exponentially decaying average。这一次对更新參数的平方进行操作,而不仅仅是对梯度的平方进行操作:

                                  
參数更新中的root mean squared error则为:
                                  

将曾经的更新规则中的学习速率替换为參数更新的RMS。则得到Adadelta更新规则:

                                 

                                 

因为Adadelta更新规则中没有了学习速率这一项,我们甚至都不用对学习速率进行设置。

RMSprop


RMSprop是一个没有发表的工作,它是Geoff Hinton在他课上提出的一种自适应学习速率方法(adaptive learning rate method)。

RMSprop和Adadelta是在几乎相同的时间各自独立产生的工作。目的都是为了缓解Adagrad的学习速率降低的问题。

实际上RMSprop和我们在Adadelta中推到的第一个更新向量是同样的:



                                                                                
RMSprop也将学习速率除以梯度平方的exponentially decaying average. Hinton建议 设置为0.9,学习速率 设置为0.001。 

Adam

Adaptive Moment Estimation (Adam)是第二种对每一个參数进行自适应学习速率计算的方法。除了像Adeadelta和RMSprop一样保存去过梯度平方和的exponentially decaying average外。Adam还保存类似momentum一样过去梯度的exponentially decaying average。
                           
                           
 和 是各自是梯度的一阶矩(均值)和二阶矩(偏方差)的预计,这就是方法名称的由来。因为 和 由全零的向量来初始化,Adam的作者观察到他们会被偏向0,特别是在initial time steps或decay rates非常小的时候(即 和 都接近于1)

他们通过计算bias-corrected 一阶矩和二阶矩的预计低消掉偏差。

                                                                           
                                                                           

然后他们使用上述项和Adadelta和RMSprop一样进行參数更新,能够得到Adam的更新规则:

                                                                            

他们建议默认设置 为0.9,  为0.999, 为10e-8。他们经验性地表明Adam可以在实践中非常好地起作用而且和一点也不比其它的适应性学习算法逊色。

算法的可视化

以下两幅动画让我们直观感受一些优化算法的优化过程。


在图5中。我们看到他们随着时间推移在损失表面的轮廓(contours of a loss surface)的移动。注意到Adagrad、Adadelta和RMSprop差点儿立马转向正确的方向并高速收敛,可是Momentum和NAG被引导偏离了轨道。这让我们感觉就像看滚下山的小球。

然而,因为NAG拥有通过远眺所提高的警惕,它可以修正他的轨迹并转向极小值。


图5. 损失表面轮廓(loss surface contours)上SGD算法表现

图6展现了各种算法在saddle point上的表现。所谓saddle point也就是某个维度是positive slope。其它维度为negative lope。前文中我们已经提及了它给SGD所带来的困难。注意到SGD、Momentum和NAG非常难打破对称。尽管后两者最后还是逃离了saddle point。然而Adagrad, RMSprop, and Adadelta非常迅速地沿着negative slope下滑。 

图6.saddle point上SGD算法表现

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

採用哪种


如今,问题来了。你如今会採用哪种优化算法呢?假设你的输入数据是稀疏的,你更可能通过採用某种自适应学习速率方法来获得最好的结果。

这么做另外一个优点是你不必去调学习速率。仅用默认值就能够取得最好的结果。


总而言之。RMSprop是Adagrad的一种针对处理Adagrad的学习速率减小的扩展。它和Adadelta是一样的。唯一不同就是Adadelta使用在更新规则的分子中使用參数更新的RMS。

Adam是将bias-correction and momentum增加到RMSprop中。RMSprop、Adadelta和Adam是非常相似的算法而且在相似的环境中性能都不错。

Kingma等人发如今优化后期因为梯度越来越稀疏,bias-correction帮助Adam微弱地击败RMSprop。综合看来,Adam可能是最佳选择。


有趣的是。近期非常多论文都採用不带momentum的普通SGD和一种简单的模拟退火(annealing schedule)学习速率。

SGD经常会达到极小值,可是花掉的时间比其它的算法多得多。SGD在更加依赖于鲁棒的初始化和模拟退火(annealing schedule)并可能被saddle points而不是局部极小值困住。

所以,假设你在意高速收敛或者在训练一个非常深非常复杂的神经网络,你应该採用一种自适应学习速率方法。


并行式与分布式SGD


鉴于无处不大规模数据解决方式和低价可得的集群。将SGD进行分布以进一步加速一个理所当然的选择

SGD自身是sequential的:我们逐步地朝着极值前进。

SGD跑起来收敛性好可是在速度非常慢,尤其是在大数据集上。相反,异步方式的SGD速度非常快。可是workers之间次优的通信会导致收敛较差。另外,我们也能够在一台机器上将SGD并行,而不用大的计算集群。以下是一些关于优化并行式和分布式SGD的算法和体系结构。





补充的博客:


原文地址:https://www.cnblogs.com/wzjhoutai/p/7124733.html