pytorch 入门 - 优化算法详解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_39446239/article/details/89335184


目标:理解更多神经网络优化方法,了解不同优化器,并书写优化器代码

神经网络的学习过程中需要利用优化算法去更新参数,训练模型。优化器的种类有多个,比较常见的是随机梯度下降算法,接下来系统梳理一下各种优化算法的原理、实现方法和优劣之处。

梯度下降法(Gradient Descent)

梯度下降算法的优化思想是每次沿着当前位置梯度的反方向为搜索方向。梯度的几何含义是:函数增长最快的方向,那么为了使损失函数达到最小,就得取负方向,即沿着反向梯度方向更新参数,使损失函数沿着下降最快的方向走。

计算损失函数梯度就是相对于各参数求偏导的矢量和,即
wi=wilearning rateLosswiw_i = w_i - \text{learning rate} * \dfrac{\partial Loss }{\partial w_i}

注:
当损失函数是凸函数时,可求得全局最优解,否则不能保证
梯度下降法的速度也未必是最快的
梯度下降算法的学习效果取决于初始值、学习率
其越接近目标值、步长越小,前进越慢

  • 梯度下降算法的优缺点:
    缺点:
      1、靠近极小值时收敛速度减慢,如下图所示;
      2、直线搜索时可能会产生一些问题;
      3、可能会“之字形”地下降。
    在这里插入图片描述
    从上图可以看出,梯度下降法在接近最优解的区域收敛速度明显变慢,利用梯度下降法求解需要很多次的迭代。

GD算法的实现方式

按每次训练过程中投入数据量的大小可将GD优化算法的实现分为以下三类:

BGD(Batch Gradient Descent)

在每次计算中,采用整个训练集的数据来计算Loss function 对参数的梯度:
W=WlrWJ(W)W= W - lr\cdot \nabla _WJ(W)

  • BGD 缺点:计算量大,计算缓慢,对于大规模数据比较棘手;对于很大的数据集来说可能会有相似样本,这样在计算梯度过程中就容易出现冗余;不能新增样本
  • BGD 优点:可以收敛到局部最优

SGD(Stochastic Gradient Descent)

SGD在每次更新过程中,只对每个样本进行梯度更新。
W=WlrWJ(W;x(i);y(i))W= W - lr\cdot \nabla _WJ(W;x^{(i)};y^{(i)})

  • SGD 优点:一次只更新一个样本,计算速度很快,而且可以新增样本;SGD虽然包含了随机性,但从期望来看是等于正确导数的;稍微减小lr时,SGD与BGD收敛性一样;
  • SGD 缺点:更新频繁,容易造成Loss震荡;SGD的噪音较多,使得SGD不是每次迭代都朝整体最优化方向前进;SGD收敛到局部最小存在随机性;

MBGD (Mini-Batch Gradient Descent)

在每次训练过程中,从总体m个样本中随机取n个样本计算Loss的梯度。
W=WlrWJ(W;x(i:i+n);y(i:i+n))W= W - lr\cdot \nabla _WJ(W;x^{(i:i+n)};y^{(i:i+n)})

  • MBGD 优点:降低更新参数的方差,即克服SGD噪音多不稳定的缺点,收敛更加稳定;充分利用dl学习框架中高度优化的矩阵操作进行更有效的梯度计算。

梯度下降算法的不足

1、不能很好保证收敛性,lr取太小的话收敛速度会过慢、lr取太大的话loss就会在极小值处不同震荡直至偏离。(措施:lr预先设置大一点,前后两次迭代的loss变化小于某个阈值后,就减小lr,但阈值的设定需要数据集本身的特点)

2、对于非凸函数,GD容易被困在局部极小值或者鞍点处(鞍点附近的loss都一样,所有维度的梯度接近0);若是用BGD,则优化停止不动;若是MBGD或SGD,那么每次找到的梯度是不同的,会发生震荡,来回跳动

3、SGD对所有参数更新采用的lr是相同的,若数据稀疏,我们更希望对出现频率低的特征进行大一点的更新,即学习率lr是一个可变量,根据特征出现频率以及更新次数发生变化

GD算法的改进

Momentum

momentum是模拟物理里动量的概念,积累之前的动量来替代真正的梯度。它通过加入γvt1\gamma v_{t-1}来加速SGD的同时可以抑制震荡。

vt=γvt1+lrwJ(w)v_t = \gamma v_{t-1} + lr*\nabla_w J(w) 一般γ\gamma常取0.9左右
w=wvtw = w-v_t

原理:模拟小球从山顶滚落的过程,若没有阻力,小球的动量会增大,若遇上阻力,速度就会变小,加入速度变化的这一项可以使梯度不变的维度上速度变快,梯度方向改变的维度上更新速度变慢,这样可以加快收敛并减小震荡。

