深度学习之优化算法

在⼀个深度学习问题中,通常我们会预先定义⼀个损失函数。有了损失函数,就可以使⽤优化算法试图使其最小化。在优化中,这样的损失函数通常被称作优化问题的⽬标函数(objectivefunction)。依据惯例,优化算法通常只考虑最小化⽬标函数。因为任何最⼤化问题都可以很容易地转化为最小化问题:只需把⽬标函数前⾯的正号或负号取相反。

在深度学习问题中,由于优化算法的⽬标函数通常是⼀个基于训练数据集的损失函数,优化的⽬标在于降低训练误差。而深度学习的实际⽬标在于降低泛化误差。为了降低泛化误差,除了使⽤优化算法降低训练误差以外,还需要注意应对过拟合。

绝⼤多数深度学习中的⽬标函数都很复杂,因此,优化在深度学习中有很多挑战,其中最主要的两个:局部最小值鞍点

1 局部最小值

对于⽬标函数 f(x),如果 f(x) 在 x 上的值⽐在 x 邻近的其他点的值更小,那么 f(x) 可能是⼀个局部最小值(local minimum)。如果 f(x) 在 x 上的值是⽬标函数在整个定义域上的最小值,那么 f(x) 是全局最小值(global minimum)。例如:f(x) = x · cos(πx)

深度学习之优化算法_第1张图片

深度学习模型的⽬标函数可能有若⼲局部最优值。当⼀个优化问题的数值解在局部最优解附近时,由于⽬标函数有关解的梯度接近或变成零,最终迭代求得的数值解可能只令⽬标函数局部最小化而⾮全局最小化。

2 鞍点

梯度接近或变成零可能是由于当前解在局部最优解附近所造成的。事实上,另⼀种可能性是当前解在鞍点(saddle point)附近。例如:f(x) = x **3

深度学习之优化算法_第2张图片

深度学习中,虽然找到⽬标函数的全局最优解很难,但这并⾮必要。接下来我们将逐⼀介绍深度学习中常⽤的优化算法。

3 梯度下降和随机梯度下降

3.1 梯度下降

以⼀维梯度下降为例,解释梯度下降算法可以降低⽬标函数值的原因。⼀维梯度是⼀个标量,也称导数。假设函数 f : R → R 的输⼊和输出都是标量。根据泰勒展开公式,可得:

                                                                       f(X + \epsilon )\approx f(X) + f{}'(X)\epsilon

假设 η 是⼀个常数,将 ϵ 替换为 −ηf ′ (x) 后,则有:

                                                                  f(X-\eta f(X)) \approx f(X) - \eta f{}'(X)^{2}

即如果当前导数 f ′ (x) != 0,按照 x := x − ηf ′ (x) 更新 x 可能降低 f(x) 的值。

梯度下降算法中的 η(取正数)叫做学习率或步长。需要注意的是,学习率过⼤可能会造成 x迈过(overshoot)最优解,甚⾄不断发散而⽆法收敛;如果学习率过小,优化算法收敛速度会过慢。如下图所示:

现考虑更⼴义的情况:⽬标函数的输⼊为向量,输出为标量,即多维梯度下降。

假设⽬标函数 f : R d → R 的输⼊是⼀个多维向量 x = [x 1 ,x 2 ,...,x d ] ⊤ 。⽬标函数 f(x) 有关 x 的梯度是⼀个由偏导数组成的向量:

                                              \bigtriangledown _{x}f(X) = [\frac{\partial f(X)}{\partial X_{1}} , \frac{\partial f(X)}{\partial X_{2}} , ......, \frac{\partial f(X)}{\partial X_{d}}]^{T} = \bigtriangledown f(X)

梯度中每个偏导数元素 ∂f(x)/∂x i 代表着 f 在 x有关输⼊ x i 的变化率。为了测量 f 沿着单位向量 u ⽅向上的变化率,在多元微积分中,我们定义f 在 x 上沿着 u ⽅向的⽅向导数为:

                                                               D_{u}f(X) = \underset{h\rightarrow 0}{lim}\frac{f(X+hu)-f(X)}{h}

由链式法则,该⽅向导数可以改写为:

                                                                          D_{u}f(X) = \bigtriangledown f(X)\cdot u

