深度学习的优化问题
深度学习的优化问题,主要是定义在训练集上的目标函数J(θ)最小,由于目标函数一般是非凸的,此处的最小一般是指一个相对最优的局部极小值。求解目标函数的最小值,可以通过梯度下降法更新模型的参数θ实现。梯度下降法有一个非常好懂的名字叫下山法,也就是在梯度的反方向(值下降的方向)更新参数。与梯度下降法密切相关的是学习率η,学习率决定了每一步沿梯度反方向更新的数值,学习率的选择不仅影响达到最小值的步数,还影响到能否达到最小值。
梯度优化的基础知识
Batch gradient descent
在整个数据集上计算梯度,(1)计算量大但没有得到与计算量成正比的回报。(根据误差传播规律,误差与采样的数量n的关系是,如10000个样本只比100个样本降低了10倍的误差)。(2)训练集是一般是冗余的,使用整个训练集进行梯度计算是没有必要的。
Stochastic gradient descent
随机梯度下降法,在每个批次的训练的过程中将数据集随机打乱,然后对每个样本进行梯度计算并更新参数。随机梯度下降法的优点,计算量小且是有机会跳到更优的位置(如下图所示),当学习率较小的时候其可定可以收敛到的局部或全局最小点。
Mini-batch gradient descent
大多数用于深度学习梯度优化方法介于批量和随机梯度(SGD)之间,使用一个以上却又不是全部样本,可称为随机小批量梯度下降或小批量梯度下降,其关键在于数据要随机打乱。其既有批量计算的稳定性,由于在小批量的计算过程中加入了一些噪声(梯度更新的时候使用的时小批量梯度的均值),其具有一定的正则性;小批量更能有效率利用计算资源,优于随机梯度和批量梯度法。
梯度优化问题的挑战
-
1 如何选择合适的学习速率,既加快收敛又保证收敛;
-
2 如何在训练中根据训练情况相应的调整学习速率;
-
3 如何根据特征相应的改变学习速率,而不是所有的特征都使用同样的学习速率,如一些特征是低频的,则其对使用大的学习速率,则效果更好;
-
4 如何避免陷于鞍点,而无法达到最小值;
-
5 设计易于收敛的模型而不是复杂的优化算法。
梯度优化的基本算法
梯度优化算法的,主要集中在如何实现梯度更合理的更新上,从而提高收敛速度并保证最终优化结果是收敛的。
SGD
SGD第k次训练的迭代 |
---|
Require:学习速率 εk、初始参数 θ |
while 停止准则未满足 do: 从训练集中采样m个样本{x1…xm}的minibatch,其中xi对应的目标为yi。 计算梯度估计: g k = + 1 m ∇ θ ∑ i L ( f ( x i , θ ) , y i ) g_{k}=+\frac{1}{m}\nabla_{\theta}\sum_{i}L(f(x_{i},\theta),y_{i}) gk=+m1∇θ∑iL(f(xi,θ),yi) 参数更新: θ = θ − ϵ k g k \theta=\theta-\epsilon_{k}g_{k} θ=θ−ϵkgk end while |
SGD算法的关键是学习速率,为保证在极小点位置收敛,需要使用较小的较小的学习速率,故需要使用一定的策略,随着迭代次数的增减进行学习速率的衰减。在TensorFlow中SGD可通过tf.train.GradientDescentOptimizer实现,其初始化的时候需要传递学习速率learning_rate,learning_rate可定义为tensor。在 eager execution 激活的情况下,learning_rate可以是一个不带参数的调用,并返回实际使用值(float),可用来进行学习速率的衰减。
#参考实现SGD学习速率的指数衰减:https://blog.csdn.net/leviopku/article/details/78508951
import tensorflow as tf
import numpy as np
x = tf.placeholder(tf.float32, shape=[None, 1], name='x')
y = tf.placeholder(tf.float32, shape=[None, 1], name='y')
w = tf.Variable(tf.constant(0.0))
global_steps = tf.Variable(0, trainable=False)
#学习率的指数衰减
learning_rate = tf.train.exponential_decay(0.1, global_steps, 10, 2, staircase=False)
loss = tf.pow(w*x-y, 2)
train_step= tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=global_steps)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for i in range(10):
sess.run(train_step, feed_dict={x:np.linspace(1,2,10).reshape([10,1]),
y:np.linspace(1,2,10).reshape([10,1])})
print(sess.run(learning_rate))
print(sess.run(global_steps))
Moment及Nesterov
Moment算法是SGD算法的加速算法加入了动量参数
α
\alpha
α,其主要思想可解释为如果总是观测到梯度g,那么它会在-g方向不停加速(通俗理解为小球沿着山坡向下滚的过程中速度会越来越快),直到达到最终速度,其最终步长大小为:
ϵ
=
ϵ
∣
∣
g
∣
∣
1
−
α
\epsilon=\frac{\epsilon||g||}{1-\alpha}
ϵ=1−αϵ∣∣g∣∣当动量参数α为0.9的时候,其对应着最大速度10倍于梯度下降法。
Moment第k次训练的迭代 |
---|
Require:学习速率
ϵ
k
\epsilon_{k}
ϵk、动量参数
α
\alpha
α Require:初始参数 θ \theta θ、初始速度 v ( 0 ) v(0) v(0) |
while 停止准则未满足 do: 从训练集中采样m个样本{ x 1 x_{1} x1… x m x_{m} xm}的minibatch,其中 x i x_{i} xi对应的目标为 y i y_{i} yi。 计算梯度估计: g k = + 1 m ∇ θ ∑ i L ( f ( x i , θ ) , y i ) g_{k}=+\frac{1}{m}\nabla_{\theta}\sum_{i}L(f(x_{i},\theta),y_{i}) gk=+m1∇θ∑iL(f(xi,θ),yi) 速度计算: v = α v − g k v=\alpha{v}-g_{k} v=αv−gk 参数更新: θ = θ + v \theta=\theta+v θ=θ+v end while |
Nesterov是Moment法的一个变种,二者的区别主要在与梯度的计算上,其可以理解为在标准的动量法中加了一个校正因子。
Nesterov第k次训练的迭代 |
---|
Require:学习速率
ϵ
k
\epsilon_{k}
ϵk、动量参数
α
\alpha
α Require:初始参数 θ \theta θ、初始速度 v ( 0 ) v(0) v(0) |
while 停止准则未满足 do: 从训练集中采样m个样本{ x 1 x_{1} x1… x m x_{m} xm}的minibatch,其中 x i x_{i} xi对应的目标为 y i y_{i} yi。 参数临时更新: θ ^ = θ + α v \hat{\theta}=\theta+\alpha{v} θ^=θ+αv 计算梯度估计: g k = + 1 m ∇ θ ∑ i L ( f ( x i , θ ^ ) , y i ) g_{k}=+\frac{1}{m}\nabla_{\theta}\sum_{i}L(f(x_{i},\hat{\theta}),y_{i}) gk=+m1∇θ∑iL(f(xi,θ^),yi) 速度计算: v = α v − g k v=\alpha{v}-g_{k} v=αv−gk 参数更新: θ = θ + v \theta=\theta+v θ=θ+v end while |
Moment和Nesterov的思想都是加速度的思想,其基础结构是一致的,故其在tensorflow中是通过tf.train.MomentumOptimizer实现的,默认use_nesterov=False为Moment方法,设置use_nesterov=True,则为Nesterov方法。get_slot函数可用来对动量法过程中新建立的速度进行跟踪。(Some Optimizer subclasses use additional variables. For example Momentum and Adagrad use variables to accumulate updates. This method gives access to these Variable objects if for some reason you need them.)动量法中,即使梯度为零,由于动量的存在其仍然可以进行参数更新。
AdaGrad
AdaGrad是一种自适应学习率的优化算法,其在更新梯度的时候将初始学习率梯度除以累计平方梯度,从而实现对不同参数的梯度调整,主要表现为:对于高频特征(累计平方梯度大),其更新较小;对于稀疏特征(累积平方梯度小),则其更新较大。然而,Goodfellow在深度学习一书中指出“经验上已经发现,对于训练深度神经网络而言,从训练开始累积平方梯度会导致有效学习率过快和过量的减小”。AdaGrad的计算量和存储空间比动量法大。
AdaGrad第k次训练的迭代 |
---|
Require:全局学习速率
ϵ
\epsilon
ϵ Require:初始参数 θ \theta θ Require:小常数 δ \delta δ,为了数值稳定大约设为1e-7;初始化梯度累积变量r=0 |
while 停止准则未满足 do: 从训练集中采样m个样本{ x 1 x_{1} x1… x m x_{m} xm}的minibatch,其中 x i x_{i} xi对应的目标为 y i y_{i} yi。 计算梯度估计: g k = + 1 m ∇ θ ∑ i L ( f ( x i , θ ) , y i ) g_{k}=+\frac{1}{m}\nabla_{\theta}\sum_{i}L(f(x_{i},\theta),y_{i}) gk=+m1∇θ∑iL(f(xi,θ),yi) 累积平方梯度: r = r + g k ⨀ g k r=r+g_{k}\bigodot{g_{k}} r=r+gk⨀gk 计算更新量: Δ θ = − ϵ δ + r ⨀ g k \Delta\theta=-\frac{\epsilon}{\delta+\sqrt{r}}{\bigodot}g_{k} Δθ=−δ+rϵ⨀gk 参数更新: θ = θ + Δ θ \theta=\theta+\Delta\theta θ=θ+Δθ end while |
Tensorflow中AdaGrad方法的实现tf.train.AdagradOptimizer,初始化的时候需要传递学习率,累加器的默认初始值为0.1。
RMSProp
RMSProp算法修改AdaGrad算法可以在非凸设定下效果更好,其主要做法是改变梯度累积为指数加权平均移动。AdaGrad根据梯度平方的整个历史收缩学习率,可能使得学习率过快、过早的减小。RMSProp算法使用指数衰减p舍弃早期的梯度(早期梯度贡献为 p k p^{k} pk,默认为0.9),使得学习速率不会过快减小,从而能在极小值处快速收敛。
RMSPropd第k次训练的迭代 |
---|
Require:全局学习速率
ϵ
\epsilon
ϵ,衰减率
ρ
\rho
ρ Require:初始参数 θ \theta θ Require:小常数 δ \delta δ,为了数值稳定大约设为1e-7;初始化梯度累积变量r=0 |
while 停止准则未满足 do: 从训练集中采样m个样本{ x 1 x_{1} x1… x m x_{m} xm}的minibatch,其中 x i x_{i} xi对应的目标为 y i y_{i} yi。 计算梯度估计: g k = + 1 m ∇ θ ∑ i L ( f ( x i , θ ) , y i ) g_{k}=+\frac{1}{m}\nabla_{\theta}\sum_{i}L(f(x_{i},\theta),y_{i}) gk=+m1∇θ∑iL(f(xi,θ),yi) 累积平方梯度: r = ρ r + ( 1 − ρ ) g k ⨀ g k r=\rho{r}+(1-\rho)g_{k}\bigodot{g_{k}} r=ρr+(1−ρ)gk⨀gk 计算更新量: Δ θ = − ϵ δ + r ⨀ g k \Delta\theta=-\frac{\epsilon}{\delta+\sqrt{r}}{\bigodot}g_{k} Δθ=−δ+rϵ⨀gk 参数更新: θ = θ + Δ θ \theta=\theta+\Delta\theta θ=θ+Δθ end while |
Tensorflow中的RMSProp算法与上面描述的略有不同,其增加了动量项(非Nesterov),可以在梯度为零的情况下实现对参数的更新。Tensorflow中通过tf.train.RMSPropOptimizer实现,默认的decay=0.9,momentmun=0.0。
Adam
Adam修正了从原点开始的一阶矩(动量)和(非中心)二阶矩的估计,Adam对超参数的选择相当鲁棒。
Adam第k次训练的迭代 |
---|
Require:全局学习速率
ϵ
\epsilon
ϵ(默认为0.001,常需要修改变小) Require:初始参数 θ \theta θ Require:小常数 δ \delta δ,为了数值稳定大约设为1e-8; Require:矩估计衰减率, ρ 1 、 ρ 2 \rho_{1}、\rho_{2} ρ1、ρ2,在区间[0,1),默认为0.9, 0.999 初始化一阶矩、二阶矩变量s=0,r=0,初始化时间步t=0 |
while 停止准则未满足 do: 从训练集中采样m个样本{ x 1 x_{1} x1… x m x_{m} xm}的minibatch,其中 x i x_{i} xi对应的目标为 y i y_{i} yi。 计算梯度估计: g k = + 1 m ∇ θ ∑ i L ( f ( x i , θ ) , y i ) g_{k}=+\frac{1}{m}\nabla_{\theta}\sum_{i}L(f(x_{i},\theta),y_{i}) gk=+m1∇θ∑iL(f(xi,θ),yi) t = t + 1 t=t+1 t=t+1 更新有偏一阶估计: s = ρ 1 s + ( 1 − ρ 1 ) g k s=\rho_{1}s+(1-\rho_{1})g_{k} s=ρ1s+(1−ρ1)gk 更新有偏二阶估计: r = ρ 2 r + ( 1 − ρ 2 ) g k ⨀ g k r=\rho_{2}{r}+(1-\rho_{2})g_{k}\bigodot{g_{k}} r=ρ2r+(1−ρ2)gk⨀gk 修正一阶矩的偏差: s = s 1 − ρ 1 t s=\frac{s}{1-{\rho_{1}}^{t}} s=1−ρ1ts 修正二阶矩的偏差: r = r 1 − ρ 2 t r=\frac{r}{1-{\rho_{2}}^{t}} r=1−ρ2tr 计算更新量: Δ θ = − ϵ s δ + r \Delta\theta=-\epsilon\frac{s}{\delta+\sqrt{r}} Δθ=−ϵδ+rs 参数更新: θ = θ + Δ θ \theta=\theta+\Delta\theta θ=θ+Δθ end while |
总结
下图所示为各种梯度优化算法的可视化,从效果来看SGD(较小的学习率)、Adadelta、Rmsprop比较稳定,收敛速度自适应学习率算法比较接近。Goodfellow在《深度学习》一书中也指出优化算法的选择没有明确的指标可以衡量,选择算法似乎主要取决于使用者对算法的熟悉度。
图片引自http://ruder.io/optimizing-gradient-descent/
参考
(1)http://ruder.io/optimizing-gradient-descent/
(2)http://ruder.io/deep-learning-optimization-2017/
(3)深度学习[M]-第8章深度学习中的优化