1 优化算法的挑战
1.1 局部最小值
1.2 鞍点
损失函数容易陷入这两个点,而下一步的梯度很小或者为0,不能继续训练。
不过,对于神经网络这样非常复杂的高维非凸模型,已经有理论证明局部最小值与全局最小值非常接近,所以第一个问题目前不算太关注,鞍点才是更关注的问题。
2 梯度下降算法
w:参数;f(w):损失函数;η:学习率;t:迭代次数(iteration);▽:梯度
2.1 全量梯度下降(BGD,batch gradient descent)
Δwt=−▽f(wt)
wt+1=wt+η∗Δwt
这里的损失函数计算的是所有训练集数据的损失。
注:知乎上latex公式转不了,看起来很不好看,可以直接转csdn上看这篇文章:https://blog.csdn.net/weixin_41786536/article/details/100187562
优点:
一般来说收敛性能比较好
缺点:
1.训练速度慢
2.对内存和算力的要求比较高,只适合小数据集的学习,目前几乎不会使用这种方法。
2.2 随机梯度下降
计算公式与全量梯度下降相同,只不过这里一次只取一个样本而不是所有的样本。
优点:
训练速度快
缺点:
每次只有一个样本,随机性比较大,很可能不收敛,非常震荡,目前几乎不会使用这种方法
2.3 小批量随机梯度下降(mini-batch stochastc gradient descent,SGD)
注:其实SGD应该是上面那个单样本随机梯度下降的简称,但现在它已经不用了,所以默认小批量随机梯度下降为SGD
每次随机取相同数量的样本,这个样本数量为batch size.
优点:
结合了全量和单样本梯度下降的优点,很大程度上减弱了二者的劣势。
缺点(与下面的几种算法相比):
1.收敛比较慢;
2.容易陷入鞍点,尤其是在这些点会非常震荡,如果可视化出来,会呈现“之”字型来回曲折;
3.对学习率比较敏感,需要合适的学习率。
2.4 带有动量的随机梯度下降(SGD with momentum)
针对SGD容易陷入鞍点的问题,这里借助物理学的动量的概念(不如说是惯性更好理解)。当损失到达一个梯度很小的地方时,SGD可能就难以继续下去了,但是类比于小球在坑坑洼洼的空间滚动,当其落入局部洼地(局部最小值)或平缓地带(鞍点),借助于惯性还是可以继续走下去的,这个惯性是当前的速度决定的,于是在这里引入速度变量vt,且v0=0.
Δwt=−▽f(wt)
vt+1=γ∗vt+η∗Δwt
wt+1=wt+vt+1
其中γ为常量(一般为0.9),代表衰减(物理里的摩擦)系数。
优点:
1.加速收敛,减小震荡;
2.缓解SGD陷入鞍点的问题。
缺点(与后面的几种算法相比):
依然有震荡现象
2.5 NAG(Nesterov accelerated gradient)
在SGD with m 的算法中,用当前时刻的梯度来计算下一时刻的速度,这可能不是非常准确,NAG方法先估计下一时刻的梯度,再用这个梯度来求速度。
Δwt=−▽f(wt+γ∗vt)
vt+1=γ∗vt+η∗Δwt
wt+1=wt+vt+1
该方法没有对参数求梯度,这就没有计算损失函数,所以在实际中不好用,一般会对wt+γ∗vt进行换元,使得能够同时计算对参数的梯度。
优点:
震荡情况比SGD with m更小
总结以上五种优化算法,其特点是对所有的参数使用相同的学习率η。但实际上,神经网络参数非常多,在训练过程中有的参数会更新得很快,下降很快;有的参数更新得很慢,下降很慢。如果对不同的更新快的参数用小的学习率,对更新慢的参数用大的学习率,能够使得所有的参数都更新的比较好。于是出现了下面的自适应优化算法。
pytorch用一个优化器提供了SGD、SGD with m和NAG的算法的实现,由于全量和单样本梯度下降几乎不会用到,所以pytorch并不提供相关算法,而且这两种也可以通过改变batch_size的大小来实现。
optimizer = optim.SGD(params, lr=required, momentum=0, dampening=0, weight_decay=0, nesterov=False)
lr:学习率
momentum:动量大小,即γ,一般设为0.9,如果为0则不带动量项。
weight_decay:l2正则化的系数
nesterov:是否使用NAG优化算法
3 自适应优化算法
3.1 Adagrad
该方法引入了各个维度的参数的梯度的平方和∑i=1t(▽f(wi))2,其中i为参数的维度,ε为很小的正数(一般为1e-7),防止分母为0.
(wt+1)i=(wt)i−∑i=1t(▽f(wi))2+εη∗(▽f(wi))i
学习率实际上是∑i=1t(▽f(wi))2η,当这个维度的参数的历史梯度较大时,学习率较小,当其历史梯度较小时,学习率较大,这就实现了对不同的参数自适应分配学习率。
优点:
1.实现了不同参数的学习率自适应分配;
2.比较适合具有系数梯度的模型,这种模型的特点就是不同参数梯度差距比较大
缺点:
在训练的后期,梯度平方和的累积项可能会非常大,导致学习率很低,使得无法继续更新,早早就不能训练 (尤其是在非凸的神经网络模型上,而在凸函数模型上无此问题)
optimizer = optim.Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0, initial_accumulator_value=0)
指数加权移动平均(EWMA)
RMSProp算法用到了指数加权移动平均。
这是一种预测方法,对历史最近的一部分观察值赋予不同权重,并且靠近当前的值赋予大权重,远离当前的值赋予小权重,求得移动平均值,以该移动平均值来预测未来的值。
yt为要预测的变量,xt为相关的一个变量.
yt=γ∗yt−1+(1−γ)∗xt=(1−γ)∗xt+γ∗yt−1=(1−γ)∗xt+(1−γ)∗γ∗∗xt−1+γ2∗yt−2.......
换元n=1−γ1,则(1−n1)n=γ1−γ1
又n→+∞lim(1−n1)n=e−1<1
所以当γ接近于1时,γ1−γ1=e−1,于是可以忽略没有γ1−γ1低的项。
因此yt=(1−γ)∗i=0∑1−γ1−1vi∗xt−i
yt就是最近的1−γ1时间步的xt的加权平均。
3.2 RMSProp
与Adagrad相比,不再累积历史全部梯度的平方和,而是只取最近的过去的一段时间内的梯度信息
vt+1=γ∗vt+(1−γ)∗(▽f(wt+1))2
(wt+1)i=(wt)i−vt+1+εη∗(▽f(wi))i
vt+1是(▽f(wt+1))2的指数加权移动平均,即1−γ1个时间步长的梯度平方和的加权平均,越靠近当前时间的梯度的权重越大。
优点:
解决了Adagrad在训练后半期难以更新的问题
optimizer =optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)
3.3 AdaDelta
对RMSProp做了改进,引入了一个额外的变量Δxt,且x0=0,用Δxt代替学习率
vt+1=γ∗vt+(1−γ)∗(▽f(wt+1))2
(wt+1)i=(wt)i−vt+1+εΔxt+ε∗(▽f(wt))i
Δxt+1=γ∗Δxt+(1−γ)∗(vt+1+εΔxt+ε∗▽f(wt)i)2
Δxt+1是(vt+1+εΔxt+ε∗▽f(wt)i)2的指数加权移动平均。
优点:
1.不需要设定全局学习率,一下子解决了调参过程中最重要的参数的选择
2.同样解决了Adagrad在训练后半期难以更新的问题
optimizer = optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-06, weight_decay=0)
3.4 Adam
Adam相当于RMSProp+momentum,将两类方法的优势结合起来,集大成者。momentum使用了一阶梯度,RMSProp使用了二阶梯度,Adam二者均使用。
一阶梯度:mt=β1∗mt−1+(1−β)∗▽f(wt)
二阶梯度:vt=β2∗vt−1+(1−β2)∗(▽f(wt))2
偏差修正:在训练的前期,梯度权值之和比较小,需要将权值之和修正为1.
mt^=1−β1mt
vt^=1−β2vt
(wt+1)i=(wt)i−vt^+εη∗mt^
其中β1=0.9,β2=0.999
optimizer =optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)
4 总结
pytorch还提供了另外四种优化算法,由于用的不是很多,我也没有仔细研究。
以目前的情况来看,Adam成为了一个无脑选择,绝大数工程和研究都用Adam,但实际上有研究证明自适应的算法不一定能找到最优的结果,这个只能算是“懒人做法”。使用最原始的SGD再配合更合适的学习率下降和训练技巧能获得更好的结果,但这对算法和模型的理解要求比较高,毕竟深度学习的研究中优化算法往往不是考虑的最重要因素,更重要的是结构模型、数据等,所以使得Adam成为了万金油的选择。