1 激活函数
激活函数的作用是使神经网络具备分层的非线性映射学习能力。
常见的激活函数为Sigoid、tanh、Relu、LeakyRelu、elu等,它们皆位于torch.nn.functional
包中。
1.1 Sigmoid函数
f
(
x
)
=
1
1
+
e
−
x
f(x)=\frac{1}{1+e^{-x}}
f(x)=1+e−x1
Sigmoid激活函数是最早采用的一类激活函数,具有指数函数的形状,它将实数映射到
[
0
,
1
]
[0, 1]
[0,1]范围内。
Sigmoid函数求导:
f
′
(
x
)
=
e
−
x
(
1
+
e
−
x
)
2
=
1
1
+
e
−
x
(
1
−
1
1
+
e
−
x
)
=
f
(
x
)
(
1
−
f
(
x
)
)
f'(x)=\frac{e^{-x}}{(1+e^{-x})^2}=\frac{1}{1+e^{-x}}(1-\frac{1}{1+e^{-x}})=f(x)(1-f(x))
f′(x)=(1+e−x)2e−x=1+e−x1(1−1+e−x1)=f(x)(1−f(x))
利用torch.nn.functional
包中的sigmoid
函数以及pytorch的自动求导机制可以绘制出Sigmoid函数及其导数图像如下:
%matplotlib inline
import torch
import torch.nn.functional as F
import matplotlib
import matplotlib.pyplot as plt
x = torch.linspace(-5, 5, 1000, requires_grad = True)
out = F.sigmoid(x)
out.backward(torch.ones(1, 1000)) # 函数值
gradient = x.grad # 导数值
'''
display
'''

