【转载】几种梯度下降方法和优化方法对比
我们在训练神经网络模型时,最常用的就是梯度下降,这篇博客主要介绍下几种梯度下降的变种(mini-batch gradient descent和stochastic gradient descent),关于Batch gradient descent(批梯度下降,BGD)就不细说了(一次迭代训练所有样本),因为这个大家都很熟悉,通常接触梯队下降后用的都是这个。这里主要介绍Mini-batch gradient descent和stochastic gradient descent(SGD)以及对比下Batch gradient descent、mini-batch gradient descent和stochastic gradient descent的效果。
Part I: 梯度下降方法对比——Batch gradient descent、Mini-batch gradient descent 和 stochastic gradient descent
一、Batch gradient descent
Batch gradient descent 就是一次迭代训练所有样本,就这样不停的迭代。整个算法的框架可以表示为:
1 | X = data_input |
Batch gradient descent的优点是理想状态下经过足够多的迭代后可以达到全局最优。但是缺点也很明显,就是如果你的数据集非常的大(现在很常见),根本没法全部塞到内存(显存)里,所以BGD对于小样本还行,大数据集就没法娱乐了。而且因为每次迭代都要计算全部的样本,所以对于大数据量会非常的慢。
二、stochastic gradient descent
为了加快收敛速度,并且解决大数据量无法一次性塞入内存(显存)的问题,stochastic gradient descent(SGD)就被提出来了,SGD的思想是每次只训练一个样本去更新参数。具体的实现代码如下:
1 | X = data_input |
如果我们的数据集很大,比如几亿条数据,num_iterationsnum_iterations 基本上 设置1,2,(10以内的就足够了)就可以。但是SGD也有缺点,因为每次只用一个样本来更新参数,会导致不稳定性大些(可以看下图(图片来自ng deep learning 课),每次更新的方向,不想batch gradient descent那样每次都朝着最优点的方向逼近,会在最优点附近震荡)。因为每次训练的都是随机的一个样本,会导致导致梯度的方向不会像BGD那样朝着最优点。
注意:代码中的随机把数据打乱很重要,因为这个随机性相当于引入了“噪音”,正是因为这个噪音,使得SGD可能会避免陷入局部最优解中。
下面来对比下SGD和BGD的代价函数随着迭代次数的变化图:
可以看到SGD的代价函数随着迭代次数是震荡式的下降的(因为每次用一个样本,有可能方向是背离最优点的)
三、Mini-batch gradient descent
mini-batch gradient descent 是batch gradient descent和stochastic gradient descent的折中方案,就是mini-batch gradient descent每次用一部分样本来更新参数,即 batch_sizebatch_size。因此,若batch_size=1batch_size=1 则变成了SGD,若batch_size=mbatch_size=m 则变成了batch gradient descent。
batch_sizebatch_size通常设置为2的幂次方,通常设置2,4,8,16,32,64,128,256,5122,4,8,16,32,64,128,256,512(很少设置大于512)。因为设置成2的幂次方,更有利于GPU加速。现在深度学习中,基本上都是用 mini-batch gradient descent,(在深度学习中,很多直接把mini-batch gradient descent(a.k.a stochastic mini-batch gradient descent)简称为SGD,所以当你看到深度学习中的SGD,一般指的就是mini-batch gradient descent)。下面用几张图来展示下mini-batch gradient descent的原理(图片来自ng deep learning 课):
下面直接给出mini-batch gradient descent的代码实现:
- 首先要把训练集分成多个batch
1 | # GRADED FUNCTION: random_mini_batches |
- 下面是在model中使用mini-batch gradient descent 进行更新参数
1 | seed = 0 |
下面来看mini-batch gradient descent 和 stochastic gradient descent 在下降时的对比图:
下面是mini-batch gradient descent的代价函数随着迭代次数的变化图:
从图中能够看出,mini-batch gradient descent 相对SGD在下降的时候,相对平滑些(相对稳定),不像SGD那样震荡的比较厉害。mini-batch gradient descent的一个缺点是增加了一个超参数 batch_sizebatch_size ,要去调这个超参数。 以上就是关于batch gradient descent、mini-batch gradient descent 和 stochastic gradient descent的内容。
Part II: 深度学习中优化方法——momentum、Nesterov Momentum、AdaGrad、Adadelta、RMSprop、Adam
我们通常使用梯度下降来求解神经网络的参数,关于梯度下降前面一篇博客已经很详细的介绍了(几种梯度下降方法对比)。我们在梯度下降时,为了加快收敛速度,通常使用一些优化方法,比如:momentum、RMSprop和Adam等。这篇博客主要介绍:
- 指数加权平均(Exponentially weighted average)
- 带偏差修正的指数加权平均(bias correction in exponentially weighted average)
- Momentum
- Nesterov Momentum
- Adagrad
- Adadelta
- RMSprop
- Adam
在介绍这几种优化方法之前,必须先介绍下 指数加权平均(Exponentially weighted average) ,因为这个算法是接下来将要介绍的三个算法的重要组成部分。
一、 指数加权平均(Exponentially weighted average)
指数加权平均是处理时间序列的常用工具,下面用一个例子来引入指数加权平均的概念。下图是一个180天的气温图(图片来自ng Coursera deep learning 课):
如果我们想找一条线去拟合这个数据,该怎么去做呢。我们知道某一天的气温其实和前几天(前一段时间)相关的,并不是一个独立的随机事件,比如夏天气温都会普遍偏高些,冬天气温普遍都会低一些。我们用\([θ1,θ2,...,θn]\)表示第1,2,…,n天的气温,我们有:
根据上面的公式我们能够画出这条线,如下图所示(图片来自ng deep learning课):
下面来正式的看一下 指数加权平均(Exponentially weighted average) 的定义:
\[V_t=βV_(t−1)+(1−β)θ_t\]
可以认为 \(V_t\) 近似是 \(\frac{1}{1−β}\) 天的平均气温,所以上面公式设置了 \(β=0.9\) ,当 \(t>10\) 时,则可以认为 \(V_t\) 近似是它前10天的平均气温。 比如,按照上面的公式,我们计算 \(V_{100}\) ,
\[ \begin{aligned}V_{100}&=0.1θ_{100}+0.9V_{99} \\ &=0.1θ_{100}+0.9(0.9V_{98}+0.1θ_{99}) \\ &=0.1θ_{100}+0.9∗0.1θ_{99}+0.9^2V_{98} \\ &=0.1θ_{100}+0.9∗0.1θ_{99}+0.9^2(0.9V_{97}+0.1θ_{98}) \\ &=0.1θ_{100}+0.9∗0.1θ_{99}+0.9^2∗0.1θ_{98}+0.9^3V_{97} \\ &=0.1θ_{100}+0.9∗0.1θ_{99}+0.9^2∗0.1θ_{98}+0.9^3∗0.1θ_{97}+0.9^4V_{96} \\ &=.... \\ &=0.1θ_{100}+0.9∗0.1θ_{99}+0.9^2∗0.1θ_{98}+0.9^3∗0.1θ_{97}+...+0.9^{99}∗0.1θ_1+0.9^{100}V_0 \end{aligned} \]
从上面的公式能够看出,实际上是个指数衰减函数。\(0.9^{10} ≈ 0.35 ≈ \frac{1}{e}\),所以这个就解释了上面的 \(\frac{1}{1−β}\)。
(ps:关于这一点为什么要用0.35及 \(\frac{1}{e}\),我是不太明白的,搜了下资料也没找到更好的资料,希望路过的有知道的大神,在下面评论交流。)
下面再看下 β 取不同值时,曲线的拟合情况(图片来自ng deep learning课):
从上图能够看出:
当 β 较大时(β=0.98 相当于每一点前50天的平均气温),曲线波动相对较小更加平滑(绿色曲线),因为对很多天的气温做了平均处理,正因为如此,曲线还会右移。 同理,当 β 较小时(β=0.5 相当于每一点前2天的平均气温),曲线波动相对激烈,但是它可以更快的适应温度的变化。 下面直接看实现指数加权平均(Exponentially weighted average)的伪代码:
1 | V0 = 0 |
关于指数加权平均的优缺点: 当 β=0.9β=0.9 时,我们可以近似的认为当前的数值是过去10天的平均值,但是显然如果我们直接计算过去10天的平均值,要比用指数加权平均来的更加准确。但是如果直接计算过去10天的平均值,我们要存储过去10天的数值,而加权平均只要存储Vt−1Vt−1一个数值即可,而且只需要一行代码即可,所以在机器学习中用的很多。
二、带偏差修正的指数加权平均(bias correction in exponentially weighted average)
上一节中介绍了指数加权平均,用的公式是: \[V_t=βV_{t−1{+(1−β)θ_t\]
我们想得到下图中的“绿线”,实际上我们得到的是下图中的“紫线”。对比这两条线,能够发现在“紫线”的起点相比“绿线”非常的低,也就是说 指数加权平均 不能很好地拟合前几天的数据,因此需要 偏差修正,解决办法就是,再令\(V_t=\frac{V_t}{1-\beta^t}\). 因此, 带偏差修正的指数加权平均(bias correction in exponentially weighted average) 公式为:
\[V_t=βV_{t−1}+(1−β)θ_t\] \[V_t=\frac{V_t}{1-\beta^t}\]
当 \(t\to\infty\) 时,\(\beta_t\to0\),这样在后期 Vt 就和 没有修正的指数加权平均一样了.
在机器学习中,多数的指数加权平均运算并不会使用偏差修正。因为大多数人更愿意在初始阶段,用一个捎带偏差的值进行运算。不过,如果在初试阶段就开始考虑偏差,指数加权移动均值仍处于预热阶段,偏差修正可以做出更好的估计。
介绍完上面的准备知识,下面正式进入正题。
三、动量(momentum)
我们使用SGD(stochastic mini-batch gradient descent,深度学习中一般称之为SGD)训练参数时,有时候会下降的非常慢,并且可能会陷入到局部最小值中,如下图所示(图片来自李宏毅《一天搞懂深度学习》)。
下面给出动量(momentum)的公式:
动量的引入就是为了加快学习过程,特别是对于高曲率、小但一致的梯度,或者噪声比较大的梯度能够很好的加快学习过程。动量的主要思想是积累了之前梯度指数级衰减的移动平均(前面的指数加权平均),下面用一个图来对比下,SGD和动量的区别:
区别: SGD每次都会在当前位置上沿着负梯度方向更新(下降,沿着正梯度则为上升),并不考虑之前的方向梯度大小等等。而动量(moment)通过引入一个新的变量 vv 去积累之前的梯度(通过指数衰减平均得到),得到加速学习过程的目的。
最直观的理解就是,若当前的梯度方向与累积的历史梯度方向一致,则当前的梯度会被加强,从而这一步下降的幅度更大。若当前的梯度方向与累积的梯度方向不一致,则会减弱当前下降的梯度幅度。
用一个图来形象的说明下上面这段话(图片来自李宏毅《一天搞懂深度学习》):
下面给出动量(momentum)的公式:
\[\begin{aligned} &V_{dW}=βV_{dW}+(1−β)dW \\ &V_{db}=βV_{db}+(1−β)db \\ &W=W-\alpha V_{dW}, b=b-\alpha V_{db}\end{aligned}\]
β的值越大,则之前的梯度对现在的方向影响越大。β一般取值为0.5,0.9,0.99。ng推荐取值0.9。这个公式是ng的在Coursera课上给出的。
关于这个公式目前主要有两种形式,*一种是原论文里的公式:(原论文:On the importance of initialization and momentum in deep learning)
使用这个公式的有: 1、 goodfellow和bengio的《deep learning》书:(8.3.2节 momentum) 2、 CS231N课(链接:http://cs231n.github.io/neural-networks-3/): 3、 keras: 4、Intel的chainer框架
*第二种是类似ng给的:
1、论文《An overview of gradient descent optimization algorithms》: 2、TensorFlow源码里的版本(链接:): 3、pytorch源码里的版本(链接: ):
4、当时修hinton的nn课时,hinton给出的版本与TensorFlow这个是一样的,链接见我的博客:
这个版本是和ng在课上给出的版本是一致的,只不过会影响下learning_rate的取值。 综上,应该是哪个版本都可以。大家根据自己的喜好使用。
由于我个人更喜欢ng/TensorFlow/pytorch那个形式,下面无论是代码还是伪代码我都会统一用ng的版本,下面给出momentum的伪代码:
1 | initialize VdW = 0, vdb = 0 //VdW维度与dW一致,Vdb维度与db一致 |
具体的代码实现为:
1 | def initialize_velocity(parameters): |
四、Nesterov Momentum
Nesterov Momentum是对Momentum的改进,可以理解为nesterov动量在标准动量方法中添加了一个校正因子。用一张图来形象的对比下momentum和nesterov momentum的区别(图片来自:http://ttic.uchicago.edu/~shubhendu/Pages/Files/Lecture6_pauses.pdf):
公式:
\[\begin{aligned}\\ v_{t+1}&=\mu v_t - \varepsilon \nabla f(\theta_t + \mu v_t) \\ \theta_{t+1} &= \theta_{t} + v_{t+1}\end{aligned}\]
但是,我们仔细观察这个算法,你会发现一个很大的缺点,这个算法会导致运行速度巨慢无比,因为这个算法每次都要计算\(\nabla_{\theta} \sum_i{L(f({x}^{i}; \theta + \alpha v), y^{i})}\),这个相当于又要把fp、bp走一遍。这样就导致这个算法的运行速度比momentum要慢两倍,因此在实际实现过程中几乎没人直接用这个算法,而都是采用了变形版本。
变形版本:把上面的公式第一步代入第二步可以得到相同的公式:\(w = w + {\beta}^2 V - (1+\beta)\alpha * \nabla\)。我这里直接使用keras风格公式了,其中 β 是动量参数,α 是学习率。
\[\begin{aligned} V_{\mathrm{d}W} &= \beta V_{\mathrm{d}W} - \alpha \mathrm{d}W \\ V_{\mathrm{d}b} &= \beta V_{\mathrm{d}b} - \alpha \mathrm{d}b \\ W &= W + \beta V \mathrm{d}W - \alpha \mathrm{d}W \\ b &= b + \beta V \mathrm{d}b - \alpha \mathrm{d}b \\\end{aligned}\]
这样写的高明之处在于,我们没必要去计算 \(∇_θ\) 了,直接利用当前已求得的 \(\mathrm{d}θ\) 去更新参数即可。这样就节省了一半的时间。
五、AdaGrad(Adaptive Gradient)
通常,我们在每一次更新参数时,对于所有的参数使用相同的学习率。而AdaGrad算法的思想是:每一次更新参数时(一次迭代),不同的参数使用不同的学习率。AdaGrad 的公式为:
关于AdaGrad,goodfellow和bengio的《deep learning》书中对此的描述是:在凸优化中,AdaGrad算法具有一些令人满意的理论性质。但是,在实际使用中已经发现,对于训练深度神经网络模型而言,从训练开始时累积梯度平方会导致学习率过早过量的减少。AdaGrad算法在某些深度学习模型上效果不错,但不是全部。
六、Adadelta
Adadelta是对Adagrad的改进,主要是为了克服Adagrad的两个缺点(摘自Adadelta论文《AdaDelta: An Adaptive Learning Rate Method》):
- the continual decay of learning rates throughout training
- the need for a manually selected global learning rate
为了解决第一个问题,Adadelta只累积过去 w 窗口大小的梯度,其实就是利用前面讲过的指数加权平均
为了解决第二个问题,Adadelta最终的公式不需要学习率 α。Adadelta的具体算法如下所示(来自论文《AdaDelta: An Adaptive Learning Rate Method》)
七、RMSprop(root mean square prop)
RMSprop是hinton老爷子在Coursera的《Neural Networks for Machine Learning》lecture6中提出的,这个方法并没有写成论文发表(不由的感叹老爷子的强大。。以前在Coursera上修过这门课,个人感觉不算简单)。同样的,RMSprop也是对Adagrad的扩展,以在非凸的情况下效果更好。和Adadelta一样,RMSprop使用指数加权平均(指数衰减平均)只保留过去给定窗口大小的梯度,使其能够在找到凸碗状结构后快速收敛。直接来看下RMSprop的算法(来自lan goodfellow 《deep learning》)
在实际使用过程中,RMSprop已被证明是一种有效且实用的深度神经网络优化算法。目前它是深度学习人员经常采用的优化算法之一。keras文档中关于RMSprop写到:This optimizer is usually a good choice for recurrent neural networks.
八、Adam(Adaptive Moment Estimation)
Adam实际上是把momentum和RMSprop结合起来的一种算法,算法流程是(摘自adam论文):
实践表明,Adam算法在很多种不同的神经网络结构中都是非常有效的。
八、该如何选择优化算法
介绍了这么多算法,那么我们到底该选择哪种算法呢?目前还没有一个共识,schaul et al 在大量学习任务上比较了许多优化算法,结果表明,RMSprop,Adadelta和Adam表现的相当鲁棒,不分伯仲。Kingma et al表明带偏差修正的Adam算法稍微好于RMSprop。总之,Adam算法是一个相当好的选择,通常会得到比较好的效果。
下面是论文《An overview of gradient descent optimization algorithms》对各种优化算法的总结:
In summary, RMSprop is an extension of Adagrad that deals with its radically diminishing learning rates. It is identical to Adadelta, except that Adadelta uses the RMS of parameter updates in the numerator update rule. Adam, finally, adds bias-correction and momentum to RMSprop. Insofar, RMSprop, Adadelta, and Adam are very similar algorithms that do well in similar circumstances. Kingma et al. [10] show that its bias-correction helps Adam slightly outperform RMSprop towards the end of optimization as gradients become sparser. Insofar, Adam might be the best overall choice
作者:天泽28 来源:CSDN 原文: - https://blog.csdn.net/u012328159/article/details/80252012 - https://blog.csdn.net/u012328159/article/details/80311892 版权声明:本文为博主原创文章,转载请附上博文链接!