深度学习--优化器

深度学习-优化器

这里是引用https://blog.csdn.net/u012759136/article/details/52302426/?ops_request_misc=&request_id=&biz_id=102&utm_term=sgd%2520adam&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-7-52302426.first_rank_v2_pc_rank_v29

https://zhuanlan.zhihu.com/p/32230623?utm_source=qq&utm_medium=social&utm_oi=1054735681825386496

基本框架

首先定义:待优化参数: w w w ,目标函数: f ( w ) f(w) f(w) ,初始学习率 α \alpha α

而后,开始进行迭代优化。在每个epoch t t t :

  1. 计算目标函数关于当前参数的梯度: g t = ∇ f ( w t ) g_{t}=\nabla f\left(w_{t}\right) gt​=∇f(wt​)
  2. 根据历史梯度计算一阶动量和二阶动量: m t = ϕ ( g 1 , g 2 , ⋯   , g t ) ; V t = ψ ( g 1 , g 2 , ⋯   , g t ) m_{t}=\phi\left(g_{1}, g_{2}, \cdots, g_{t}\right) ; V_{t}=\psi\left(g_{1}, g_{2}, \cdots, g_{t}\right) mt​=ϕ(g1​,g2​,⋯,gt​);Vt​=ψ(g1​,g2​,⋯,gt​),
  3. 计算当前时刻的下降梯度: η t = α ⋅ m t / V t \eta_{t}=\alpha \cdot m_{t} / \sqrt{V_{t}} ηt​=α⋅mt​/Vt​
  4. 根据下降梯度进行更新: w t + 1 = w t − η t w_{t+1}=w_{t}-\eta_{t} wt+1​=wt​−ηt​

步骤3、4对于各个算法都是一致的,主要的差别就体现在1和2上。

非自适应学习率

SGD

SGD没有动量的概念

SGD为随机梯度下降,每一次迭代计算数据集的mini-batch的梯度,然后对参数进行更新,现在的SGD一般都指mini-batch gradient descent:
深度学习--优化器
优点:

  1. 当训练数据太多时,利用整个数据集更新往往时间上不显示。batch的方法可以减少机器的压力,并且可以更快地收敛。
  2. 当训练集有很多冗余时(类似的样本出现多次),batch方法收敛更快。以一个极端情况为例,若训练集前一半和后一半梯度相同。那么如果前一半作为一个batch,后一半作为另一个batch,那么在一次遍历训练集时,batch的方法向最优解前进两个step,而整体的方法只前进一个step。

缺点:

  1. 选择合适的learning rate比较困难,对所有的参数更新使用同样的learning rate。对于稀疏数据或者特征,有时我们可能想更新快一些对于不经常出现的特征,对于常出现的特征更新慢一些,这时候SGD就不太能满足要求了
  2. SGD容易收敛到局部最优,在某些情况下可能被困在鞍点【但是在合适的初始化和学习率设置下,鞍点的影响其实没这么大】

Momentum

为了抑制SGD的震荡,SGDM认为梯度下降过程可以加入惯性。下坡的时候,如果发现是陡坡,那就利用惯性跑的快一些。SGDM全称是SGD with momentum,在SGD基础上引入了一阶动量:

Momentum参考了物理中动量的概念,前几次的梯度也会参与到当前的计算中,但是前几轮的梯度叠加在当前计算中会有一定的衰减。
深度学习--优化器
优点:

下降初期时,使用上一次参数更新,下降方向一致,乘上较大的 μ μ μ能够进行很好的加速;下降中后期时,在局部最小值来回震荡的时候, g r a d i e n t → 0 gradient\to0 gradient→0, μ μ μ使得更新幅度增大,跳出陷阱;在梯度改变方向的时候, μ μ μ能够减少更新
总而言之,momentum项能够在相关方向加速SGD,抑制振荡,从而加快收敛

Nesterov

NAG全称Nesterov Accelerated Gradient,是在SGD、SGD-M的基础上的进一步改进,改进点在于步骤1。在时刻t的主要下降方向是由累积动量决定的,自己的梯度方向说了也不算,那与其看当前梯度方向,不如先看看如果跟着累积动量走了一步,那个时候再怎么走。因此,NAG在步骤1,不计算当前位置的梯度方向,而是计算如果按照累积动量走了一步,那个时候的下降方向:

