常见Optimizer


深度学习中的常用框架,无论是PyTorch还是TensorFlow,都封装了很多优化器。那么各优化器之间有什么优点和缺点呢。下面我们就回顾一下主流的优化算法。

1. 前言

当前大部分的优化算法,其本质都是梯度下降(Gradient Descent),只是不同的算法,对梯度下降进行了不同的优化。那么什么是梯度呢,梯度就是一个函数对其参数求一介偏导数。梯度的特性就是,函数在该点处沿着梯度的方向变化最快。因此梯度下降算法被用于求无约束凸函数的最小值。
假设目标函数 J ( θ ) J(\theta) J(θ),梯度下降算法流程如下:

  • 计算目标函数关于参数 θ \theta θ的梯度: g t = ▽ θ t J ( θ ) g_t = \bigtriangledown_{\theta_t}J(\theta) gt=θtJ(θ)
  • 根据历史梯度计算一介动量和二阶动量: m t = ϕ ( g 1 , g 2 , . . . g t ) , V t = ψ ( g 1 , g 2 , . . . g t ) m_t = \phi(g_1, g_2, ... g_t), V_t = \psi(g_1, g_2, ... g_t) mt=ϕ(g1,g2,...gt),Vt=ψ(g1,g2,...gt)
  • 计算当前时刻的下降梯度: ▽ t = η × m t V t \bigtriangledown_t = \eta \times \frac{m_t}{\sqrt{V_t}} t=η×Vt mt
  • 根据下降梯度,更新 θ \theta θ θ t + 1 = θ t − ▽ t \theta_{t+1} = \theta_t - \bigtriangledown_t θt+1=θtt

2. Gradient Descent

GD算法是最传统的梯度下降算法,他没有动量的概念,也就是说 m t = g t , V t = 1 m_t = g_t, V_t = 1 mt=gt,Vt=1。参数更新方式为: θ t + 1 = θ t − η × g t \theta_{t+1} = \theta_t - \eta \times g_t θt+1=θtη×gt
沿着梯度的防线不断减小模型参数,从而最小化目标函数。如下图所示:
梯度下降
但是梯度下降也有一些不足:

  1. 训练速度慢,每次迭代都需要遍历所有样本,会使训练过程十分缓慢。
  2. 容易陷入局部最优,有些点梯度为0,导致参数不再发生变化,但不一定是目标函数的最优值(如鞍点)。
  3. 随着梯度的变小,参数更新很慢。

3. Batch Gradient Descent (BGD)

为了解决GD算法每输入一个样本就要更新一次参数,从而导致的效率低下的问题。BGD做了一些改进,不再是对每个样本都进行参数更新,而是对整体样本进行参数更新,假设现在有n各样本。BGD算法如下:
θ t + 1 = θ t − η × 1 n × ∑ i = 0 n ▽ θ t J ( θ , x i , y i ) \theta_{t+1} = \theta_t - \eta \times \frac{1}{n} \times \sum_{i=0}^n \bigtriangledown_{\theta_t}J(\theta, x_i, y_i) θt+1=θtη×n1×i=0nθtJ(θ,xi,yi)
BGD的核心就是先算整体样本的梯度均值,在根据这个梯度均值进行参数更新。这样会大大加快参数更新速度,但还有更好的方法。

4. Stochastic Gradient Descent(SGD)

随机梯度下降,不再是用整体梯度的均值进行跟新了,而是从训练数据中选取一个样本进行参数更新:
θ t + 1 = θ t − η × ▽ θ t J ( θ , x i , y i ) \theta_{t+1} = \theta_t - \eta \times\bigtriangledown_{\theta_t}J(\theta, x_i, y_i) θt+1=θtη×θtJ(θ,xi,yi)
从上边公式看跟GD算法一样,他们的主要区别是,SGD是从训练数据中随机选一个样本,而GD算法是所有训练数据都参与计算。SGD由于每次参数更新只需要一个样本,所以参数更新很快,但由于单个样本存在误差,不能很好代表整体数据,可能会导致梯度下降方向不是整体的最优方向,结果就是梯度下降的波动很大。
SGD的优点就是能更快的收敛,虽然波动很大,会走很多弯路,但一般总能收敛,而且速度要快得多。
但它依然没有解决局部最优的问题。

5. Mini-batch Gradient Descent(MBGD)

于是人们想到了一种办法,就是BGD和SGD的折中,选择一个mini_batch,batch_size为m:
θ t + 1 = θ t − η × 1 m × ∑ i = x x + m − 1 ▽ θ t J ( θ , x i , y i ) \theta_{t+1} = \theta_t - \eta \times \frac{1}{m} \times \sum_{i=x}^{x+m-1}\bigtriangledown_{\theta_t}J(\theta, x_i, y_i) θt+1=θtη×m1×i=xx+m1θtJ(θ,xi,yi)
MBGD即保证了训练速度,又保证了最优收敛的准确率。
但是以上所有优化算法都存在一个问题,就是learning rate,如果选的太小会导致收敛很慢,太大会让loss function在极小值处来回震荡,错过极小值。

