深度学习之优化算法

  • Post author:
  • Post category:其他



入门小菜鸟,希望像做笔记记录自己学的东西,也希望能帮助到同样入门的人,更希望大佬们帮忙纠错啦~侵权立删。


目录


一、优化算法与深度学习


1、优化算法对于深度学习的意义


2、优化算法与深度学习的关系


3、优化算法在深度学习中的主要挑战


(1)局部最小值


(2)鞍点


二、深度学习中优化算法的常见算法


1、梯度下降


(1)批量梯度下降(BGD)


(2)随机梯度下降(SGD)


(3)小批量随机梯度下降(MBGD)——最常用的梯度下降算法


2、动量法


(1)背景与引入原因


(2)原理


(3)优缺点


3、AdaGrad算法(自适应梯度)


(1)背景与引入原因


(2)原理


(3)优缺点


4、RMSProp算法(均方根传递算法)


(1)背景与引入原因


(2)原理


(3)优缺点


5、AdaDelta算法


(1)原理


(2)优缺点


6、Adam算法


(1)原理


(2)优缺点


三、总结


四、常用优化算法的代码调库实现


一、优化算法与深度学习

1、优化算法对于深度学习的意义

深度学习中的优化问题通常指的是:寻找模型上的一组参数θ,它能显著地降低(最小化)代价函数J(θ),通常也有采取最大化问题转化为最小化问题再优化的方法。

优化算法直接影响模型的训练效率。

2、优化算法与深度学习的关系

优化算法的目的:降低训练误差;

深度学习的目的:降低泛化误差;

因此深度学习需要优化算法+应对过拟合。

3、优化算法在深度学习中的主要挑战

🌳解析解和数值解🌳

解析解:通过严格的公式所求得的解,即问题的真正解(给出任意的自变量就可以求出其因变量);

数值解:指给出一系列对应的自变量,采用数值方法求出的解,即近似解,如通过有限元的方法、 数值逼近、插值的方法得到的解(别人只能利用数值计算的结果,而不能随意给出自变量并求出计算值)

由于深度学习中绝大多数目标函数非常复杂,难以找到或者不存在解析解,因此需要使用基于数值方法的优化算法来找到近似解。

两个主要挑战:

(1)局部最小值

局部最小值:在某一区域内,函数的取值达到了最小,但如果将这个区域扩展到定义域上来,那么这个局部最小值就不一定是最小的。

全局最小值:在定义域内,函数值最小。

深度学习模型的目标函数中可能有若干个局部最小值。当优化问题的数值解在局部最优解附近时,目标函数有关解的梯度接近或变成0,难以跳出局部最小值这个小区域,最终迭代求得的数值解可能只能让目标函数局部最小化而非全局最小化。


跳出局部最小值附近区域的方法见下:

A、使用随机梯度下降算法,有机会跳出“局部最小值”,但也可能跳出“全局最小值”;

B、使用“模拟退火”算法,模拟退火每一步都在以一定的概率接受比当前解更差的结果,在每次迭代过程中,接受“次优解”的概率要随着时间的推移而逐渐降低,从而保证算法的稳定;

C、以多组不同的参数值初始化多个神经网络,按照标准的方法训练之后,以其中误差最小的解作为最终的参数;

D、借助动量法,冲出局部最小值;

E、根据经验不断调整学习率

(2)鞍点

鞍点:一个不是局部极值点的驻点(eg:
y=x^{3}
的(0,0)或者在某些方向上是局部最小,在另某些方向上是局部最大);

驻点:函数在一点处的一阶导数为0(一阶导数为0的点不一定是极值)

如果当前的数值解在鞍点附近,也会使目标函数有关解的梯度接近或变成0,难以跳出鞍点附近这个小区域,最终迭代求得的数值解难以全局最小化。

🌳如何证明一个点为鞍点🌳

Hessian 矩阵(海森矩阵)是一个凸函数,并且是正半定的。通过这一属性,我们可以测试临界点 x =(x1,x2,…,xn)是局部最大值,或者是局部最小值还是鞍点。如下所示:

