深度学习框架目前基本上都是使用梯度下降算法及其变种进行优化,通常意义上大家会认为原始的梯度下降算法是最弱的,但事实上并非如此。
很多的论文依然采用了原始的梯度下降算法而不采用更高级的算法,今天就以一个实践例子来给大家展示一下各类算法的比较,所有内容参考文章【An overview of gradient descent optimization algorithms】。
01梯度下降算法
本文目标不是为了从零开始讲清楚优化算法,所以有些细节和基础就略过。
梯度下降算法,即通过梯度的反方向来进行优化,批量梯度下降(Batch gradient descent)用公式表述如下:
写成伪代码如下:
for i in range(nb_epochs):
params_grad = evaluate_gradient(loss_function, data, params)
params = params - learning_rate * params_grad
上面的梯度下降算法用到了数据集所有的数据,这在解决实际问题时通常是不可能,想想imagenet1000有100G以上的图像,内存装不下,速度也很慢。
我们需要在线能够实时计算,于是一次取一个样本,就有了随机梯度下降(Stochastic gradient descent),简称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
sgd方法缺点很明显,梯度震荡,所以就有了后来大家常用的小批量梯度下降算法(Mini-batch gradient descent)。
伪代码如下:
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
下面我们要形成共识,说sgd算法,实际上指的就是mini-batch gradient descent算法,没有人会去一次拿整个数据集或者一个样本进行优化。
当然还是要总结一下SGD算法的毛病。
(1)学习率大小和策略选择困难,想必动手经验丰富的自然懂。
(2)学习率不够智能,对所有参数一视同仁。
(3)同时面临局部极值和鞍点的问题。
可能有同学不知道鞍点怎么回事,简单来说,它就是在某一些方向梯度下降,另一些方向梯度上升,形状似马鞍,抄来一张图如下,红点就是鞍点。
02梯度下降方法改进
1.1 动量法
改进方法那么多,我觉得真正最有用的就是它。
前面说了梯度下降算法是按照梯度的反方向进行参数更新,但是刚开始的时候梯度不稳定呀,方向改变是很正常的,梯度就是抽疯了似的一下正一下反,导致做了很多无用的迭代。
而动量法做的很简单,相信之前的梯度。如果梯度方向不变,就越发更新的快,反之减弱当前梯度。
画成图就是这样。
效果对比就这意思。
1.2 nesterov动量法
仍然是动量法,只是它要求这个下降更加智能。
既然动量法已经把前一次的梯度和当前梯度融合,那何不更进一步,直接先按照前一次梯度方向更新一步将它作为当前的梯度,看下面的式子就明白了。
如上图,自己领会。
nesterov的好处就是,当梯度方向快要改变的时候,它提前获得了该信息,从而减弱了这个过程,再次减少了无用的迭代。
1.3 Adagrad法
思路很简单,不同的参数是需要不同的学习率的,有的要慢慢学,有的要快快学,所以就给了一个权重咯,而且是用了历史上所有的梯度幅值。
1.4 Adadelta与Rmsprop
adagrad用了所有的梯度,问题也就来了,累加的梯度幅值是越来越大的。导致学习率前面的乘因子越来越小,后来就学不动了呀。
adadelta就只是动了一丢丢小心思,只累加了一个窗口的梯度,而且计算方法也更有效。
并且,将学习率用前一时刻参数的平方根来代替,最终更新算法变成了这样。
把γ变成0.9,就是RMSprop方法了,这个方法在Hinton的课程中使用的,没有发表成论文,毕竟有adadelta了,没有发表必要。与adadelta另一个不同是还是需要学习率的,建议0.001。
1.5 adam法
最后就是这个adam算法,作为最晚出现的,当然是集大成者。
adam对梯度的一阶和二阶都进行了估计与偏差修正,使用梯度的一阶矩估计和二阶矩估计来动态调整每个参数的学习率。
看出来了吧,与adadelta和rmsprop如出一辙,与momentum sgd也颇为相似。上面的式子根据梯度对参数更新的幅度进行了动态调整,所以adam对学习率没有那么敏感。
1.6 adamax
将adam使用的二阶矩变成更高阶,就成了adamax算法。
1.7 nadam法
nag加上adam,就成了nadam方法,即带有动量项的adam,所以形式也很简单,如下,可以将其分别与adam算法和nag算法的式子比较看看。
说了这么多,对上面各种方法从一个鞍点开始优化,表现如何的预期效果图如下。
理论上,就是上面这样的。文章作者会告诉你对于数据稀疏的问题,用自适应学习率算法就好了,而且使用人家推荐的参数就好。其中,adam会最佳。
03各种方法表现究竟如何
上面说了这么多理论,分析起来头头是道,各种改进版本似乎各个碾压SGD算法,然而根据笔者经验,仔细调优后的SGD算法绝对吊打其他算法。
实验结果看下图,基础任务模型和数据集上次已经说过,此处不再赘述。
所有方法都采用作者们的默认配置,并且进行了比较,不好的结果就不拿出来了。
- nesterov方法,与sgd算法同样的配置。
- adam算法,m1=0.9,m2=0.999,lr=0.001。
- rms算法,rms_decay=0.9,lr=0.001。
- adagrad,adadelta学习率不敏感。