6. Momentum

momentum的核心思想,参数更新时在一定程度上保留之前的更新方向,同时又用当前batch的梯度微调最终的更新方向。也就是通过积累之前的动量来加速当前的梯度。从这个算法开始我们就要引入动量的概念了。其中动量为:
m t + 1 = β 1 × m t + ( 1 − β 1 ) × g t m_{t+1} = \beta_1 \times m_t + (1-\beta_1) \times g_t mt+1=β1×mt+(1β1)×gt
其中 β 1 \beta_1 β1的经验值为0.9。参数更新公式为:
θ t + 1 = θ t − m t + 1 \theta_{t+1} = \theta_t - m_{t+1} θt+1=θtmt+1
从以上公式中可以看出,t时刻下降的方向不仅跟当前点的梯度有关,而且跟之前积累的梯度相关。该方法可以缓解梯度波动较大的问题,加速收敛。

7. Nesterov Accelerated Gradient

NAG算法可以说是之前momentum算法的变种,他在梯度更新时做了一个矫正,具体做法是在当前的梯度上添加上上一时刻的动量 β 1 m t \beta_1m_t β1mt。公式如下:
m t + 1 = β 1 m t + ( 1 − β 1 ) × ▽ θ t J ( θ t − β 1 m t ) θ t + 1 = θ t − m t + 1 m_{t+1} = \beta_1m_t + (1-\beta_1)\times\bigtriangledown_{\theta_t}J(\theta_t-\beta_1m_t) \\ \theta_{t+1} = \theta_t - m_{t+1} mt+1=β1mt+(1β1)×θtJ(θtβ1mt)θt+1=θtmt+1
加上nesterov后,梯度在经过大的跳跃之后,会对下一步的梯度计算进行矫正( θ t − β 1 m t \theta_t - \beta_1m_t θtβ1mt)。

8. Adagrad

Adaptive Gradient(自适应梯度),从这之后就会介绍一些自适应学习率的优化方法。AdaGrad其实是对学习率进行了一个约束,对于经常更新的参数,我们已经积累了足够的关于他的信息,不希望被单个样本影响太大,所以希望学习速率慢一些;对于不经常更新的参数,我们了解的信息太少,希望能从每个偶然出现的样本中学习到更多的信息,因此希望学习速率高一些。在该方法中开始使用二介动量,标志着自适应学习率时代的到来。
那么二介动量是个啥呢,他可以用来衡量历史更新频率,定义是迄今为止所有梯度值的平方和: V t = ∑ i = 1 t g t 2 V_t = \sum_{i=1}^{t}g_t^2 Vt=i=1tgt2,其中 g t g_t gt为t时刻的梯度。根据前言中的公式( m t = 1 m_t=1 mt=1):
V t = ∑ i = 1 t g t 2 V_t = \sum_{i=1}^{t}g_t^2 Vt=i=1tgt2
参数更新公式如下:
θ t + 1 = θ t − η g t V t + ϵ \theta_{t+1} = \theta_t - \eta\frac{g_t}{\sqrt{V_t + \epsilon}} θt+1=θtηVt+ϵ gt
在梯度下降的基础上,对梯度增加了分母:梯度平方累积和的平方根。频繁更新的梯度,则累积的分母项逐渐偏大,那么更新的步长(stepsize)相对就会变小,而稀疏的梯度,则导致累积的分母项中对应值比较小,那么更新的步长则相对比较大。
然而这种优化方法仍然需要定义超参数 η \eta η,而且随着训练的增加会使参数更新量趋近0,导致训练提前结束,无法学习。

9. Adadelta