⽅向导数 Du f(x) 给出了 f 在 x 上沿着所有可能⽅向的变化率。为了最小化 f,我们希望找到 f能被降低最快的⽅向。因此,我们可以通过 u 来最小化⽅向导数 Du f(x)。

由于 Du f(x) = ∥∇f(x)∥·∥u∥·cos(θ) = ∥∇f(x)∥·cos(θ),其中 θ 为 ∇f(x) 和 u 之间的夹⻆,当θ = π,cos(θ) 取得最小值-1。因此,当 u 在梯度⽅向 ∇f(x) 的相反⽅向时,⽅向导数 Du f(x) 被最小化。所以,我们可能通过下⾯的 梯度下降算法来不断降低⽬标函数 f 的值:

                                                                        X := X-\eta \bigtriangledown f(X)

3.2 随机梯度下降

当训练数据集很⼤时,梯度下降算法可能会难以使⽤。为了解决这一问题,考虑⽬标函数:

                                                                      f(X) = \frac{1}{n}\sum_{i=1}^{n}f_{i}(X)

其中 fi (x) 是有关索引为 i 的训练数据点的损失函数。需要强调的是,梯度下降每次迭代的计算开销随着 n 线性增长。因此,当 n 很⼤时,每次迭代的计算开销很⾼。

于是随机梯度下降算法便被需要。在每次迭代时,该算法随机均匀采样 i 并计算 ∇fi (x)。事实上,随机梯度 ∇fi (x) 是对梯度 ∇f(x) 的⽆偏估计:

                                                      E_{i}\bigtriangledown f_{i}(X)=\frac{1}{n}\sum_{i=1}^{n}\bigtriangledown f_{i}(X)=\bigtriangledown f(X)

3.3 小批随机梯度下降

⼴义上,每次迭代可以随机均匀采样⼀个由训练数据点索引所组成的小批量 B。类似地,我们可以使⽤:

                                                                 \bigtriangledown f_{\ss }(X)=\frac{1}{|\ss |}\sum_{i\in \ss }\bigtriangledown f_{i}(X)

来更新 x:

                                                                   X := X-\eta \bigtriangledown f_{\ss }(X)

其中 |B| 代表批量中索引数量,η(取正数)称作学习率或步长。同样,小批量随机梯度 ∇fB (x) 也是对梯度 ∇f(x) 的⽆偏估计:

                                                                E_{\ss }\bigtriangledown f_{\ss }(X)=\bigtriangledown f(X)

该算法称小批量随机梯度下降。该算法每次迭代的计算开销为 O(|B|)。因此,当批量较小时,每次迭代的计算开销也较小。

4 动量法

梯度下降的问题:考虑⼀个输⼊为⼆维向量 x = [x 1 ,x 2 ] ⊤ ,输出为标量的⽬标函数 f : R 2 → R。下⾯为该函数的等⾼线⽰意图(每条等⾼线表⽰相同函数值的点:越靠近中间函数值越小)。

深度学习之优化算法_第3张图片

上图中,红⾊三⻆形代表参数 x 的初始值。带箭头的线段表⽰每次迭代时参数的更新。由于⽬标函数在竖直⽅向(x 2 轴⽅向)上⽐在⽔平⽅向(x 1 轴⽅向)弯曲得更厉害,梯度下降迭代参数时会使参数在竖直⽅向⽐在⽔平⽅向移动更猛烈。因此,我们需要⼀个较小的学习率从而避免参数在竖直⽅向上 overshoot。这就造成了上图中参数向最优解移动速度的缓慢。

动量法的提出是为了应对梯度下降的上述问题。Momentum的想法很简单,就是多更新一部分上一次迭代的更新量,来平滑这一次迭代的梯度。从物理的角度上解释,就像一个小球滚落的时候会受到自身历史动量的影响,所以才叫动量(Momentum)算法。这样做直接的效果就是使得梯度下降的的时候转弯掉头的幅度不那么大了,能够更加平稳、快速地冲向局部最小点。

