梯度下降算法是机器学习中应用非常广泛的优化算法,也是众多机器学习算法中最常用的优化方法。几乎每个最先进的机器学习或深度学习库目前都包含梯度下降算法不同变体的实现。然而,它们就像一个黑盒优化器,很难得到关于它们优缺点的实际解释。本文旨在介绍梯度下降算法的不同变体,以帮助用户根据自己的特定需求使用它。本文首先介绍了梯度下降算法的三个框架,然后介绍了它们存在的问题和挑战,然后介绍了如何进行改进以解决这些问题,然后介绍了如何在并行环境或分布式环境中使用梯度下降算法。***,指出一些有助于梯度下降的策略。目录三种梯度下降优化框架批量梯度下降随机梯度下降小批量梯度下降问题和挑战梯度下降优化算法MomentumNesterov加速梯度AdagradAdadeltaRMSpropAdam算法可视化选择哪种优化算法?ParallelandDistributedSDGHogwild!DownpourSGDDelay-tolerantAlgorithmsforSGDTensorFlowElasticAveragingSGDMoreSDGoptimizationstrategiesRandomshufflingoftrainingsetandcourselearningBatchnormalizationEarlyStoppingGradientnoise三个梯度下降优化框架的总结和参考Derivative)intheoppositedirection??θJ(θ)不断更新模型参数以达到目标函数的最小点(收敛),更新步长为η。存在三种梯度下降算法框架。区别在于每次学习(更新模型参数)使用的样本数量。每次更新使用不同的样本将导致每次学习的准确率和学习时间不同。批量梯度下降(Batchgradientdescent)每次使用全量训练集样本更新模型参数,即:θ=θ?η??θJ(θ)代码如下:epochs为最大迭代次数由用户输入。从申诉代码可以看出,每次使用所有训练集样本计算损失函数loss_function的梯度params_grad,然后用学习率learning_rate在梯度的反方向更新模型的各个参数params.通常,现有的一些机器学习库会提供梯度计算API。如果要自己写代码计算,需要在程序调试过程中验证梯度计算是否正确。批量梯度下降每次学习都使用整个训练集,所以它的优点是每次更新都会往正确的方向走,可以保证收敛到极值点(凸函数收敛到全局极值点,非凸函数可能会收敛到局部极值点),但缺点是每次学习时间太长,如果训练集很大需要消耗大量内存,全梯度下降不能更新在线模型参数.随机梯度下降(Stochasticgradientdescent)随机梯度下降算法每次从训练集中随机选择一个样本进行学习,即:θ=θ?η??θJ(θ;xi;yi)批量梯度下降算法将每次都使用所有训练样本,因此这些计算是多余的,因为每次都使用完全相同的一组样本。随机梯度下降算法每次只随机选择一个样本来更新模型参数,因此每次学习都非常快,并且可以在线更新。它的代码如下:随机梯度下降的缺点是每次更新都可能不遵循正确的方向,因此会带来优化波动(扰动),如下图所示:图1SGD扰动但从另一个角度来看,随机梯度下降带来的波动的好处是,对于相似的盆地区域(即很多局部极小点),这种波动的特性可能使得优化方向从当前的局部极小点跳到另一个更好的局部极小点,这样一个非凸函数就有可能最终收敛到一个更好的局部极小点,甚至是全局极小点。由于波动,迭代次数(学习次数)会增加,即收敛速度会变慢。但最终会和全梯度下降算法一样收敛,即凸函数收敛于全局极值点,非凸损失函数收敛于局部极值点。小批量梯度下降(Mini-batchgradientdescent)小批量梯度下降结合了批量梯度下降和随机梯度下降,在每次更新速度和更新次数之间取得了平衡。每次更新从训练集中随机选择m。mθ=θ?η??θJ(θ;xi:i+m;yi:i+m)代码如下:与随机梯度下降相比,Mini-batch梯度下降降低了收敛波动性,即降低了参数更新Variance,使更新更稳定。与全梯度下降相比,提高了每次学习的速度。并且不需要担心内存瓶颈,可以使用矩阵运算进行高效计算。一般来说,每次更新随机选取[50,256]个样本进行学习,但也要根据具体问题来选取。在实践中,可以进行多次实验,选择更适合更新速度和更新次数的样本数。mini-batch梯度下降可以保证收敛性,常用于神经网络。问题与挑战虽然梯度下降算法效果很好并且被广泛使用,但它也存在一些挑战和问题需要解决:选择合理的学习率是困难的。如果学习率太小,会导致收敛很慢。学习率太大会阻碍收敛,即会围绕极值点震荡。学习率调整(也称为学习率调度、Learningrateschedules)[11]试图在每次更新过程中改变学习率,例如退火。通常在每次迭代中使用一些预先设定的策略或衰减一个小的阈值。无论采用何种调整方式,都需要事先固定下来,因此无法适应每次学习时数据集的特点[10]。模型的所有参数每次都以相同的学习率更新。如果数据特征稀疏或者每个特征有不同的值统计特征和空间,那么每次更新时不能对每个参数使用相同的学习率,那些很少出现的特征应该使用比较大的学习率。对于非凸目标函数,很容易陷入那些次优的局部极值点,比如在神经网络中。那么如何避免呢。Dauphin[19]指出更严重的问题不是局部极值点,而是鞍点。梯度下降优化算法下面将讨论深度学习社区中经常使用的一些梯度优化方法来解决有吸引力的问题,但不包括在高维数据中不可行的算法,例如牛顿法。如果Momentum在峡谷区域(有些方向比其他方向陡峭得多,经常在局部极值点发现)[1],SGD会在这些地方附近振荡,导致收敛缓慢。在这种情况下,动量(Momentum)就可以解决[2]。Momentum在参数更新项上增加一个更新量(即动量项),即:νt=γνt?1+η?θJ(θ),θ=θ?νt其中动量项超参数γ<1一般较小大于或等于0.9。它的作用如下图所示:图2不加动量图3加动量和动量就像一个球从山顶滚下来,滚下来的时候把之前的动量累积起来(动量不断增加),所以速度变得越来越快,直到你到达终点线。同理,在更新模型参数时,对于那些当前梯度方向与之前梯度方向相同的参数,进行加强,即这些方向更快;对于那些当前梯度方向与之前梯度方向不同的参数,则进行切割,即在那些方向上减速。因此,可以获得更快的收敛速度和更小的振荡。从山上滚下的NesterovacceleratedGradient(NAG)球会盲目地选择坡度。更好的方法应该是在遇到上坡之前减速。Nesterov加速梯度(NAG,Nesterovgradientacceleration)在计算参数的梯度时,不仅增加了动量项,还在损失函数中减去动量项,即计算?θJ(θ?γνt?1),这样方式估计下一个参数将在哪里。即:νt=γνt?1+η??θJ(θ?γνt?1),θ=θ?νt如下图所示:图4NAG更新的详细介绍可以参考IlyaSutskever的博士论文[9].假设动量因子参数γ=0.9,先计算当前梯度项,如上图蓝色小向量所示,然后加上动量项,这样得到一个大的跳跃,如中的蓝色大向量上图。这是一个仅包含动量项的更新。而NAG先做一个大跳跃(momentum项),然后加上一个用momentum计算的小电流梯度(上图红色向量)修正一下,得到上图绿色向量。这可以防止更新太快而无法提高响应能力,如RNN[8]中那样。通过以上两种方法,可以实现在每次学习过程中根据损失函数的斜率进行自适应更新,从而加速SGD的收敛。在下一步中,每个参数都需要根据参数的重要性进行自适应更新。AdagradAdagrad[3]也是一种基于梯度的优化算法,可以对每个参数适配不同的学习率,对稀疏特征获得较大的学习更新,对非稀疏特征获得较小的学习更新,因此该优化算法适用于处理具有稀疏特征数据。院长等。[4]发现Adagrad可以提高SGD的鲁棒性,Google用它来训练大规模神经网络(见图片识别Youtube视频中的猫)。彭宁顿等人。[5]使用Adagrad在GloVe中训练词嵌入。频繁出现的词被分配较小的更新,而不频繁出现的词被分配较大的更新。Adagrad的主要优点是可以为每个参数适配不同的学习率,一般手册设置为0.01。同时它的缺点是需要计算参数梯度序列的平方和,学习率趋势是不断衰减,最后达到一个很小的值。下面的Adadelta就是用来解决这个问题的。AdamAdaptiveMomentEstimation(Adam)也是一种不同参数的自适应学习率方法。与Adadelta和RMSprop的区别在于它计算历史梯度衰减的方法不同。它不使用历史方形衰减。其衰减方式与momentum类似,如下:mt=β1mt?1+(1?β1)gtvt=β2vt?1+(1?beta2)g2tmt和vt分别为梯度的加权均值和加权偏置方差,初始值为0向量。Adam的作者发现它们趋向于0向量(向量接近0),尤其是当衰减因子(衰减率)β1、β2接近1时。为了改善这个问题,对mt和vt进行bias-corrected:mt^=mt1?betat1vt^=vt1?betat2最后,Adam的更新方程为:θt+1=θt?ηvt^??√+?mt^Suggesteddefaultvalues在论文中:β1=0.9,β2=0.999,ε=10?8。论文中将Adam与其他几个自适应学习率进行对比,结果都比较好。算法可视化下面两张图对上述优化方法进行直观比较,如图:图5各SGD优化方法在losssurface上的表现从上图可以看出Adagrad、Adadelta和RMSprop可以迁移立即在损失表面上在正确的运动方向上实现快速收敛。而Momentum和NAG会导致偏差(off-track)。同时,NAG能够在出现偏差后快速纠正其航向,因为它基于梯度修正提高了响应能力。图6SGD优化方法在losssurface的鞍点处的表现Momentum和NAG一直在鞍点梯度为零的方向震荡,很难打破鞍点位置的对称性;Adagrad、RMSprop和Adadelta可以快速转移到梯度不为零的方向。从上面两张图可以看出,自适应学习率方法(Adagrad、Adadelta、RMSprop、Adam)在这些场景下的收敛速度和收敛性都比较好。如何选择SGD优化器如果你的数据特征稀疏,那么你最好使用自适应学习率SGD优化方法(Adagrad、Adadelta、RMSprop和Adam),因为你不需要在迭代过程中人为调整学习率调整。RMSprop是Adagrad的扩展,类似于Adadelta,但是改进版的Adadelta使用RMS自动更新学习率,不需要设置初始学习率。而Adam在RMSprop的基础上使用了动量和偏差校正。RMSprop、Adadelta和Adam在相似情况下的表现相似。Kingma[15]指出,受益于偏差校正,Adam比RMSprop略好,因为它的梯度在接近收敛时变得更稀疏。因此,Adam可能是目前最好的SGD优化方法。有趣的是,许多最近的论文使用原始的SGD梯度下降算法,对学习率(无动量项)进行简单的退火调整。现有研究表明:SGD可以收敛到最小值点,但可能比其他SGD耗时更长,且依赖于鲁棒的初值和学习率退火调整策略,容易陷入局部最小值点,甚至是鞍点。因此,如果您关心收敛速度或训练深度或复杂网络,您应该选择具有自适应学习率的SGD优化方法。并行和分布式SGD如果你正在处理非常大的数据集并且有可用的机器集群,那么并行或分布式SGD是一个非常好的选择,因为它可以大大提高速度。SGD算法的性质决定了它是串行的(step-by-step)。那么如何做异步处理就是一个问题。虽然serial可以保证收敛,但是如果训练集很大,速度是一个瓶颈。如果异步进行更新,可能会导致不收敛。下面将讨论如何执行并行或分布式SGD。并行一般是指同一台机器上的多核并行,分布式是指集群处理。HogwildNiu[23]提出了一种称为Hogwild的并行SGD方法。此方法在多个CPU时间上并行化。处理器通过共享内存访问参数,并且这些参数没有被锁定。它为每个cpu分配不重叠的部分参数(分配是互斥的),每个cpu只更新自己负责的参数。这种方法只适用于处理稀疏数据特征。这种方法几乎可以达到最佳的收敛速度,因为相同的信息不会在CPU之间被改写。DownpourSGDDownpourSGD是由Dean[4]提出的SGD的异步变体,用于DistBelief(GoogleTensorFlow的前身)。它在训练子集上同时训练模型的多个副本。这些副本将它们的更新发送到参数服务器(PS,parameterserver)。每个参数服务器只更新一部分互斥的参数,副本之间没有通信。因此,可能会导致参数发散,不利于收敛。SGDMcMahan和Streeter[12]的延迟容忍算法通过开发不仅适应过去的梯度而且更新延迟的延迟容忍算法扩展了AdaGrad。这种方法在实践中被证明是有效的。TensorFlowTensorFlow[13]是谷歌开源的一个大型机器学习库,其前身是DistBelief。已在大量移动设备或大规模分布式集群上使用,并在实践中得到检验。其分布式实现基于图计算,将图划分为多个子图,每个计算实体作为图中的一个计算节点,它们通过Rend/Receive进行通信。ElasticAveragingSGDZhang等人。[14]提出弹性平均SGD(EASGD),通过弹性力(存储参数的参数服务器中心)连接每个工作以异步更新参数。更多的SGD优化策略接下来介绍更多的SGD优化策略,进一步提升SGD的性能。此外,还有许多其他优化策略,可以在[22]中找到。为了使学习过程更加无偏,Shuffling和CurriculumLearning应该在每次迭代中对训练集中的样本进行随机洗牌。另一方面,在很多情况下,我们一步一步地解决问题,将训练集按有意义的顺序排列会提高模型的性能和SGD的收敛性。如何建立有意义的训练集排列被认为称为课程学习[16]。Zaremba和Sutskever[17]使用课程学习来训练LSTM来解决一些简单的问题,表明组合策略或混合策略优于按训练难度递增的顺序对训练集进行排序。(没看懂,不好)Batchnormalization为了便于训练,我们通常会按照0均值1方差来初始化参数。随着不断的训练,参数会进行不同程度的更新,这样这些参数就会失去0均值1方差的分布特性。随着网络结构的加深,这会减慢训练速度并放大参数变化。批量归一化[18]在每次小批量反向传播后将参数重新归一化为0均值和1方差。这允许更大的学习率和更少的参数初始化点的努力。批量归一化充当正则化,减少甚至消除对Dropout的需求。Earlystoppingontheverificationset如果损失函数在多次连续迭代中不再显着降低,那么训练应该提前终止,详情参见NIPS2015Tutorialslides,或者查看一些防止过拟合的方法。GradientnoiseGradientnoise[21]是在每次迭代的梯度计算中加入一个高斯分布的随机误差N(0,σ2t),即gt,i=gt,i+N(0,σ2t)的方差需要高斯误差待退火:σ2t=η(1+t)γ在梯度中加入随机误差会增加模型的鲁棒性,即使初始参数值选择不当,适合训练特别深的负责网络。原因是加入随机噪声更有可能跳过局部极值点,找到更好的局部极值点,这在深度网络中更为常见。总结上面介绍了梯度下降算法的三个框架,其中使用最广泛的是mini-batch梯度下降。然后我们重点介绍SGD的一些优化方法:Momentum、NAG、Adagrad、Adadelta、RMSprop和Adam,以及一些异步SGD方法。***,介绍了其他一些提高SGD性能的优化建议,例如:trainingsetrandomshufflingandcurriculumlearning(洗牌和课程学习)、batchnormalization、earlystopping和Gradientnoise。希望这篇文章为您提供了一些关于如何使用不同梯度优化算法的指导。如果有更多的优化建议或者方法希望大家提出来?或者你用什么技巧和方法来更好的训练SGD?可以一起交流吗?谢谢。
