问题
深度学习中有很多优化函数,常见的那些你还记得它的定义以及优缺点吗?
背景知识
深度学习网络训练中,有很多可供选择的优化函数如SGD、Adam等等,到底用哪个好呢?其实这个问题没有确切的答案,优化函数是需要配合损失函数使用的,说白了,优化函数也是一种超参数,是需要尝试的,哪个效果好就用哪个……
这些优化函数其实差别不大,都是基于一个基本框架来演进的,所以下面先介绍下优化算法的基本框架:
1.优化算法框架
假设当前时刻待优化的参数为 θ t \theta_{t} θt,损失函数为 J ( θ ) J(\theta) J(θ) ,学习率为 η \eta η,参数更新的框架为:
-
计算损失函数关于当前参数的梯度: g t = ∇ J ( θ t ) g_t = \nabla J(\theta_t) gt=∇J(θt)
-
根据历史梯度计算一阶动量和二阶动量: 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 \Delta \theta_t = -\eta*\frac{m_t}{\sqrt{V_t}} Δθt=−η∗Vtmt
-
根据下降梯度更新参数: θ t + 1 = θ t + Δ θ t \theta_{t+1} = \theta_t + \Delta \theta_t θt+1=θt+Δθt
2.指数加权移动平均值
SGD只计算当前梯度更新参数,完全没有考虑历史梯度,但这样有一个问题是假如当前参数处在损失函数的局部最低点处,即梯度为0,因为梯度为0,所以参数不再更新,也就是说不管你之前历史梯度多大,下降地多快,只要你当前梯度为0,那就只能停在这里,也就意味着冲不出这个局部最低点。要解决这个问题就需要将历史梯度考虑进来,但是这里又有一个问题:历史梯度那么多,全部都考虑吗,还是只考虑一部分?其实我们只要考虑最近的一段历史梯度即可,这段历史梯度怎么截就用到了指数加权移动平均值的概念。
假设
v
t
−
1
v_{t-1}
vt−1是
t
−
1
t-1
t−1 时刻的指数加权移动平均值,
θ
t
\theta_t
θt 是当前
t
t
t时刻的观测值,那么
时刻的指数加权移动平均值为:
υ
t
=
β
υ
t
−
1
+
(
1
−
β
)
θ
t
=
(
1
−
β
)
θ
t
+
(
1
−
β
)
β
θ
t
−
1
+
β
2
υ
t
−
2
=
(
1
−
β
)
θ
t
+
(
1
−
β
)
β
θ
t
−
1
+
(
1
−
β
)
β
2
θ
t
−
2
+
β
3
υ
t
−
3
.
.
.
(
递推
)
=
(
1
−
β
)
θ
t
+
∑
i
=
1
t
−
1
(
1
−
β
)
β
i
θ
t
−
i
\upsilon_t = \beta \upsilon_{t-1} + ( 1- \beta)\theta_t \\ \quad = ( 1- \beta)\theta_t + ( 1- \beta)\beta\theta_{t-1}+ \beta^2\upsilon_{t-2}\\ \quad= ( 1- \beta)\theta_t + ( 1- \beta)\beta\theta_{t-1}+ ( 1- \beta)\beta^2\theta_{t-2}+ \beta^3\upsilon_{t-3} \ ...(递推)\\ = (1-\beta)\theta_t + \sum_{i=1}^{t-1}(1-\beta)\beta^i \theta_{t-i}
υt=βυt−1+(1−β)θt=(1−β)θt+(1−β)βθt−1+β2υt−2=(1−β)θt+(1−β)βθt−1+(1−β)β2θt−2+β3υt−3 ...(递推)=(1−β)θt+i=1∑t−1(1−β)βiθt−i
其中
0
≤
β
≤
1
0 \leq \beta \leq 1
0≤β≤1,从指数加权移动平均值的最终形式可以看出,
i
i
i 表示的是距离当前时刻的时间长短,
i
i
i 越大代表着距离当前时刻越久远,且由于其系数中指数部分的影响,其系数越小,也就是说距离当前时刻越远的历史梯度对当前时刻的指数加权移动平均值的贡献越少,这时候若我们设置一个阈值来对贡献量进行筛选,便使得当前时刻的指数加权移动平均值只考虑距离当前时刻较近的那些历史梯度,这就对应了名字中的“移动”这个概念。
除了第 t t t时刻的观测值权重为 1 − β 1-\beta 1−β 外,其他时刻的观测值权重为 ( 1 − β ) β i (1-\beta)\beta^{i} (1−β)βi.由于通常对于那些权重小于 1 e \frac{1}{e} e1的观测值可以忽略不计,所以忽略掉那些权重小于这个阈值的观测值之后,上式就可以看做是在求指数加权移动平均值。
3.SGD(Stochastic Gradient Descent)
SGD不考虑历史梯度,所以当前时刻的一阶动量即为当前时刻的梯度
m
t
=
g
t
m_t=g_t
mt=gt,且二阶动量V_t=E
缺点:容易陷入局部最优。由于SGD只考虑当前时刻的梯度,在局部最优点的当前时刻梯度为 0 ,根据上面计算公式可知,此时参数不再进行更新,故陷入局部最优的状态。
但是虽然SGD有陷入局部最优的缺陷,但还是很常用。我的理解是:以上分析是针对一个参数 θ i \theta_i θi来说的,即使其中一个参数陷入局部最优,但其他参数还是会继续更新,所以大概率会将陷入局部最优的那个参数拖离局部最优点,于是该参数可以继续更新。 所以整体来说并不会像单个参数那样陷入局部最优就出不来,所以还是可以work的。
4.改进策略及算法
- 引入历史梯度的一阶动量,代表算法:Momentum、NAG
- 引入历史梯度的二阶动量,代表算法:AdaGrad、RMSProp、AdaDelta
- 同时引入历史梯度的一阶动量及二阶动量,代表算法:Adam、Nadam
5. Momentum
Momentum是一个优化算法的名称,最初是由Polyak于1964年提出的,后经过一系列的改进被广泛应用于深度学习中的优化算法。
Momentum算法的目的是在梯度下降算法上加入加速项,以帮助在优化过程中快速地穿过局部极小值,同时可以减少振荡。 在Momentum算法中,一个动量项被引入到梯度下降的更新公式中:使用上一次梯度的方向和当前时间步的梯度的方向的加权平均作为更新方向,从而减少振荡,增加在参数空间中的移动速度。 典型的Momentum算法的梯度更新公式如下:
v 0 = 0 v t = γ v t − 1 + η ∇ θ J ( θ ) θ t = θ t − 1 − v t \begin{aligned} v_0 &= 0 \\ v_t &= \gamma v_{t-1} + \eta\nabla_{\theta}J(\theta) \\ \theta_t &= \theta_{t-1} - v_t \end{aligned} v0vtθt=0=γvt−1+η∇θJ(θ)=θt−1−vt
其中, v t v_t vt 表示 t t t 时刻的动量, η \eta η 表示学习率, γ \gamma γ 表示动量衰减率, J ( θ ) J(\theta) J(θ) 是参数为 θ \theta θ 的损失函数的值, ∇ θ \nabla_{\theta} ∇θ 表示对参数求梯度, θ t \theta_t θt 表示 t t t 时刻的参数值, θ t − 1 \theta_{t-1} θt−1 表示上一时刻的参数值。
根据上述公式,动量可以被视为一个指示在方向上移动的加速器。如果一个参数是在每个步骤上收敛,带动量的方法可以通过降低移动方向上的振动来速度收敛。当考虑到当前渐进方向的瞬间也包括运动的惯性时,动量效应非常自然。
使用动量的优点之一是对于凸损失函数,它可以减少参数收敛的时间。另一个是,对于非凸的情况,它可以帮助默认的梯度下降算法更好地避免局部最小值和鞍点。
相比于标准的梯度下降方法,Momentum方法需要两个超参数 η \eta η和 γ \gamma γ,需要进行仔细的调整来获得最佳性能。
6.NAG (Nesterov Accelerated Gradient)
除了利用惯性(一阶动量)跳出局部沟壑外,我们还可以尝试往前看一步,即:在Momentum考虑历史梯度的基础上,把当前梯度转换为未来梯度。
想象一下你走到一个盆地,四周都是略高的小山,你觉得没有下坡的方向,那就只能待在这里了。可是如果你爬上高地,就会发现外面的世界还很广阔。因此,我们不能停留在当前位置去观察未来的方向,而要向前多看一步。我们知道Momentum在时刻 t t t的主要下降方向是由历史梯度(惯性)决定的,当前时刻的梯度权重较小,那不如不用管当前梯度,而是先看看如果跟着惯性走了一步,那个时候外面的世界是怎样的。也即在Momentum的基础上将当前时刻的梯度换成下一时刻的梯度。
从数学角度来说,在每次迭代时,NAG首先沿着动量方向 v t v_t vt 前进一步,得到参数的一个估计值 θ ~ t \tilde{\theta}_t θ~t:
θ ~ t = θ t − γ v t − 1 \tilde{\theta}_t = \theta_t - \gamma v_{t-1} θ~t=θt−γvt−1
其中, γ \gamma γ 是动量衰减率。
然后,在估计值 θ ~ t \tilde{\theta}_t θ~t 的位置计算梯度 ∇ f ( θ ~ t ) \nabla f(\tilde{\theta}_t) ∇f(θ~t),并将梯度用于更新参数 θ t \theta_t θt:
v t = γ v t − 1 + η ∇ f ( θ ~ t ) v_t = \gamma v_{t-1} + \eta \nabla f(\tilde{\theta}_t) vt=γvt−1+η∇f(θ~t)
θ t + 1 = θ t − v t \theta_{t+1} = \theta_t - v_t θt+1=θt−vt
因此,NAG的参数更新公式可以写为:
v t = γ v t − 1 + η ⋅ ∇ f ( θ ~ t ) θ t + 1 = θ t − v t θ ~ t = θ t − γ v t − 1 \begin{aligned} v_t &= \gamma v_{t-1} + \eta\cdot \nabla f(\tilde{\theta}t)\ \theta{t+1} &= \theta_t - v_t\ \tilde{\theta}t &= \theta_t - \gamma v{t-1}\end{aligned} vt=γvt−1+η⋅∇f(θ~t) θt+1=θt−vt θ~t=θt−γvt−1
相比于标准Momentum算法,NAG在许多问题上更加的有效。
以上的两个概念只引入了一阶动量。而二阶动量的出现,才意味着**“自适应学习率”**优化算法时代的到来。在SGD及其引入一阶动量的改进算法中,均以相同的学习率去更新参数。但是,以相同的学习率进行变化经常是不合理的。
在神经网络中,参数需要用不同的学习率进行更新。对于经常更新的参数,我们已经积累了大量关于它的知识,不希望被单个样本影响太大,希望学习速率慢一些;对于那些偶尔更新的参数,我们了解的信息太少,希望能从每个偶然出现的样本身上多学一些,即学习速率大一些。
6.AdaGrad
AdaGrad(自适应梯度算法)是一种自适应学习率优化算法,由Duchi等人在2011年提出。相比于传统梯度下降算法,AdaGrad可以在训练过程中自适应地调整每个参数的学习率,使得每个参数的更新速度可以根据历史梯度信息的情况进行自适应调整,从而更加高效地搜索最优解。
AdaGrad的核心思想是,对于不同的参数,根据其历史梯度信息来调整学习率。具体来说,对于参数
θ
j
\theta_j
θj,AdaGrad维护一个梯度历史的累加平方和
G
t
,
j
G_{t,j}
Gt,j,并且将其用于调整该参数的学习率
η
j
\eta_j
ηj。在每次迭代中,AdaGrad计算参数
j
j
j 的梯度
g
t
,
j
g_{t,j}
gt,j,然后对
G
G
G 进行累加平方和计算,得到历史梯度平方和变化量
d
t
d_t
dt:
d
t
=
∑
i
=
1
t
g
i
,
j
2
d_t = \sum_{i=1}^t g_{i,j}^2
dt=i=1∑tgi,j2
然后,使用
d
t
d_t
dt 对学习率进行调整,并用调整后的学习率来更新参数
θ
j
\theta_j
θj:
θ
j
,
t
+
1
=
θ
j
,
t
−
η
d
t
+
ϵ
g
t
,
j
\theta_{j,t+1} = \theta_{j,t} - \frac{\eta}{\sqrt{dt}+\epsilon}g_{t,j}
θj,t+1=θj,t−dt+ϵηgt,j
其中,
η
\eta
η 是初始学习率,
ϵ
\epsilon
ϵ 是为了防止除以零而添加的小常数。需要注意的是,
d
t
d_t
dt 是一个累加值,因此学习率在训练过程中会不断减小。
总的来说,AdaGrad算法的主要优点是可以自适应地调整学习率,并且可以适用于稀疏数据或非平稳目标函数。但是,它的缺点在于学习率会不断减小,可能会导致学习率收敛得过慢,并不能很好地适用于非凸的优化问题。
7.RMSProp / AdaDelta
RMSProp和AdaDelta都是梯度下降的变种算法,用于优化神经网络中的参数。它们有助于解决标准梯度下降算法中的一些问题,如学习率的选择以及参数更新过程中的振荡和停滞。
RMSProp算法是一种自适应学习率方法,它在更新参数时保持学习速率被平滑。具体来说,RMSProp使用指数移动平均数来估计过去梯度的平方值。这个平均值被用来调整每个参数的学习速率,对于那些具有较大梯度的参数,RMSProp会降低学习速率,反之,对于那些具有相对较小梯度的参数,它会增加学习速率。
AdaDelta算法也是一种自适应学习率方法,但它使用了另一种方法来自适应地调整学习速率。在AdaDelta中,每个参数的更新规则取决于历史平均梯度和历史平均更新值,而不是像RMSProp那样使用梯度平方的指数移动平均数。这些历史平均值可以看作是对更新的短期和长期影响的衡量,它们被用来调整每个参数的学习速率。与RMSProp不同,AdaDelta几乎可以自动确定合适的学习率,从而使其更稳定和更易于使用。
总之,RMSProp和AdaDelta都是非常可靠的优化算法,它们可以使神经网络的收敛速度更快,减少过度拟合风险,提高训练效率和准确性。但是,在选择哪种算法时,需要根据具体问题的不同来进行实验和验证。
8.Adam
在 RMSProp 的基础上再考虑一阶动量(Momentum)。具体如下:
Adam是一种梯度下降算法的优化器,用于训练深度神经网络。Adam的全称为Adaptive Moment Estimation,它结合了Adagrad和RMSprop算法的优点,实现了自适应学习率及动量更新。Adam通过计算每个参数的梯度的一阶矩和二阶矩估计,自适应地调整学习率。
具体来说,Adam维护每个参数的梯度平均值和平方平均值,并计算出学习率的适当下降量。Adam还使用一个动量项来强调先前梯度的重要性。因此,Adam可以有效地平衡速度和精度,并且可以更轻松地避免深度神经网络中的局部最小值问题。
Adam具有以下特点:
- 自适应学习率:Adam使用自适应学习率方法,使学习率针对每个参数自适应地调整。.
- 动量项:Adam使用动量项来加速梯度下降,并在平滑梯度时避免俯冲现象。
- 计算高效:Adam计算的一阶矩和二阶矩估计使用指数移动平均数,计算高效。
Adam在很多机器学习任务和深度神经网络中表现很好,因此是目前最常使用的优化器之一。
9.Nadam
Nadam是Adam的变体之一,是一种基于梯度下降算法的优化器。Nadam将Adam中的动量更新策略进行了改进,以便更好地利用动量信息来加速优化过程。它的主要思想是保留Adam算法中自适应梯度调整和Nesterov动量更新的优点,并且在样本值较小时自适应地较少使用动量更新。
具体来说,Nadam在Adam算法的基础上增加了一个Nesterov动量项,使得在梯度方向变化时更加平滑。同时,与Adam算法中的自适应梯度调整相同,Nadam计算出每个参数的梯度方向,并将其除以梯度的平均平方值的平方根来调整步长。因此,这个步骤使用了自适应的学习率调整方法,因此不需要手动设置学习率。当梯度较小时,Nadam使用更少的动量更新来平衡自适应调整步骤的影响。
Nadam在实践中表现出良好的性能,并且在训练深度神经网络时,可以明显地提高收敛速度和性能。与Adam相比,Nadam更具有鲁棒性,具有更大的学习率,更有效地处理稀疏梯度,缓解了Adam在处理非凸函数时的无法收敛的问题,因此是一个非常有前途的优化器。