Adagrad的方式,参数更新量调整的方式过于激进,因此可以考虑调整二介动量的计算方式,使其变得平缓:不使用全部的历史梯度,而是使用过去一段时间窗口下的梯度,并且也不直接存储这些梯度值,仅仅是计算对应的平均值(滑动平均),从而避免二介动量持续累积,导致训练提前结束的问题出现。
V t = β 2 V t − 1 + ( 1 − β 2 ) ( ▽ θ t J ( θ t ) ) 2 V_t = \beta_2 V_{t-1} + (1-\beta_2)(\bigtriangledown_{\theta_t}J(\theta_t))^2 Vt=β2Vt1+(1β2)(θtJ(θt))2
θ t + 1 = θ t − η g t V t + ϵ \theta_{t+1} = \theta_t - \eta \frac{g_t}{\sqrt{V_t + \epsilon}} θt+1=θtηVt+ϵ gt
从上面公式可以发现,参数更新还是需要提供learning rate的。作者在上边的基础上做了进一步处理:
g t = ▽ θ t J ( θ t ) g_t = \bigtriangledown_{\theta_t}J(\theta_t) gt=θtJ(θt)
E [ g t 2 ] t = ρ ⋅ E [ g t 2 ] t − 1 + ( 1 − ρ ) ⋅ g t 2 E[g_t^2]_t = \rho ·E[g_t^2]_{t-1} + (1-\rho) · g_t^2 E[gt2]t=ρE[gt2]t1+(1ρ)gt2
▽ t = ∑ i = 1 t − 1 Δ θ r E [ g t 2 ] t + ϵ \bigtriangledown_t = \frac{\sum_{i=1}^{t-1}\Delta\theta_r}{\sqrt{E[g_t^2]_t + \epsilon}} t=E[gt2]t+ϵ i=1t1Δθr
从上边公式可以看出,更新量 ▽ t \bigtriangledown_t t不再以来learnging rate。

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

10. RMSprop

RMSprop,将AdaGrad的梯度平方和累加修改为指数加权移动平均,可以是其在非凸设定下效果更好。
g t = ▽ θ t J ( θ t ) g_t = \bigtriangledown_{\theta_t}J(\theta_t) gt=θtJ(θt)
E [ g t 2 ] t = ρ ⋅ E [ g t 2 ] t − 1 + ( 1 − ρ ) ⋅ g t 2 E[g_t^2]_t = \rho ·E[g_t^2]_{t-1} + (1-\rho) · g_t^2 E[gt2]t=ρE[gt2]t1+(1ρ)gt2
▽ t = η ⋅ g t E [ g t 2 ] t + ϵ \bigtriangledown_t = \frac{\eta · g_t}{\sqrt{E[g_t^2]_t + \epsilon}} t=E[gt2]t+ϵ ηgt
根据经验,有一些超参数的设定可以参考, η = 0.001 , ρ = 0.9 , ϵ = 1 e − 9 \eta=0.001, \rho=0.9, \epsilon=1e-9 η=0.001,ρ=0.9,ϵ=1e9

  • RMSprop依然以来learning rate
  • RMSprop算是Adagrad的一种发展,和Adadelta的变体,效果趋于二者之间
  • 适合处理非平稳目标(包括季节性、周期性),对RNN比较友好。

11. Adam

Adaptive Moment Estimation,结合了前面一阶动量,二阶动量的方法。算法伪代码如下:

Adam

首先初始化 m 0 , v 0 , t m_0, v_0, t m0,v0,t为0, θ 0 \theta_0 θ0为初始化参数,然后循环执行如下步骤:
m t + 1 = β 1 m t + ( 1 − β 1 ) ▽ θ t J ( θ t ) m_{t+1} = \beta_1 m_t + (1-\beta_1)\bigtriangledown_{\theta_t}J(\theta_t) mt+1=β1mt+(1β1)θtJ(θt)
V t + 1 = β 2 V t + ( 1 − β 2 ) ( ▽ θ t J ( θ t ) ) 2 V_{t+1} = \beta_2 V_t + (1-\beta_2) (\bigtriangledown_{\theta_t}J(\theta_t))^2 Vt+1=β2Vt+(1β2)(θtJ(θt))2
由于m,v都是0初始化的,根据上边公式, β 1 \beta_1 β1默认0.9的话,会使m趋近于0,尤其是训练初期,因此对其进行处理:
m ^ t + 1 = m t + 1 1 − β 1 t \hat{m}_{t+1} = \frac{m_{t+1}}{1-\beta_1^t} m^t+1=1β1tmt+1
v ^ t + 1 = v t + 1 1 − β 2 t \hat{v}_{t+1} = \frac{v_{t+1}}{1-\beta_2^t} v^t+1=1β2tvt+1
最终更新参数:
θ t + 1 = θ t − η m ^ t + 1 V ^ t + 1 + ϵ \theta_{t+1} = \theta_t - \eta \frac{\hat{m}_{t+1}}{\sqrt{\hat{V}_{t+1} + \epsilon}} θt+1=θtηV^t+1+ϵ m^t+1
通常默认情况下, β 1 = 0.9 , β 2 = 0.9999 , ϵ = 1 0 − 8 \beta_1=0.9, \beta_2=0.9999, \epsilon=10^{-8} β1=0.9,β2=0.9999,ϵ=108。Adam对超参数的选择比较鲁棒,在很多情况下算作默认工作性能比较优秀的优化器。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值