赞
踩
前馈神经网络也称为是深度前馈网络或者多层感知机,它是最基础的深度学习模型。
前馈神经网络的目标是在函数空间中寻找相对正确的函数。函数空间是由所选的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 ,但是对于其他层并没有任何要求。
也可以将每一层想象成由许多并行的单元组成,每个单元表示一个向量到标量的函数:每个单元的输入来自于前一层的许多单元,单元根据自己的激活函数来计算单元的输出。激活函数是前馈神经网络具有非线性表达能力的核心因素。因此每个单元类似于一个神经元。
解决线性模型缺陷的方法是:采用核技巧,将线性模型作用在 ϕ ( x ⃗ ) \phi(\vec{x}) ϕ(x )上,而不是原始输入 x ⃗ \vec{x} x 上。其中 ϕ \phi ϕ是一个非线性变换。
可以认为:通过 ϕ \phi ϕ,提供了 x ⃗ \vec{x} x 的一个新的representation。
当 ϕ \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
)映射到所需输出。
除此之外还需要给出下列条件:
假设输入样本为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_()
事实上梯度下降法对这些隐单元的表现仍然足够好,原因是:
神经网络的训练算法通常并不会达到代价函数的局部最小值,而仅仅是显著地降低代价函数的值即可。因此实际上训练过程中一般无法到达代价函数梯度为零的点,所以代价函数取最小值时梯度未定义是可以接受的。
不可微的点通常只是在有限的、少数的点上不可微,在这些不可微的点通常左导数、右导数都存在。
神经网络训练的软件实现通常返回左导数或者右导数其中的一个,而不是报告导数未定义或者产生一个错误。这是因为计算机计算 0 点的导数时,由于数值误差的影响实际上不可能计算到理论上 0 点的导数,而是一个微小的偏离:向左侧偏离就是左导数,向右侧偏离就是右导数。
接受输入向量 x ⃗ \vec{x} x 。
计算仿射变换 z = w ⃗ T x ⃗ + b z =\vec{w}^{T}\vec{x}+b z=w Tx +b。
激活函数也称作是隐单元。
可以使用单位函数 g ( z ) = z g(z)=z g(z)=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),从而使得修正线性单元在初始时尽可能的对训练集中大多数输入呈现激活状态。
有许多修正线性单元的扩展存在,这些扩展保证了它们能在各个位置都保持非零的梯度。大多数扩展的表现与修正线性单元相差无几,偶尔表现的更好。
在引入修正线性单元之前,大多数神经网络使用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激活函数是个更好的选择,尽管它存在饱和问题。
当一个很大的梯度流过 relu 神经元时,可能导致梯度更新到一种特别的状态:在这种状态下神经元无法被其他任何数据点再次激活。此后流过这个神经元的梯度将变成 0,该单元在训练过程中不可逆的死亡。
如果学习率设置的过高,可能会发现网络中大量神经元都会死掉。整个训练过程中,这些神经元都不会被激活。
正则化常用于缓解模型过拟合。过拟合发生的原因是模型的容量过大,而正则化可以对模型施加某些限制,从而降低模型的有效容量。
目前有多种正则化策略。
正则化策略代表了某种先验知识,即:倾向于选择简单的模型。
在深度学习中,大多数正则化策略都是基于对参数进行正则化。正则化以偏差的增加来换取方差的减少,而一个有效的正则化能显著降低方差,并且不会过度增加偏差。
在深度学习的实际应用中,不要因为害怕过拟合而采用一个小模型,推荐采用一个大模型并使用正则化。
正则化之后的目标函数为 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} θ 的大小合适,即一个合适的模型。
正则化对于梯度更新的影响是:每一步执行梯度更新之前,会对权重向量乘以一个常数因子来收缩权重向量。因此L2 正则化也被称作“权重衰减”。
L 2 L_2 L2正则化表明:
模型参数 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}) ∇w Jˇ(w ;X,y )=∇w Jˇ(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)解,而 正则化并不会导致稀疏解。
提高模型泛化能力的一个最直接的方法是采用更多的数据来训练。但是通常在现实任务中,我们拥有的数据量有限。解决该问题的一种方法是:创建一些虚拟的数据用于训练。
数据集增强仅仅用于模型的训练,而不是用于模型的预测。即:不能对测试集、验证集执行数据集增强。
当比较机器学习算法基准测试的结果时,必须考虑是否采用了数据集增强。通常情况下,人工设计的数据集增强方案可以大大减少模型的泛化误差。当两个模型的泛化性能比较时,应该确保这两个模型使用同一套人工设计的数据集增强方案。
注意数据集增强和预处理的区别:数据集增强会产生更多的输入数据,而数据预处理产生的输入数据数量不变。
对于分类问题来说,创建虚拟数据非常简单。对于一个分类器,它将高维的输入 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 度变换。
某些线性变换难以执行。如:平面外的绕轴旋转(类似于翻页)难以通过简单的几何运算在输入图片上实现。
有两种添加噪声的策略:输入噪声注入、权重噪声注。
输入噪声注入是将噪声作用于输入的数据集,这也是一种数据集增强方法。对于某些模型,在输入上注入方差极小的噪音等价于对权重施加参数范数正则化(Bishop,1995a,b)。但是输入噪声注入远比简单地收缩参数强大,尤其是噪声被添加到隐单元的输入上时。
权重噪声注入是将噪音作用于权重。这项技术主要用于循环神经网络。权重噪声注入可以解释为:将权重视作不确定的随机变量(拥有某个概率分布),向权重注入噪声是对该随机变量采样得到的一个随机值。
当训练一个容量较大的模型时会经常发现:训练误差逐渐降低,但是验证误差先下降后上升。当验证误差没有进一步改善时,算法就提前终止。这种策略被称作早停early stopping。
早停是深度学习中最常用的正则化形式,因为它简单、有效。
当训练终止时,返回的不是最新的模型参数,而是验证误差最小的模型参数,因此需要频繁存储模型参数。
早停算法:
输入:当前验证集的误差非最小值的次数、验证集验证的间隔 、初始参数。
输出:最佳参数、获得最佳参数时迭代的步数。
算法步骤:先进行初始化。然后迭代直至满足条件停止。
可以认为早停是一个非常高效的超参数选择算法:训练步数是一个超参数,该超参数在验证误差上具有 U形曲线。
所谓的删除,即指定该该隐单元的输出都为 0。一旦隐单元的权重为0,则该隐单元对后续神经元的影响均为 0 。
输入层和输出层的神经元不会被删除,因为这两个层的神经元的数量是固定的。理论上可以对输入层应用dropout ,使得可以有机会删除一个或者多个输入特征。但实际工程中,通常不会这么做。
隐单元删除发生在一个训练样本的训练期间。
不同的训练样本,其删除的隐单元的集合是不同的,因此裁剪得到的小网络是不同的。
不同的训练样本,隐单元被删除的概率 都是相同的。
在不同batch 之间的同一个训练样本,其删除的隐单元的集合也是不同的。
在不同的梯度更新周期,会从完整的网络中随机删除不同的神经元,因此裁剪得到的小网络是不同的。但是在这个过程中,隐单元被删除的概率是相同的。
可以指定某一个隐层或者某几个隐层执行dropout,而没有必要针对所有的隐层执行dropout 。
可以对网络的每个隐单元指定不同的删除概率,但实际工程中,通常不会这么做。
有些优化算法是非迭代的,可以直接解析求解最优解;有些优化算法是迭代的,但是它们是初始值无关的。深度学习不具有这两类性质,通常是迭代的,且与初始值相关。
深度学习中,大多数算法都受到初始值的影响。初始值能够决定:算法最终是否收敛、以及收敛时的收敛速度有多快、以及收敛到一个代价函数较高还是较低的值。
深度学习中,初始值也会影响泛化误差,而不仅仅是目标函数的最优化。因为如果选择一个不好的初始值,则最优化的结果会落在参数空间的一个较差的区域。此时会导致模型一个较差的泛化能力。
目前深度学习中,选择初始化策略是启发式的。
通常的参数初始化策略为:随机初始化权重,偏置通过启发式挑选常数,额外的参数也通过启发式挑选常数。
也可以使用机器学习来初始化模型的参数。在同样的数据集上,即使是用监督学习来训练一个不相关的任务,有时也能够得到一个比随机初始化更好的初始值。原因是:监督学习编码了模型初始参数分布的某些信息。
偏置的初始化通常更容易。大多数情况下,可以设置偏置初始化为零。
有时可以设置偏置初始化为非零,这发生在下面的三种情况:
如果偏置是作为输出单元,则初始化偏置为非零值。假设初始权重足够小,输出单元的输出仅由初始化偏置决定,则非零的偏置有助于获取正确的输出边缘统计。
有时选择偏置的初始值以免初始化引起激活函数饱和。如:ReLU 激活函数的神经元的偏置设置为一个小的正数,从而避免ReLU 初始时就位于饱和的区域。
有时某个单元作为开关来决定其他单元是使用还是不使用。此时偏置应该非零,从而打开开关。
该适用于隧道型曲面,梯度下降法在狭长的隧道型函数上表现不佳,如下图所示
而动量法每次更新都吸收一部分上次更新的余势。这样主体方向的更新就得到了更大的保留,从而效果被不断放大。物理上这就像是推一个很重的铁球下山,因为铁球保持了下山主体方向的动量,所以在隧道上沿两侧震荡测次数就会越来越少。
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
该算法的特点是自动调整学习率,适用于稀疏数据。梯度下降法在每一步对每一个参数使用相同的学习率,这种一刀切的做法不能有效的利用每一个数据集自身的特点。
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提出来的。
如果把Adadelta里面梯度的平方和看成是梯度的二阶矩,那么梯度本身的求和就是一阶矩。Adam算法在Adadelta的二次矩基础之上又引入了一阶矩。而一阶矩,其实就类似于动量法里面的动量。
下层输入的变化可能趋向于变大或者变小,导致上层落入饱和区,使得学习过早停止。
通过层层叠加,高层的输入分布变化会非常剧烈。这就使得高层需要不断去适应底层的参数更新变化。这就要求我们需要非常谨慎的设定学习率、初始化权重、参数更新策略。
与 BN 不同,LN 是对单个样本的同一层的神经元进行归一化,同层神经元使用相同的均值和方差。对于该层神经元,不同样本可以使用的均值和方差不同。
与之相比,BN 是对每个神经元在mini batch 样本之间计算均值和方差。对每个神经元,mini batch 中的所有样本在该神经元上都使用相同的均值和方差。但是不同神经元使用不同的均值和方差。
因此LN 不依赖于batch size,也不依赖于网络深度。因此它适合在线学习,也适合于RNN 网络。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。