如果 H 在 x 处为正定矩阵时,则函数 f 在 x 处有一个局部极小值;

如果 H 在 x 处为负定矩阵时,则函数 f 在 x 处有一个局部极大值;

如果 H 在 x 处为不定矩阵时(即同时有正特征值和负特征值),则函数 f 在 x 处为鞍点。

海森矩阵如下所示:

目标函数的鞍点通常比局部最小值更常见。


跳出鞍点附近区域的方法见下:

A、随机梯度算法;

B、偶尔进行随机扰动


二、深度学习中优化算法的常见算法

1、梯度下降

在训练模型之前会先定义一个代价函数,然后通过不停迭代训练数据利用梯度下降算法来减少每次迭代的误差,从而使得代价函数最小化。梯度下降算法主要包括三种:随机梯度下降、batch梯度下降和mini-batch梯度下降。最常用的是mini-batch梯度下降算法。

(1)批量梯度下降(BGD)

最原始的梯度下降法是计算训练数据集中所有样本的损失函数的平均。

优点:

A、一次迭代是对所有样本进行计算,此时利用矩阵进行操作,实现了并行;

B、由全数据集确定的方向能够更好地代表样本总体,从而更准确地朝向极值所在的方向。当目标函数为凸函数时,BGD一定能够得到全局最优。

缺点:

显而易见,当训练集样本数很高时,计算开销会很大。

(2)随机梯度下降(SGD)

为了减少计算开销,SGD——在训练模型的时候,每次迭代只从训练数据中随机抽取一个样本来计算梯度更新模型的参数。

优点:

由于不是在全部训练数据上的损失函数,而是在每轮迭代中,随机优化某一个样本的损失函数,这样每一轮参数的更新速度大大加快。

缺点:

A、准确度下降。由于即使在目标函数为强凸函数的情况下,SGD仍旧无法做到线性收敛;

B、由于单个样本并不能代表全体样本的趋势,SGD很容易受到噪声的干扰,训练时间长,代价函数最终会围绕全局最小值或者局部极小值震荡;

C、不易于并行实现

(3)小批量随机梯度下降(MBGD)——最常用的梯度下降算法

为了避免SGD和BGD中存在的问题,小批量梯度下降应运而生——对每个批次中的n个训练样本(随机取样获取——重复采样或不重复采样),其中n常为32、64、128、256、512等,小批量梯度下降只执行一次更新。

优点:

A、通过矩阵运算,每次在一个batch上优化神经网络参数的速度不会比单个数据慢太多;

B、每次使用一个batch可以大大减小收敛所需要的迭代次数,同时可以使收敛到的结果更加接近梯度下降的效果,并且收敛速度相对较快,虽然在迭代的时候不能保证每次迭代都向全局最小值进行收敛,但是整体的趋势是向全局最小值进行收敛的。

C、可实现并行化。

缺点:batch size的不当选择可能会带来一些问题:

🎈在合理范围内,增大batch size的好处:

  • 内存利用率提高了,大矩阵乘法的并行化效率提高;
  • 跑完一次 epoch所需的迭代次数减少,对于相同数据量的处理速度进一步加快;
  • 在一定范围内,一般来说 batch size 越大,其确定的下降方向越准,引起训练震荡越小。

🎈盲目增大batch size的坏处:

  • 内存利用率提高了,但是可能会造成内存/显存溢出;
  • 泛化能力下降:batch size 增大到一定程度,可能会在训练期间对网络的准确性产生负面影响,其确定的下降方向已经基本不再变化,因为它减少了梯度下降的随机性。

🎈使用小batch size的优点:

  • 帮助训练“跳出”之前可能陷入的局部最小值;
  • 随机性更大,泛化性能更好。

🎈使用小batch size的缺点:

产生更不稳定、更随机的权重更新