nesterov项在梯度更新时做一个校正,避免前进太快,同时提高灵敏度。 Nesterov的改进就是让之前的动量直接影响当前的动量。
g t = ∇ θ t − 1 f ( θ t − 1 − η ∗ μ ∗ m t − 1 ) g_t=\nabla_{\theta_{t-1}}{f(\theta_{t-1}-\eta*\mu*m_{t-1})} gt​=∇θt−1​​f(θt−1​−η∗μ∗mt−1​)
m t = μ ∗ m t − 1 + g t m_t=\mu*m_{t-1}+g_t mt​=μ∗mt−1​+gt​
Δ θ t = − η ∗ m t \Delta{\theta_t}=-\eta*m_t Δθt​=−η∗mt​
所以,加上nesterov项后,梯度在大的跳跃后,进行计算对当前梯度进行校正。
深度学习--优化器

momentum首先计算一个梯度(短的蓝色向量),然后在加速更新梯度的方向进行一个大的跳跃(长的蓝色向量),nesterov项首先在之前加速的梯度方向进行一个大的跳跃(棕色向量),计算梯度然后进行校正(绿色梯向量)

其实,momentum项和nesterov项都是为了使梯度更新更加灵活,对不同情况有针对性。

自适应学习率

上面提到的方法对于所有参数都使用了同一个更新速率。但是同一个更新速率不一定适合所有参数。比如有的参数可能已经到了仅需要微调的阶段,但又有些参数由于对应样本少等原因,还需要较大幅度的调动。

Adagrad

SGD及其变种以同样的学习率更新每个参数,但深度神经网络往往包含大量的参数,这些参数并不是总会用得到(想想大规模的embedding)。对于经常更新的参数,我们已经积累了大量关于它的知识,不希望被单个样本影响太大,希望学习速率慢一些;对于偶尔更新的参数,我们了解的信息太少,希望能从每个偶然出现的样本身上多学一些,即学习速率大一些。

二阶动量出现了

Adagrad其实是对学习率进行了一个约束。Adagard在训练的过程中可以自动变更学习的速率,设置一个全局的学习率,而实际的学习率与以往的参数模和的开方成反比。即:
n t = n t − 1 + g t 2 n_t=n_{t-1}+g_t^2 nt​=nt−1​+gt2​
Δ θ t = − η n t + ϵ ∗ g t \Delta{\theta_t}=-\frac{\eta}{\sqrt{n_t+\epsilon}}*g_t Δθt​=−nt​+ϵ ​η​∗gt​
此处,对 g t gt gt从1到t进行一个递推形成一个约束项regularizer,
− 1 ∑ r = 1 t ( g r ) 2 + ϵ -\frac{1}{\sqrt{\sum_{r=1}^t(g_r)^2+\epsilon}} −∑r=1t​(gr​)2+ϵ ​1​, ϵ用来保证分母非0

优点:

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

缺点:

  1. 由公式可以看出,仍依赖于人工设置一个全局学习率
    η设置过大的话,会使regularizer过于敏感,对梯度的调节太大;
    中后期,分母上梯度平方的累加将会越来越大,使gradient→0,使得训练提前结束。其学习率是单调递减的,训练后期学习率非常小
  2. 更新时,左右两边的单位不同一

Adadelta

Adadelta是对Adagrad的扩展,最初方案依然是对学习率进行自适应约束,但是进行了计算上的简化。
Adagrad会累加之前所有的梯度平方,而Adadelta只累加固定大小的项,并且也不直接存储这些项,仅仅是近似计算对应的平均值。
n t = ν ∗ n t − 1 + ( 1 − ν ) ∗ g t 2 n_t=\nu*n_{t-1}+(1-\nu)*g_t^2 nt​=ν∗nt−1​+(1−ν)∗gt2​
通过这个衰减系数,我们令每一个时刻的 随之时间按照 指数衰减,这样就相当于我们仅使用离当前时刻比较近的 信息,从而使得还很长时间之后,参数仍然可以得到更新。这就避免了二阶动量持续累积、导致训练过程提前结束的问题了
Δ θ t = − η n t + ϵ ∗ g t \Delta{\theta_t} = -\frac{\eta}{\sqrt{n_t+\epsilon}}*g_t Δθt​=−nt​+ϵ ​η​∗gt​
特点:

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