⼴义上,以小批量随机梯度下降为例(当批量⼤小等于训练集⼤小时,该算法即为梯度下降;批量⼤小为 1 即为随机梯度下降),对小批量随机梯度算法做如下修改:

                                                                               V:=\gamma V+\eta \bigtriangledown f_{\ss }(X)

                                                                               X:=X-V

其中 v 是当前速度,γ 是动量参数。

当前速度 v 的更新可以理解为对 [η/(1 − γ)]*∇fB (x) 做指数加权移动平均。因此,动量法的每次迭代中,参数在各个⽅向上移动幅度不仅取决当前梯度,还取决过去各个梯度在各个⽅向上是否⼀致。当过去的所有梯度都在同⼀⽅向,例如都是⽔平向右,那么参数在⽔平向右的移动幅度最⼤。如果过去的梯度中在竖直⽅向上时上时下,那么参数在竖直⽅向的移动幅度将变小。这样,我们就可以使⽤较⼤的学习率,从而收敛更快。如下图:

深度学习之优化算法_第4张图片

为了更好的理解动量参数 γ,让我们考虑⼀个简单的问题:每次迭代的小批量随机梯度 ∇fB (x) = g。由于所有小批量随机梯度都在同⼀⽅向,动量法在该⽅向使参数移动加速:

                                                                  V_{1}:=\eta\cdot g

                                                                  V_{2}:=\gamma V_{1}+\eta \cdot g=\eta g(\gamma +1)

                                                                  V_{3}:=\gamma V_{2}+\eta \cdot g=\eta g(\gamma ^{2}+\gamma +1)

                                                                  ......

                                                                 V_{n}:=\gamma V_{n-1}+\eta \cdot g=\eta g(\gamma ^{n}+......+\gamma ^{2}+\gamma +1)

                                                                 V_{inf}=\underset{n\rightarrow \infty }{lim}\eta g\frac{1-\gamma ^{n}}{1-\gamma }=\frac{\eta g}{1-\gamma }

当 γ = 0.99, 最终的速度将是学习率乘以相应小批量随机梯度 ηg 的 100 倍⼤。

动量法可以提升随机梯度下降,对于某些问题可以选⽤较⼤学习率从而加快收敛。

5 Adagrad算法

⽆论是梯度下降、随机梯度下降、小批量随机梯度下降还是使⽤动量法,模型参数中的每⼀个元素在相同时刻都使⽤同⼀个学习率来⾃我迭代。当⼀个模型的损失函数为 L,参数为⼀个多维向量 [x1 ,x2 ] ⊤ 时,该向量中每⼀个元素在更新时都使⽤相同的学习率,例如在学习率为 η 的梯度下降中:

                                                                            x_{1}:=x_{1}-\eta \frac{\partial L}{\partial x_{1}}

                                                                            x_{2}:=x_{2}-\eta \frac{\partial L}{\partial x_{2}}

其中元素 x 1 和 x 2 都使⽤相同的学习率 η 来⾃我迭代。如果让 x 1 和 x 2 使⽤不同的学习率⾃我迭代呢?
Adagrad 就是⼀个在迭代过程中不断⾃我调整学习率,并让模型参数中每个元素都使⽤不同学习率的优化算法。

假设x = [4,9] ⊤ ,按元素操作后:

   • 按元素相加:x + 1 = [5,10] ⊤
   • 按元素相乘:x ⊙ x = [16,81] ⊤
   • 按元素相除:72/x = [18,8] ⊤
   • 按元素开⽅: √ x = [2,3] ⊤

Adagrad其实就是对学习率加上了一个正则约束regularizer:

                                                                      \frac{\eta }{\sqrt{s+\epsilon }}=\frac{\eta }{\sqrt{\sum_{r=1}^{t}g_{r}^{2}+\epsilon }}

  • 在前期g_{r}比较小的时候,regularizer较大,能够放大梯度;
  • 在前期g_{r}比较大的时候,regularizer较小,能够约束梯度;
  • 适合处理稀疏梯度;

Adagrad 的算法会使⽤⼀个梯度按元素平⽅的累加变量 s,并将其中每个元素初始化为 0。在每次迭代中,⾸先计算小批量梯度 g,然后将该梯度按元素平⽅后累加到变量 s:

                                                                             s:=s+g\odot g