✨小技巧:

  1. 当模型训练到稳定在一定的loss时(先保存模型再进行下一阶段“细训练”或者直接一步到位,在代码中加设判断loss或者准确率的“恒稳”,如果符合条件再进行下面的操作),想更精细化地降低loss,提高准确率,可以设置batch size为1,即做SGD,慢慢降低error;
  2. 当有足够算力时,选取更小一些的batch size(<32)

2、动量法

(1)背景与引入原因

梯度下降法的缺点是每次更新沿着当前位置的梯度方向进行,更新仅仅取决于所在的位置以及对应参数的权重大小。

🍄举个栗子:

假设一个n维的问题,目标函数在某一点
x=\left \{ x_{1},x_{2},...,x_{n} \right \}
处(即需要更新变动的参数集合)
x_{1}
方向的梯度绝对值要远小于
x_{2}
,那么在
x_{2}
方向更新的速度就会很快,震荡就很严重;

问题一:降低学习率可以减小
x_{2}
方向变化的幅度,但是整体的更新速度也就随着变慢了(其他属性的方向变化速度也会因学习率的降低而被拖慢);

问题二:增大学习率则会使
x_{2}
方向的变化速度增大,导致在这方向上不断越过最优解并逐渐发散。

因此动量法应运而生。

(2)原理

A、公式

设损失函数在时间步t的小批量随机梯度为
g_{t}
,时间步t的自变量为
x_{t}
,学习率为
\eta _{t}
。在时间步0,动量法创建速度变量
v_{0}
,并将其初始化为0。在时间步 t > 0,动量法对每次迭代的步骤做如下修改:

其中动量超参数
\gamma
满足0 <
\gamma
< 1。且当
\gamma
= 0时,动量法等价于小批量随机梯度下降。

B、解释

🌳指数加权移动平均(EMA或EWMA)🌳

即以指数式递减加权的移动平均,各数值的加权影响力随时间而指数式递减,越近期的数据加权影响力越重,但较旧的数据也给予一定的加权值(越远越小)。

y_{t}
是时间步 t 的移动加权平均值,
\gamma
表示加权系数,
x_{t}
表示时间步 t 的输入。


\gamma _{t}
展开得:


n=1/(1-\gamma)
,那么
(1-1/n)^{n}= \gamma ^{1/(1-\gamma)}
,又因为
\lim_{n\rightarrow \infty }(1-1/n)^{n}=e^{-1}
,所以当
\gamma \rightarrow 1
时,
\gamma ^{1/(1-\gamma)} = e^{-1}

如果把
e^{-1}
当作一个比较小的数,可以在近似中忽略所有含
\gamma^{1/(1-\gamma)}
和比
\gamma^{1/(1-\gamma)}
更高阶的系数的项。

\gamma _{t}
的重要性是
x_{t}

\gamma /(1-\gamma )
倍(即1-n);
\gamma _{t-1}
可认为表示前
\gamma /(1-\gamma )
时间步(从时间步t往前推)的加权输入
x_{t}
,即对于
\gamma _{t}
来说,之前的输入
x_{t-i}
的重要性是当前输入
x_{t}

\gamma ^{i}
倍,这是它的意义所在(由最先的
\gamma _{t}
与各时间步的输入的关系式到最终的递推形式关系式)。

🌳EMA到动量法🌳

对动量法的速度变量做变性,得:

是不是和EMA的式子很像?即输入的
x_{t}
即为上式中的最右边的括号里那一块块。

相对于小批量随机梯度下降,动量法在每个时间步的自变量更新量近似于将前者对应的最近
1/(1-\gamma)
个时间步的更新量做了指数加权移动平均后再除以
(1-\gamma)

所以在动量法中,自变量在各个方向上的移动幅度不仅取决于当前梯度,还取决于过去的各个梯度在各个方向上是否一致。

(3)优缺点

优点:

在梯度下降初期时,使用上一次的参数更新,下降方向一致,能够很好地进行加速;在下降中后期,在局部最小值来回震荡,使得更新幅度增大,跳出局部最小值附近区域(靠前面的“惯性”),解决了梯度下降的问题

