梯度下降最常见的三种变形是BGD,SGD,MBGD,区别是梯度下降时用多少数据来计算目标函数的梯度。
批量梯度下降法(Batch Gradient Descent)更新参数时使用所有样本来进行更新。
随机梯度下降法(Stochastic Gradient Descent)更新参数时使用一个样本来进行更新。(但平时提到的SGD是指的Mini-batch SGD)。
小批量梯度下降法(Mini-batch Gradient Descent)对于m个样本的训练集,更新参数时使用n样本来进行更新,1<n<m:
批量梯度下降法和随机梯度下降法对比:
- 训练速度:随机梯度下降法由于每次仅仅采用一个样本来迭代,训练速度很快,而批量梯度下降法在样本量很大时,训练速度稍慢。
- 收敛速度:由于随机梯度下降法一次迭代一个样本,导致迭代方向变化很大,不能很快的收敛到局部最优解。
- 准确度:BGD对于凸函数可以收敛到全局最优解,对于非凸函数可以收敛到局部最优解。随机梯度下降法用于仅仅用一个样本决定梯度方向,导致解很有可能不是最优。SGD每次使用一个样本进行梯度计算方差比较大,目标函数波动比较大,但因此可以带来波动,使优化的方向从当前的局部极小值跳到另一个更好的局部极小值,学习率足够小时,SGD可以收敛到BGD一样的效果。
- 在线学习:BGD不支持在线更新,SGD支持在线更新。
- 内存占用:BGD数据集大到无法放入内存中时就不再适用。
小批量梯度下降法优势:结合了BGD与SGD的优点每一次利用一小批样本进行梯度的更新,既可以保证参数优化方向的稳定性,目标函数不会出现剧烈震荡,同时又能加快收敛速度,减小计算量。
缺点:
- 不能保证很好的收敛性,学习率太小,收敛速度会很慢,如果太大,损失函数在极小值处发生震荡甚至偏离。(可以先设定大一点的学习率,当两次迭代之间的变化低于某个阈值后,就减小学习率)。对于非凸函数,还要避免陷于局部极小值处,或者鞍点处,因为鞍点周围的error是一样的,所有维度的梯度都接近于0,SGD 很容易被困在这里。(会在鞍点或者局部最小点震荡跳动,因为在此点处,如果是训练集全集带入即BGD,则优化会停止不动,如果是mini-batch或者SGD,每次找到的梯度都是不同的,就会发生震荡,来回跳动。)
- SGD对所有参数更新时应用同样的 learning rate,如果我们的数据是稀疏的,我们更希望对出现频率低的特征进行大一点的更新。LR会随着更新的次数逐渐变小。
鞍点:一个光滑函数的鞍点邻域的曲线,曲面,或超曲面,都位于这点的切线的不同边。例如这个二维图形,像个马鞍:在x-轴方向往上曲,在y-轴方向往下曲,鞍点就是(0,0)。
为了应对上面的两点挑战就有了下面这些算法。
梯度下降法改进
在深度学习优化算法中,Momentum、RMSprop、Adam都涉及指数加权平均,它对趋势的一种刻画:
其中,θt是第t时刻的实际值,vt近似为t时刻1/(1-β)个θ的指数加权平均值,如当β=0.1时,vt代表了近10天的平均值;β=0.02时,vt近似代表了近50天的平均值,如图,红色是β=0.1、绿色是β=0.02时vt的变化,蓝色为真实值。其实,Vt不仅仅包含这个窗口内的值,它包含从t=0开始的所有值的加权平均。但之前的那些值都太小可以忽略。:
偏差修正(Bias correction):使用指数加权平均时每个最新数据值,依赖于以前的数据结果,在最开始的阶段,没有足够的数据组成window_size,最开始的平均值求出来都偏小。如果v0=0,那么v1 = (1-β)*θ1,特别地,如果β=0.9,那么v1 = 0.1 * θ1,这样导致v1会特别小,需要进行一定程度的修正, 具体的修正方法是对vt除以( 1-β^t),当t特别小的时候,可以起到较为明显的修正效果,但是当t变大时,分母接近于1,基本没有进行修正。Vt变为 Vt = Vt / (1 -
β ^ t)
。这样最开始t比较小,Vt就会大一些。随着t的增大β^t
逐渐变为0,Vt也就恢复成原来的样子了。
动量梯度下降法(Momentum)
基本思想:计算梯度的指数加权平均数,并利用该梯度更新权重。假设我们使用梯度下降法更新参数的路径如下图中蓝色的线所示:
SGD遇到某个维度梯度比较大会被大梯度方向带着一直往那边走,但是这不一定是最快的下降方向。表现出来就是犹豫不决,来回波动,收敛很慢。当在不同方向上梯度相差非常大,会来回波动。而且你不能用比较大的学习速率,可能会直接超出函数的范围。只能用较小的学习率,导致学习非常慢。
不关心之前的梯度是多少,如果本地梯度很小,它就会走的很慢。
会发生震荡而迟迟不能接近极小值。路径上下波动减慢了梯度下降法的速度,如果用更大的学习率,可能导致这种波动进一步加大,变成图中的紫色的线。因此,我们希望在纵轴上学习慢一点,消除这种上下的摆动,在横轴上,希望快速从左向右移动,移动到最小值处。
因为,观察可以发现纵轴分量时而向上,时而向下,取平均值就比较小了。而横轴分量一直是向右的,取平均值不会造成太大影响。这就是Momentum的原理。使用Momentum之后的效果:收敛更快
Momentum优化关注以前的梯度是多少,它将梯度当做加速度来使用,想象一个保龄球在光滑的表面滚下一个平缓的斜坡:最开始很慢,但速度会越来越快,为了模拟某种摩擦机制并防止动量增长多大,该算法引入了一个新的超参数β,简称动量,其必须设置在0(高摩擦),1(无摩擦)之间。一个推荐的值为0.9
指数加权平均数,可以反映近一段时间的趋势,在纵轴上的上下波动,使得平均值接近于0,而在横轴方向的平均值仍然较大。
公式 Momentum 通过加入 γ*vt−1 ,可以加速 SGD, 并且抑制震荡
其中,beta一般取值0.9,是指数加权平均的参数。alpha是学习速率。
优点
- 相比SGD,获得更快的收敛速度,减小震荡。如果在峡谷地区(某些方向较另一些方向上陡峭得多,常见于局部极值点),SGD会在这些地方附近振荡,从而导致收敛速度慢。这种情况下,动量(Momentum)便可以解决。动量在参数更新项中加上一次更新量(即动量项,相当于指数加权平均),
- 对方向一致的参数能够加速学习,对梯度改变方向的参数能够减少其更新,因此就是momentum能够在相关方向上加速学习,抑制振荡,从而加速收敛。
缺点
- 小球总是跟着斜坡走,没有考虑斜坡之后的变化趋势。到达最优解之后,还在继续大幅度更新,会越过最优解。这种情况相当于小球从山上滚下来时是在盲目地沿着坡滚,如果它能具备一些先知,例如快要上坡时,就知道需要减速了的话,适应性会更好。
- 增加了一个超参数来微调,一般情况下,推荐β为0.9
当我们将一个小球从山上滚下来时,没有阻力的话,它的动量会越来越大,但是如果遇到了阻力,速度就会变小。
加入的这一项,可以使得梯度方向不变的维度上速度变快,梯度方向有所改变的维度上的更新速度变慢,这样就可以加快收敛并减小震荡。
Adagrad
前面的算法在每次更新时,所有的参数学习速率都是相同的。Adagrad在每一个更新步骤中对于每一个模型参数θi使用不同的学习速率ηi,不同参数的学习速率是不同的。
Adagrad可以在每一个时刻t针对每一个参数使用不一样的学习速率。对于梯度比较大的地方使用较小的学习速率,对于梯度比较小的地方使用较大的学习速率。因此,AdaGrad非常适合于处理稀疏数据。
在每一个时刻t针对每一个参数的更新学习速率的调节是利用该参数的历史梯度信息来完成的。
G是对应的参数i到目前时刻t位置的梯度g的平方的和,epsilon为常数防止除0,一般为1e-7,1e-8。另外,如果不做开方操作,算法的效果会非常差。
优点:算法利用历史的梯度信息自动调节学习率,省去了人工调整学习速率的烦恼。
缺点:分母上对梯度平方的不断累加,累加值会随着训练不断的增加,使得学习速率不断的减小,直到无限小模型学习不到任何东西为止
RMSprop(root mean square prop)
为了降低Adagrad中学习速率衰减过快,在后期学习率可能无限小导致找不到有用的解的问题。RMSprop在Adagrad的基础上使用梯度平方的指数加权平均来代替累加梯度的平方和。变量s可以看做是最近1/(1-gamma)
个时刻的g平方的加权平均。参数的学习速率不会再一直下降了。
RMSprop 在mini batch中计算梯度g,然后对该梯度按照元素进行平方,记为,然后其进行指数加权平均,记为s:然后,和Adagrad一样,将每个参数的学习速率调整一遍,同样是按元素操作:
eta为初始学习速率。和Adagrad一样,每个参数都拥有自己的学习速率。
优点
- 参数学习率避免了在训练过程中一直下降的问题。
- RMSprop改进了Adagrad学习速率衰减过快的问题,同时其适用于处理非平稳。
缺点:依然依赖一个全局学习率。
对梯度较大的方向减小其学习速率,在梯度较小的方向上增加其学习速率。
假设纵轴代表参数b,横轴代表参数W:减缓?方向的学习,即纵轴方向,同时加快,至少不是减缓横轴方向的学习, RMSprop 算法可以实现这一点,将趋势变成下面的绿色的线:
Adam(Adaptive Moment Estimation)
Adam普适性强,效果好,速度也很快。Adam是组合了Momentum 和 RMSprop的优化算法。使用了Momentum动量变量v和RMSprop按梯度平方的指数加权平均变量s,并将它们初始化为0。在每次迭代中,首先计算梯度g,并递增迭代次数:
和Momentum类似,给定超参数(作者建议0.9),对梯度g进行指数加权平均得到动量变量v:
和RMSprop一样,给定超参数(作者建议0.999),对梯度g的平方进行指数加权平均得到s:
v和s可以近似看做最近1/(1-beta1)
个时刻梯度g和最近1/(1-beta2)
个时刻梯度g的平方的加权平均。假设beta1=0.9, beta2=0.999,v和s都初始化为0,那么在时刻t=1我们得到v=0.1g, s=0.001g
。可以看到,在迭代的初期,v和s可能过小而无法准确的估计g和g的平方。为此,Adam使用了Bias Correction:
在迭代初期t比较小,上式分母接近于0,相当于放大了v和s。在迭代后期,分母接近于1,偏差修正几乎不再有影响。它使用的是经过偏差修正后的指数加权平均数
接下来,Adam使用偏差修正后的v和s,重新调整每个参数的学习速率:
最后,更新参数:
总结
主要从两个出发点来提出优化算法:
- 调整更新的步长。比如SGD是梯度g;Momentum是g的指数加权平均;NAG是改进的Momentum,利用之后的梯度信息,提前刹车;
- 调整学习速率,每个参数都有自己的学习速率。比如Adagrad使用梯度平方的累加和调整;RMSprop使用梯度平方的指数加权平均调整;Adadelta使用梯度平方的指数加权平均,再加上参数改变量平方的指数加权平均来调整;
目标:优化收敛速度
优化算法 | 备注 |
---|---|
SGD | 最朴素的优化算法,梯度直接作为改变量 |
Momentum | 改进SGD: 使用梯度进行指数加权平均,作为新的改变量 |
NAG | 改进Momentum: 先用上一个时刻的改变量近似得到下一时刻的位置,再使用该时刻的梯度进行指数加权平均,作为最终的改变量 |
目标:每个参数都有自己的学习率
优化算法 | 备注 |
---|---|
Adagrad | 使用梯度平方累加和,再开根号作为分母;初始eta为分子;来调整学习速率 |
RMSprop | 改进Adagrad: 使用梯度平方的指数加权平均,再开根号作为分母;初始化eta为分子;来调整学习速率 |
Adadelta | 改进Adagrad: 使用梯度平方的指数加权平均,再开根号作为分母;参数改变量平方的指数加权平均作为分子;来调整学习速率 |
目标:更普适的方法
优化算法 | 备注 |
---|---|
Adam | 综合Momentum和RMSprop,并使用Bias Correction来修正。Momentum用来确定更新步长,RMSprop用来调节学习速率 |
参考:
- 吴恩达人工智能课程 https://mooc.study.163.com/smartSpec/detail/1001319001.htm
- 大佬的博客 http://ruder.io/optimizing-gradient-descent/index.html#batchgradientdescent4.
- LUON 动手深度学习 https://zh.gluon.ai/chapter_optimization/index.html
NAG
NAG全称是Nesterov accelerated gradient,它赋予Momentum感知接下来斜坡情况的能力。Momentum的小球只会沿着斜坡的方向走,即时到达了山底,再往前走又是上山的路了,它还在继续走,因为它不知道后面的情况到底是什么。NAG就是期望一个更聪明的小球,它可以预知之后关于坡度的一些情况。这样当它发现快到达山底的时候就减速,准备停下来了。
相比于Momentum唯一的变化,就是把在当前位置的求梯度,换成了对近似下一个位置求的梯度:
Momentum:
NAG:
在Momentum中,在t时刻,我们知道而且我们还可以求出目标函数在$\theta_t$处的导数,然后就可以更新动量然后,就可以更新参数了:
我们先把V_t的计算分成两部分:历史积累动量 + 目标函数在当前位置的梯度。可以看到,既考虑了历史上的动量(更新方向与速度),也考虑了在当前点的更新方向与速度。两者相加之后就得到了新的动量,用来新一轮的更新。可以看到这里起重要作用的是后面一部分:目标函数在当前位置的梯度,它表示当前位置坡度的走向,而这将会被算入总的动量中一点一点的决定我们小球的走向。
为了感知接下来的坡度走向,我们需要知道下一刻的位置从而可以计算出在该位置的梯度。但是,如果不先完成更新现在的位置,就无法得知下一刻的位置;可是我们想根据下一刻的位置来更新当前的位置。。。
怎么办,先有鸡还是先有蛋?
一般遇到这种问题的解决办法就是:近似
我们先近似的模拟向前走一步!怎么走那?原来的走法是历史动量 + 当前位置梯度。近似一下,我们只保留第一部分:历史动量。模拟走出这一步,作为小球的下一个位置。这个位置的梯度近似的包含了后面斜坡的走势信息,我们用这个去更新原来的动量,再去更新参数,就为当前时刻的更新引入了斜坡将来的信息了。
下面我们从图上来感受下:
第一段小蓝线:当前位置的梯度
第二段长蓝线:之前积累的动量
第一段棕线:先近似的滚一下,先把历史动量滚出来!
第一段红线:小球近似滚之后,新位置的梯度
第一段绿线:NAG最终的更新动量
蓝色是 Momentum 的过程,会先计算当前的梯度,然后在更新后的累积梯度后会有一个大的跳跃。
而 NAG 会先在前一步的累积梯度上(brown vector)有一个大的跳跃,然后衡量一下梯度做一下修正(red vector),这种预期的更新可以避免我们走的太快。
NAG 可以使 RNN 在很多任务上有更好的表现。
目前为止,我们可以做到,在更新梯度时顺应 loss function 的梯度来调整速度,并且对 SGD 进行加速。
我们还希望可以根据参数的重要性而对不同的参数进行不同程度的更新。
总结一下:
- NAG通过引入下一个时刻位置的梯度信息,来感知之后的坡度,从而调整我们的更新步伐。
- 先按照历史累积动量走一次,新的位置近似认为是下一个时刻的位置;然后求出下一个时刻的位置的梯度;
- 也就是把原位置的梯度换成了下一个时刻位置的梯度。下一个时刻位置是通过走历史动量近似得到的。
优点
-
参数的更新,在Momentum的基础上又考虑了斜坡的走向。“提前刹车”
到目前位置,我们利用累积动量加速了SGD,并且根据感知斜坡走向来调整我们的更新。
我们还想做:根据每个参数的重要性,改善不同参数的学习速率,重要的大一些,不重要的小一些。
不同参数赋予不同更新幅度: AdaGrad、Adadelta、RMSprop。
Adadelta
和RMSprop一样,另外一个解决Adagrad学习率只降不升问题的算法是Adadelta。更有意思的是,Adadelta没有学习率这个超参数。
Adadelta和RMSprop一样,累积量s不再使用梯度g平方直接累加。而是使用梯度g平方的指数加权平均来计算s。首先,在mini batch上计算梯度g;然后,使用梯度g的平方来做指数加权平均:
然后,计算当前需要迭代的参数的变换量g’:
上式中的dela_x初始化为0,并用当前迭代变换量g’的平方的指数加权平均来更新:
最后,更新参数:
优点
- 同样解决了Adagrad的学习率不断下降的问题
- 同时没有了超参数学习速率。使用参数改变量的平方的指数加权平均,然后开根号来代替