然后我们将模型参数中每个元素的学习率通过按元素操作重新调整⼀下:

                                                                            g{}':=\frac{\eta }{\sqrt{s+\epsilon }}\odot g

                                                                               X:=X-g{}'

其中 η 是初始学习率,ϵ 是为了维持数值稳定性而添加的常数,例如 10 **(−7) 。请注意其中按元素开⽅、除法和乘法的操作。这些按元素操作使得模型参数中每个元素都分别拥有⾃⼰的学习率。由于梯度按元素平⽅的累加变量 s 出现在分⺟,Adagrad 的核⼼思想是:如果模型损失函数有关⼀个参数元素的偏导数⼀直都较⼤,那么就让它的学习率下降快⼀点;反之,如果模型损失函数有关⼀个参数元素的偏导数⼀直都较小,那么就让它的学习率下降慢⼀点。然而,由于 s ⼀直在累加按元素平⽅的梯度,每个元素的学习率在迭代过程中⼀直在降低或不变。所以在有些问题下,当学习率在迭代早期降得较快时且当前解依然不理想时,Adagrad 在迭代后期可能较难找到⼀个有⽤的解。

 Adagrad 是⼀个在迭代过程中不断⾃我调整学习率,并让模型参数中每个元素都使⽤不同学习率的优化算法。

缺点:

  • 任然还是会依赖初始的人工设置的学习率;
  • \eta设置过大的话,会使regularizer过于敏感,对梯度的调节太大;
  • 中后期,分母上的梯度平方的累加会越来越大,会使得gradient\rightarrow 0,使得训练提前结束;

6 RMSProp算法

在Adagrad⾥,由于学习率分⺟上的变量 s ⼀直在累加按元素平⽅的梯度,每个元素的学习率在迭代过程中⼀直在降低或不变。所以在有些问题下,当学习率在迭代早期降得较快时且当前解依然不理想时,Adagrad 在迭代后期可能较难找到⼀个有⽤的解。为了应对这⼀问题,RMSProp 算法对 Adagrad 做了⼀点小小的修改。

RMSProp 算法会使⽤⼀个梯度按元素平⽅的指数加权移动平均变量 s,并将其中每个元素初始化为 0。在每次迭代中,⾸先计算小批量梯度 g,然后对该梯度按元素平⽅后做指数加权移动平均并计算 s:

                                                                                 s:=\gamma s+(1-\gamma )g\odot g

然后我们将模型参数中每个元素的学习率通过按元素操作重新调整⼀下:

                                                                                      g{}':=\frac{\eta }{\sqrt{s+\epsilon }}\odot g

                                                                                          X:=X-g{}'

其中 η 是初始学习率,ϵ 是为了维持数值稳定性而添加的常数,例如 10 **(−8 )。和 Adagrad ⼀样,模型参数中每个元素都分别拥有⾃⼰的学习率。

需要强调的是:RMSProp 只在 Adagrad 的基础上修改了变量 s 的更新⽅法——将累加改成了指数加权移动平均。因此,每个元素的学习率在迭代过程中既可能降低⼜可能升⾼。

  •  RMSProp 和 Adagrad 的不同在于,RMSProp 使⽤了梯度按元素平⽅的指数加权移动平均变量来调整学习率。
  • 通过调整指数加权移动平均中 γ 参数的值可以控制学习率的变化。
  • 依然依赖全局学习率;
  • RMSProp其实是Adagrad的发展而来,更一般化;
  • 适合处理非平稳目标,对RNN效果很好;

7 Adadelta算法

Adadelta是Adagrad的扩展,最初方案依然是对学习率进行自适应约束,但是进行了计算上的简化。Adagrad会累加之前所有梯度的平方,而Adadelta只是累加大小的项,并且也不直接存储这些项,仅仅只是近似的计算对应的平均值。

在Adagrad⾥,由于学习率分⺟上的变量 s ⼀直在累加按元素平⽅的梯度,每个元素的学习率在迭代过程中⼀直在降低或不变。所以在有些问题下,当学习率在迭代早期降得较快时且当前解依然不理想时,Adagrad 在迭代后期可能较难找到⼀个有⽤的解。在RMSProp中介绍了应对该问题的⼀种⽅法:对梯度按元素平⽅使⽤指数加权移动平均而不是累加。