从图像可以看出,Sigmoid在定义域内处处可导,且两侧导数逐渐趋于0,即
lim
x
→
∞
f
′
(
x
)
=
0
\lim_{x\rightarrow\infty}f'(x)=0
x→∞limf′(x)=0说明Sigmoid函数是一种软饱和函数(梯度渐近趋于零)。
缺点1: 当
x
x
x的值较大或较小时,函数的导数趋于0,会导致梯度消失的现象。
缺点2: 输出非零均值,会导致模型训练收敛速度变慢。
1.2 tanh函数
f
(
x
)
=
e
x
−
e
−
x
e
x
+
e
−
x
=
2
∗
S
i
g
m
o
i
d
(
2
x
)
−
1
f(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}}=2*Sigmoid(2x)-1
f(x)=ex+e−xex−e−x=2∗Sigmoid(2x)−1
tanh是Sigmoid函数的变体,它将实数映射到
[
−
1
,
1
]
[-1, 1]
[−1,1]范围内,因此输出为零均值,模型收敛速度会更快。
同理,将上面的代码中的sigmoid改为tanh
可以得到tanh以及导数图像如下:
缺点: tanh也存在梯度消失问题。
1.3 Relu(线性修正单元)
f
(
x
)
=
max
(
0
,
x
)
f(x)=\max(0, x)
f(x)=max(0,x)
特点: 导数简单,无指数或除法运算。
Relu导数:
f
(
x
)
=
{
0
x
<
0
1
x
≥
0
f(x)=\begin{cases} 0&x\lt0 \\1 &x\ge0 \end{cases}
f(x)={01x<0x≥0
函数图像:
优点1: 缓解梯度消失问题(
x
>
0
x\gt0
x>0)。
优点2: 计算速度快。
优点3: 收敛速度快。
优点4: Relu会使一部分神经元的输出为0,即网络变得稀疏,可以一定程度上缓解过拟合。
此外,Relu函数存在神经元死亡的问题:
随着训练的推进,部分输入会落入硬饱和区(负半轴),导致对应权重无法更新。这种现象称为神经元死亡。
权值更新过程:
w
=
w
−
α
∂
L
∂
w
w=w-\alpha\frac{\partial{L}}{\partial{w}}
w=w−α∂w∂L当
∂
L
∂
w
\frac{ \partial{L}}{\partial{w}}
∂w∂L很大时,更新权重后该神经元可能落入硬饱和区,此时该神经元的输出为0,梯度也为0,对应权值无法再更新,这个神经元就再也无法激活。
为了解决这个问题,提出了多个Relu的变体。
1.4 Leaky_Relu
f
(
x
)
=
max
(
σ
x
,
x
)
f(x)=\max(\sigma x, x)
f(x)=max(σx,x)其中,
σ
\sigma
σ是一个很小的正常数。这样既修正了数据分布,又保留了负轴的一些值,使得负轴信息不全部丢失。
基于LeakyRelu,有Randomized Relu
和Parameterized Relu
两个变体,前者首先随机选择
σ
\sigma
σ的值,然后在测试过程中进行修正;后者将
σ
\sigma
σ作为神经元的参数进行训练;
1.5 ELU(指数线性单元)
f
(
x
)
=
{
x
x
<
0
α
(
e
x
−
1
)
x
≥
0
f(x)=\begin{cases} x&x\lt0 \\\alpha (e^x-1) &x\ge0 \end{cases}
f(x)={xα(ex−1)x<0x≥0
ELU及其导数图像如下:
ELU融合了Sigmoid和Relu的特点,左侧具有软饱和性,右侧无饱和性。
优点: 左侧软饱和使得ELU对输入变化或噪声更加鲁棒,右侧无饱和使得ELU能够有效缓解梯度消失问题。此外,ELU的零均值输出使得其收敛速度更快。
1.6 MAXOUT
f
(
x
)
=
max
(
w
1
T
x
+
b
1
,
w
2
T
x
+
b
2
,
.
.
.
,
w
k
T
x
+
b
k
)
f(x)=\max(w_1^Tx+b1, w_2^Tx+b2, ... , w_k^Tx+bk)
f(x)=max(w1Tx+b1,w2Tx+b2,...,wkTx+bk)
MAXOUT相当于在中间增加了含k个神经元的“隐隐含层”,输出为k个神经元中输出的最大值,如下图所示:
优点1: MAXOUT的拟合能力非常强,可以拟合任意的凸函数。(当
w
2
=
0
,
b
2
=
0
,
.
.
.
,
w
k
=
0
,
b
k
=
0
w_2=0, b_2=0, ..., w_k=0, b_k=0
w2=0,b2=0,...,wk=0,bk=0时,退化为Relu。)
优点2: MAXOUT能够缓解梯度消失问题;由于不会陷入硬饱和区,可以规避神经元死亡的缺点;
缺点: 增加了参数数量和计算量。
1.7 激活函数的选取
- 通常来说,很少会把各种激活函数串在一个网络中使用
- 如果使用Relu, 一定要小心设置学习率,预防出现过多的“神经元死亡”问题。
- 可以选择LeakyRelu、PRelu或者maxout解决神经元死亡问题。
- 最好不要使用Sigmoid、可以尝试使用tanh,但效果通常不会有Relu或maxout好。
2 梯度下降法
梯度下降法主要有三种变体:批量梯度下降法、随机梯度下降法、小批量梯度下降法
2.1 批量梯度下降法(BGD)
① 对于给定的迭代次数,基于在整个数据集上求出的损失函数对输入的参数向量
w
w
w计算梯度向量。
②参数更新:
Δ
w
∗
=
−
η
∑
k
=
1
a
l
l
∂
J
∂
w
∗
w
∗
t
+
1
=
w
∗
t
+
Δ
w
∗
\Delta w_* = -\eta\sum_{k=1}^{all}\frac{\partial J}{\partial w_*}\\w_*^{t+1} = w_*^t+\Delta w_*
Δw∗=−ηk=1∑all∂w∗∂Jw∗t+1=w∗t+Δw∗
缺点1: 每次更新需要在整个数据集上求出所有的偏导数,因此批量梯度下降法的速度比较慢。
缺点2: 不能以“在线”的形式更新模型,也就是不能在运行中试试加入新的样本进行运算。
2.2 随机梯度下降法(SGD)
相比批量梯度下降法,SGD
的每次更新,是对数据集中的一个样本(x, y)求出损失函数,然后对其求响应偏导数,进行参数更新:
Δ
w
∗
=
−
η
∂
J
∂
w
∗
w
∗
t
+
1
=
w
∗
t
+
Δ
w
∗
\Delta w_* = -\eta\frac{\partial J}{\partial w_*}\\w_*^{t+1} = w_*^t + \Delta w_*
Δw∗=−η∂w∗∂Jw∗t+1=w∗t+Δw∗
优点1: 对于批量梯度下降,在每次更新前,需要对所有样本求解梯度值,因此会对相似样本求算梯度值,在较大数据集上的计算会出现冗余。而随机梯度下降每次仅对一个样本求梯度,去除了这种冗余情况。
优点2: 随机梯度下降的运行速度快,且能够“在线”学习。
优点3: 随机梯度下降中会使得目标函数剧烈的波动,从而使得目标函数可能从一个局部极小值点跳入另一个更小的极小值点。。
缺点1: 随机梯度下降的持续波动使得训练的过程难以停止下来。
缺点2: 每次正反向传播只计算一个样本,串行条明显,硬件利用率不高。
2.3 小批量梯度下降法(Mini-Batch Gradient Descent)
Mini-batch
集合了SGD和BGD的优点,在每次更新中,对n个样本构成的一批数据,计算损失函数,并对相应的参数求导,然后进行参数更新:
Δ
w
∗
=
−
η
∑
k
=
1
n
∂
J
∂
w
∗
w
∗
t
+
1
=
w
∗
t
+
Δ
w
∗
\Delta w_* = -\eta\sum_{k = 1}^{n}\frac{\partial J}{\partial w_*}\\w_*^{t+1} = w_*^t + \Delta w_*
Δw∗=−ηk=1∑n∂w∗∂Jw∗t+1=w∗t+Δw∗
优点1: 相比SGD,降低了更新参数的方差,收敛过程更为稳定。
优点2: 相比BGD,不用对整个数据集求损失函数,可减少计算冗余,加快收敛速度。
小批量梯度下降法通常是训练神经网络的首选算法。
2.4 梯度下降会遇到的问题
在进行梯度下降的过程中,会面临三种情况:
1. 平原问题: 在平原中,梯度变化非常的缓慢。
2. 鞍点: 梯度下降可能会停在鞍点位置。
3. 局部极值点: 梯度下降可能会停在局部极值点而非全局极值点。
下图展示了这三种情况:
上述三种情况的出现,使得选取不同初始点时,最终会收敛到不同的局部极值,得到不同的优化效果。
3 优化算法
3.1 学习率调整策略
学习率 η \eta η是深度学习中一个重要的参量,如何调整学习率是训练出好模型的关键要素之一。
学习率过大
学习率过大,则梯度更新过快,容易出现超调现象:在极值点两端不断发散,或是剧烈震荡。此时,随着迭代次数增大,损失函数没有减小的趋势。
学习率过小
学习率过小,则梯度更新太慢,会导致无法快速地找到好的下降的方向。此时,随着迭代次数增大,损失函数几乎不变。
学习率设置规则
- 小梯度,设置大的学习率,加快梯度更新。
- 大梯度,设置小的学习率,减缓剧烈震荡。
学习率调整方法
1. 基于经验的手动调整
\space\space\space\space
通过尝试不同的固定学习率,如0.1, 0.01, 0.001等,观察迭代次数和损失函数的变化关系,找到损失函数下降最快对应的学习率。
2. 基于策略的调整
(1)
η
=
1
1
+
d
e
c
a
y
r
a
t
e
∗
e
p
o
c
h
n
u
m
η
0
\eta = \frac{1}{1+decayrate*epochnum}\eta_0
η=1+decayrate∗epochnum1η0其中,
e
p
o
c
h
n
u
m
epochnum
epochnum表示遍历数据集的次数,
d
a
c
a
y
r
a
t
e
dacayrate
dacayrate为衰减率。随着迭代次数的增多,学习率会逐渐下降。
(2)指数衰减(exponentially decay)
η
=
0.9
5
e
p
o
c
h
n
u
m
η
0
\displaystyle \eta = 0.95^{epochnum}\eta_0
η=0.95epochnumη0
(3)其他
η
=
k
e
p
o
c
h
n
u
m
η
0
\eta = \frac{k}{\sqrt{epochnum}}\eta_0
η=epochnumkη0
3.2 梯度下降优化算法
在PyTorch中,优化算法被包含在torch.optim
包中,optim中实现了大多数常用的优化算法如:Momenbum法(动量法)、Adagrad法(自适应梯度)、Nesterov加速梯度法(NAG)、Adadelta法、RMSprop法、适应性动量估计法(Adam)
3.2.1 Momenbum法(动量法)
动量法在利用梯度时,不仅仅利用当前次迭代所计算出的梯度值,还使用之前一段时间的梯度值。 v t = γ v t − 1 + η ∇ J θ t = θ t − 1 − v t v_t = \gamma v_{t-1} + \eta\nabla J\\\theta^t = \theta^{t-1} - v_t vt=γvt−1+η∇Jθt=θt−1−vt其中, v t − 1 v_{t-1} vt−1表示之前所有步骤所累积的动量和。 γ \gamma γ为动量因子,通常取0.9,使得早期梯度对当前梯度的影响越来越小。 γ v t − 1 \gamma v_{t-1} γvt−1为动量项,当前后梯度方向一致时,能够加速学习,前后方向不一致时,能够抑制震荡。
动量法就是一种犹如小球自动滚动下山的方法,它使得损失函数能够根据惯性越过平原、鞍点或局部极值点的位置。而动量因子的作用就类似于摩擦力的作用,使得结果不至于发散。
下图是使用动量法之后的情况:
下降初期: 梯度方向相同,加快下降速度。
平原阶段: 当前梯度为零,但累积的动量会使其顺利越过平原。
鞍点位置: 同样的,累积的动量会使得其越过鞍点位置。
局部极值点位置: 同样的,如果累积的动量足够大,可以使得其越过局部极值点位置。
全局极值点位置: 如果没有动量项,在学习率较大的情况下会使得损失函数在极值点位置来回“震荡”,而动量项可以减少“震荡”的幅度,从而加速收敛到全局极值点。
3.2.2 加速梯度法(NAG)
加速梯度法是动量法的改进,是一种更加“聪明”的滚动下山的方式。动量法每一步需要将历史梯度和当前梯度进行合并,然后再进行梯度下降;而加速梯度法先按照历史梯度往前走一小步,然后与该位置的“超前”梯度进行合并,因此可以在这个超前的位置看到梯度,从而修正这一步的梯度。
v
t
=
γ
v
t
−
1
+
η
∇
J
(
θ
t
−
γ
v
t
−
1
)
θ
t
=
θ
t
−
1
−
v
t
v_t = \gamma v_{t-1} + \eta\nabla J(\theta_t - \gamma v_{t-1})\\\theta_t = \theta_{t-1}-v_t
vt=γvt−1+η∇J(θt−γvt−1)θt=θt−1−vt
其中,
∇
J
(
θ
t
−
γ
v
t
−
1
)
\nabla J(\theta_t - \gamma v_{t-1})
∇J(θt−γvt−1)作用是“预测”下次参数位置近似值,也就是告诉我们参数大致会变为多少,这种预更新方法能防止大幅震荡,且不会错过最小值,并对参数更新更加敏感。
这种基于预测的更新方法,可避免过快地前进,提高了算法响应能力。
3.2.3 Adagrad法(自适应梯度)
AdaGrad
算法利用之前计算的梯度来自动变更学习率
e
t
a
eta
eta, 学习率的修正方式如下:
θ
t
=
θ
t
−
1
−
η
∑
r
=
1
t
−
1
(
∇
J
)
r
+
ϵ
(
∇
J
)
t
\theta_{t} = \theta_{t-1} - \frac{\eta}{\sqrt{\sum_{r=1}^{t-1}(\nabla J)_r+\epsilon}}(\nabla J)_t
θt=θt−1−∑r=1t−1(∇J)r+ϵη(∇J)t
其中,
ϵ
\epsilon
ϵ是一个平滑项,用于避免分母为0, 它的数量级通常在
1
0
−
7
10^{-7}
10−7。
优点
①每次迭代不需要手工调整学习率,能够实现学习率自动更新。
②大梯度对应小的学习率,小梯度对应大的学习率,从而加快收敛。
缺点
①学习率单调递减,训练后期学习率非常小,这使得网络更新能力越来越弱,最终梯度趋于零,使得训练提前结束。
②更新时,左右两边单位不一致。
③仍依赖于人工设置一个全局学习率
η
\eta
η。
3.2.4 Adadelta法
Adadelta
法是Adagrad
法的一个延伸,它旨在于解决Adagrad的是三个问题。
①第一个问题:仅利用Adagrad分母中累计项离当前时间点比较近的项。
Adagrad会累加之前所有的梯度平方,而Adadelta只累加固定大小的项(累积窗口限制到固定的尺寸w,即只取最近的w个状态。)
②第二、三个问题:改进为
θ
t
=
θ
t
−
1
−
R
M
S
[
Δ
θ
]
t
−
1
R
M
S
[
g
]
t
g
t
\theta_t = \theta_{t-1} -\frac{RMS[\Delta\theta]_{t-1}}{RMS[g]_t}g_t
θt=θt−1−RMS[g]tRMS[Δθ]t−1gt其中,
g
t
=
(
∇
J
)
t
R
M
S
[
Δ
θ
]
t
=
E
[
Δ
θ
2
]
t
+
ϵ
,
E
[
Δ
θ
2
]
t
=
γ
E
[
Δ
θ
2
]
t
−
1
+
(
1
−
γ
)
Δ
θ
t
2
,
R
M
S
[
g
]
t
=
E
[
g
2
]
t
+
ϵ
,
E
[
g
2
]
t
=
γ
E
[
g
2
]
t
−
1
+
(
1
−
γ
)
g
t
2
g_t = (\nabla J)_t\\RMS[\Delta\theta]_t = \sqrt{E[\Delta\theta^2]_t+\epsilon}, E[\Delta\theta^2]_t = \gamma E[\Delta\theta^2]_{t-1} + (1-\gamma)\Delta\theta_t^2,\\RMS[g]_t = \sqrt{E[g^2]_t+\epsilon}, E[g^2]_t = \gamma E[g^2]_{t-1} + (1-\gamma)g_t^2
gt=(∇J)tRMS[Δθ]t=E[Δθ2]t+ϵ,E[Δθ2]t=γE[Δθ2]t−1+(1−γ)Δθt2,RMS[g]t=E[g2]t+ϵ,E[g2]t=γE[g2]t−1+(1−γ)gt2
从上式可以看出,Adadelta法并不依赖于全局学习率。
3.2.5 RMSprop法(均方根传播)
RMSprop
法和Adadelta
法类似,也是Adagrad法的改进,相比于Adagrad历史梯度,RMSprop算法增加了一个衰减系数来控制历史信息的权重。
E
[
g
2
]
t
=
0.9
E
[
g
2
]
t
−
1
+
0.1
g
t
2
θ
t
+
1
=
θ
t
−
η
E
[
g
2
]
t
+
ϵ
g
t
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}}g_t
E[g2]t=0.9E[g2]t−1+0.1gt2θt+1=θt−E[g2]t+ϵηgt
从本质上看,可认为RMSprop将不同方向梯度的方差考虑进来,对于波动大的(方差大),梯度给小一点,对于波动小的,梯度给大一点,这样整个下降路径就缓和很多,提高计算效率。
square
表示在梯度那里使用了平方项,mean
表示梯度的加权平均,root
表示分母梯度平方再开方。
特点
① 仍然依赖于全局学习率
② 很好的解决了训练过程过早结束的问题
③ RMSprop适合处理非平稳目标(将不同方向梯度的方差考虑进来),对RNN效果很好。
3.2.6 Adam法(自适应动量估计法)
Adam
算法结合了momentum
和RMSprop
,包含梯度平均和梯度方差的信息,将momentum和RMSprop的权重更新方法结合在一起。
Adam法本质上是带有动量项RMSprop,它利用梯度的一阶估计和二阶估计动态调整每个参数的学习率。
梯度的一阶矩:
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_2v_{t-1}+(1-\beta_2)g_t^2
vt=β2vt−1+(1−β2)gt2其中,
β
1
\beta_1
β1为momentum
超参数,用来调试梯度平均方程;
β
2
\beta_2
β2为RMSprop
超参数,用来调试计算梯度方差过程。
偏差校正:
m
^
t
=
m
t
1
−
β
1
t
v
^
t
=
v
t
1
−
β
2
t
\hat{m}_t = \frac{m_t}{1-\beta_1^t} \hat{v}_t = \frac{v_t}{1-\beta_2^t}
m^t=1−β1tmtv^t=1−β2tvt近似为对期望的无偏估计。
参数更新:
θ
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
3.2.7 各种优化方法对比
下图给出了不同梯度下降法损失平面等高线对比:
可以看到,Adagrad
、Adadelta
、RMSprop
能够快速找到正确前进方向并以相似的速度很快收敛。而动量法和NAG
法则找错了方向,让小球沿着梯度下降的方向前进,但NAG
法能够往前看并对前面的情况作出相应,所有可以很快改正方向,向正确方向前进。
下图给出了不同梯度下降法在鞍点处的表现:
可以看到,SGD法、动量法、NAG法都很难打破“对称性”带来的壁垒,尽管最后两者设法逃脱了鞍点;Adagrad、Adadelta、RMSprop能够快速的沿着负斜率的方向前进。
优化器选择
① SGD通常训练时间长,且容易陷入鞍点,但在好的初始化和学习率调整策略的情况下可以得到更可靠的结果。
② 在网络较为复杂,且对收敛速度有要求的情况下,通常使用学习率自适应的优化方法(Adagrad法、Adadleta法、RMSprop法以及Adam法等)。
③ Adadelta, RMSprop, Adam是比较相近的算法,在一般情况下表现差不多。
3.3 PyTorch中使用优化算法
下面举例说明在PyTorch中如何使用各种优化算法:
3.2.1 SGD
PyTorch中的SGD指的是小批量梯度下降法(Mini-batch),并且通过传入参数可以实现momentum和NAG优化。SGD类原型如下:
CLASS
:torch.optim.SGD(params, lr = <required parameters>, momentum = 0, dampening = 0, weight_decay = 0, nesterov = False)
params
:要优化的参数,需传入model.parameters()lr
:学习率,由经验设置,通常可设置为0.1、0.01、0.001等momentum
:动量因子,通常设置为0.9dampening
:动量抑制系数weight_decay
:权值衰减(L2正则项),用于防止过拟合nesterov
:是否使能NAG优化,默认为否
下面是一个简单的例子:
# 1. 实例化SGD类,得到优化器对象optimizer
optimizer = torch.optim.SGD(model.parameters(), lr = 0.1, momentum = 0.9)
# 2. 在计算梯度之前先将之前计算的梯度清零,因为反向传播时只需要当前次迭代的梯度
optimizer.zero_grad()
# 3. 计算出损失函数,并利用backward()自动计算梯度
loss_fun(model(input), target).backward()
# 4. 最后利用optimizer.step()更新参数
optimizer.step()
上述过程总共经过了四步:创建优化器、历史梯度清零、重新计算梯度、更新参数。实际上第一步在整个模型训练中只需要执行一次,后三步需要在每次迭代过程中反复执行。
3.2.2 Adagrad
前面提到,AdaGrad
算法利用之前计算的梯度来自动变更学习率
e
t
a
eta
eta, 学习率的修正方式如下:
θ
t
=
θ
t
−
1
−
η
∑
r
=
1
t
−
1
(
∇
J
)
r
+
ϵ
(
∇
J
)
t
\theta_{t} = \theta_{t-1} - \frac{\eta}{\sqrt{\sum_{r=1}^{t-1}(\nabla J)_r+\epsilon}}(\nabla J)_t
θt=θt−1−∑r=1t−1(∇J)r+ϵη(∇J)t
其中,
ϵ
\epsilon
ϵ是一个平滑项,用于避免分母为0, 它的数量级通常在
1
0
−
7
10^{-7}
10−7。
Adagrad原型如下:
CLASS
:torch.optim.Adagrad(params, lr = 0.01, lr_decay = 0, weight_decay = 0, initial_accumulator_value = 0)
params
:要优化的参数,可传入model.parameters()lr
:初始学习率,由经验设置,通常可设置为0.1、0.01、0.001等lr_decay
:学习率衰减项,根据源码可以看到貌似是计算出的学习率再乘lr_decayweight_decay
:权值衰减(L2正则项),用于防止过拟合initial_accumulator_value
:初始梯度累计值,一般就默认为0
PyTorch中使用Adagrad等优化方法和SGD类似,就不举例了。
3.2.4 RMSprop
RMSprop
是G.Hinton提出的一个优化算法,它是Adagrad的改进算法。原型如下:
CLASS
:torch.optim.RMSprop(params, lr = 0.01, alpha = 0.99, eps = 1e-08, weight_decay = 0, momentum = 0, centered = False)
其中,params、lr、weight_decay, momentum的解释同上,eps的意义在前文也有解释,因此下面重点介绍其他两个参数:
alpha
:平滑系数,具体作用需要参看RMSprop算法的过程,默认为0.99centered
:如果使能,则计算中心化的RMSprop,梯度会根据方差的估计进行归一化。
3.2.4 Adam
前面提到,Adam
算法结合了momentum
和RMSprop
,包含梯度平均和梯度方差的信息,将momentum和RMSprop的权重更新方法结合在一起。
Adam原型如下:
CLASS
:torch.optim.Adam(params, lr = 0.001, betas = (0.9, 0.999), eps = 1e-08, weight_decay = 0, amsgrad = False
其中,params、lr、weight_decay的解释同上,eps的意义在前文也有解释,因此下面重点介绍其他两个参数:
- betas:需要传入一个tupe:(
β
1
,
β
2
\beta_1, \beta_2
β1,β2),它们分别是
momentum
和RMSprop
的超参数 - amsgrad:是否使用Adam算法的变体AMSGrad,该算法详见On the Convergence of Adam and Beyond,默认不使用。
4 过拟合问题
过拟合是指一个算法在训练数据上表现很好,但在测试数据上或新的数据上的表现,与在训练数据上差别很大。
4.1 出现过拟合的原因
① 过拟合一个可能的原因是模型特征维度过高,模型假设过于复杂,参数过多。
② 数据中的噪声过多,如果完全拟合的话,与真实情景差异更大。
③ 数据量有限,使得模型无法真正了解整个数据的真实分布。
④ 权值学习迭代次数足够多的,拟合了训练数据中的噪声和训练样例中没有代表性的特征。
过拟合使得模型过度的拟合了训练数据,而没有考虑到泛化能力。
4.2防止过拟合的方法
4.2.1 Early stoppping (早停法)
\space\space\space\space
早停法是一种用来避免模型在训练数据上过拟合的正则化方法:一旦训练过程中出现性能开始下降,可以停止训练与学习。
4.2.2 Weight Decay (权值衰减法)
\space\space\space\space
权值衰减法是从我们大脑工作借鉴来的方法,我们的大脑会将一些无用的连接进行清除。
在机器学习中,引入L2范数正则化来衰减权重,在一定程度上减少模型过拟合的问题,因此权重衰减也叫L2正则化。
L2正则化的原理如下,在损失函数上增加L2范数惩罚项:
L
=
L
0
+
λ
2
n
∑
w
2
L=L_0+\frac{\lambda}{2n}\sum w^2
L=L0+2nλ∑w2
对加入L2正则化的损失函数求导:
∂
L
∂
w
=
∂
L
0
∂
w
+
λ
n
w
\frac{\partial L}{\partial w}=\frac{\partial L_0}{\partial w}+\frac{\lambda}{n}w
∂w∂L=∂w∂L0+nλw
权值更新过程:
w
=
w
−
η
(
∂
L
0
∂
w
+
λ
n
w
)
=
(
1
−
η
λ
n
)
w
−
η
∂
L
0
∂
w
w=w-\eta (\frac{\partial L_0}{\partial w}+\frac{\lambda}{n}w)=(1-\frac{\eta\lambda}{n})w-\eta\frac{\partial L_0}{\partial w}
w=w−η(∂w∂L0+nλw)=(1−nηλ)w−η∂w∂L0其中,
λ
\lambda
λ为正则项系数,也叫权重衰减系数;
w
w
w的系数
1
−
η
λ
n
<
0
\displaystyle1-\frac{\eta\lambda}{n}\lt 0
1−nηλ<0,即对权重进行了衰减。
4.2.3 Dropout
2012年,Hinton在其论文《improving neural networks by preventing co-adaptation of feature detectors》提出Dropout,并在同年应用于Alexnet网络。
Dropout的基本思想在于减少部分神经元之间的联合适应性,从而提高神经网络的性能。因为神经元之间的协同作用(某些神经元必须依赖其他神经元才能发挥作用)造成了过拟合现象。大致做法为:在训练的时候,将部分神经元失活(在前向传播的时候,让某个神经元的激活值以一定的概率p停止工作,输出为零),阻断部分神经元之间的协同作用。
如上图所示,失活一定的神经元之后,网络的结构发生了变化,从而减少了冗余连接,防止过拟合。
Dropout的工作流程如下:
① 前向传播
通常的前向传播计算公式:
z
i
l
+
1
=
w
i
l
+
1
y
l
+
b
i
l
+
1
y
i
l
+
1
=
f
(
z
i
(
l
+
1
)
z_i^{l+1} = \boldsymbol w_i^{l+1}\boldsymbol y^l + b_i^{l+1}\\y_i^{l+1} = f(z_i^{(l+1)}
zil+1=wil+1yl+bil+1yil+1=f(zi(l+1)采用Dropout后:
r
j
(
l
)
∼
B
e
r
n
o
u
l
l
i
(
p
)
y
~
(
l
)
=
r
(
l
)
∗
y
(
l
)
r_j^(l)\sim Bernoulli(p)\\\tilde{y}^{(l)} = \boldsymbol r^{(l)} * \boldsymbol y^{(l)}
rj(l)∼Bernoulli(p)y~(l)=r(l)∗y(l)
4.2.4 数据集增广
当训练样本过少,但模型特征维度过高时,网络很容易拟合一些噪声特征,从而发生过拟合现象,因此将数据集进行增广可以有效的防止过拟合。
数据增广的基本方法由:①采集更多的训练样本;②对当前训练样本通过一定规则扩充数据,如图像数据进行平移、旋转、缩放等。
数据增广操作通常在数据集预处理时进行,torchvision
中的transforms
包提供了一些的常用数据增广和变换操作,详见torchvision.transforms。
其中支持的变换如下:
1. torchvison.transforms.CenterCrop(size)
中心裁剪,当size为int时,裁剪为(size * size)大小;当size = (h, w)时,裁剪为(h, w)大小。
2. torchvision.transforms.ColorJitter(brightness = 0, contrast = 0, saturation = 0, hue = 0)
随机改变图像的亮度、对比度、饱和度和色度。
3. torchvision.transforms.FiveCrop(size)
将一张图根据四个角和中心裁剪出5张,如下图:
4. torchvision.transforms.Grayscale(num_output_channel = 1)
转换为灰度图,如果num_output_channel = 1则输出总的灰度图,如果 = 3,则输出三个通道的灰度图。
5. torchvision.transforms.Pad(padding, fill = 0, padding_mode = 'constant')
对图像进行padding操作
6. torchvision.transforms.RandomAffine(degrees, translate = None, scale = None, shear = None, resample = False, fillcolor = 0)
对图像进行仿射变换,保持中心不变。degrees旋转、translate平移、scale缩放
7. torchvision.transforms.RandomApply(transforms, p = 0.5)
随机是否应用变换
8. torchvision.transforms.RandomChoice(transforms)
随机应用transforms列表中的一种变换
9. torchvision.transforms.RandomCrop(size, padding = None, pad_if_needed = False, fill = 0, padding_mode = 'constant
)
随机位置裁剪
10. torchvision.transfoms.RandomGrayscale(p = 0.1)
以概率 p 进行随机灰度转换
11. torchvision.transforms.RandomHorizontalFlip(p = 0.5)
以概率 p 进行随机水平翻转
12. torchvision.transforms.RandomOrder(transforms)
以随机的顺序应用一系列变换
13. torchvision.transforms.RandomPerspective(distortion_scale = 0.5, p = 0.5, interpolation = 3)
以概率 p 进行随机透视变换
14. torchvision.transforms.RandomResizedCrop(size, scale = (0.08, 1.0), ratio = (0.75, 1.3333333333333), interpolation = 2)
对图像进行随机缩放和裁剪
15. torchvision.transforms.RandomRotation(degrees, resample = False, expand = False, center = None)
随机旋转图像
16. torchvision.transforms.RandomVerticalFlip(p = 0.5)
以概率 p 进行随机垂直翻转
17. torchvision.transforms.Resize(size, interpolation = 2
对图像进行resize操作
18. torchvision.transforms.TenCrop(size, vertical_filp = False)
类似FiveCrop,将图像裁剪出10张图。
此外,torchvision.transforms
还提供了一个类将上述变换进行组合:
19. torchvision.transforms.Compose(transforms)
transform = transforms.Compose([
transforms.CenterCrop(10),
transforms.ToTensor(),
])
总结
本文总结了一些深度学习的基础知识,包括激活函数、梯度下降法及优化、过拟合问题等,同时总结了如何在PyTorch中使用激活函数、优化算法以及利用torchvision中的变换进行数据集增广等。