数学思维的理解:参数每次更新的数值不再是直接取负梯度值,二是参考上一步的数值,有点类似做一个“滑动平均”?此外对于加速SGD且抑制震荡这块,可以这么理解:梯度计算结果和上次更新值vt1v_{t-1}符号一致时,相当于对最终对参数更新得更多,而符号不一致时,相当于更新得少。

下降初期时,使用上一次参数更新,下降方向一致,乘上较大的γ\gamma能够进行很好的加速
下降中后期时,在局部最小值来回震荡的时候,gradient→0,γ\gamma 使得更新幅度增大,跳出陷阱
在梯度改变方向的时候,γ\gamma 能够减少更新

  • 缺点:缺乏先知信息,若知道快要上坡了,就知道减速的话,适应性会更好

Nesterov Accelerated Gradient

同momentum 一样,在更新参数时添加动量信息,不同的是在计算梯度时,不是计算当前位置的梯度,而是计算未来位置的梯度
vt=γvt1+lrwJ(wγvt1)v_t = \gamma v_{t-1} + lr*\nabla_w J(w- \gamma v_{t-1}) 一般γ\gamma常取0.9左右
w=wvtw = w-v_t

相比于momentum,NAG收敛速度会更快一点,这种算法能够提前看到前方的地形梯度,若前面的梯度比当前位置大,就可以把步子迈得大一点,若前方梯度小,就可以迈小步一点,这个大与小是相对于当前位置梯度信息去计算的。

避免前进太快,同时提高灵敏度
在这里插入图片描述
momentum首先计算一个梯度(短的蓝色向量),然后在加速更新梯度的方向进行一个大的跳跃(长的蓝色向量),nesterov项首先在之前加速的梯度方向进行一个大的跳跃(棕色向量),计算梯度然后进行校正(绿色梯向量)

自适应优化算法

该算法针对GD算法的第三个不足(lr对数据频率的变化)做一个改进,即可以对低频参数做较大的更新,对高频参数做较小的更新。对于稀疏数据来说表现更好,且很好的提高了SGD的鲁棒性。

首先介绍自适应算法的第一个实现方法

Adagrad

对学习率做一个约束:
gt=WJ(W)g_t = \nabla _WJ(W)
nt=nt1+gt2n_t = n_{t-1} + g_t^2
Wt=Wtlrnt+ϵgtW_t = W_t - \dfrac{lr}{\sqrt{\smash[b]{n_t +\epsilon}}}*g_tϵ\epsilon保证分母不为0,ntn_t表示从1到t的梯度累计,形成一个约束。

前期gt较小的时候, regularizer较大,能够放大梯度
后期gt较大的时候,regularizer较小,能够约束梯度
适合处理稀疏梯度

缺点:
1、由公式可以看出,仍依赖于人工设置一个全局学习率
2、η设置过大的话,会使regularizer过于敏感,对梯度的调节太大
3、中后期,分母上梯度平方的累加将会越来越大,使gradient→0,使得训练提前结束

Adadelta

对Adagrad的改进(解决Adagrad学习率急剧下降的问题),将ntn_t做一个变换,变成梯度平方的衰减平均数

E[g2]t=γE[g2]t1+(1γ)gt2E[g^2]_t = \gamma E[g^2]_{t-1} + (1-\gamma)g_t^2

w=wlrE[g2]t+ϵgtw = w - \dfrac{lr}{\sqrt{\smash[b]{E[g^2]_t+\epsilon}}}*g_t

此处的lr还是需要预先设定的,但作者经过一定的改进之后将学习率lr 替换成了每次更新值的求和开方值。
在这里插入图片描述

训练初中期,加速效果不错,很快
训练后期,反复在局部最小值附近抖动

RMSprop

RMSprop 也是为了解决Adagrad 学习率急剧下降的问题。RMSprop可以算作Adadelta的一个特例:当γ\gamma=0.5时,E[g2]t=γE[g2]t1+(1γ)gt2E[g^2]_t = \gamma E[g^2]_{t-1} + (1-\gamma)g_t^2就变为了求梯度平方和的平均数。 如果再求根的话,就变成了RMS(均方根):

w=wlrRMS[g]tgtw = w - \dfrac{lr}{RMS[g]_t}*g_t

其实RMSprop依然依赖于全局学习率
RMSprop算是Adagrad的一种发展,和Adadelta的变体,效果趋于二者之间
适合处理非平稳目标
对于RNN效果很好

Adam

本质上是带有动量项的RMSprop,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。Adam的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。公式如下:
在这里插入图片描述

结合了Adagrad善于处理稀疏梯度和RMSprop善于处理非平稳目标的优点
对内存需求较小
为不同的参数计算不同的自适应学习率
也适用于大多非凸优化
适用于大数据集和高维空间
Adam 就是在RMSprop 的基础上加了bias-correction 和 momentum的效果

优化算法的选择

对于稀疏数据,尽量使用自适应学习方法,即Adagrad,Adadelta, RMSprop, Adam

SGD通常训练时间更长,容易陷入鞍点,但是在好的初始化和学习率调度方案的情况下,结果更可靠

