1 前馈神经网络
前馈神经网络也称为是深度前馈网络或者多层感知机,它是最基础的深度学习模型。
1.1 基本概念
前馈神经网络的目标是在函数空间中寻找相对正确的函数。函数空间是由所选的architecture决定的,而函数空间中的具体函数是由parameters决定的。
前馈神经网络之所以称作前馈的,是因为信息从输入
x
⃗
\vec{x}
x到输出
y
y
y是单向流动的,并没有从输出到模型本身的反馈连接。
前馈神经网络通常使用许多不同的函数复合而成,这些函数如何复合则由一个有向无环图来描述。最简单的情况:有向无环图是链式结构。
假设有三个函数
f
1
,
f
2
,
f
3
f_1,f_2,f_3
f1,f2,f3组成链式复合结构,则:
f
(
x
⃗
)
=
f
3
(
f
2
(
f
1
(
x
⃗
)
)
)
f(\vec{x})=f_3(f_2(f_1(\vec{x})))
f(x)=f3(f2(f1(x))) 。其中:
f
1
f_1
f1被称作网络的第一层,
f
2
f_2
f2为网络第二层,
f
3
f_3
f3称为网络第三层。链的全长称作模型的深度或者神经网络的层数。
深度前馈网络的最后一层也称作输出层。
给定训练样本 ( x ⃗ , y ) (\vec{x},y) (x,y),要求输出层的输出 f ( x ⃗ ) ≈ y f(\vec{x}) \approx y f(x)≈y ,但是对于其他层并没有任何要求。
- 因为无法观测到除了输出层以外的那些层的输出,因此那些层被称作隐藏层(hidden layer) 。
- 学习算法必须学习如何利用隐层来配合输出层来产生想要的结果。
- 通常每个隐层的输出都是一个向量而不是标量,这些隐层的输出向量的维数决定了前馈神经网络的宽度。
也可以将每一层想象成由许多并行的单元组成,每个单元表示一个向量到标量的函数:每个单元的输入来自于前一层的许多单元,单元根据自己的激活函数来计算单元的输出。激活函数是前馈神经网络具有非线性表达能力的核心因素。因此每个单元类似于一个神经元。
1.2 特征学习
- 线性模型简单高效,且易于求解。但是它有个明显的缺陷:模型的能力被局限在线性函数中,因此它无法理解任意两个输入变量间的非线性相互作用 。
解决线性模型缺陷的方法是:采用核技巧,将线性模型作用在 ϕ ( x ⃗ ) \phi(\vec{x}) ϕ(x)上,而不是原始输入 x ⃗ \vec{x} x上。其中 ϕ \phi ϕ是一个非线性变换。
可以认为:通过 ϕ \phi ϕ,提供了 x ⃗ \vec{x} x的一个新的representation。
- 有三种策略来选择这样的非线性变换 。
- 使用一个通用的 ϕ \phi ϕ,如无限维的 ϕ \phi ϕ(采用基于 RBF核的核技巧)。
当 ϕ \phi ϕ具有足够高的维数,则总是有足够的能力来适应训练集,但是对于测试集的泛化往往不佳。这是因为:通用的 ϕ \phi ϕ通常只是基于局部平滑的原则,并没有利用足够多的先验知识来解决高级问题。
- 手动设计 ϕ \phi ϕ 。
这种方法对于专门的任务往往需要数十年的努力(如语音识别任务)。
- 通过模型自动学习 ϕ \phi ϕ。
这是深度学习采用的策略。以单层隐层的深度前馈网络为例: y = f ( x ⃗ , θ ⃗ , w ⃗ ) = ϕ ( x ⃗ ; θ ⃗ ) T w ⃗ y=f(\vec{x},\vec{\theta},\vec{w})=\phi(\vec{x};\vec{\theta})^T\vec{w} y=f(x,θ,w)=ϕ(x;θ)Tw 。此时有两个参数:
参数
θ
⃗
\vec{\theta}
θ:从一族函数中学习
ϕ
\phi
ϕ,其中
ϕ
\phi
ϕ定义了一个隐层。
参数
w
⃗
\vec{w}
w:将
ϕ
(
x
⃗
)
\phi(\vec{x})
ϕ(x)映射到所需输出。
- 深度学习中,将representation参数化为 ϕ ( x ⃗ , θ ⃗ ) \phi(\vec{x},\vec{\theta}) ϕ(x,θ),并使用优化算法来寻找 θ ⃗ \vec{\theta} θ从而得到一个很好的 representation。
- 如果使用一个非常宽泛的函数族 ϕ ( x ⃗ , θ ⃗ ) \phi(\vec{x},\vec{\theta}) ϕ(x,θ),则能获得第一种方案的好处:适应能力强。
- 如果将先验知识编码到函数族
ϕ
(
x
⃗
,
θ
⃗
)
\phi(\vec{x},\vec{\theta})
ϕ(x,θ)中,则能获得第二种方案的好处:有人工先验知识。
因此深度学习的方案中,只需要寻找合适的、宽泛的函数族 ,而不是某一个映射函数 。
- 通过特征学习来改善模型不仅仅适用于前馈神经网络,也适用于几乎所有的深度学习模型。
1.3 训练
- 训练一个深度前馈网络和训练一个线性模型的选项相同:选择优化算法、代价函数、输出单元的形式。
除此之外还需要给出下列条件:
- 由于深度前馈网络引入了隐层的概念,因此需要选择适用于隐层的激活函数。激活函数接受隐层的输入值,给出了隐层的输出值。
- 深度前馈网络的网络结构也需要给出,其中包括:有多少层网络、每层网络有多少个单元、层级网络之间如何连接。
- 深度神经网络训练时需要计算复杂函数的梯度,通常这采用反向传播算法(back propagation)和它的现代推广来完成。
2. 使用pytorch定义简单神经网络
假设输入样本为64个,输入层维度为1000,只包括一层隐藏层,隐藏层维度为100,输出层维度为10个。
使用链式法则求导的代码如下所示:
num_samples = 64 # N
dim_in, dim_hid, dim_out = 1000, 100, 10 # IN H OUT
x = torch.randn(num_samples, dim_in) # N * IN
y = torch.randn(num_samples, dim_out) # N * OUT
w1 = torch.randn(dim_in, dim_hid) # IN * H
w2 = torch.randn(dim_hid, dim_out) # H * OUT
eta = 1e-6
for i in range(1000):
#Forward pass
h = x @ w1 # N * H
h_relu = h.clamp(min = 0) # N * H
y_pred = h_relu @ w2 # N * OUT
#Loss
loss = (y_pred - y).pow(2).sum().item()
print('times is {}, loss is {}'.format(i, loss))
#Backward pass
grad_y_pred = 2.0 * (y_pred - y) # N * OUT
grad_w2 = (h_relu.t()) @ (grad_y_pred) #H * OUT = (H * N) * (N * OUT),其中(H * N) = (N * H).T
grad_h_relu = grad_y_pred @ ((w2.t()))# N * H = (N * OUT) * (OUT * H),其中(OUT * H) = (H * OUT).T
grad_h = grad_h_relu.clone()
grad_h[h < 0] = 0
grad_w1 = (x.t()) @ (grad_h) # IN * H = (IN * N) * (N * H)
w1 = w1 - eta * grad_w1
w2 = w2 - eta * grad_w2
使用pytorch自动求导代码如下所示:
num_samples = 64 # N
dim_in, dim_hid, dim_out = 1000, 100, 10 # IN H OUT
x = torch.randn(num_samples, dim_in) # N * IN
y = torch.randn(num_samples, dim_out) # N * OUT
w1 = torch.randn(dim_in, dim_hid, requires_grad=True) # IN * H
w2 = torch.randn(dim_hid, dim_out, requires_grad=True) # H * OUT
eta = 1e-6
for i in range(1000):
#Forward pass
h = x @ w1 # N * H
h_relu = h.clamp(min = 0) # N * H
y_pred = h_relu @ w2 # N * OUT
#Loss
loss = (y_pred - y).pow(2).sum()
print('times is {}, loss is {}'.format(i, loss.item()))
loss.backward()
#Backward pass
with torch.no_grad():
w1 -= eta * w1.grad #如果写成w1 = w1 - eta * w1.grad就会报错
w2 -= eta * w2.grad
w1.grad.zero_()
w2.grad.zero_()
3. 激活函数
- 激活函数的设计是一个非常活跃的研究领域,并且目前还没有明确的指导性理论,难以决定何时采用何种类型的激活函数是最佳方案。
- 通常不能预先判断哪种类型的激活函数工作的最好,所以设计过程中需要反复试错,通过测试集评估其性能来选择合适的激活函数。
- 一般默认采用的激活函数是修正线性单元(relu),但是仍然有许多其他类型的激活函数。
- 某些激活函数可能并不是在所有的输入上都是可微的。如:修正线性单元 g ( z ) = max { 0 , z } g(z)=\max{\{0,z\}} g(z)=max{0,z}在 z = 0 z=0 z=0处不可微,这使得在该点处梯度失效。
事实上梯度下降法对这些隐单元的表现仍然足够好,原因是:
-
神经网络的训练算法通常并不会达到代价函数的局部最小值,而仅仅是显著地降低代价函数的值即可。因此实际上训练过程中一般无法到达代价函数梯度为零的点,所以代价函数取最小值时梯度未定义是可以接受的。
-
不可微的点通常只是在有限的、少数的点上不可微,在这些不可微的点通常左导数、右导数都存在。
-
神经网络训练的软件实现通常返回左导数或者右导数其中的一个,而不是报告导数未定义或者产生一个错误。这是因为计算机计算 0 点的导数时,由于数值误差的影响实际上不可能计算到理论上 0 点的导数,而是一个微小的偏离:向左侧偏离就是左导数,向右侧偏离就是右导数。
- 大多数激活函数的工作过程都可以描述为下面三步:
-
接受输入向量 x ⃗ \vec{x} x。
-
计算仿射变换 z = w ⃗ T x ⃗ + b z =\vec{w}^{T}\vec{x}+b z=wTx+b。
-
激活函数也称作是隐单元。
3.1 线性激活函数
可以使用单位函数 g ( z ) = z g(z)=z g(z)=z作为激活函数 。但如果网络的每一层都是由线性变换组成,则网络作为整体也是线性的。这会降低网络的表达能力,因此线性激活函数较少使用。
3.2 修正线性单元(relu)
- 修正线性单元采用激活函数 g ( z ) = m a x { 0 , z } g(z)=max\{0, z\} g(z)=max{0,z},它和线性单元非常类似,区别在于:修正线性单元在左侧的定义域上输出为零。
-
优点:采用基于梯度的优化算法时,非常易于优化。当修正线性单元处于激活状态时,导数为常数1 ;当修正线性单元处于非激活状态时,导数为常数0 。修正线性单元的二阶导数几乎处处为零。
-
缺点:无法通过基于梯度的方法学习那些使得修正线性单元处于非激活状态的参数,因为此时梯度为零。
-
对于修正线性单元 h ⃗ = g ( W T x ⃗ + b ⃗ ) \vec{h}=g(W^{T}\vec{x}+\vec{b}) h=g(WTx+b),初始化时可以将 b ⃗ \vec{b} b的所有元素设置成一个小的正值(如0.1),从而使得修正线性单元在初始时尽可能的对训练集中大多数输入呈现激活状态。
-
有许多修正线性单元的扩展存在,这些扩展保证了它们能在各个位置都保持非零的梯度。大多数扩展的表现与修正线性单元相差无几,偶尔表现的更好。
3.3 sigmoid / tanh
-
在引入修正线性单元之前,大多数神经网络使用sigmoid函数 g ( z ) = σ ( z ) g(z)=\sigma(z) g(z)=σ(z),或者双曲正切函数 g ( z ) = t a n h ( z ) g(z)=tanh(z) g(z)=tanh(z)作为激活函数。这两个激活函数密切相关,因为 t a n h ( z ) = 2 σ ( 2 z ) − 1 tanh(z)=2\sigma(2z)-1 tanh(z)=2σ(2z)−1 。
-
与修正线性单元不同,sigmoid单元和tanh单元在其大部分定义域内都饱和,仅仅当 z z z在 0 附近才有一个较高的梯度,这会使得基于梯度的学习变得非常困难。因此,现在不鼓励将这两种单元用作前馈神经网络中的激活函数。
- 如果选择了一个合适的代价函数(如对数似然函数)来抵消了sigmoid的饱和性,则这两种单元可以用作输出单元(而不是隐单元)。
- 如果必须选用sigmoid激活函数时,tanh激活函数通常表现更佳。因为tanh函数在 0点附近近似于单位函数
g
(
z
)
=
z
g(z)=z
g(z)=z。
- sigmoid激活函数在前馈神经网络之外的神经网络中更为常见。
有一些网络不能使用修正线性单元,因此sigmoid激活函数是个更好的选择,尽管它存在饱和问题。
- 循环神经网络:修正线性单元会产生信息爆炸的问题。
- 一些概率模型:要求输出在 0~1 之间。
3.3 激活函数对比
- sigmoid主要缺点:
- 容易饱和从而使得梯度消失。当激活函数取值在接近0或者1时会饱和,此时梯度为近似为0。
- 函数输出不是零中心的。这会导致后续神经元的输出数值总是正数。
- tanh :
- 优点:函数输出是零中心的。
- 缺点:容易饱和从而使得梯度消失。
tanh 激活函数几乎在所有场合都是优于sigmoid 激活函数的。但是有一种情况例外:如果要求函数输出是0~1 之间(比如表征某个概率),则二者之间必须用sigmoid。
- relu:
- 优点:对随机梯度下降的收敛有巨大的加速作用,而且非常容易计算。
- 缺点:可能导致神经元死掉。
当一个很大的梯度流过 relu 神经元时,可能导致梯度更新到一种特别的状态:在这种状态下神经元无法被其他任何数据点再次激活。此后流过这个神经元的梯度将变成 0,该单元在训练过程中不可逆的死亡。
如果学习率设置的过高,可能会发现网络中大量神经元都会死掉。整个训练过程中,这些神经元都不会被激活。
- leaky relu :为了解决 relu 死亡神经元的问题的尝试,但是效果并不明显。
4. 正则化
-
正则化常用于缓解模型过拟合。过拟合发生的原因是模型的容量过大,而正则化可以对模型施加某些限制,从而降低模型的有效容量。
-
目前有多种正则化策略。
- 有些正则化策略是向模型添加额外的约束,如增加对参数的限制。这是对参数的硬约束。
- 有些正则化策略是向目标函数增加额外项。这是对参数的软约束。
-
正则化策略代表了某种先验知识,即:倾向于选择简单的模型。
-
在深度学习中,大多数正则化策略都是基于对参数进行正则化。正则化以偏差的增加来换取方差的减少,而一个有效的正则化能显著降低方差,并且不会过度增加偏差。
-
在深度学习的实际应用中,不要因为害怕过拟合而采用一个小模型,推荐采用一个大模型并使用正则化。
4.1 参数范数正则化
- 一些正则化方法通过对目标函数 J J J添加一个参数范数正则化项 Ω ( θ ⃗ ) \Omega(\vec{\theta}) Ω(θ)来限制模型的容量capacity 。
正则化之后的目标函数为 J ⃗ ( θ ⃗ ; X , y ⃗ ) = J ⃗ ( θ ⃗ ; X , y ⃗ ) + α Ω ( θ ⃗ ) \vec{J}(\vec{\theta};X,\vec{y})=\vec{J}(\vec{\theta};X,\vec{y}) +\alpha\Omega(\vec{\theta}) J(θ;X,y)=J(θ;X,y)+αΩ(θ)。
α \alpha α为正则化项的系数,它衡量正则化项 Ω ( θ ⃗ ) \Omega(\vec{\theta}) Ω(θ)和标准目标函数 J ⃗ \vec{J} J的比重。 α = 0 \alpha=0 α=0则没有正则化。 α \alpha α越大则正则化项越重要。如果最小化 J ⃗ \vec{J} J,则会同时降低J和参数 θ ⃗ \vec{\theta} θ的规模。
- 参数范数正则化可以缓解过拟合。
如果 α \alpha α设置的足够大,则参数 θ ⃗ \vec{\theta} θ就越接近零。这意味着模型变得更简单,简单的模型不容易过拟合(但是可能欠拟合)。
对于神经网络,这意味着很多隐单元的权重接近0,于是这些隐单元在网络中不起任何作用。此时大的神经网络会变成一个小的网络。
在 α \alpha α从零逐渐增加的过程中存在一个中间值,使得参数 θ ⃗ \vec{\theta} θ的大小合适,即一个合适的模型。
- 选择不同的 Ω \Omega Ω的形式会产生不同的解,常见的形式有 L 2 L_2 L2正则化和 L 1 L_1 L1正则化。
4.1.1 L2正则化
- L 2 L_2 L2正则化通常被称作岭回归或者Tikhonov正则化。
- 正则化项为 Ω ( θ ⃗ ) = 1 2 ∣ ∣ θ ∣ ∣ 2 \Omega(\vec{\theta})=\frac{1}{2}||\theta||^{2} Ω(θ)=21∣∣θ∣∣2。系数 1 2 \frac{1}{2} 21是为了使得导数的系数为 1。
- 该正则化形式倾向于使得参数 θ ⃗ \vec{\theta} θ更接近零。
- 假设 θ ⃗ \vec{\theta} θ参数就是权重 w ⃗ \vec{w} w,没有偏置参数,则 J ⃗ ( θ ⃗ ; X , y ⃗ ) = J ⃗ ( θ ⃗ ; X , y ⃗ ) + α 2 w ⃗ T w ⃗ \vec{J}(\vec{\theta};X,\vec{y})=\vec{J}(\vec{\theta};X,\vec{y}) +\frac{\alpha}{2}\vec{w}^{T}\vec{w} J(θ;X,y)=J(θ;X,y)+2αwTw,对应的梯度为 ∇ w ⃗ J ˇ ( w ⃗ ; X , y ⃗ ) = ∇ w ⃗ J ˇ ( w ⃗ ; X , y ⃗ ) + α w ⃗ \nabla_{\vec{w}} \check { J }(\vec{w};X,\vec{y}) = \nabla_{\vec{w}} \check { J }(\vec{w};X,\vec{y})+\alpha \vec{w} ∇wJˇ(w;X,y)=∇wJˇ(w;X,y)+αw 。
正则化对于梯度更新的影响是:每一步执行梯度更新之前,会对权重向量乘以一个常数因子来收缩权重向量。因此L2 正则化也被称作“权重衰减”。
L 2 L_2 L2正则化表明:
- 只有显著减小目标函数 J J J的那个方向的参数会相对保留下来。
- 无助于减小目标函数 J J J的方向(该方向上 H H H特征值较小,或者说该方向上 J J J的曲率较小,或者说该方向上 J J J的曲线更接近于直线),因为在这个方向上移动不会显著改变梯度,因此这个不重要方向上的分量会因为正则化的引入而被衰减掉。
4.1.2 L1正则化
-
模型参数 w ⃗ \vec{w} w的 L 1 L_1 L1的正则化形式为: Ω ( θ ⃗ ) = ∣ ∣ w ⃗ ∣ 1 = ∑ i ∣ w i ∣ \Omega(\vec{\theta})=||\vec{w}|_1=\sum \limits_{i} |w_i| Ω(θ)=∣∣w∣1=i∑∣wi∣ 。即各个参数的绝对值之和。
-
L 1 L_1 L1正则化后的目标函数: J ⃗ ( θ ⃗ ; X , y ⃗ ) = J ⃗ ( θ ⃗ ; X , y ⃗ ) + α ∣ ∣ ∣ w ∣ ∣ 1 \vec{J}(\vec{\theta};X,\vec{y})=\vec{J}(\vec{\theta};X,\vec{y}) +\alpha|||w||_{1} J(θ;X,y)=J(θ;X,y)+α∣∣∣w∣∣1 。
对应的梯度为 ∇ w ⃗ J ˇ ( w ⃗ ; X , y ⃗ ) = ∇ w ⃗ J ˇ ( w ⃗ ; X , y ⃗ ) + α s i g n ( w ⃗ ) \nabla_{\vec{w}} \check { J }(\vec{w};X,\vec{y}) = \nabla_{\vec{w}} \check { J }(\vec{w};X,\vec{y})+\alpha sign( \vec{w}) ∇wJˇ(w;X,y)=∇wJˇ(w;X,y)+αsign(w)。其中 s i g n ( ⋅ ) sign(\cdot) sign(⋅)函数取自变量的符号:
如果自变量大于零,则取值为 1;如果自变量小于零,则取值为 -1;如果自变量为零,则取值为零。
L 1 L_1 L1正则化对于梯度更新的影响是:不再是线性地缩放每个 w i w_i wi( L 2 L_2 L2正则化项的效果),而是减去与 s i g n ( w i ) sign(w_i) sign(wi)同号的常数因子。
L 1 L_1 L1正则化项更容易产生稀疏(sparse)解,而 正则化并不会导致稀疏解。
- 在 L 1 L_1 L1正则化中, w i ∗ w_i^* wi∗的绝对值越小,该维的特征越容易被稀疏化。
- L 1 L_1 L1正则化的这一性质已经被广泛地用作特征选择: L 1 L_1 L1正则化使得部分特征子集的权重为零,表明相应的特征可以被安全地忽略。
4.2 数据集增强
-
提高模型泛化能力的一个最直接的方法是采用更多的数据来训练。但是通常在现实任务中,我们拥有的数据量有限。解决该问题的一种方法是:创建一些虚拟的数据用于训练。
-
数据集增强仅仅用于模型的训练,而不是用于模型的预测。即:不能对测试集、验证集执行数据集增强。
-
当比较机器学习算法基准测试的结果时,必须考虑是否采用了数据集增强。通常情况下,人工设计的数据集增强方案可以大大减少模型的泛化误差。当两个模型的泛化性能比较时,应该确保这两个模型使用同一套人工设计的数据集增强方案。
-
注意数据集增强和预处理的区别:数据集增强会产生更多的输入数据,而数据预处理产生的输入数据数量不变。
4.2.1 线性变换
- 对于某些任务来说,创建虚拟数据非常困难。如:在密度估计任务中,除非预先知道了密度函数,否则无法产生新的虚拟数据。
对于分类问题来说,创建虚拟数据非常简单。对于一个分类器,它将高维的输入 x ⃗ \vec{x} x映射到类别 y y y。这意味着这种映射规则是不随坐标系的改变而改变的。因此可以通过线性变换,将训练集中的 ( x ⃗ , y ) (\vec{x}, y) (x,y)变换为 ( x ⃗ ′ , y ) (\vec{x}^ { \prime }, y) (x′,y)从而产生了新的数据 ( x ⃗ ′ , y ) (\vec{x}^ { \prime }, y) (x′,y) 。对图像分类问题,数据集增强特别有效。数据集增强也可以应用于语音识别任务。
- 常见的图片数据集增强方法:
-
将训练图像沿着每个方向平移几个像素产生新的图像。
-
对训练图像进行旋转、翻转或者缩放。
-
对训练图像进行随机裁剪。实际上,随机裁剪图像的操作也可以被认为是预处理步骤,而不是数据集增强。
-
对训练图像进行颜色抖动:调整饱和度、调整亮度、调整对比度、调整锐度。
对比度:图像画面的明暗反差程度。对比度越高,则图片亮的地方更亮,暗的地方越暗。
亮度:图像的明暗程度。亮度越高,则图像整体越亮。
饱和度:图像颜色种类的多少。饱和度越高,则图像的颜色种类越多,图像越鲜艳。
锐度:图像的边缘轮廓的锐利程度。锐度越高,则图像的边缘越清晰。
- 在使用线性变换执行数据集增强时需要注意:
-
某些线性变换会改变正确的类别。如:字符识别任务中, b/d以及6/9的图像, 不能执行水平翻转变换和旋转 180 度变换。
-
某些线性变换难以执行。如:平面外的绕轴旋转(类似于翻页)难以通过简单的几何运算在输入图片上实现。
4.3 噪声添加
有两种添加噪声的策略:输入噪声注入、权重噪声注。
输入噪声注入是将噪声作用于输入的数据集,这也是一种数据集增强方法。对于某些模型,在输入上注入方差极小的噪音等价于对权重施加参数范数正则化(Bishop,1995a,b)。但是输入噪声注入远比简单地收缩参数强大,尤其是噪声被添加到隐单元的输入上时。
权重噪声注入是将噪音作用于权重。这项技术主要用于循环神经网络。权重噪声注入可以解释为:将权重视作不确定的随机变量(拥有某个概率分布),向权重注入噪声是对该随机变量采样得到的一个随机值。
4.4 早停
-
当训练一个容量较大的模型时会经常发现:训练误差逐渐降低,但是验证误差先下降后上升。当验证误差没有进一步改善时,算法就提前终止。这种策略被称作早停early stopping。
-
早停是深度学习中最常用的正则化形式,因为它简单、有效。
-
当训练终止时,返回的不是最新的模型参数,而是验证误差最小的模型参数,因此需要频繁存储模型参数。
4.4.1 早停算法
-
早停算法:
输入:当前验证集的误差非最小值的次数、验证集验证的间隔 、初始参数。
输出:最佳参数、获得最佳参数时迭代的步数。
算法步骤:先进行初始化。然后迭代直至满足条件停止。 -
可以认为早停是一个非常高效的超参数选择算法:训练步数是一个超参数,该超参数在验证误差上具有 U形曲线。
- 早停策略通过控制训练步数来控制模型的有效容量capacity 。
- 早停策略只需要跑一轮训练就能够得到很多的超参数(即:训练步数)及其对应的验证误差。
- 早停是正则化的一种非常不起眼的形式,其优点有:
- 它几乎不需要干涉基本的训练过程,适合任何模型。
- 可以单独使用,或者与其他的正则化策略相结合。
- 早停不仅有正则化的好处,还有降低计算成本的好处。
4.5 dropout
- dropout:在前向传播过程中,对网络中的每个隐层,每个隐单元都以一定的概率 p d r o p p_{drop} pdrop被删除,最后得到一个规模更小的网络。在反向传播过程中,仅仅针对该小网络进行权重更新。
-
所谓的删除,即指定该该隐单元的输出都为 0。一旦隐单元的权重为0,则该隐单元对后续神经元的影响均为 0 。
-
输入层和输出层的神经元不会被删除,因为这两个层的神经元的数量是固定的。理论上可以对输入层应用dropout ,使得可以有机会删除一个或者多个输入特征。但实际工程中,通常不会这么做。
-
隐单元删除发生在一个训练样本的训练期间。
不同的训练样本,其删除的隐单元的集合是不同的,因此裁剪得到的小网络是不同的。
不同的训练样本,隐单元被删除的概率 都是相同的。
在不同batch 之间的同一个训练样本,其删除的隐单元的集合也是不同的。 -
在不同的梯度更新周期,会从完整的网络中随机删除不同的神经元,因此裁剪得到的小网络是不同的。但是在这个过程中,隐单元被删除的概率是相同的。
-
可以指定某一个隐层或者某几个隐层执行dropout,而没有必要针对所有的隐层执行dropout 。
-
可以对网络的每个隐单元指定不同的删除概率,但实际工程中,通常不会这么做。
- 定义一个掩码向量 μ ⃗ \vec{\mu} μ,它给出了哪些隐单元被保留哪些隐单元被删除:掩码为 0 的位置对应的隐单元被删除,掩码为1 的位置对应的隐单元被保留。定义 J ( θ , μ ⃗ ⃗ ) J(\vec{\theta,\vec{\mu}}) J(θ,μ)为参数 θ ⃗ \vec{\theta} θ和掩码 u ⃗ \vec{u} u共同定义的模型代价,dropout的目标是最小化 E μ ⃗ J ( θ ⃗ , μ ⃗ ) E_{\vec{\mu}}J(\vec{\theta}, \vec{\mu}) EμJ(θ,μ)。
- 这里采用期望,因为掩码向量 u ⃗ \vec{u} u是一个随机向量,对于每个训练样本 u ⃗ \vec{u} u都可能不同。
- 因为掩码向量具有指数多个,因此期望包含了指数多项。实际应用中,可以通过抽样 u ⃗ \vec{u} u来获得期望的无偏估计。
5.深度模型的优化
5.1 参数初始化策略
-
有些优化算法是非迭代的,可以直接解析求解最优解;有些优化算法是迭代的,但是它们是初始值无关的。深度学习不具有这两类性质,通常是迭代的,且与初始值相关。
-
深度学习中,大多数算法都受到初始值的影响。初始值能够决定:算法最终是否收敛、以及收敛时的收敛速度有多快、以及收敛到一个代价函数较高还是较低的值。
-
深度学习中,初始值也会影响泛化误差,而不仅仅是目标函数的最优化。因为如果选择一个不好的初始值,则最优化的结果会落在参数空间的一个较差的区域。此时会导致模型一个较差的泛化能力。
-
目前深度学习中,选择初始化策略是启发式的。
- 大多数初始化策略使得神经网络初始化时实现一些良好的性质。但是这些性质能否在学习过程中保持,难以保证。
- 有些初始化点从最优化的观点是有利的,但是从泛化误差的观点来看是不利的。
设定一个好的初始化策略是困难的,因为神经网络最优化任务至今都未被很好理解。 - 对于初始点如何影响泛化误差的理论是空白的,几乎没有任何指导。
-
通常的参数初始化策略为:随机初始化权重,偏置通过启发式挑选常数,额外的参数也通过启发式挑选常数。
-
也可以使用机器学习来初始化模型的参数。在同样的数据集上,即使是用监督学习来训练一个不相关的任务,有时也能够得到一个比随机初始化更好的初始值。原因是:监督学习编码了模型初始参数分布的某些信息。
5.1.1 权重初始化
- 通常权重的初始化是从高斯分布或者均匀分布中挑选出来的值。
- 从高斯分布还是均匀分布中挑选,看起来似乎没有很大差别,实际上也没有被仔细研究。
- 该分布的范围(如均匀分布的上、下限)对优化结果和泛化能力有很大的影响。
- 初始权重的大小很重要,下面的因素决定了权重的初始值的大小:
- 更大的初始权重具有更强的破坏对称性的作用,有助于避免冗余的单元。
- 更大的初始权重也有助于避免梯度消失。
- 更大的初始权重也容易产生梯度爆炸。
- 循环神经网络中,更大的初始权重可能导致混沌现象:对于输入中的很小的扰动非常敏感,从而导致确定性算法给出了随机性结果。
- 关于如何初始化网络,正则化和最优化有两种不同的角度:
- 从最优化角度,建议权重应该足够大,从而能够成功传播信息。
- 从正则化角度,建议权重小一点(如 正则化),从而提高泛化能力。
- 实践中,通常需要将初始权重范围视作超参数。如果计算资源允许,可以将每层权重的初始数值范围设置为一个超参数,然后使用超参数搜索算法来挑选这些超参数。
5.1.2 偏置初始化
-
偏置的初始化通常更容易。大多数情况下,可以设置偏置初始化为零。
-
有时可以设置偏置初始化为非零,这发生在下面的三种情况:
-
如果偏置是作为输出单元,则初始化偏置为非零值。假设初始权重足够小,输出单元的输出仅由初始化偏置决定,则非零的偏置有助于获取正确的输出边缘统计。
-
有时选择偏置的初始值以免初始化引起激活函数饱和。如:ReLU 激活函数的神经元的偏置设置为一个小的正数,从而避免ReLU 初始时就位于饱和的区域。
-
有时某个单元作为开关来决定其他单元是使用还是不使用。此时偏置应该非零,从而打开开关。
6. 优化算法
6.1 动量法(Momentum)
该适用于隧道型曲面,梯度下降法在狭长的隧道型函数上表现不佳,如下图所示
- 函数主体缓缓向右方下降
- 在主体方向两侧各有一面高墙,导致垂直于主体方向有更大的梯
度 - 梯度下降法会在隧道两侧频繁震荡
而动量法每次更新都吸收一部分上次更新的余势。这样主体方向的更新就得到了更大的保留,从而效果被不断放大。物理上这就像是推一个很重的铁球下山,因为铁球保持了下山主体方向的动量,所以在隧道上沿两侧震荡测次数就会越来越少。
v
t
=
γ
v
t
−
1
+
η
∇
θ
J
(
θ
)
v_{t} = \gamma v_{t-1} + \eta \nabla_{\theta}J(\theta)
vt=γvt−1+η∇θJ(θ)
θ
t
=
θ
t
−
1
−
v
t
\theta_{t} = \theta_{t-1} - v_{t}
θt=θt−1−vt
6.2 Adagrad
该算法的特点是自动调整学习率,适用于稀疏数据。梯度下降法在每一步对每一个参数使用相同的学习率,这种一刀切的做法不能有效的利用每一个数据集自身的特点。
Adagrad 是一种自动调整学习率的方法:
- 随着模型的训练,学习率自动衰减
- 对于更新频繁的参数,采取较小的学习率
- 对于更新不频繁的参数,采取较大的学习率
6.3 Adadelta(Adagrad的改进算法)
Adagrad的一个问题在于随着训练的进行,学习率快速单调衰减。Adadelta则使用梯度平方的移动平均来取代全部历史平方和。
定义移动平均:
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
Adadelta 的第一个版本也叫做 RMSprop,是Geoff Hinton独立于Adadelta提出来的。
6.4 Adam
如果把Adadelta里面梯度的平方和看成是梯度的二阶矩,那么梯度本身的求和就是一阶矩。Adam算法在Adadelta的二次矩基础之上又引入了一阶矩。而一阶矩,其实就类似于动量法里面的动量。
7. Normalization
7.1 batch normalization
- batch normalization是优化神经网络的一大创新。
- 它并不是一个优化算法,而是一个自适应的、调整参数模型的方法。
- 它试图解决训练非常深的神经网络的困难。
- 深度神经网络训练困难的一个重要原因是:深度神经网络涉及很多层的叠加,而每一层的参数更新会导致上一层的输入数据分布发生变化。这会带来两个问题:
-
下层输入的变化可能趋向于变大或者变小,导致上层落入饱和区,使得学习过早停止。
-
通过层层叠加,高层的输入分布变化会非常剧烈。这就使得高层需要不断去适应底层的参数更新变化。这就要求我们需要非常谨慎的设定学习率、初始化权重、参数更新策略。
7.2 layer normalization
与 BN 不同,LN 是对单个样本的同一层的神经元进行归一化,同层神经元使用相同的均值和方差。对于该层神经元,不同样本可以使用的均值和方差不同。
与之相比,BN 是对每个神经元在mini batch 样本之间计算均值和方差。对每个神经元,mini batch 中的所有样本在该神经元上都使用相同的均值和方差。但是不同神经元使用不同的均值和方差。
因此LN 不依赖于batch size,也不依赖于网络深度。因此它适合在线学习,也适合于RNN 网络。