Contents
MP 模型
人工神经元是神经网络中的逻辑基础。一个神经元可以有多个输入
x
1
,
x
2
,
…
,
x
n
x_1, x_2, \dots, x_n
x1,x2,…,xn 但只能有一个输出
h
(
x
)
h(x)
h(x) 。神经元状态定义为输入信号的线性组合
z
≡
∑
i
=
0
n
w
i
x
i
z \equiv \sum\limits_{i = 0}^n w_ix_i
z≡i=0∑nwixi
因为截距项
x
0
x_0
x0 恒为
1
1
1 ,有时也将
w
0
w_0
w0 称为 bias unit 。得到了神经元状态后,还需要经过 activation function 的映射才能得到输出。一般情况下,激活函数选用 sigmoid 函数
h
(
x
)
=
g
(
z
)
h(x) = g(z)
h(x)=g(z)
Perceptron Learning Algorithm
对于单个 MP 模型,如果选择激活函数为阶跃函数,即可得到感知器模型。
g
(
z
)
=
s
i
g
n
(
z
)
g(z) = sign(z)
g(z)=sign(z)
这一模型可以将样本逐个加入模型,根据输出结果和标签的差别调整模型内部的权重大小,和人类的学习过程较为类似。首先定义规范化样本
x
i
′
=
y
(
i
)
x
(
i
)
x_i' = y^{(i)}x^{(i)}
xi′=y(i)x(i)
其中 x x x 为增广样本向量,标签 y ∈ { 1 , − 1 } y \in \{1, -1\} y∈{1,−1} 。这一操作相当于对标签为负的样本做一次中心对称。
可以看出,左侧原样本集是线性可分的。右侧的规范化样本虽然变得线性不可分,但是所有样本都到了分界线的一侧。也就是说,为了使
h
(
x
)
=
s
i
g
n
(
w
T
x
)
=
y
h(x) = sign(w^Tx) = y
h(x)=sign(wTx)=y 只需要
w
T
x
′
>
0
w^Tx' > 0
wTx′>0 。此时参数
w
w
w 的变化范围被样本集相对原点张角最大的数据所限制,模型训练时很可能使样本落在分界线上。为了避免这种情况,引入
b
>
0
b > 0
b>0 并规定
w
T
x
′
≥
b
w^Tx' \ge b
wTx′≥b
训练过程中,超平面需要尽可能多的将被错分的样本分对,而那些已经分对的样本则不再参与迭代。因此定义错分样本集合
η
=
{
i
∣
w
T
x
i
′
<
b
}
\eta = \{i|w^Tx'_i < b\}
η={i∣wTxi′<b}
和准则函数
J
(
w
)
=
∑
i
∈
η
w
T
x
i
′
J(w) = \sum\limits_{i \in \eta} w^Tx'_i
J(w)=i∈η∑wTxi′
对于一组线性可分的样本,必然存在一个
w
w
w 使所有样本被正确分类。这时有
η
=
ϕ
\eta = \phi
η=ϕ 且
J
=
0
J = 0
J=0 ,训练终止。如果样本集线性不可分,模型有可能不收敛。迭代过程中可以用梯度下降求解
max
w
J
(
w
)
\max\limits_w J(w)
wmaxJ(w) 其中
∇
w
J
=
∑
i
∈
η
x
i
′
\nabla_wJ = \sum\limits_{i \in \eta} x'_i
∇wJ=i∈η∑xi′
Artificial Neural Network
单个感知器可以实现二分类的功能,也就是判断样本是否属于某个类。如果对于所有可能的类别都建立一个感知器,即可实现多分类的功能。但是这样的多输出感知器还有一个问题,就是不能处理线性不可分的数据集。对于这个问题,其中的一种解决办法是增加感知器的层数。有理论证明了下述结论
三层感知器可以实现任意的逻辑运算。当激活函数为 sigmoid 时,可以逼近任何多元函数。
人工神经网络就是利用这样的思想实现的。一个 ANN 由以下几部分组成
名称 | 数量 | 符号 | 功能 |
---|---|---|---|
input layer | 1 1 1 | x x x 或 a 0 a^0 a0 | 存储输入数据 |
hidden layers | L L L | a i a^i ai | 计算输出与反向传播误差 |
output layer | 1 1 1 | h ( x ) h(x) h(x) | 将输出向量归一化 |
一般来说,神经网络会逐个检查输入样本,并对每个样本执行下述算法
for
l
∈
[
1
,
L
]
{
z
l
=
forward propagation of
a
l
−
1
a
l
=
g
(
z
l
)
}
h
(
x
)
=
a
L
/
∣
∣
a
L
∣
∣
initialize
δ
L
for
l
∈
[
1
,
L
]
.reversed
{
δ
l
−
1
=
back propagation of
a
l
and
δ
l
update
a
l
according to
δ
l
}
\begin{aligned} & \text{for }l \in [1, L]\ \{\\ & \qquad z^l = \text{forward propagation of }a^{l - 1}\\ & \qquad a^l = g(z^l)\\ & \}\\ & h(x) = a^L / ||a^L||\\ & \text{initialize }\delta^L\\ & \text{for }l \in [1, L]\text{.reversed }\{\\ & \qquad \delta_{l - 1} = \text{back propagation of }a^l\text{ and }\delta_l\\ & \qquad \text{update }a^l\text{ according to }\delta_l\\ & \}\\ \end{aligned}
for l∈[1,L] {zl=forward propagation of al−1al=g(zl)}h(x)=aL/∣∣aL∣∣initialize δLfor l∈[1,L].reversed {δl−1=back propagation of al and δlupdate al according to δl}
其中反向传播算法是整个学习过程的核心。其基本思想是:将输出层的误差反向传播至中间各层,每层对应的参数梯度均可由当前层的误差计算出来。误差的形式化定义如下
δ
j
=
∂
J
∂
z
j
∈
R
s
j
+
1
\delta_j = \frac{\partial J}{\partial z^j} \in \mathbb{R}^{s_j+1}
δj=∂zj∂J∈Rsj+1
下文中将会以层为单位进行推导。为了方便起见,将前一层的激活函数的导数记为
f
f
f 。在没有特别说明的情况下,激活函数取 sigmoid 。因此有
f
(
a
)
=
a
(
1
−
a
)
f(a) = a(1 - a)
f(a)=a(1−a)
可以将误差项化为1
δ
j
=
∂
J
∂
a
j
⊙
f
(
a
j
)
\delta_j = \frac{\partial J}{\partial a^j} \odot f(a^j)
δj=∂aj∂J⊙f(aj)
对于第 l l l 层来说,只需要计算 ∂ J / ∂ a l − 1 \partial J / \partial a^{l - 1} ∂J/∂al−1 即可完成反向传播的计算。下面将会按不同层的种类来讨论前向传播和反向传播的具体算法,设当前层为 j j j 。
输出层
输出层的前向传播公式已经包含在前面的伪代码中了,此处主要推导其误差项的公式。本文使用的代价函数是带
l
2
l_2
l2 正则化的交叉熵函数
J
(
w
)
=
−
[
∑
k
=
1
K
y
k
log
h
k
(
x
)
+
(
1
−
y
k
)
log
(
1
−
h
k
(
x
)
)
]
+
λ
2
∑
θ
∈
w
θ
2
J(w) = -\left[\sum\limits_{k=1}^Ky_k\log h_k\left(x\right) + (1-y_k)\log\left(1-h_k(x)\right) \right] + \frac{\lambda}{2} \sum_{\theta \in w} \theta^2
J(w)=−[k=1∑Kykloghk(x)+(1−yk)log(1−hk(x))]+2λθ∈w∑θ2
其中
w
w
w 表示全部在模型中出现的参数。对于输出层来说,我们有
h
(
x
)
=
g
(
z
L
)
h(x) = g\left(z^L\right)
h(x)=g(zL)
首先对代价函数进行改写
J
(
Θ
)
≡
−
[
∑
k
=
1
K
log
(
1
−
g
(
z
k
L
)
)
+
y
k
log
g
(
z
k
L
)
log
(
1
−
g
(
z
k
L
)
)
]
=
−
[
∑
k
=
1
K
log
(
1
−
g
(
z
k
L
)
)
+
y
k
z
k
L
]
=
∑
k
=
1
K
−
y
k
z
L
−
log
(
1
−
g
(
z
k
L
)
)
\begin{array}{rcl} J(\Theta) &\equiv& -\left[\sum\limits_{k=1}^K \log\left(1-g\left(z_k^L\right)\right) + y_k\frac{\log g\left(z_k^L\right)}{\log\left(1-g\left(z_k^L\right)\right)} \right]\\ &=& -\left[\sum\limits_{k=1}^K \log\left(1-g\left(z_k^L\right)\right) + y_kz_k^L \right]\\ &=& \sum\limits_{k=1}^K -y_kz^L - \log\left(1-g\left(z_k^L\right)\right) \end{array}
J(Θ)≡==−[k=1∑Klog(1−g(zkL))+yklog(1−g(zkL))logg(zkL)]−[k=1∑Klog(1−g(zkL))+ykzkL]k=1∑K−ykzL−log(1−g(zkL))
直接对上式求导可得
δ
L
=
−
y
+
g
(
z
L
)
=
h
(
x
)
−
y
\delta_L = -y + g(z^L) = h(x) - y
δL=−y+g(zL)=h(x)−y
Fully Connected Layers
全连接层将上一层的输入按照一定的权重输出,其参数为二维矩阵
Θ
\Theta
Θ 。其前向传播的算法如下
add
a
0
j
−
1
=
1
z
j
=
Θ
j
−
1
a
j
−
1
a
j
=
g
(
z
j
)
\begin{aligned} & \text{add }a^{j - 1}_0 = 1\\ & z^j = \Theta^{j - 1}a^{j - 1}\\ & a^j = g\left(z^j\right)\\ \end{aligned}
add a0j−1=1zj=Θj−1aj−1aj=g(zj)
设
a
x
a^x
ax 的长度为
s
x
s_x
sx 则参数
Θ
\Theta
Θ 的大小为
s
j
×
(
s
j
−
1
+
1
)
s_j \times (s_{j - 1} + 1)
sj×(sj−1+1) ,其中
Θ
23
j
\Theta^j_{23}
Θ23j 表示第
j
j
j 层第
3
3
3 个神经元对第
j
+
1
j + 1
j+1 层第
2
2
2 个神经元的贡献。有了前向传播的公式,可以很方便的推出反向传播算法
∂
J
∂
a
j
−
1
=
(
∂
z
j
∂
a
j
−
1
)
T
∂
J
∂
z
j
=
(
Θ
j
−
1
)
T
δ
j
\frac{\partial J}{\partial a^{j - 1}} = \left(\frac{\partial z^j}{\partial a^{j - 1}}\right)^T \frac{\partial J}{\partial z^j} = \left(\Theta^{j - 1}\right)^T \delta_j
∂aj−1∂J=(∂aj−1∂zj)T∂zj∂J=(Θj−1)Tδj
因此
δ
j
−
1
=
(
Θ
j
−
1
)
T
δ
j
⊙
f
(
a
j
−
1
)
\delta_{j - 1} = (\Theta^{j - 1})^T\delta_j \odot f(a^{j - 1})
δj−1=(Θj−1)Tδj⊙f(aj−1)
由误差项计算梯度时可以使用下面的公式
∂
J
∂
Θ
j
=
δ
j
(
a
j
−
1
)
T
+
λ
Θ
j
\frac{\partial J}{\partial \Theta^j} = \delta_j \left(a^{j - 1}\right)^T + \lambda\Theta^j
∂Θj∂J=δj(aj−1)T+λΘj
需要注意这里的 a i a^i ai 是包含偏移项的,但 δ i + 1 \delta_{i + 1} δi+1 是不含偏移项误差的,否则向量维数不匹配。
Convolutional Layers
卷积层的输入和输出都是图片,用二维矩阵来表示。卷积层中每个神经元负责提取一个 local receptive field 的区域特征2
举例来说,上图中的神经元负责一个 5 × 5 5 \times 5 5×5 的区域。可以看出,在上面这个 28 × 28 28 \times 28 28×28 的图像中最多可以找到 24 × 24 24 \times 24 24×24 各不同的窗口,也就对应着 24 × 24 24 \times 24 24×24 个神经元。设各个矩阵的大小为
- 窗口 s × s s \times s s×s
- 输入图像 s j − 1 × s j − 1 s_{j - 1} \times s_{j - 1} sj−1×sj−1
- 输出图像 s j × s j s_j \times s_j sj×sj
可以看出他们有如下关系
s
j
=
s
j
−
1
−
s
+
1
s_j = s_{j - 1} - s + 1
sj=sj−1−s+1
Kernel
卷积层的参数包括权重
w
w
w 和偏移
b
b
b 两部分。一般来说
w
w
w 是一个二维矩阵,大小为
s
×
s
s \times s
s×s 。定义神经元状态
z
j
=
b
j
+
a
j
−
1
⊗
w
j
z^j = b^j + a^{j - 1} \otimes w^j
zj=bj+aj−1⊗wj
可以看出,不同位置的神经元对应的参数却是相同的,这一性质被称为 shared weights and bias 。正是因为这个性质的存在,卷积层的参数个数远小于全连接层,所以可以更有效的防止过拟合。
Padding
从上面的讨论中可以看到两件事
- 经过一次卷积就会使图像变小一点
- 输入图像中不同的像素被覆盖的次数不同
为了解决这两个问题,卷积层引入了填充的概念。设填充数为
p
p
p 则意味着输入图像在卷积前,会被
p
p
p 圈
0
0
0 所包裹。例如
[
1
2
3
4
]
⟶
p
=
2
[
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
2
0
0
0
0
3
4
0
0
0
0
0
0
0
0
0
0
0
0
0
0
]
\left[\begin{array}{cc} 1 & 2\\ 3 & 4 \end{array}\right] \mathop\longrightarrow\limits^{p = 2} \left[\begin{array}{cccccc} 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 1 & 2 & 0 & 0\\ 0 & 0 & 3 & 4 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0\\ 0 & 0 & 0 & 0 & 0 & 0\\ \end{array}\right]
[1324]⟶p=2⎣⎢⎢⎢⎢⎢⎢⎡000000000000001300002400000000000000⎦⎥⎥⎥⎥⎥⎥⎤
反向传播
记
δ
j
′
\delta'_j
δj′ 为
δ
j
\delta_j
δj 经过
s
−
1
s - 1
s−1 层填充后得到的矩阵,可以证明3
δ
j
−
1
=
δ
j
′
⊗
rot180
(
w
j
)
\delta_{j - 1} = \delta'_j \otimes \text{rot180}(w^j)
δj−1=δj′⊗rot180(wj)
由当前层的误差矩阵可以计算出核矩阵的梯度
∂
J
∂
w
j
=
a
j
−
1
⊗
δ
j
+
λ
w
j
\frac{\partial J}{\partial w^j} = a^{j - 1} \otimes \delta_j + \lambda w^j
∂wj∂J=aj−1⊗δj+λwj
如果当前层进行了填充,那么还需要对
a
j
−
1
a^{j - 1}
aj−1 进行裁剪。对偏移项有
∂
J
∂
b
j
=
∣
∣
δ
j
∣
∣
1
+
λ
b
j
\frac{\partial J}{\partial b^j} = ||\delta_j||_1 + \lambda b^j
∂bj∂J=∣∣δj∣∣1+λbj
Stride Length
上文中,我们对于每个可能的窗口都用一个神经元加以记录。实际操作中也可以每隔
k
−
1
k - 1
k−1 个窗口设置一个神经元。这里
k
k
k 也可以理解为窗口每次滑动的距离。前向传播中步长可以缩小输出图像的大小,因此在进行反向传播前,需要先把被跳过的神经元加入矩阵。例如矩阵
A
=
[
1
2
3
4
]
A = \left[\begin{array}{cc} 1 & 2\\ 3 & 4 \end{array}\right]
A=[1324]
使经过步长为
2
2
2 的卷积得到的。那么恢复后就应该变成
A
=
[
1
0
2
0
0
0
3
0
4
]
A = \left[\begin{array}{ccc} 1 & 0 & 2\\ 0 & 0 & 0\\ 3 & 0 & 4 \end{array}\right]
A=⎣⎡103000204⎦⎤
多通道
卷积层的输入和输出可以是一个多通道的图片,这时就需要用三维矩阵来表示。设
a
x
a^x
ax 所表示的矩阵有
c
x
c_x
cx 个通道,那么
w
w
w 将会是一个四维矩阵大小为
s
×
s
×
c
j
−
1
×
c
j
s \times s \times c_{j - 1} \times c_j
s×s×cj−1×cj
在进行前向传播时 c j c_j cj 个三维卷积核会分别与 a j − 1 a^{j - 1} aj−1 进行卷积运算。每个卷积核可以求出一个二维图片,最终组成一个 c j c_j cj 个通道的输出。对于每个三维卷积核,其 c j − 1 c_{j - 1} cj−1 层会分别与输入的 c j − 1 c_{j - 1} cj−1 层卷积,并将得到的图像求和。
对于反向传播,可以先假设 c j − 1 = 1 c_{j - 1} = 1 cj−1=1 进行说明。此时 δ j \delta_j δj 每一层的误差都来自于这一层。因为 δ j \delta_j δj 的层与层之间相互独立,所以只需要将其反向传播的误差相加即可得到 δ j − 1 \delta_{j - 1} δj−1 。
计算 w j w^j wj 的梯度时,总可以找到一个输入中的通道和一个输出中的通道与 w j w^j wj 中的任意一个二维矩阵相对应。而 b j b^j bj 与输出图像的通道相对应。因此二者原公式都不需要改变。
Pooling Layer
为了缩小卷积层输出的尺寸,池化层将二维图像分割成一系列不相交的区域,并选取一个代表元素来替换这个区域。需要注意的是,池化层的输出并不经过激活函数。常用的池化层主要有两种,一种选择区域内的最大值,另一种选择平均值。反向传播时,最大池化层可以将误差全部传递给最大值出现的位置,而平均值池化层可以将误差平均分配给区域内的全部神经元。因为池化层本身并没有参数,所以不需要计算梯度。
随机初始化
上述算法可以应用于梯度下降以训练模型。需要注意的是,各个权重的初始值如果相同,则所有神经元都是对称的,无法区分。因此实际上一般在
0
0
0 的一个小临域内随机选取初始值
Θ
i
j
l
∈
(
−
ϵ
,
ϵ
)
\Theta_{ij}^l \in (-\epsilon,\epsilon)
Θijl∈(−ϵ,ϵ)
由于初始值会对模型的收敛性造成影响,因此在模型不收敛时可以考虑更换初始值。
符号 ⊙ \odot ⊙ 表示矩阵的 Hadamard 乘积,也就是 Matlab 里的点乘。 ↩︎