优化算法进阶
ill-conditioned Problem 是hessian matrix的状态比较差,不过我不懂…,两种方法解决:
- Preconditioning很多优化算法都应用了,例如Adam, RMSProp, AdaGrad, Adelta, KFC, Natural gradient and other secord-order optimization algorithms.
- Averaging history gradient: like momentum, which allows larger learning rates to accelerate convergence; applied in Adam, RMSProp, SGD momentum.
最大学习率
最大学习率是为了让目标函数在不同的方向有一个比较好的学习率,避免出现梯度更新的一些问题
f ( x ) = 0.1 x 1 2 + 2 x 2 2 f(\boldsymbol{x})=0.1x_1^2+2x_2^2 f(x)=0.1x12+2x22
根据凸优化的一些理论,为了保证 f ( x ) f(x) f(x)的达到最优的收敛情况,通常设置 η < 1 / L \eta<1/L η<1/L,但是为了保证真的收敛成功,我们要令 η < 2 / L \eta<2/L η<2/L,L是不同x的方向的求导值
例如上面的x1求导后是0.2,
η
<
10
\eta<10
η<10;x2是
η
<
0.5
\eta<0.5
η<0.5,所以最后我们满足两个条件,把学习率设置成0.4
可以看到在x2方向的学习步比较大,而在x1方向是缓慢收敛
Momentum Algorithm
动量法的提出是为了解决梯度下降的上述问题。设时间步
t
t
t 的自变量为
x
t
\boldsymbol{x}_t
xt,学习率为
η
t
\eta_t
ηt。
在时间步
t
=
0
t=0
t=0,动量法创建速度变量
m
0
\boldsymbol{m}_0
m0,并将其元素初始化成 0。在时间步
t
>
0
t>0
t>0,动量法对每次迭代的步骤做如下修改:
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:
m t ← β m t − 1 + ( 1 − β ) g t , x t ← x t − 1 − α t m t , \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, \end{aligned} mtxt←βmt−1+(1−β)gt,←xt−1−αtmt,
α t = η t 1 − β \alpha_t = \frac{\eta_t}{1-\beta} αt=1−βηt
其中,动量超参数 β \beta β满足 0 ≤ β < 1 0 \leq \beta < 1 0≤β<1。当 β = 0 \beta=0 β=0 时,动量法等价于小批量随机梯度下降。
从实验中观察梯度下降在使用动量法后的迭代轨迹:
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
\eta=0.4
η=0.4 和动量超参数
β
=
0.5
\beta=0.5
β=0.5 时,动量法在竖直方向上的移动更加平滑,且在水平方向上更快逼近最优解。下面使用较大的学习率
η
=
0.6
\eta=0.6
η=0.6,此时自变量也不再发散。
会用一种更加平缓的方式去接近最优点.
数学原理不做赘述,因为我也不会… Exponential Moving Average,由指数加权移动平均理解动量法
Pytorch Class
在Pytorch中,torch.optim.SGD
已实现了Momentum。
d2l.train_pytorch_ch7(torch.optim.SGD, {'lr': 0.004, 'momentum': 0.9},
features, labels)
Adagrad
在“动量法”一节里我们看到当 x 1 x_1 x1和 x 2 x_2 x2的梯度值有较大差别时,需要选择足够小的学习率使得自变量在梯度值较大的维度上不发散。但这样会导致自变量在梯度值较小的维度上迭代过慢。动量法依赖指数加权移动平均使得自变量的更新方向更加一致,从而降低发散的可能。本节我们介绍AdaGrad算法,它根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,从而避免统一的学习率难以适应所有维度的问题。
AdaGrad算法会使用一个小批量随机梯度 g t \boldsymbol{g}_t gt按元素平方的累加变量 s t \boldsymbol{s}_t st。在时间步0,AdaGrad将 s 0 \boldsymbol{s}_0 s0中每个元素初始化为0。在时间步 t t t,首先将小批量随机梯度 g t \boldsymbol{g}_t gt按元素平方后累加到变量 s t \boldsymbol{s}_t st:
s t ← s t − 1 + g t ⊙ g t , \boldsymbol{s}_t \leftarrow \boldsymbol{s}_{t-1} + \boldsymbol{g}_t \odot \boldsymbol{g}_t, st←st−1+gt⊙gt,
其中 ⊙ \odot ⊙是按元素相乘。接着,我们将目标函数自变量中每个元素的学习率通过按元素运算重新调整一下:
x t ← x t − 1 − η s t + ϵ ⊙ g t , \boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \frac{\eta}{\sqrt{\boldsymbol{s}_t + \epsilon}} \odot \boldsymbol{g}_t, xt←xt−1−st+ϵη⊙gt,
其中 η \eta η是学习率, ϵ \epsilon ϵ是为了维持数值稳定性而添加的常数,如 1 0 − 6 10^{-6} 10−6。这里开方、除法和乘法的运算都是按元素运算的。这些按元素运算使得目标函数自变量中每个元素都分别拥有自己的学习率。
需要强调的是,小批量随机梯度按元素平方的累加变量 s t \boldsymbol{s}_t st出现在学习率的分母项中。因此,如果目标函数有关自变量中某个元素的偏导数一直都较大,那么该元素的学习率将下降较快;反之,如果目标函数有关自变量中某个元素的偏导数一直都较小,那么该元素的学习率将下降较慢。然而,由于 s t \boldsymbol{s}_t st一直在累加按元素平方的梯度,自变量中每个元素的学习率在迭代过程中一直在降低(或不变)。所以,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能较难找到一个有用的解。
RMSProp
RMSProp算法对AdaGrad算法做了修改。该算法源自Coursera上的一门课程,即“机器学习的神经网络”。
不同于AdaGrad算法里状态变量
s
t
\boldsymbol{s}_t
st是截至时间步
t
t
t所有小批量随机梯度
g
t
\boldsymbol{g}_t
gt按元素平方和,RMSProp算法将这些梯度按元素平方做指数加权移动平均。具体来说,给定超参数
0
≤
γ
0
0 \leq \gamma 0
0≤γ0计算
v t ← β v t − 1 + ( 1 − β ) g t ⊙ g t . \boldsymbol{v}_t \leftarrow \beta \boldsymbol{v}_{t-1} + (1 - \beta) \boldsymbol{g}_t \odot \boldsymbol{g}_t. vt←βvt−1+(1−β)gt⊙gt.
和AdaGrad算法一样,RMSProp算法将目标函数自变量中每个元素的学习率通过按元素运算重新调整,然后更新自变量
x t ← x t − 1 − α v t + ϵ ⊙ g t , \boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \frac{\alpha}{\sqrt{\boldsymbol{v}_t + \epsilon}} \odot \boldsymbol{g}_t, xt←xt−1−vt+ϵα⊙gt,
其中 η \eta η是学习率, ϵ \epsilon ϵ是为了维持数值稳定性而添加的常数,如 1 0 − 6 10^{-6} 10−6。因为RMSProp算法的状态变量 s t \boldsymbol{s}_t st是对平方项 g t ⊙ g t \boldsymbol{g}_t \odot \boldsymbol{g}_t gt⊙gt的指数加权移动平均,所以可以看作是最近 1 / ( 1 − β ) 1/(1-\beta) 1/(1−β)个时间步的小批量随机梯度平方项的加权平均。如此一来,自变量每个元素的学习率在迭代过程中就不再一直降低(或不变)。
AdaDelta
另一个常用优化算法AdaDelta算法也针对AdaGrad算法在迭代后期可能较难找到有用解的问题做了改进 。有意思的是,AdaDelta算法没有学习率这一超参数。
原理
AdaDelta算法也像RMSProp算法一样,使用了小批量随机梯度 g t \boldsymbol{g}_t gt按元素平方的指数加权移动平均变量 s t \boldsymbol{s}_t st。在时间步0,它的所有元素被初始化为0。给定超参数 0 ≤ ρ 0 0 \leq \rho 0 0≤ρ0,同RMSProp算法一样计算
s t ← ρ s t − 1 + ( 1 − ρ ) g t ⊙ g t . \boldsymbol{s}_t \leftarrow \rho \boldsymbol{s}_{t-1} + (1 - \rho) \boldsymbol{g}_t \odot \boldsymbol{g}_t. st←ρst−1+(1−ρ)gt⊙gt.
与RMSProp算法不同的是,AdaDelta算法还维护一个额外的状态变量 Δ x t \Delta\boldsymbol{x}_t Δxt,其元素同样在时间步0时被初始化为0。我们使用 Δ x t − 1 \Delta\boldsymbol{x}_{t-1} Δxt−1来计算自变量的变化量:
g t ′ ← Δ x t − 1 + ϵ s t + ϵ ⊙ g t , \boldsymbol{g}_t' \leftarrow \sqrt{\frac{\Delta\boldsymbol{x}_{t-1} + \epsilon}{\boldsymbol{s}_t + \epsilon}} \odot \boldsymbol{g}_t, gt′←st+ϵΔxt−1+ϵ⊙gt,
其中 ϵ \epsilon ϵ是为了维持数值稳定性而添加的常数,如 1 0 − 5 10^{-5} 10−5。接着更新自变量:
x t ← x t − 1 − g t ′ . \boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \boldsymbol{g}'_t. xt←xt−1−gt′.
最后,我们使用 Δ x t \Delta\boldsymbol{x}_t Δxt来记录自变量变化量 g t ′ \boldsymbol{g}'_t gt′按元素平方的指数加权移动平均:
Δ x t ← ρ Δ x t − 1 + ( 1 − ρ ) g t ′ ⊙ g t ′ . \Delta\boldsymbol{x}_t \leftarrow \rho \Delta\boldsymbol{x}_{t-1} + (1 - \rho) \boldsymbol{g}'_t \odot \boldsymbol{g}'_t. Δxt←ρΔxt−1+(1−ρ)gt′⊙gt′.
可以看到,如不考虑 ϵ \epsilon ϵ的影响,AdaDelta算法与RMSProp算法的不同之处在于使用 Δ x t − 1 \sqrt{\Delta\boldsymbol{x}_{t-1}} Δxt−1来替代超参数 η \eta η。
Adam
Adam算法在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均
原理:
Adam算法使用了动量变量 m t \boldsymbol{m}_t mt和RMSProp算法中小批量随机梯度按元素平方的指数加权移动平均变量 v t \boldsymbol{v}_t vt,并在时间步0将它们中每个元素初始化为0。给定超参数 0 ≤ β 1 < 1 0 \leq \beta_1 < 1 0≤β1<1(算法作者建议设为0.9),时间步 t t t的动量变量 m t \boldsymbol{m}_t mt即小批量随机梯度 g t \boldsymbol{g}_t gt的指数加权移动平均:
m t ← β 1 m t − 1 + ( 1 − β 1 ) g t . \boldsymbol{m}_t \leftarrow \beta_1 \boldsymbol{m}_{t-1} + (1 - \beta_1) \boldsymbol{g}_t. mt←β1mt−1+(1−β1)gt.
和RMSProp算法中一样,给定超参数
0
≤
β
2
<
1
0 \leq \beta_2 < 1
0≤β2<1(算法作者建议设为0.999),
将小批量随机梯度按元素平方后的项
g
t
⊙
g
t
\boldsymbol{g}_t \odot \boldsymbol{g}_t
gt⊙gt做指数加权移动平均得到
v
t
\boldsymbol{v}_t
vt:
v t ← β 2 v t − 1 + ( 1 − β 2 ) g t ⊙ g t . \boldsymbol{v}_t \leftarrow \beta_2 \boldsymbol{v}_{t-1} + (1 - \beta_2) \boldsymbol{g}_t \odot \boldsymbol{g}_t. vt←β2vt−1+(1−β2)gt⊙gt.
由于我们将
m
0
\boldsymbol{m}_0
m0和
s
0
\boldsymbol{s}_0
s0中的元素都初始化为0,
在时间步
t
t
t我们得到
m
t
=
(
1
−
β
1
)
∑
i
=
1
t
β
1
t
−
i
g
i
\boldsymbol{m}_t = (1-\beta_1) \sum_{i=1}^t \beta_1^{t-i} \boldsymbol{g}_i
mt=(1−β1)∑i=1tβ1t−igi。将过去各时间步小批量随机梯度的权值相加,得到
(
1
−
β
1
)
∑
i
=
1
t
β
1
t
−
i
=
1
−
β
1
t
(1-\beta_1) \sum_{i=1}^t \beta_1^{t-i} = 1 - \beta_1^t
(1−β1)∑i=1tβ1t−i=1−β1t。需要注意的是,当
t
t
t较小时,过去各时间步小批量随机梯度权值之和会较小。例如,当
β
1
=
0.9
\beta_1 = 0.9
β1=0.9时,
m
1
=
0.1
g
1
\boldsymbol{m}_1 = 0.1\boldsymbol{g}_1
m1=0.1g1。为了消除这样的影响,对于任意时间步
t
t
t,我们可以将
m
t
\boldsymbol{m}_t
mt再除以
1
−
β
1
t
1 - \beta_1^t
1−β1t,从而使过去各时间步小批量随机梯度权值之和为1。这也叫作偏差修正。在Adam算法中,我们对变量
m
t
\boldsymbol{m}_t
mt和
v
t
\boldsymbol{v}_t
vt均作偏差修正:
m ^ t ← m t 1 − β 1 t , \hat{\boldsymbol{m}}_t \leftarrow \frac{\boldsymbol{m}_t}{1 - \beta_1^t}, m^t←1−β1tmt,
v ^ t ← v t 1 − β 2 t . \hat{\boldsymbol{v}}_t \leftarrow \frac{\boldsymbol{v}_t}{1 - \beta_2^t}. v^t←1−β2tvt.
接下来,Adam算法使用以上偏差修正后的变量 m ^ t \hat{\boldsymbol{m}}_t m^t和 m ^ t \hat{\boldsymbol{m}}_t m^t,将模型参数中每个元素的学习率通过按元素运算重新调整:
g t ′ ← η m ^ t v ^ t + ϵ , \boldsymbol{g}_t' \leftarrow \frac{\eta \hat{\boldsymbol{m}}_t}{\sqrt{\hat{\boldsymbol{v}}_t} + \epsilon}, gt′←v^t+ϵηm^t,
其中 η \eta η是学习率, ϵ \epsilon ϵ是为了维持数值稳定性而添加的常数,如 1 0 − 8 10^{-8} 10−8。和AdaGrad算法、RMSProp算法以及AdaDelta算法一样,目标函数自变量中每个元素都分别拥有自己的学习率。最后,使用 g t ′ \boldsymbol{g}_t' gt′迭代自变量:
x t ← x t − 1 − g t ′ . \boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \boldsymbol{g}_t'. xt←xt−1−gt′.
以上内容从动手学习深度学习上获取,感觉自己被劝退了 - -
数据增强
这里提到的数据增强,现在主流的是图像增强,通过对训练图像做一系列随机改变,来产生相似但又不同的训练样本,从而扩大训练数据集的规模. 随机改变训练样本可以降低模型对某些属性的依赖,从而提高模型的泛化能. 例如,我们可以对图像进行不同方式的裁剪,使感兴趣的物体出现在不同位置,从而减轻模型对物体出现位置的依赖性。我们也可以调整亮度、色彩等因素来降低模型对色彩的敏感度
翻转和裁剪
左右翻转图像通常不改变物体的类别。它是最早也是最广泛使用的一种图像增广方法。下面我们通过torchvision.transforms模块创建RandomHorizontalFlip实例来实现一半概率的图像水平(左右)翻转。
上下翻转不如左右翻转通用。但是至少对于样例图像,上下翻转不会造成识别障碍。下面我们创建RandomVerticalFlip实例来实现一半概率的图像垂直(上下)翻转。
在我们使用的样例图像里,猫在图像正中间,但一般情况下可能不是这样。在5.4节(池化层)里我们解释了池化层能降低卷积层对目标位置的敏感度。除此之外,我们还可以通过对图像随机裁剪来让物体以不同的比例出现在图像的不同位置,这同样能够降低模型对目标位置的敏感性。
在下面的代码里,我们每次随机裁剪出一块面积为原面积 10 % ∼ 100 % 10\% \sim 100\% 10%∼100%的区域,且该区域的宽和高之比随机取自 0.5 ∼ 2 0.5 \sim 2 0.5∼2,然后再将该区域的宽和高分别缩放到200像素。
shape_aug = torchvision.transforms.RandomResizedCrop(200, scale=(0.1, 1), ratio=(0.5, 2))
apply(img, shape_aug) ##自己写的一个函数,不用理~
变化颜色
我们可以从4个方面改变图像的颜色:亮度(brightness)、对比度(contrast)、饱和度(saturation)和色调(hue)。在下面的例子里,我们将图像的亮度随机变化为原图亮度的 50 % 50\% 50%( 1 − 0.5 1-0.5 1−0.5) ∼ 150 % \sim 150\% ∼150%( 1 + 0.5 1+0.5 1+0.5)。
apply(img, torchvision.transforms.ColorJitter(brightness=0.5, contrast=0, saturation=0, hue=0))
随机变化图像的色调:
apply(img, torchvision.transforms.ColorJitter(brightness=0, contrast=0, saturation=0, hue=0.5))
随机变化图像的对比度:
apply(img, torchvision.transforms.ColorJitter(brightness=0, contrast=0.5, saturation=0, hue=0))
同时设置如何随机变化图像的亮度(brightness)、对比度(contrast)、饱和度(saturation)和色调(hue)
color_aug = torchvision.transforms.ColorJitter(brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5)
apply(img, color_aug)
叠加使用图像增广方法
上面的方法我们是可以叠加使用的。
augs = torchvision.transforms.Compose([
torchvision.transforms.RandomHorizontalFlip(), color_aug, shape_aug])
apply(img, augs)
模型微调
迁移学习transfering learning的一种技术:微调fine tuning
迁移学习(transfer learning),将从源数据集学到的知识迁移到目标数据集上。例如,虽然ImageNet数据集的图像大多跟椅子无关,但在该数据集上训练的模型可以抽取较通用的图像特征,从而能够帮助识别边缘、纹理、形状和物体组成等。这些类似的特征对于识别椅子也可能同样有效。
微调
微调由以下4步构成。
- 在源数据集(如ImageNet数据集)上预训练一个神经网络模型,即源模型。
- 创建一个新的神经网络模型,即目标模型。它复制了源模型上除了输出层外的所有模型设计及其参数。我们假设这些模型参数包含了源数据集上学习到的知识,且这些知识同样适用于目标数据集。我们还假设源模型的输出层跟源数据集的标签紧密相关,因此在目标模型中不予采用。
- 为目标模型添加一个输出大小为目标数据集类别个数的输出层,并随机初始化该层的模型参数。
- 在目标数据集(如椅子数据集)上训练目标模型。我们将从头训练输出层,而其余层的参数都是基于源模型的参数微调得到的。
当目标数据集远小于源数据集时,微调有助于提升模型的泛化能力。
在目标模型中,对输出层使用较大的学习率,对其他层使用较小的学习率。因为前面都是用较小的学习率去提取特征,只有最后要输出结果,需要用较大学习率去学习到结果。