缺点:

该方法依靠时间累积的梯度影响进行参数更新步幅的大小,但是并不是根据先验条件进行更新步幅大小的判断(即不知道哪里该加速哪里该减速,一律根据”惯性“加速)

总结:

梯度下降法的两个问题中,动量法主要解决的是问题二的”发散“问题(根据不断的前步修正,使得自变量的更新方向更加一致,从而降低发散的可能)。

3、AdaGrad算法(

自适应梯度)

(1)背景与引入原因

梯度下降法和动量法都存在”一荣俱荣,一损俱损“的问题(自变量各个维度的更新步幅一致),难以进行协调,因此AdaGrad算法应运而生:他根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率
\eta
,对稀疏参数进行大幅更新和对频繁参数进行小幅更新,从而避免统一的学习率难以适应所有维度的问题



因此,Adagrad方法非常适合处理稀疏数据。

(2)原理

在时间步长中,Adagrad方法基于每个参数计算的过往梯度,为不同参数设置不同的学习率。

在MBGD的基础上,加入了一个梯度的累加变量
s_{t}
,其中
g_{t}
是MBGD求出的梯度:

其中
\varepsilon
是一个很小的数字,常为
10^{-6}
,为了维持数值的稳定性,防止
s_{t}
为0,
\alpha
是学习率。

注意:这里的开方、除法和乘法都是按元素运算的。这些按元素运算使得目标函数自变量中每个自变量中的每个元素都分别有自己的学习率。

(3)优缺点

优点:

不需要手工来调整学习率。大多数参数使用了默认值0.01,且保持不变。

缺点:

它的缺点是分母会不断积累,这样学习率就会收缩并最终会变得非常小。因为随着学习速度的越来越小,模型的学习能力迅速降低,而且收敛速度非常慢,需要很长的训练和学习,即学习速度降低,甚至提前结束训练。因此AdaGrad需要改善学习率不断下降的问题。

4、RMSProp算法(均方根传递算法)

(1)背景与引入原因

前面的AdaGrad算法的学习率处于单调递减状态,因此在前期迭代时降得很快且当前解依然不尽人意时,迭代后期可能由于学习率过小,难以找到一个有用的解。因此,RMSProp算法应运而生。

(2)原理

即一个改变二阶动量计算方法的策略:不累积全部历史梯度,而只关注过去一段时间窗口的下降梯度,采用动量法中的指数加权移动平均值的思路。

s_{t} \leftarrow \gamma s_{t-1}+(1-\gamma )g_{t} \odot g_{t}

其中,
0\leq \gamma < 1

即可看作最近
1/(1-\gamma )
个时间步的小批量随机梯度平方项的加权平均(再前面的都太小了,忽略不计),这样自变量每个元素的学习率在迭代过程中就不会再是一直降低(或不变)了。

(3)优缺点

优点:

A、前面AdaGrad的优点;

B、防止学习率衰减或梯度消失等问题的出现;

缺点:

仍然依赖于全局的学习率
\alpha
,需要手动初始化
\alpha

5、AdaDelta算法

它也是针对AdaGrad算法进行了改进,不同于RMSProp算法是它没有学习率这个超参数。

(1)原理

s_{t} \leftarrow \rho s_{t-1}+(1-\rho )g_{t} \odot g_{t}

x_{t} \leftarrow x_{t-1}-g_{t}'

其中,
\rho
对应于RMSProp算法中的
\gamma

\Delta x_{t}
在时间步0时被初始化为0,它用来记录自变量变化量
g_{t}'
按元素平方的指数加权移动平均;

其中
\varepsilon
是一个很小的数字,常为
10^{-5}
,为了维持数值的稳定性,防止
s_{t}
为0

(2)优缺点

优点:

A、前面RMSProp算法的优点;

B、使用
\sqrt{\Delta x_{t-1}}
来代替超参数学习率

缺点:

