总结一下关于梯度下降的问题
梯度下降是最小化目标函数的方法
梯度下降变体
==目的:考虑到 更新参数的准确性 和 时间,从而作出权衡 ==
batch gradient descent
公式:
θ
=
θ
−
η
⋅
∇
θ
J
(
θ
)
\theta = \theta - \eta· \nabla_{\theta}J(\theta)
θ=θ−η⋅∇θJ(θ)
优点: 批量梯度下降保证收敛到凸误差表面的全局最小值和非凸表面的局部最小值。
缺点: 需要计算整个数据集的梯度来执行一次更新,因此速度慢;不允许模型在线更新。
代码:
for i in range(nb_epochs):
params_grad = evaluate_gradient(loss_function , data , params)
params = params - learning_rate * params_grad
stochastic gradient descent
随机梯度下降SGD对每个训练示例 ( x i , y i ) (x_{i}, y_{i}) (xi,yi)执行参数更新
公式:
θ
=
θ
−
η
⋅
∇
θ
J
(
θ
;
x
(
i
)
;
y
(
i
)
)
\theta = \theta - \eta· \nabla_{\theta}J(\theta; x^{(i)}; y^{(i)})
θ=θ−η⋅∇θJ(θ;x(i);y(i))
优点: 速度快,也可以用来在线更新参数。
缺点: 更新时,方差高,导致目标函数大幅波动。
代码:
for i in range(nb_epochs):
np.random.shuffle(data)
for example in data:
params_grad = evaluate_gradient(loss_function , example , params)
params = params - learning_rate * params_grad
mini-batch gradient descent
小批量梯度下降采取了折中的方法,通常使用较多的方法
公式:
θ
=
θ
−
η
⋅
∇
θ
J
(
θ
;
x
(
i
:
i
+
n
)
;
y
(
i
:
i
+
n
)
)
\theta = \theta - \eta· \nabla_{\theta}J(\theta; x^{(i:i+n)}; y^{(i:i+n)})
θ=θ−η⋅∇θJ(θ;x(i:i+n);y(i:i+n))
优点:
- 减少了参数更新的方差,这样可以更稳定;
- 速度比 batch gradient descent 快
挑战:
- 选择合适的学习率很难。学习率太大会导致收敛速度慢,学习率太大可能会导致损失函数在最小值附近波动甚至发散。
- learning rate 需提前定义,对所有参数更新都使用同样的learning rate。有这样一种情况:对于不经常出现的特征,希望更新快一点,对于常出现的特征希望更新慢一点,因此可能无法适应数据集的所有特征。
- 在鞍点震荡
解释一下鞍点,计算机中有很多名词,乍一看没有概念,查一查,还真像那么回事。人生也一样,很多事不理解,但却是有道理的。
鞍点是一个非局部极值点的驻点。如上图所示,从该点出发的一个方向是函数的极大值点,而在另一个方向是函数的极小值点。知道鞍点长什么样即可。
代码:
for i in range(nb_epochs):
np.random.shuffle(data)
for batch in get_batches(data , batch_size =50):
params_grad = evaluate_gradient(loss_function, batch, params)
params = params - learning_rate * params_grad
梯度下降优化算法
针对上述的挑战,所以提出一些梯度下降优化算法。
Momentum(动量)
SGD有可能会在峡谷地区的某些局部极值点震荡。这种情况下,可以使用Momentum解决。如图1,我自己找的图。图2是论文中的图片。
Momentum是加速SGD并抑制震荡的方法,如图2(b)。论文中写“accelerate SGD in the relevant direction”,此处我不是特别理解,何为“相关”方向?
公式:
v
t
=
γ
v
t
−
1
+
η
⋅
∇
θ
J
(
θ
)
θ
=
θ
−
v
t
\begin{aligned} v_{t} &= \gamma v_{t-1} + \eta· \nabla_{\theta}J(\theta) \\ \theta &= \theta - v_{t} \end{aligned}
vtθ=γvt−1+η⋅∇θJ(θ)=θ−vt
γ
\gamma
γ一般设置为0.9
Nesterov accelerated gradient(NAG)
公式:
v
t
=
γ
v
t
−
1
+
η
⋅
∇
θ
J
(
θ
−
γ
v
t
−
1
)
θ
=
θ
−
v
t
\begin{aligned} v_{t} &= \gamma v_{t-1} + \eta· \nabla_{\theta}J(\theta - \gamma v_{t-1}) \\ \theta &= \theta - v_{t} \end{aligned}
vtθ=γvt−1+η⋅∇θJ(θ−γvt−1)=θ−vt
将
γ
\gamma
γ设置为0.9。momentum先计算一个梯度(短的蓝色向量),然后在加速更新梯度的方向进行一个大的跳跃(长的蓝色向量)。nesterov首先在之前加速的梯度方向进行一个大的跳跃(棕色向量),测量梯度,然后进行矫正(绿色向量),这种更新可以防止梯度更新的太快,从而提升响应能力。
Adagrad
针对这个挑战,Adagrad使学习率适应参数,对不频繁出现的参数执行较大的更新,对频繁出现的参数执行较小的更新。因此,Adagrad非常适合处理稀疏数据。
NLP 新词发现 可以优先使用Adagrad了!
在以上方法中,每个参数 θ i \theta_{i} θi都使用相同的学习率 η \eta η。而在Adagrad中,在每个时间步长 t t t中,对每个参数 θ i \theta_{i} θi使用不同的学习率。
首先设 g t , i g_{t, i} gt,i为第t轮第i个参数的梯度,即 g t , i = ∇ θ t J ( θ t , i ) g_{t,i} = \nabla_{\theta_{t}} J(\theta_{t,i}) gt,i=∇θtJ(θt,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 修改一般学习率
η
\eta
η
最后,公式为:
θ
t
+
1
,
i
=
θ
t
,
i
−
η
G
t
,
i
i
+
ϵ
⋅
g
t
,
i
\theta_{t+1, i} = \theta_{t, i} - \frac{\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 R^{d\times d}
Gt∈Rd×d为对角矩阵,每个对角元素
i
i
i为对应参数
θ
i
\theta_{i}
θi从步长
1
至
t
1至t
1至t的梯度平方和。
ϵ
\epsilon
ϵ是一个平滑项,用于避免分母为0(通常为
1
e
−
8
1e-8
1e−8)。
因为,
G
t
,
i
i
G_{t,ii}
Gt,ii和
g
t
,
i
g_{t,i}
gt,i之间执行逐元素相乘,所以公式可以写为:
θ
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没有平方根运算( 1 G t , i i + ϵ \frac{1}{\sqrt{G_{t,ii} + \epsilon }} Gt,ii+ϵ1),算法效果会更差。[论文写到的,我没有验证]
优点:
- 自动调整学习率。大多使用默认值0.01
缺点:
- Adagrad 分母累积平方梯度( 1 G t , i i + ϵ \frac{1}{\sqrt{G_{t,ii} + \epsilon }} Gt,ii+ϵ1),其中每项皆为正数,因此在训练过程中不断变大,从而导致整体的学习率( η G t , i i + ϵ \frac{\eta}{\sqrt{G_{t,ii} + \epsilon }} Gt,ii+ϵη)变小。若梯度趋于0,则训练结束。
Adadelta
Adadelta是Adagrad的扩展。Adadelta没有累积所有过去的平方梯度,而是将累积过去梯度的窗口限制为某个固定大小的 w w w。
看论文,Adadelta的公式解释有点绕。不如,这样?
version 1:
Adadelta的公示就是将Adagrad的平方根运算(
1
G
t
,
i
i
+
ϵ
\frac{1}{\sqrt{G_{t,ii} + \epsilon }}
Gt,ii+ϵ1)变成了(
1
E
[
g
2
]
t
+
ϵ
\frac{1}{\sqrt{E[g^{2}]_t + \epsilon }}
E[g2]t+ϵ1)
其中,梯度平均值
E
[
g
2
]
t
E[g^2]_{t}
E[g2]t仅取决于先前的平均值和当前的梯度。
E
[
g
2
]
t
E[g^2]_{t}
E[g2]t的公式如下,并设置
γ
\gamma
γ为与动量相似的值,大约为0.9.
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_{t}^2
E[g2]t=γE[g2]t−1+(1−γ)gt2
又因为(
1
E
[
g
2
]
t
+
ϵ
\frac{1}{\sqrt{E[g^{2}]_t + \epsilon }}
E[g2]t+ϵ1)是梯度的均方根(RMS),所以可以将公式写为:
1
E
[
g
2
]
t
+
ϵ
=
1
R
M
S
[
g
]
t
\frac{1}{\sqrt{E[g^{2}]_t + \epsilon }}= \frac{1}{RMS[g]_t}
E[g2]t+ϵ1=RMS[g]t1
version 2:
略
RMSprop
和 Adadelta 版本1一样。(大概是因为和Adadelta同时提出的原因)
公式:
E
[
g
2
]
t
=
0.9
E
[
g
2
]
t
−
1
+
0.1
g
t
2
θ
t
+
1
=
θ
t
−
η
E
[
g
2
]
t
+
ϵ
\begin{aligned} E[g^2]_t &= 0.9E[g^2]_{t-1} + 0.1g_{t}^2 \\ \theta_{t+1} &= \theta _t - \frac{\eta}{\sqrt{ E[g^2]_t + \epsilon}} \end{aligned}
E[g2]tθt+1=0.9E[g2]t−1+0.1gt2=θt−E[g2]t+ϵη
建议: η \eta η=0.01
Adam
Adaptive Moment Estimation(Adam)自适应矩估计:也是计算每个参数的自适应学习率的方法。
除了像 Adadelta 和 RMSprop 一样存储 之前平方梯度
v
t
v_{t}
vt 的 指数衰减平均值,还保留了之前梯度
m
t
m_{t}
mt指数衰减平均值,这一点与动量类似。
公式:
m
t
^
=
m
t
1
−
β
1
t
v
^
t
=
v
t
1
−
β
2
t
\begin{aligned} \hat{m_{t}} &= \frac{m_{t}}{1-\beta_{1}^{t}} \\ \hat v_{t} &= \frac{v_{t}}{1-\beta_{2}^{t}} \end{aligned}
mt^v^t=1−β1tmt=1−β2tvt
m t m_{t} mt 和 v t v_{t} vt 分别是 对梯度 的 一阶矩(均值) 和 二阶矩(非中心方差) 的 估计值。当 m t m_{t} mt 和 v t v_{t} vt初始化为0向量时,Adam的作者发现它们都偏向于0,尤其是在初始化的步骤和当衰减率很小的时候(例如β1和β2趋向于1)。
通过计算偏差校正的一阶矩和二阶矩估计来抵消偏差:
m
=
β
1
m
t
−
1
+
(
1
−
β
1
)
g
t
v
t
=
β
2
v
t
−
1
+
(
1
−
β
2
)
g
t
2
\begin{aligned} m &= \beta_{1}m_{t-1} + (1-\beta_{1})g_{t} \\ v_{t} &= \beta_{2}v_{t-1} + (1-\beta_{2})g_{t} ^{2} \end{aligned}
mvt=β1mt−1+(1−β1)gt=β2vt−1+(1−β2)gt2
由此,Adam的更新规则为:
θ
t
+
1
=
θ
t
−
η
v
^
t
+
ϵ
m
^
t
\theta_{t+1} = \theta_{t} - \frac{\eta}{\sqrt{\hat v_{t}} + \epsilon} \hat m_{t}
θt+1=θt−v^t+ϵηm^t
作者建议设置β1默认值为0.9,β2为0.999,ϵ为10−8。
AdamW
Adam存在某些无法收敛的情况。AdamW是Adam+L2正则化的基础上改进的算法。
Lookahead
https://arxiv.org/pdf/1907.08610.pdf
Lamb
加速预训练进程。模型在进行大批量数据训练时,能够维持梯度更新的精度。
WarmUp
warm-up操作就是在刚开始训练的时候先使用一个较小的学习率,训练一些epoches,等模型稳定时再修改为预先设置的学习率进行训练。
优化SGD的其它策略
- shuffling and curriculum learning
- batch normalization
感觉 batch normalization 可以挖一下 - early stopping
- gradient noise
梯度更新时添加一个符合高斯分布(N(0, σ 2 \sigma^{2} σ2))的噪声
g t , i = g t , i + N ( 0 , σ 2 ) g_{t,i} = g_{t,i} + N(0, \sigma^{2}) gt,i=gt,i+N(0,σ2)
参考论文:An overview of gradient descent optimization
algorithms