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