当时间步t较小时,”过去各时间步小批量随机梯度的按元素平方“权值之和会较小,并不能统一体现每一时间步内所有时间步梯度所占的权重大小对比(比如说t比较小的时候,每个时间步梯度的按元素平方所占的权重大小都很小,虽然彼此之间是有相对的大小对比关系;但是当t比较大时,每个时间步梯度的按元素平方所占的权重大小都较大,比如随便假设t=2时,两个权值为0.01和0.03,t=10时,几个权值为1,3,……)

6、Adam算法

主要是结合了动量法和RMSprop算法,即动量法+RMSprop算法+偏差修正。

(1)原理

🌳时间步t的动量变量
v_{t}
——小批量随机梯度
g_{t}
的指数加权移动平均(
E(g_{t})
):

v_{t} \leftarrow \beta v_{t-1}+(1-\beta )g_{t}

其中,
0\leq \beta _{1}< 1
,第一次估计的指数衰减率,通常设为0.9;

🌳用于调整学习率的
s_{t}
——小批量随机梯度
g_{t}
的按元素平方的指数加权移动平均(
E(g_{t}^{2})
):

s_{t}\leftarrow \beta _{2}s_{t-1}+(1-\beta _{2})g_{t}\odot g_{t}

其中,
0\leq \beta _{2}< 1
,第二次估计的指数衰次减率,通常设为0.999;

Note:以上两者在时间步0时都被初始化为0

🌳两者的偏差修正

当时间步t较小时,过去各时间步小批量随机梯度权值之和会较小,并不能统一体现每一时间步内所有时间步梯度所占的权重大小对比(比如说t比较小的时候,每个时间步梯度所占的权重大小都很小,虽然彼此之间是有相对的大小对比关系;但是当t比较大时,每个时间步梯度所占的权重大小都较大,如果能把他们都统一在一个范围内那么数据就更“漂亮”了),因此我们对求得的
v_{t},s_{t}
都再除以权值之和,从而使过去各时间步小批量随机梯度权值之和为1。

由前面动量法的公式可得:
v_{t}=(1-\beta _{1})\sum_{i=1}^{t}\beta _{1}^{t-i}g_{i}

权值和为:
(1-\beta _{1})\sum_{i=1}^{t}\beta _{1}^{t-i} = 1-\beta _{1}^{t}

s_{t}
类似)

因此有:

\hat{v_{t}}\leftarrow \frac{v_{t}}{1-\beta_{1}^{t} }

\hat{s_{t}}\leftarrow \frac{s_{t}}{1-\beta_{2}^{t} }

🌳运用类似RMSProp算法的方法进行迭代

g_{t}'\leftarrow \frac{\eta \hat{v_{t}}}{\sqrt{\hat{s_{t}}}+\varepsilon }

x_{t} \leftarrow x_{t-1}-g_{t}'

其中
\varepsilon
是一个很小的数字,常为
10^{-8}
,为了维持数值的稳定性,
\eta
为学习率

(2)优缺点

优点:

  • 计算上讲究效率
  • 小内存要求
  • 非常适合于数据和/或参数方面的问题
  • 适合非平稳的目标
  • 适用于非常稀梳梯度的问题

缺点:

A、可能不收敛

随着时间窗口的变化,遇到的数据可能发生巨变,使得
v_{t}
可能会时大时小,不是单调变化。这就可能在训练后期引起学习率的震荡,导致模型无法收敛。

B、可能错过全局最优解


三、总结

最常用是SGD和Adam,但是各有优缺点,因此有一种改进方法:前期用Adam,享受Adam快速收敛的优势;后期切换到SGD,慢慢寻找最优解。


四、常用优化算法的代码调库实现

SGD:

from torch import optim
optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9)

Adam:

optimizer = optim.Adam(model.parameters(), lr = 0.0001)

清空当前梯度(反向传播前):

optimizer.zero_grad() #清空当前梯度

参数更新(反向传播后):

optimizer.step() #参数更新

欢迎大家在评论区批评指正,谢谢~



版权声明:本文为weixin_55073640原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。