如果在意更快的收敛,并且需要训练较深较复杂的网络时,推荐使用学习率自适应的优化方法

Adadelta,RMSprop,Adam是比较相近的算法,在相似的情况下表现差不多

在想使用带动量的RMSprop,或者Adam的地方,大多可以使用Nadam取得更好的效果

牛顿法和拟牛顿法(Newton’s method & Quasi-Newton Methods)

牛顿法的每一步需要求解目标函数的海塞矩阵的逆矩阵;拟牛顿法通过正定矩阵近似还塞矩阵的逆矩阵或海塞矩阵,简化计算过程。

牛顿法:主要是利用迭代点XkX_k处的一阶导数和二阶导数去拟合一个二次函数,并求当前函数的极小值,然后将当前函数的极小值作为新的迭代点并不断重复该过程,直至得到满足精度的极小值点。

牛顿法的具体实现:基本牛顿法全局牛顿法

基本牛顿法

(1)原理:对于一维函数问题,将函数的极值问题转化为求f’(x)=0的问题。将函数做二阶泰勒展开: 对等式两边关于x求导,得到 得到x=xkf(xk)f(xk)x=x_k-\dfrac{f’(x_k)}{f’’(x_k)} 这是牛顿法的基本更新公式
(2)基本流程:
在这里插入图片描述
优缺点:

1、牛顿法收敛速度很快且具有局部二阶收敛性
2、基本牛顿法依赖于初始点的选取,若初始点未足够靠近极小点,会导致算法不收敛.

全局牛顿法

全局牛顿法是对基本牛顿法的一个改进
(1)基本流程
在这里插入图片描述
(2)Armijo搜索
就是在更新xkx_k时,给dkd_k加一个系数,改变更新值

共轭梯度法(Conjugate Gradient)

共轭梯度法介于最速下降法和牛顿法之间,用于求解无约束最优化问题,仅需要利用一阶导数的相关信息,但可以克服最速下降法收敛慢的缺点,是解决大型线性方程组/大型非线性最优化的有效方法之一。

基本思想:将共轭性和最速下降法相结合,利用已知点的梯度构造一组共轭方向,并沿该方向搜索,求出目标函数的极小点。

目标函数:x=argmin12xTAxbxx^* = arg \min\dfrac {1}{2}x^TAx -bx
梯度:f(x)=Axb=0\nabla f(x^*) = Ax^* - b =0

共轭方向的定义:
在这里插入图片描述
算法流程:
在这里插入图片描述
其中r为残差,d为梯度方向,x为出发点

优点:存储量小,具有步收敛性,稳定性高,且不需要任何外来参数。

GD与另两种优化算法的比较

  • 梯度下降法和牛顿法:
    两者都是梯度求解,而牛顿法/拟牛顿法是用二阶海塞矩阵的逆矩阵或伪逆矩阵求解。相对而言,使用牛顿法收敛更快,但是每次迭代时间比梯度下降法长。

  • 梯度下降算法和共轭梯度法:
    共轭梯度法克服了梯度下降算法中的收敛慢的缺点。

pytorch优化函数包

torch.optim是一个实现了各种优化算法的库。大部分常用的方法得到支持,并且接口具备足够的通用性,使得未来能够集成更加复杂的方法。

优化函数 特殊参数说明
torch.optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-06, weight_decay=0) lr_decay (float, 可选) – 学习率衰减;weight_decay (float, 可选) – 权重衰减(L2惩罚)
torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0) betas (Tuple[float, float], 可选) – 用于计算梯度以及梯度平方的运行平均值的系数;eps (float, 可选) – 为了增加数值计算的稳定性而加到分母里的项;
torch.optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False) momentum (float, 可选) – 动量因子;alpha (float, 可选) – 平滑常数;centered (bool, 可选) – 如果为True,计算中心化的RMSProp,并且用它的方差预测值对梯度进行归一化;
torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False) dampening (float, 可选) – 动量的抑制因子;nesterov (bool, 可选) – 使用Nesterov动量
torch.optim.Adamax(params, lr=0.002, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
torch.optim.ASGD(params, lr=0.01, lambd=0.0001, alpha=0.75, t0=1000000.0,weight_decay=0) lambd (float, 可选) – 衰减项; alpha (float, 可选) – eta更新的指数 ; t0 (float, 可选) – 指明在哪一次开始平均化

以SGD为例,实现完整的一次优化步骤:

optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
optimizer.zero_grad() #累计梯度清零
loss_fn(model(input), target).backward() # backward计算损失函数梯度
optimizer.step() # 做一次更新

参考资料

1)https://blog.csdn.net/u012759136/article/details/52302426/
2)https://www.cnblogs.com/shixiangwan/p/7532830.html
3)https://pytorch-cn.readthedocs.io/zh/latest/package_references/torch-optim/

展开阅读全文

没有更多推荐了,返回首页