优化算法进阶
深度学习最全优化方法总结比较(SGD,Adagrad,Adadelta,Adam,Adamax,Nadam)
Notebook 8.1中MBGD,依然存在问题:
- learning rate的选择:
- 容易收敛到局部最优
so各种优化方法出现。
ill-conditioned problem
矩阵的条件数(condition number)度量了 问题的解对于输入数据中的扰动以及解决问题过程的舍入误差roundoff errors的敏感度。
条件数会告诉你解决一个线性系统会在多大程度上放大数据中的噪声,Think of it as a measure of amplification, a gain.
如果cond = 1e8, 那么当解决一个线性系统时,这个解会把噪声放大约1e8倍。e.g.: 数据是双精度的,噪声是最低有效位那种量级(too小小小小),那条件数影响不大; however,来自现实世界的数据,误差可能在1%左右,当噪声被放大1e8倍,噪声将主导最后的解。
Condition number of Hessian Matrix:
c
o
n
d
H
=
λ
m
a
x
λ
m
i
n
cond_{H} = \frac{\lambda_{max}}{\lambda_{min}}
condH=λminλmax
最大学习率
maximum learning rate
-
对于 f ( x ) f(x) f(x),根据凸优化结论,学习率 η < 1 L \eta <\frac{1}{L} η<L1,取
=
时,有最快收敛速度,其中 L = m a x x ∇ 2 f ( x ) L=max_x\nabla ^2f(x) L=maxx∇2f(x)比如对一个凸优化表面,大概路线(快+收敛):
-
如果要收敛,需要满足 : η < 2 L \eta <\frac{2}{L} η<L2,大于 2 L \frac{2}{L} L2会发散
-
1 L < η < 2 L \frac{1}{L}<\eta <\frac{2}{L} L1<η<L2时,大概路线如下(不那么快+收敛):
ill-conditioned例子,看看会对优化带来什么影响:
f
(
x
)
=
0.1
x
1
2
+
2
x
2
2
c
o
n
d
H
=
4
0.2
=
20
→
ill-conditioned
f(\boldsymbol{x})=0.1x_1^2+2x_2^2\\ cond_{H} = \frac{4}{0.2} = 20 \quad \rightarrow \quad \text{ill-conditioned}
f(x)=0.1x12+2x22condH=0.24=20→ill-conditioned
%matplotlib inline
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l
import torch
eta = 0.4
def f_2d(x1, x2):
return 0.1 * x1 ** 2 + 2 * x2 ** 2
def gd_2d(x1, x2, s1, s2):
return (x1 - eta * 0.2 * x1, x2 - eta * 4 * x2, 0, 0)
d2l.show_trace_2d(f_2d, d2l.train_2d(gd_2d))
代码中, η = 0.4 \eta = 0.4 η=0.4,同时满足在 x 1 , x 2 x_1,x_2 x1,x2两个方向上的收敛条件。
- x 2 x_2 x2方向: f f f对 x 2 x_2 x2求二阶导得 L = 4 L=4 L=4; 0.25 = 1 L < η < 2 L = 0.5 0.25=\frac{1}{L}<\eta <\frac{2}{L} =0.5 0.25=L1<η<L2=0.5。所以可以看到,梯度下降路线在 x 2 x_2 x2方向上,在最优解的两侧来回走Z字路线(不那么快+收敛)。
- x 1 x_1 x1方向: f f f对 x 1 x_1 x1求二阶导得 L = 0.2 L=0.2 L=0.2; η < 1 L = 5 < 2 L = 10 \eta <\frac{1}{L}=5<\frac{2}{L} =10 η<L1=5<L2=10,学习率0.4相对于5、10取得比较小,所以优化速度较慢。
- 如果想要快速下降,根据 x 1 x_1 x1的计算结果, η \eta η应该取得接近5?但显然为了满足 x 2 x_2 x2方向的收敛条件0.5,不可以取这么大,快速和收敛难以两全,凸显出ill-conditioned的矛盾。
下面将学习率调得稍大一点,
η
=
0.6
\eta = 0.6
η=0.6,此时自变量在竖直方向不断越过最优解并逐渐发散。
Supp: Preconditioning
在二阶优化中,我们使用Hessian matrix的逆矩阵(或者pseudo inverse)来左乘梯度向量
i
.
e
.
Δ
x
=
H
−
1
g
i.e. \Delta_{x} = H^{-1}\mathbf{g}
i.e.Δx=H−1g,这样的做法称为precondition
相当于将
H
H
H映射为一个单位矩阵,拥有分布均匀的Spectrum(
λ
m
a
x
\lambda_{max}
λmax与
λ
m
i
n
\lambda_{min}
λmin非常接近),也即我们去优化的等价标函数的Hessian matrix为良好的单位矩阵(identity matrix)。
e.g.: 原本的优化问题是扁平的优化表面,preconditioning之后变成了接近圆的优化表面,这样在两个方向(例子中的
x
1
,
x
2
x_1,x_2
x1,x2)都可以选择较大的学习率,收敛速度会更快,不会被某一个方向限制。
Solution to ill-condition
- Preconditioning gradient vector: 被应用于 Adam, RMSProp, AdaGrad, Adelta, KFC, Natural gradient 以及其他二阶优化算法,包括牛顿法。
- Averaging history gradient: like momentum, which allows larger learning rates to accelerate convergence; applied in Adam, RMSProp, SGD momentum.
先讲第二种解决办法,momentum,再讲preconditioning。
Momentum
模拟物理里动量的概念,累计之前的动量来代替真正的梯度。更新公式:
Version 1:
m
t
←
β
m
t
−
1
+
η
t
g
t
,
x
t
←
x
t
−
1
−
m
t
,
\begin{aligned} \boldsymbol{m}_t &\leftarrow \beta \boldsymbol{m}_{t-1} + \eta_t \boldsymbol{g}_t, \\ \boldsymbol{x}_t &\leftarrow \boldsymbol{x}_{t-1} - \boldsymbol{m}_t, \end{aligned}
mtxt←βmt−1+ηtgt,←xt−1−mt,
Another version (2):
m
t
←
β
m
t
−
1
+
(
1
−
β
)
g
t
,
x
t
←
x
t
−
1
−
α
t
m
t
,
α
t
=
η
t
1
−
β
\begin{aligned} \boldsymbol{m}_t &\leftarrow \beta \boldsymbol{m}_{t-1} + (1-\beta) \boldsymbol{g}_t, \\ \boldsymbol{x}_t &\leftarrow \boldsymbol{x}_{t-1} - \alpha_t \boldsymbol{m}_t,\\\alpha_t &= \frac{\eta_t}{1-\beta} \end{aligned}
mtxtαt←βmt−1+(1−β)gt,←xt−1−αtmt,=1−βηt
其中,动量超参数
β
=
0
\beta =0
β=0时,momentum算法相当于小批量随机梯度下降(mini-batch gradient descent)。
对version1,在实践中,通常 β = 0.5 , 0.9 , 0.99 \beta=0.5, 0.9, 0.99 β=0.5,0.9,0.99 ,分别对应最大 2 倍、10 倍、100 倍的步长(for more details,搜索Exponential Moving Average(EMA)指数加权移动平均)
和自适应学习率一样, β \beta β 也可以使用某种策略在训练时进行自适应调整;一般初始值是一个较小的值,随后会慢慢变大。
momentum轨迹,例子f(x)未变:
def momentum_2d(x1, x2, v1, v2):
v1 = beta * v1 + eta * 0.2 * x1
v2 = beta * v2 + eta * 4 * x2
return x1 - v1, x2 - v2, v1, v2
eta, beta = 0.4, 0.5
d2l.show_trace_2d(f_2d, d2l.train_2d(momentum_2d))
可以看到使用较小的学习率
η
=
0.4
,
β
=
0.5
\eta=0.4,\beta=0.5
η=0.4,β=0.5时,动量法在竖直方向上的移动更加平滑,且在水平方向上更快逼近最优解。
η
=
0.6
\eta=0.6
η=0.6时,也不像之前GD那样会发散:
SGD 的更新步长只是梯度乘以学习率;而momentum,步长还取决于历史梯度序列的大小和排列;当许多连续的梯度指向相同的方向时,步长会被不断增大。
在pytorch中,torch.optim.SGD
可以实现momentum,指出momentum超参数即可(即前面的
β
\beta
β)
d2l.train_pytorch_ch7(torch.optim.SGD, {'lr': 0.004, 'momentum': 0.9}, features, labels)
AdaGrad
由于ill-conditioned problem,需要选择足够小的学习率使得自变量在梯度值较大的维度上不发散,但这样会导致自变量在梯度值较小的维度上迭代过慢(SGD)。
ill-condition解决办法之一 ,momentum依赖EMA使得自变量的更新方向更加一致,从而降低发散的可能。
AdaGrad算法,属于另一类解决办法preconditiong,它根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,从而避免统一的学习率难以适应所有维度的问题 。
Algorithm
s
t
\boldsymbol{s}_t
st:square gradient ,截至时间步
t
t
t所有小批量随机梯度
g
t
\boldsymbol{g}_t
gt按元素平方和。
t
=
0
t=0
t=0时,
s
t
\boldsymbol{s}_t
st中每个元素初始化为0。
s
t
←
s
t
−
1
+
g
t
⊙
g
t
x
t
←
x
t
−
1
−
η
s
t
+
ϵ
⊙
g
t
\begin{aligned} \boldsymbol{s}_t &\leftarrow \boldsymbol{s}_{t-1} + \boldsymbol{g}_t \odot \boldsymbol{g}_t\\\boldsymbol{x}_t &\leftarrow \boldsymbol{x}_{t-1} - \frac{\eta}{\sqrt{\boldsymbol{s}_t + \epsilon}} \odot \boldsymbol{g}_t \end{aligned}
stxt←st−1+gt⊙gt←xt−1−st+ϵη⊙gt
其中,
η
\eta
η人工设置;常数
ϵ
\epsilon
ϵ保证分母非0,维持数值稳定性,如
1
0
−
6
10^{-6}
10−6。
这里开方、除法和乘法的运算都是按元素运算的。这些按元素运算使得目标函数自变量中每个元素都分别拥有自己的学习率。(
η
s
t
+
ϵ
\frac{\eta}{\sqrt{\boldsymbol{s}_t + \epsilon}}
st+ϵη看作是学习率)
Feature
- 自变量中每个元素的学习率在迭代过程中一直在降低(或不变)。
- 约束项regularizer − 1 s t + ϵ - \frac{1}{\sqrt{\boldsymbol{s}_t + \epsilon}} −st+ϵ1的存在,使得:如果目标函数有关自变量中某个元素的偏导数一直都较大,那么该元素的学习率将下降较快;反之,如果目标函数有关自变量中某个元素的偏导数一直都较小,该元素的学习率将下降较慢。
- 适合处理稀疏梯度
缺点:
- 由公式可以看出,仍依赖于人工设置一个全局学习率 η \eta η,如果过大,会使regularizer过于敏感,对梯度的调节太大
- 学习率是单调递减的,训练中后期,分母越来越大,可能导致训练困难,甚至提前结束
例子,还是那个f(x):
%matplotlib inline
import math
import torch
import sys
sys.path.append("/home/kesci/input")
import d2lzh1981 as d2l
def adagrad_2d(x1, x2, s1, s2):
g1, g2, eps = 0.2 * x1, 4 * x2, 1e-6 # 前两项为自变量梯度
s1 += g1 ** 2
s2 += g2 ** 2
x1 -= eta / math.sqrt(s1 + eps) * g1
x2 -= eta / math.sqrt(s2 + eps) * g2
return x1, x2, s1, s2
def f_2d(x1, x2):
return 0.1 * x1 ** 2 + 2 * x2 ** 2
eta = 0.4
d2l.show_trace_2d(f_2d, d2l.train_2d(adagrad_2d))
epoch 20, x1 -2.382563, x2 -0.158591
η = 0.4 \eta=0.4 η=0.4,轨迹平滑,学习率递减,迭代后期自变量移动幅度较小:
η
=
2
\eta=2
η=2,相对于momentum和SGD来说是一个非常大的学习率,可以看到以很快的速度到达最优点,且比较平衡:
Implement
def get_data_ch7():
data = np.genfromtxt('/home/kesci/input/airfoil4755/airfoil_self_noise.dat', delimiter='\t')
data = (data - data.mean(axis=0)) / data.std(axis=0)
return torch.tensor(data[:1500, :-1], dtype=torch.float32), \
torch.tensor(data[:1500, -1], dtype=torch.float32)
features, labels = get_data_ch7()
def init_adagrad_states():
s_w = torch.zeros((features.shape[1], 1), dtype=torch.float32)
s_b = torch.zeros(1, dtype=torch.float32)
return (s_w, s_b)
def adagrad(params, states, hyperparams):
eps = 1e-6
for p, s in zip(params, states):
s.data += (p.grad.data**2)
p.data -= hyperparams['lr'] * p.grad.data / torch.sqrt(s + eps)
训练:
d2l.train_ch7(adagrad, init_adagrad_states(), {'lr': 0.1}, features, labels)
或者使用pytorch提供的AdaGrad算法:
d2l.train_pytorch_ch7(torch.optim.Adagrad, {'lr': 0.1}, features, labels)