原文:An overview of gradient descent optimization algorithms
梯度下降算法
梯度下降算法(Gradient Descent Optimization)是神经网络模型训练最常用的优化算法。详见:梯度下降
梯度下降算法的原理:
-
目标函数 J ( θ ) J(\theta) J(θ) 关于参数 θ \theta θ 的梯度将是损失函数(loss function)上升最快的方向。
-
要最小化 loss,只需要将参数沿着梯度相反的方向前进一个步长,就可以实现目标函数(loss function)的下降。这个步长 η \eta η 又称为学习速率。
-
参数更新公式如下:
θ ← θ − η ⋅ ∇ J ( θ ) \theta\leftarrow \theta-\eta \cdot \nabla J(\theta) θ←θ−η⋅∇J(θ)
-
其中 ∇ J ( θ ) \nabla J(\theta) ∇J(θ) 是参数的梯度。
根据计算目标函数采用数据量的不同,梯度下降算法又可以分为:
-
批量梯度下降算法(Batch Gradient Descent,BGD)
其 J ( θ ) J(\theta) J(θ) 是在整个训练集上计算的,如果数据集比较大,可能会面临内存不足问题,而且收敛速度一般比较慢。
-
随机梯度下降算法(Stochastic Gradient Descent,SGD)
又称为在线学习,即得到了一个样本,就可以执行一次参数更新。所以其收敛速度会快一些,但是有可能出现目标函数值震荡现象,因为高频率的参数更新导致了高方差。
-
小批量梯度下降算法(Mini-batch Gradient Descent,MBGD)。
是折中方案,选取训练集中一个小批量样本(一般是2的倍数,如32,64,128等)计算,这样可以保证训练过程更稳定,而且采用批量训练方法也可以利用矩阵计算的优势,是目前最常用的梯度下降算法。
问题与挑战:
-
选择一个合适的学习率可能是困难的。学习率太小会导致收敛的速度很慢,学习率太大会妨碍收敛,导致损失函数在最小值附近震荡甚至发散。
-
虽然可以通过退火等方法调整学习率,但策略和阈值需要预先设定好,因此无法适应数据集的特点。
-
另一方面,SGD 不易处理鞍点处的梯度。由于鞍点周围的误差相同,梯度在所有方向都接近于0,导致 SGD 很难脱离鞍点。
Momentum
-
SGD(指上面的三种方法)的参数更新完全依赖于当前的 batch,容易出现震荡现象
-
动量(Momentum optimization) 通过将当前时刻的梯度 η ∇ θ J ( θ ) \eta \nabla_\theta J( \theta) η∇θJ(θ),与前一时刻的梯度 v t − 1 v_{t-1} vt−1 按照动量的系数 γ γ γ 进行加权,使得当前的更新方向包括当前梯度的方向与之前时刻梯度的方向,来增加更新的稳定性,同时抑制 SGD 的震荡现象。
v t = γ v t − 1 + η ∇ θ J ( θ ) v_t = \gamma v_{t-1} + \eta \nabla_\theta J( \theta) vt=γvt−1+η∇θJ(θ)
θ = θ − v t \theta = \theta - v_t θ=θ−vt
-
动量的系数 γ γ γ 表示保留原来方向的程度,通常设置为 0.9 左右。
-
当梯度指向收敛方向时,动量加快更新速度;当梯度改变方向时,动量降低更新速度。从而在保证收敛更快的同时,减弱震荡现象。
NAG
-
NAG 即 Nesterov accelerated gradient 涅斯捷罗夫梯度加速。相比于momentum,NAG 增加了一个将要去哪里的指示,因而在曲面上升时,能够降低更新速度。
-
NAG 在计算参数的梯度时,在损失函数中减去了动量项,即计算 ∇ θ J ( θ − γ ν t − 1 ) \nabla_{\theta}J(\theta-\gamma \nu_{t-1}) ∇θJ(θ−γνt−1),这种方式预估了下一次参数所在的近似位置。即:
ν t = γ ν t − 1 + η ⋅ ∇ θ J ( θ − γ ν t − 1 ) \nu_t=\gamma\nu_{t-1}+\eta \cdot \nabla_{\theta}J(\theta-\gamma \nu_{t-1}) νt=γνt−1+η⋅∇θJ(θ−γνt−1)
θ = θ − ν t \theta=\theta-\nu_t θ=θ−νt
-
动量系数 γ γ γ 通常设置为 0.9 左右。
-
在 Momentum 中,首先计算当前梯度项(小蓝色向量),然后加上动量项,这样便得到了大的跳跃(大蓝色的向量)。
-
NAG 首先进行一个大的跳跃(动量项)(棕色向量),然后加上一个小的使用了动量计算的当前梯度(红色向量)进行修正得到绿色的向量,避免更新过猛。
Adagrad
-
Adagrad 即 Adaptive Gradient Algorithm 自适应梯度算法。
-
上面的方法在参数更新过程中,使用了固定的学习率。Adagrad 则是让学习率适应参数。
-
对于出现次数较少的特征,对其采用更大的学习率,对于出现次数较多的特征,对其采用较小的学习率。因而适用于处理稀疏数据。
-
令 g t , i g_{t, i} gt,i 为在 t t t 时刻目标函数关于参数 θ i \theta_i θi 的梯度:
g t , i = ∇ θ J ( θ i ) g_{t, i} = \nabla_\theta J( \theta_i ) gt,i=∇θJ(θi)
-
通常,在 t t t 时刻,对参数 θ i \theta_i θi 的更新过程为:
θ t + 1 , i = θ t , i − η ⋅ g t , i \theta_{t+1, i} = \theta_{t, i} - \eta \cdot g_{t, i} θt+1,i=θt,i−η⋅gt,i
-
使用 Adagrad 时,在 t t t 时刻,基于对 θ i \theta_i θi 计算过的历史梯度,对每一个参数 θ i \theta_i θi 的学习率进行了修正:
θ t + 1 , i = θ t , i − η G t , i i + ϵ ⋅ g t , i \theta_{t+1, i} = \theta_{t, i} - \dfrac{\eta}{\sqrt{G_{t, ii} + \epsilon}} \cdot g_{t, i} θt+1,i=θt,i−Gt,ii+ϵη⋅gt,i
-
其中, G t ∈ R d × d G_{t} \in \mathbb{R}^{d \times d} Gt∈Rd×d 是一个对角矩阵。
-
第 i i i 行的对角元素 e i i e_{ii} eii 为第 i i i 个参数 θ i \theta_i θi 的历史梯度的平方和。
-
ϵ \epsilon ϵ 是一个平滑参数,为了使得分母不为 0 (通常 ϵ \epsilon ϵ 取 e − 8 e−8 e−8 )。
-
另外如果分母不开根号,算法性能会很糟糕。
-
再进一步,将所有 G t , i i , g t , i G_{t,ii},g_{t,i} Gt,ii,gt,i 的元素写成向量 G t , g t G_t,g_t Gt,gt , Adagrad 算法参数更新的矩阵形式如下:
θ t + 1 = θ t − η G t + ϵ ⊙ g t \theta_{t+1} = \theta_{t} - \frac{\eta}{\sqrt{G_{t}+\epsilon}} \odot g_{t} θt+1=θt−Gt+ϵη⊙gt
-
其中 ⊙ 为点乘,即矩阵对应元素相乘。
-
Adagrad 的优点是无需手动调整学习率。在大多数的应用场景中,通常采用常数0.01。
-
Adagrad的缺点是它在分母中累加梯度的平方,在整个训练过程中,累加的和会持续增长,导致学习率最终变得无限小,无法学习到新的特征。
Adadelta
-
Adadelta 是 Adagrad 的一种扩展算法,以处理 Adagrad 学习速率单调递减的问题。
-
Adadelta 只计算窗口大小为 w w w 的最近历史梯度,同时,并不存储历史平方梯度值,而是将梯度的平方递归地表示成所有历史梯度平方的均值。
-
在 t t t 时刻的均值 E [ g 2 ] t E[g^2]_t E[g2]t 只取决于先前的均值和当前的梯度(分量 γ γ γ 类似于动量项):
E [ g 2 ] t = γ E [ g 2 ] t − 1 + ( 1 − γ ) g t 2 E[g^2]_t = \gamma E[g^2]_{t-1} + (1 - \gamma) g^2_t E[g2]t=γE[g2]t−1+(1−γ)gt2
-
在 Adagrad 中梯度更新值为:
Δ θ t = − η G t + ϵ ⊙ g t \Delta \theta_t = - \dfrac{\eta}{\sqrt{G_{t} + \epsilon}} \odot g_{t} Δθt=−Gt+ϵη⊙gt
-
在 Adadelta 中梯度更新值为:
Δ θ t = − η E [ g 2 ] t + ϵ g t \Delta \theta_t = - \dfrac{\eta}{\sqrt{E[g^2]_t + \epsilon}} g_{t} Δθt=−E[g2]t+ϵηgt
-
由于分母仅仅是梯度的均方根(root mean squared,RMS)误差,可以简写为:
Δ θ t = − η R M S [ g ] t g t \Delta \theta_t = - \dfrac{\eta}{RMS[g]_{t}} g_t Δθt=−RMS[g]tηgt
-
将上面公式改成关于 θ t θ_t θt 的:
E [ Δ θ 2 ] t = γ E [ Δ θ 2 ] t − 1 + ( 1 − γ ) Δ θ t 2 E{{\left[ \Delta {{\theta }^{2}} \right]}_{t}}=\gamma E{{\left[ \Delta {{\theta }^{2}} \right]}_{t-1}}+(1-\gamma )\Delta \theta _{t}^{2} E[Δθ2]t=γE[Δθ2]t−1+(1−γ)Δθt2
-
参数更新的均方根误差为:
R M S [ Δ θ ] t = E [ Δ θ 2 ] t + ϵ RMS[\Delta \theta]_{t} = \sqrt{E[\Delta \theta^2]_t + \epsilon} RMS[Δθ]t=E[Δθ2]t+ϵ
-
由于 R M S [ Δ θ ] t RMS[\Delta \theta]_{t} RMS[Δθ]t 未知,使用之前时刻的 RMS 值 R M S [ Δ θ ] t − 1 RMS[\Delta \theta]_{t-1} RMS[Δθ]t−1 替换先前的更新规则中的学习率 η η η。最终的 Adadelta 更新公式为:
Δ θ t = − R M S [ Δ θ ] t − 1 R M S [ g ] t g t \Delta \theta_t = - \dfrac{RMS[\Delta \theta]_{t-1}}{RMS[g]_{t}} g_{t} Δθt=−RMS[g]tRMS[Δθ]t−1gt
θ t + 1 = θ t + Δ θ t \theta_{t+1} = \theta_t + \Delta \theta_t θt+1=θt+Δθt
-
使用 Adadelta 算法,无需设置默认的学习率,因为更新规则中已经移除了学习率。
RMSprop
-
RMSprop 未发表,但是也是自适应学习率的算法,由Geoff Hinton提出。
-
RMSprop 和 Adadelta 在相同的时间里被独立的提出,都起源于对Adagrad 的极速递减的学习率问题的求解。
-
实际上,RMSprop 是 Adadelta 的第一个更新向量的特例:
E [ g 2 ] t = 0.9 E [ g 2 ] t − 1 + 0.1 g t 2 E[g^2]_t = 0.9 E[g^2]_{t-1} + 0.1 g^2_t E[g2]t=0.9E[g2]t−1+0.1gt2
θ t + 1 = θ t − η E [ g 2 ] t + ϵ g t \theta_{t+1} = \theta_{t} - \dfrac{\eta}{\sqrt{E[g^2]_t + \epsilon}} g_{t} θt+1=θt−E[g2]t+ϵηgt
-
同样,RMSprop 将学习率分解成一个平方梯度的指数衰减的平均。Hinton 建议将 γ γ γ 设置为 0.9,对于学习率 η η η,一个好的固定值为0.001。
Adam
-
Adam 即 Adaptive Moment Estimation 自适应矩估计,是另一种自适应学习率的算法,Adam对每一个参数都计算自适应的学习率。
-
除了像 Adadelta 和 RMSprop一样存储一个指数衰减的历史平方梯度的平均 v t v_t vt,Adam 同时还保存一个历史梯度的指数衰减均值 m t m_t mt,类似于动量:
m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t mt=β1mt−1+(1−β1)gt
v t = β 2 v t − 1 + ( 1 − β 2 ) g t 2 v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2 vt=β2vt−1+(1−β2)gt2
-
m t m_t mt 和 v t v_t vt 分别是对梯度的一阶矩(均值)和二阶矩(非确定的方差)的估计,正如该算法的名称。
-
Adam 的作者发现当衰减率(decay rate)很小时( β 1 β_1 β1和 β 2 β_2 β2 接近于 1), m t m_t mt 和 v t v_t vt 最终都趋于0,因而Adam算法最终对一阶矩和二阶矩进行校正( β 1 β_1 β1和 β 2 β_2 β2 的上标 t t t 代表 t t t 次方):
m ^ t = m t 1 − β 1 t \hat{m}_t = \dfrac{m_t}{1 - \beta^t_1} m^t=1−β1tmt
v ^ t = v t 1 − β 2 t \hat{v}_t = \dfrac{v_t}{1 - \beta^t_2} v^t=1−β2tvt
-
最终 Adam 的更新规则为:
θ t + 1 = θ t − η v ^ t + ϵ m ^ t \theta_{t+1} = \theta_{t} - \dfrac{\eta}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t θt+1=θt−v^t+ϵηm^t
-
作者建议 β 1 β_1 β1 取默认值为 0.9, β 2 β_2 β2 为0.999, ϵ ϵ ϵ 为 1 0 − 8 10^{−8} 10−8。
算法的选择
-
如果输入数据是稀疏的,选择任一自适应学习率算法可能会得到最好的结果。选用这类算法的另一个好处是无需调整学习率,选用默认值就可能达到最好的结果。
-
RMSprop 是 Adagrad 的扩展形式,用于处理在 Adagrad 中急速递减的学习率。
-
RMSprop 与 Adadelta 相同,所不同的是 Adadelta 在更新规则中使用参数的均方根进行更新。
-
Adam 是将偏差校正和动量加入到 RMSprop 中。
-
在这样的情况下,RMSprop、Adadelta 和 Adam 是很相似的算法并且在相似的环境中性能都不错。在优化后期由于梯度变得越来越稀疏,偏差校正能够帮助Adam微弱地胜过 RMSprop。
-
综合看来,Adam可能是最佳的选择。
-
有趣的是,最近许多论文中采用不带动量的 SGD 和一种简单的学习率的退火策略。通常 SGD 能够找到最小值点,但是比其他优化的 SGD 花费更多的时间,与其他算法相比,SGD 更加依赖鲁棒的初始化和退火策略,同时,SGD 可能会陷入鞍点,而不是局部极小值点。
-
因此,如果想要快速收敛和训练一个深层的或者复杂的神经网络,可以选择一个自适应学习率的方法。