深度学习(二)——神经网络基础
神经网络的通用分类
人工神经网络模型可以理解为一组基本处理单元,它们紧密地相互连接,对输入进行类似的数学运算后生成期望的输出。基于信息在网络中传播的方式,可以将神经网络分为两个通用类别:
- 前馈神经网络
前馈神经网络中信息的流动只能由前到后,也就是说,一层神经元的输出只能作为后一层神经元的输入。这些网络架构可以被称为有向无环图。典型的模型有多层感知器(MLP)和卷积神经网络(CNN)。 - 反馈神经网络
反馈神经网络具有形成有向循环的连接,也就是说,后一层神经元也可以反过来作为前一层神经元的输入,这就使得神经网络可以处理序列数据。反馈网络还具有记忆能力,可以在其内部存储器中存储信息和序列关系。典型的模型有循环神经网络(RNN)和长短期记忆网络(LSTM)。
神经网络的基本结构
下面以多层感知器为例,介绍神经网络的基本结构。
基础架构
下图给出了MLP网络架构的一个例子:
这是一个五层感知器的例子,每一层分别含有3、4、2、3、3个人工神经元。我们用这个例子来介绍人工神经网络的一些特点:
分层架构:人工神经网络包含层次化的处理级别。每个级别被称为“网络层”,由许多人工神经元组成。网络层也可以细分为三种:
- 第一层为输入层,用于接受数据输入。
- 最后一层为输出层,用于产生输出数据并进行预测。
- 其余层都被称为隐藏层或者隐含层,用于执行各种处理和运算。
人工神经元:每层中的基本处理单元被称为人工神经元。人工神经元存储了各个输入的权重和其特有的激活函数。
密集连接:神经网络中的各个人工神经元是相互连接的,可以相互通信。前一层神经元的输出是后一层神经元的输入,这也是前馈神经网络的基本特征。并且,对于多层感知器,层中的每一个神经元都直接连接到前一个层的所有神经元。
人工神经元
下图给出了人工神经元的基本组成:
对于含有n个输入的人工神经元来说,它会进行如下的计算:
- 对n个输入加权求和,在必要的时候,我们会加入一个常数b作为偏置,这里计算得到的值称为激励值。
- 对于加权求和后的结果,再通过激活函数运算后产生结果,再输出。
综合来说,单个人工神经元的输入是
a
1
a_1
a1-
a
n
a_n
an,输出为
f
(
∑
i
=
1
n
w
i
⋅
a
i
+
b
)
f(\sum_{i=1}^{n}w_i\cdot a_i+b)
f(∑i=1nwi⋅ai+b),写成向量形式为:
O
u
t
p
u
t
=
f
(
w
⃗
⋅
a
⃗
)
Output=f(\vec{w}\cdot\vec{a})
Output=f(w⋅a)
其中
w
0
=
b
,
a
0
=
1
w_0=b,a_0=1
w0=b,a0=1。对于单个神经元来说,它的训练目标就是确定最优的
w
w
w和
b
b
b使得输出与真实值的误差最小。
激活函数
激活函数指的是一组非线性函数。它能够将非线性的特性引入神经网络,使得神经网络具备拟合各种非线性分类数据的能力。常用的激活函数主要有以下几个:
Sigmoid函数:
在逻辑回归中,曾经介绍过Sigmoid函数,它的基本数学公式如下:
f
(
x
)
=
1
1
+
e
−
x
f(x)=\frac{1}{1+e^{-x}}
f(x)=1+e−x1
图像如下:
Tanh函数:
Tanh函数是双曲函数的一种,它和Sigmoid函数具有类似的图像,只不过将值域扩展到了(-1,1)。
f
(
x
)
=
e
x
−
e
−
x
e
x
+
e
−
x
f(x)=\frac{e^x-e^{-x}}{e^x+e^{-x}}
f(x)=ex+e−xex−e−x
图像如下:
ReLU函数:
线性修正单元激活函数(ReLU函数)是一种改进的激活函数,可以解决Sigmoid函数的一些问题,在这篇博文的最后我们将会对其进行讨论。Relu函数的公式如下:
f
(
x
)
=
{
x
,
x
>
0
0
,
x
≤
0
f(x)=\left\{\begin{aligned} x,x>0\\ 0,x\leq0 \end{aligned}\right.
f(x)={x,x>00,x≤0
函数图像如下:
模型训练与反向传播算法
和机器学习相同,多层感知器的模型训练基本思想也是首先定义损失函数,然后利用梯度下降法及其变种算法进行迭代训练,直到参数不再变化或变化极小。一般可采用的损失函数有MSE、SVM铰链损失函数、交叉熵等。这里给出最常用的MSE的损失函数形式:
M
S
E
:
E
=
1
2
∑
n
(
y
n
−
y
^
n
)
2
MSE:E=\frac{1}{2}\sum_n(y_n-\hat{y}_n)^2
MSE:E=21n∑(yn−y^n)2
人工神经网络和一般的机器学习模型不同,人工神经网络是多层的,可以认为是很多个机器学习基本模型的连接,因此在求解梯度上存在一定的特殊性。因此,人工神经网络的训练方法被称为反向传播算法,在初始化权重后,反向传播算法分为两个步骤:
- 前向传播
- 反向传播
前向传播
所谓前向传播,实际上也是模型训练完成后的预测过程。数据从输入层输入,然后一层一层地进行运算,直到最后在输出层得到一个最终预测输出 y ^ \hat{y} y^。
举一个简单的例子说明这个过程,假设我们有一个含两个隐含层和一个输出层的多层感知器,并且每一层只含有一个神经元,输入层也只有一个神经元(即输入数据只有一维),连接情况及各层的权重如图所示:
接下来依次计算:
- 第一层为输入层,输入输出都为 A 0 A_0 A0.
- 第二层为隐含层,输入 A 0 A_0 A0,输出 A 1 = f ( w 1 A 0 ) A_1=f(w_1A_0) A1=f(w1A0).
- 第三层为隐含层,输入 A 1 A_1 A1,输出 A 2 = f ( w 2 A 1 ) A_2=f(w_2A_1) A2=f(w2A1).
- 第四层为输出层,输入 A 2 A_2 A2,输出 A 3 = f ( w 3 A 2 ) A_3=f(w_3A_2) A3=f(w3A2).
就这样逐层进行计算,最终的计算结果 A 3 A_3 A3就是模型的输出 y ^ \hat{y} y^,可以用于预测和训练模型。含有多个神经元的网络也是如此。
反向传播
经过权重初始化和前向传播后,我们可以得到网络中的所有参数以及中间运算结果,接下来我们就要根据误差即损失函数的情况,更新模型中的权重,这个步骤被称为反向传播。前向传播和反向传播合起来称为模型的一轮训练过程。
在阐述具体过程前,让我们回顾一下梯度下降算法的参数迭代公式:
θ
n
+
1
=
θ
n
−
η
∂
L
∂
θ
n
\theta_{n+1}=\theta_n-\eta\frac{\partial L}{\partial\theta_n}
θn+1=θn−η∂θn∂L
这个迭代公式对于单层的感知器和输出层也是适用的。但对于多层的感知器,我们需要从最后一层输出层开始,一层一层向前计算梯度、更新参数,这也是“反向传播”这个名称的由来。
接下来我们用一个简单的例子来推导反向传播算法的参数迭代公式。仍然使用上面用的这个例子:
假设使用MSE作为损失函数,只考虑一个样本(多个样本的情况只是对多个样本进行求和),并且神经元中不加入偏置(偏置实际上只是多一维输入)。损失函数如下:
L
(
w
1
,
w
2
,
w
3
)
=
1
2
(
y
^
−
y
)
2
L(w_1,w_2,w_3)=\frac{1}{2}(\hat{y}-y)^2
L(w1,w2,w3)=21(y^−y)2
∂
L
∂
y
^
=
y
^
−
y
\frac{\partial L}{\partial\hat{y}}=\hat{y}-y
∂y^∂L=y^−y
我们必须要明确的一点是,我们的训练目标是找到最优的
w
1
w_1
w1,
w
2
w_2
w2,
w
3
w_3
w3使得误差最小,我们要更新的模型参数也是这三个,因此,根据梯度下降法的基本思想,我们需要求出损失函数对它们三者的偏导数,忽略输入层,由链式法则,从最后一层开始逐层计算如下:
第
三
层
:
∂
L
∂
w
3
=
∂
L
∂
A
3
∂
A
3
∂
D
3
∂
D
3
∂
w
3
=
(
y
^
−
y
)
f
′
(
D
3
)
A
2
=
δ
3
A
2
第
二
层
:
∂
L
∂
w
2
=
∂
L
∂
A
2
∂
A
2
∂
D
2
∂
D
2
∂
w
2
=
δ
3
w
3
⋅
f
′
(
D
2
)
A
1
=
δ
2
A
1
第
一
层
:
∂
L
∂
w
1
=
∂
L
∂
A
1
∂
A
1
∂
D
1
∂
D
1
∂
w
1
=
δ
2
w
2
⋅
f
′
(
D
1
)
A
0
=
δ
1
A
0
\begin{aligned} 第三层:\frac{\partial L}{\partial w_3}=\frac{\partial L}{\partial A_3}\frac{\partial A_3}{\partial D_3}\frac{\partial D_3}{\partial w_3}=(\hat{y}-y)f^{'}(D_3)A_2=\delta_3A_2\\ 第二层:\frac{\partial L}{\partial w_2}=\frac{\partial L}{\partial A_2}\frac{\partial A_2}{\partial D_2}\frac{\partial D_2}{\partial w_2}=\delta_3w_3\cdot f^{'}(D_2)A_1=\delta_2A_1\\ 第一层:\frac{\partial L}{\partial w_1}=\frac{\partial L}{\partial A_1}\frac{\partial A_1}{\partial D_1}\frac{\partial D_1}{\partial w_1}=\delta_2w_2\cdot f^{'}(D_1)A_0=\delta_1A_0 \end{aligned}
第三层:∂w3∂L=∂A3∂L∂D3∂A3∂w3∂D3=(y^−y)f′(D3)A2=δ3A2第二层:∂w2∂L=∂A2∂L∂D2∂A2∂w2∂D2=δ3w3⋅f′(D2)A1=δ2A1第一层:∂w1∂L=∂A1∂L∂D1∂A1∂w1∂D1=δ2w2⋅f′(D1)A0=δ1A0
由上面的公式不难看出,对于
∂
L
∂
w
i
\frac{\partial L}{\partial w_i}
∂wi∂L和
δ
i
\delta_i
δi之间存在一些递推关系:
∂
L
∂
w
i
=
δ
i
A
i
−
1
δ
i
=
δ
i
+
1
w
i
+
1
f
′
(
D
i
)
\begin{aligned} \frac{\partial L}{\partial w_i}=\delta_iA_{i-1}\\ \delta_i=\delta_{i+1}w_{i+1}f^{'}(D_i)\\ \end{aligned}
∂wi∂L=δiAi−1δi=δi+1wi+1f′(Di)
有了上面这个重要结论,我们将它进行推广。考虑一层中含有多个神经元的情况,由于在单个神经元中,我们对多个输入的处理是进行加和,所以在求导时,正确的处理也是进行求和。另外要明确的是,一个神经元应当对应一个
δ
\delta
δ。这样我们可以把反向传播算法的递推关系公式推广到普遍情况:
对于第
i
i
i层第
j
j
j个神经元,它的误差信号记作
δ
j
i
\delta_j^i
δji,设它的激活函数为
f
(
x
)
f(x)
f(x),有
m
m
m个输入记作
x
1
,
.
.
.
,
x
m
x_1,...,x_m
x1,...,xm,权重分别为
w
1
,
.
.
.
,
w
m
w_1,...,w_m
w1,...,wm,它的激励值为
a
j
i
=
x
1
w
1
+
.
.
.
+
x
m
w
m
a_j^i=x_1w_1+...+x_mw_m
aji=x1w1+...+xmwm。后一层有
n
n
n个神经元,第
k
k
k个神经元具有
δ
k
i
+
1
\delta^{i+1}_k
δki+1,对于
δ
j
i
\delta_j^i
δji神经元输入的权重为
w
k
′
w^{'}_k
wk′。它的
δ
j
i
\delta_j^i
δji有着这样的递推关系:
δ
j
i
=
f
′
(
a
j
i
)
∑
k
=
1
n
w
k
′
δ
k
i
+
1
\delta_j^i=f^{'}(a_j^i)\sum_{k=1}^{n}w^{'}_k\delta^{i+1}_k
δji=f′(aji)k=1∑nwk′δki+1
对于一个输入
x
k
x_k
xk和它对应的权重
w
k
w_k
wk,一次训练的参数更新公式如下:
w
k
:
=
w
k
−
η
δ
j
i
x
k
w_k:=w_k-\eta\delta_j^ix_k
wk:=wk−ηδjixk
其中
:
=
:=
:=为赋值符号,
η
\eta
η称为学习率。完整的学习过程将会包含很多次迭代,直到损失函数达到收敛。
梯度消失和梯度爆炸
反向传播算法成功地应用于各种神经的情况。但由于每一层之间使用链式法则即乘法进行传播,这就意味着每一层的梯度是指数级别增长的。当网络很深时,学习过程时可能会遭受梯度消失和梯度爆炸的问题,这主要取决于激活函数的选择。
当梯度较小时,可能会产生梯度消失问题。以sigmoid函数为例,sigmoid函数的导数最大值约为0.25。考虑一个5层的网络,传递到第一层时,梯度将会衰减为 ( 0.25 ) 5 = 0.0009 (0.25)^5=0.0009 (0.25)5=0.0009,我们知道,当小数过小超出表示精度时,计算机会将它作为机器零来处理,因此就会导致初始几层的参数基本不会更新。这就是梯度消失问题。
同样的,当梯度较大时,可能会产生梯度爆炸问题。当输出层梯度大于1时,经过多层传递,很可能导致前几层的梯度非常巨大,每一次训练参数变化很大,使得模型训练困难,也很容易“走”出合理的区域。
梯度消失和梯度爆炸问题的一个解决方案是改进激活函数。一种改进的激活函数就是前面介绍的ReLU函数。它的一大特点是未激活时梯度为0,激活后梯度恒为1,由于0和1在指数运算时的不变性,就可以有效地防止梯度消失和梯度爆炸问题。
另外还有一种解决方案是梯度裁剪。简要地说就是设定一个阈值,如果求出来的梯度大于这个阈值,我们就将梯度强行缩减为等于阈值。这样也可以防止梯度爆炸问题。