事实上,Adadelta 也是⼀种应对这个问题的⽅法。并且它没有学习率参数。

Adadelta 算法和 RMSProp ⼀样,使⽤了⼀个梯度按元素平⽅的指数加权移动平均变量 s,并将其中每个元素初始化为 0。在每次迭代中,⾸先计算小批量梯度 g,然后对该梯度按元素平⽅后做指数加权移动平均并计算 s:

                                                                              s:=\rho s+(1-\rho )g\odot g

然后计算当前需要更新的参数的变化量:

                                                                                g{}'=\frac{\sqrt{\bigtriangleup X+\epsilon }}{\sqrt{s+\epsilon }}\odot g

                                                                                      X:=X-g{}'

其中 ϵ 是为了维持数值稳定性而添加的常数,例如 10 **(−5) 。和 Adagrad ⼀样,模型参数中每个元素都分别拥有⾃⼰的学习率。其中 ∆x 初始化为零张量,并做如下 g ′ 按元素平⽅的指数加权移动平均:

                                                                          \bigtriangleup X:=\rho \bigtriangleup X+(1-\rho )g{}'\odot g

  • Adadelta不需要手动调整学习速率
  • 对超参数不敏感
  • 将对每个维度的学习率单独分离出来
  • 最小化梯度下降时的计算量
  • 对大梯度,噪声,不同架构具有很好的健壮性
  • 对本地或分布式环境都可很好的应用
  • 在训练初期,加速效果不错,很快;在训练后期,反复在局部最小值附近抖动

8 Adam算法

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

Adam 是动量法和RMSProp的组合优化算法。Adam 算法会使⽤⼀个动量变量 v 和⼀个 RMSProp 中梯度按元素平⽅的指数加权移动平均变量s,并将它们中每个元素初始化为 0。在每次迭代中,⾸先计算小批量梯度 g,并递增迭代次数:

                                                                                        t:=t+1

然后对梯度做指数加权移动平均并计算动量变量 v:

                                                                                 V:=\beta _{1}V+(1-\beta_{1} )g

该梯度按元素平⽅后做指数加权移动平均并计算 s:

                                                                                s:=\beta _{2}s+(1-\beta_{2} )g\odot g

在 Adam 算法⾥,为了减轻 v 和 s 被初始化为 0 在迭代初期对计算指数加权移动平均的影响,做如下的偏差修正:

                                                                                      \hat{V}:=\frac{V}{1-\beta _{1}^{t}}

                                                                                       \hat{s}:=\frac{s}{1-\beta _{2}^{t}}

可以看到,当 0 ≤ β1 ,β2 < 1 时(算法作者建议分别设为 0.9 和 0.999),当迭代后期 t 较⼤时,偏差修正⼏乎就不再有影响。使⽤以上偏差修正后的动量变量和 RMSProp 中梯度按元素平⽅的指数加权移动平均变量,将模型参数中每个元素的学习率通过按元素操作重新调整⼀下:

                                                                                     g{}'=\frac{\eta \hat{V}}{\sqrt{\hat{s}+\epsilon }}

                                                                                    X:=X-g{}'

其中 η 是初始学习率,ϵ 是为了维持数值稳定性而添加的常数,例如 10 **(−8) 。和 Adagrad ⼀样,模型参数中每个元素都分别拥有⾃⼰的学习率。

其中 V、s分别是对梯度的一阶矩和二阶矩的估计,可以看做是对期望E|g|和E|g**2|的估计。V^、s^是对V,s的校正,可以近似为对期望的无偏估计。可以看出直接对梯度的矩估计对内存没有额外的要求,而且可以根据梯度进行动态调整,g‘中的η 的系数对学习率η 形成一个动态约束,而且有明确的范围。

特点:

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

Adam建议的参数设定:

TensorFlow:learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-08.

Keras:lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0

Blocks:learning_rate=0.002, beta1=0.9, beta2=0.999, epsilon=1e-08, decay_factor=1

Lasagne:learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-08

Caffe:learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-08

MxNet:learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8

Torch:learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8

 

你可能感兴趣的:(深度学习,BGD/SGD/MBGD,动量法,Adagrad,Adam)