Adam

SGD-Momentum在SGD基础上增加了一阶动量,AdaGrad和AdaDelta在SGD基础上增加了二阶动量。把一阶动量和二阶动量都用起来,就是Adam了——Adaptive + Momentum。

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

一阶动量:
m t = μ ∗ m t − 1 + ( 1 − μ ) ∗ g t m_t=\mu*m_{t-1}+(1-\mu)*g_t mt​=μ∗mt−1​+(1−μ)∗gt​
二阶动量:
n t = ν ∗ n t − 1 + ( 1 − ν ) ∗ g t 2 n_t=\nu*n_{t-1}+(1-\nu)*g_t^2 nt​=ν∗nt−1​+(1−ν)∗gt2​
m t ^ = m t 1 − μ t \hat{m_t}=\frac{m_t}{1-\mu^t} mt​^​=1−μtmt​​
n t ^ = n t 1 − ν t \hat{n_t}=\frac{n_t}{1-\nu^t} nt​^​=1−νtnt​​
Δ θ t = − m t ^ n t ^ + ϵ ∗ η \Delta{\theta_t}=-\frac{\hat{m_t}}{\sqrt{\hat{n_t}}+\epsilon}*\eta Δθt​=−nt​^​ ​+ϵmt​^​​∗η
其中, m t m_t mt​, n t n_t nt​分别是对梯度的一阶矩估计和二阶矩估计,可以看作对期望 E ∣ g t ∣ E|g_t| E∣gt​∣, E ∣ g t 2 ∣ E|g_t^2| E∣gt2​∣的估计; m t ^ \hat{m_t} mt​^​, n t ^ \hat{n_t} nt​^​是对 m t m_t mt​, n t n_t nt​的校正,这样可以近似为对期望的无偏估计。
可以看出,直接对梯度的矩估计对内存没有额外的要求,而且可以根据梯度进行动态调整,而 − m t ^ n t ^ + ϵ -\frac{\hat{m_t}}{\sqrt{\hat{n_t}}+\epsilon} −nt​^​ ​+ϵmt​^​​对学习率形成一个动态约束,而且有明确的范围。

特点:

  1. 结合了Adagrad善于处理稀疏梯度和RMSprop善于处理非平稳目标的优点
  2. 对内存需求较小
  3. 为不同的参数计算不同的自适应学习率
  4. 也适用于大多非凸优化
  5. 适用于大数据集和高维空间

Adamax

Adamax是Adam的一种变体,此方法对学习率的上限提供了一个更简单的范围。公式上的变化如下:
n t = m a x ( ν ∗ n t − 1 , ∣ g t ∣ ) n_t=max(\nu*n_{t-1},|g_t|) nt​=max(ν∗nt−1​,∣gt​∣)
Δ x = − m t ^ n t + ϵ ∗ η \Delta{x}=-\frac{\hat{m_t}}{n_t+\epsilon}*\eta Δx=−nt​+ϵmt​^​​∗η

Nadam

Nadam类似于带有Nesterov动量项的Adam。加上:
深度学习--优化器

小结

  1. 对于稀疏数据,尽量使用学习率可自适应的优化方法,不用手动调节,而且最好采用默认值
  2. SGD通常训练时间更长,容易陷入鞍点,但是在好的初始化和学习率调度方案的情况下,结果更可靠
  3. 如果在意更快的收敛,并且需要训练较深较复杂的网络时,推荐使用学习率自适应的优化方法。
  4. Adadelta,RMSprop,Adam是比较相近的算法,在相似的情况下表现差不多。
  5. 在想使用带动量的RMSprop,或者Adam的地方,大多可以使用Nadam取得更好的效果
  6. 主流的观点认为:Adam等自适应学习率算法对于稀疏数据具有优势,且收敛速度很快;但精调参数的SGD(+Momentum)往往能够取得更好的最终结果。
上一篇:爬虫之初识


下一篇:【考试反思】联赛模拟测试 13 (To be continued)