卷积神经网络
卷积神经网络是目前计算机视觉中使用最普遍的模型结构。本章节主要向读者介绍卷积神经网络的一些基础模块,包括:
- 卷积(Convolution)
- 池化(Pooling)
- ReLU激活函数
- 批归一化(Batch Normalization)
- 丢弃法(Dropout)
下图是一个典型的卷积神经网络结构,多层卷积和池化层组合作用在输入图片上,在网络的最后通常会加入一系列全连接层,ReLU激活函数一般加在卷积或者全连接层的输出上,网络中通常还会加入Dropout来防止过拟合。
说明:
在卷积神经网络中,计算范围是在像素点的空间邻域内进行的,卷积核参数的数目也远小于全连接层。卷积核本身与输入图片大小无关,它代表了对空间邻域内某种特征模式的提取。比如,有些卷积核提取物体边缘特征,有些卷积核提取物体拐角处的特征,图像上不同区域共享同一个卷积核。当输入图片大小不一样时,仍然可以使用同一个卷积核进行操作。
卷积(Convolution)
这一小节将为读者介绍卷积算法的原理和实现方案,并通过具体的案例展示如何使用卷积对图片进行操作,主要涵盖如下内容:
- 卷积计算
- 填充(padding)
- 步幅(stride)
- 感受野(Receptive Field)
- 多输入通道、多输出通道和批量操作
- 飞桨卷积API介绍
- 卷积算子应用举例
卷积计算
卷积是数学分析中的一种积分变换的方法,在图像处理中采用的是卷积的离散形式。这里需要说明的是,在卷积神经网络中,卷积层的实现方式实际上是数学中定义的互相关 (cross-correlation)运算,与数学分析中的卷积定义有所不同,这里跟其他框架和卷积神经网络的教程保持一致,都使用互相关运算作为卷积的定义,具体的计算过程如下图所示。
说明:
卷积核(kernel)也被叫做滤波器(filter),假设卷积核的高和宽分别为 k h k_h kh和 k w k_w kw,则将称为 k h × k w k_h×k_w kh×kw卷积,比如3×5卷积,就是指卷积核的高为3, 宽为5。
- 如图(a)所示:左边的图大小是3×3,表示输入数据是一个维度为3×3的二维数组;中间的图大小是2×2,表示一个维度为2×2的二维数组,我们将这个二维数组称为卷积核。先将卷积核的左上角与输入数据的左上角(即:输入数据的(0, 0)位置)对齐,把卷积核的每个元素跟其位置对应的输入数据中的元素相乘,再把所有乘积相加,得到卷积输出的第一个结果:
0×1+1×2+2×4+3×5=25
其余计算可以以此类推;
卷积核的计算过程可以用下面的数学公式表示,其中 a代表输入图片, b代表输出特征图,w是卷积核参数,它们都是二维数组,
Σ
(
u
,
v
)
\Sigma(u,v)
Σ(u,v)表示对卷积核参数进行遍历并求和。
填充(padding)
在上面的例子中,输入图片尺寸为3×3,输出图片尺寸为2×2,经过一次卷积之后,图片尺寸变小。卷积输出特征图的尺寸计算方法如下(卷积核的高和宽分别为
k
h
k_h
kh和
k
w
k_w
kw):
如果输入尺寸为4,卷积核大小为3时,输出尺寸为4−3+1=2。当卷积核尺寸大于1时,输出特征图的尺寸会小于输入图片尺寸。如果经过多次卷积,输出图片尺寸会不断减小。为了避免卷积之后图片尺寸变小,通常会在图片的外围进行填充(padding),如下图所示。
如图(a)所示:填充的大小为1,填充值为0。填充之后,输入图片尺寸从4×4变成了6×6,使用3×3的卷积核,输出图片尺寸为4×4。
如图(b)所示:填充的大小为2,填充值为0。填充之后,输入图片尺寸从4×4变成了8×8,使用3×3的卷积核,输出图片尺寸为6×6。
如果在图片高度方向,在第一行之前填充
p
h
1
p_{h1}
ph1 行,在最后一行之后填充
p
h
2
p_{h2}
ph2 ;在图片的宽度方向,在第1列之前填充
p
w
1
p_{w1}
pw1 列,在最后1列之后填充
p
w
2
p_{w2}
pw2 列;则填充之后的图片尺寸为
(
H
+
p
h
1
+
p
h
2
)
×
(
W
+
p
w
1
+
p
w
2
)
(H+p_{h1}+p_{h2})×(W+p_{w1}+p_{w2})
(H+ph1+ph2)×(W+pw1+pw2)。经过大小为
k
h
×
k
w
k_h×k_w
kh×kw的卷积核操作之后,输出图片的尺寸为:
在卷积计算过程中,通常会在高度或者宽度的两侧采取等量填充,上面计算公式也就变为:
卷积核大小通常使用1,3,5,7这样的奇数,如果使用的填充大小为
p
h
=
(
k
h
−
1
)
/
2
p_h=(k_h-1)/2
ph=(kh−1)/2,
p
w
=
(
k
w
−
1
)
/
2
p_w=(k_w-1)/2
pw=(kw−1)/2,则卷积之后图像尺寸不变。例如当卷积核大小为3时,padding大小为1,卷积之后图像尺寸不变;同理,如果卷积核大小为5,padding大小为2,也能保持图像尺寸不变。
步幅(stride)
下图中是步幅为2的卷积过程,卷积核在图片上移动时,每次移动大小为2个像素点。
当宽和高方向的步幅分别为
s
h
s_h
sh和
s
w
s_w
sw时,输出特征图尺寸的计算公式是:
感受野(Receptive Field)
输出特征图上每个点的数值,是由输入图片上大小为
k
h
×
k
w
k_h×k_w
kh×kw的区域的元素与卷积核每个元素相乘再相加得到的,所以输入图像上
k
h
×
k
w
k_h×k_w
kh×kw区域内每个元素数值的改变,都会影响输出点的像素值。我们将这个区域叫做输出特征图上对应点的感受野。感受野内每个元素数值的变动,都会影响输出点的数值变化。比如3×3卷积对应的感受野大小就是3×3,如下图所示。
而当通过两层3×3的卷积之后,感受野的大小将会增加到5×5.
因此,当增加卷积网络深度的同时,感受野将会增大,输出特征图中的一个像素点将会包含更多的图像语义信息。
多输入通道、多输出通道和批量操作
前面介绍的卷积计算过程比较简单,实际应用时,处理的问题要复杂的多。例如:对于彩色图片有RGB三个通道,需要处理多输入通道的场景。输出特征图往往也会具有多个通道,而且在神经网络的计算中常常是把一个批次的样本放在一起计算,所以卷积算子需要具有批量处理多输入和多输出通道数据的功能,下面将分别介绍这几种场景的操作方式。
- 多输入通道场景
上面的例子中,卷积层的数据是一个2维数组,但实际上一张图片往往含有RGB三个通道,要计算卷积的输出结果,卷积核的形式也会发生变化。假设输入图片的通道数为
C
i
n
C_i{_n}
Cin输入数据的形状是
C
i
n
×
H
i
n
×
W
i
n
{C_i{_n}×H_i{_n}×W_i{_n}}
Cin×Hin×Win,计算过程如下图所示。
1、 对每个通道分别设计一个2维数组作为卷积核,卷积核数组的形状是
C
i
n
×
k
h
×
k
w
C_i{_n}×k_h×k_w
Cin×kh×kw
2、 对任一通道
C
i
n
∈
[
0
,
C
i
n
)
C_{in} \in [0, C_{in})
Cin∈[0,Cin)分别用大小为
k
h
×
k
w
k_h×k_w
kh×kw的卷积核在大小为
H
i
n
×
W
i
n
H_i{_n}×W_i{_n}
Hin×Win二维数组上做卷积。
3、 将这
C
i
n
C_{in}
Cin 个通道的计算结果相加,得到的是一个形状为
H
o
u
t
×
W
o
u
t
H_{out}\times{W_{out}}
Hout×Wout 的二维数组。
- 多输出通道场景
一般来说,卷积操作的输出特征图也会具有多个通道 C o u t C_{out} Cout,这时我们需要设计 C o u t C_{out} Cout个维度为 C i n × k h × k w C_i{_n}×k_h×k_w Cin×kh×kw的卷积核,卷积核数组的维度是 C o u t × C i n × k h × k w C_{out}×C_i{_n}×k_h×k_w Cout×Cin×kh×kw,如下图 所示。
- 对任一输出通道 c o u t ∈ [ 0 , C o u t ) c_{out} \in [0, C_{out}) cout∈[0,Cout),分别使用上面描述的形状为 C i n × k h × k w C_{in}\times{k_h}\times{k_w} Cin×kh×kw的卷积核对输入图片做卷积。
- 将这 C o u t C_{out} Cout个形状为 H o u t × W o u t H_{out}\times{W_{out}} Hout×Wout的二维数组拼接在一起,形成维度为 C o u t × H o u t × W o u t C_{out}\times{H_{out}}\times{W_{out}} Cout×Hout×Wout的三维数组。
说明:
通常将卷积核的输出通道数叫做卷积核的个数。
- 批量操作
在卷积神经网络的计算中,通常将多个样本放在一起形成一个mini-batch进行批量操作,即输入数据的维度是 N × C i n × H i n × W i n N\times{C_{in}}\times{H_{in}}\times{W_{in}} N×Cin×Hin×Win。由于会对每张图片使用同样的卷积核进行卷积操作,卷积核的维度与上面多输出通道的情况一样,仍然是 C o u t × C i n × k h × k w C_{out}\times C_{in}\times{k_h}\times{k_w} Cout×Cin×kh×kw,输出特征图的维度是 N × C o u t × H o u t × W o u t N\times{C_{out}}\times{H_{out}}\times{W_{out}} N×Cout×Hout×Wout,如下图所示。
池化(Pooling)
池化是使用某一位置的相邻输出的总体统计特征代替网络在该位置的输出,其好处是当输入数据做出少量平移时,经过池化函数后的大多数输出还能保持不变。比如:当识别一张图像是否是人脸时,我们需要知道人脸左边有一只眼睛,右边也有一只眼睛,而不需要知道眼睛的精确位置,这时候通过池化某一片区域的像素点来得到总体统计特征会显得很有用。由于池化之后特征图会变得更小,如果后面连接的是全连接层,能有效的减小神经元的个数,节省存储空间并提高计算效率。 如下图所示,将一个2×2的区域池化成一个像素点。通常有两种方法,平均池化和最大池化。
- 如图(a):平均池化。这里使用大小为2×2的池化窗口,每次移动的步幅为2,对池化窗口覆盖区域内的像素取平均值,得到相应的输出特征图的像素值。
- 如图(b):最大池化。对池化窗口覆盖区域内的像素取最大值,得到输出特征图的像素值。当池化窗口在图片上滑动时,会得到整张输出特征图。池化窗口的大小称为池化大小,用 k h × k w k_h \times k_w kh×kw表示。在卷积神经网络中用的比较多的是窗口大小为2×22 \times 22×2,步幅为2的池化。
与卷积核类似,池化窗口在图片上滑动时,每次移动的步长称为步幅,当宽和高方向的移动大小不一样时,分别用
s
w
s_w
sw和
s
h
s_h
sh表示。也可以对需要进行池化的图片进行填充,填充方式与卷积类似,假设在第一行之前填充
p
h
1
p_{h1}
ph1行,在最后一行后面填充
p
h
2
p_{h2}
ph2行。在第一列之前填充
p
w
1
p_{w1}
pw1列,在最后一列之后填充
p
w
2
p_{w2}
pw2列,则池化层的输出特征图大小为:
在卷积神经网络中,通常使用2×2大小的池化窗口,步幅也使用2,填充为0,则输出特征图的尺寸为:
通过这种方式的池化,输出特征图的高和宽都减半,但通道数不会改变。
激活函数
前面介绍的网络结构中,普遍使用Sigmoid函数做激活函数。在神经网络发展的早期,Sigmoid函数用的比较多,而目前用的较多的激活函数是ReLU。这是因为Sigmoid函数在反向传播过程中,容易造成梯度的衰减。让我们仔细观察Sigmoid函数的形式,就能发现这一问题。
Sigmoid激活函数定义如下:
ReLU激活函数的定义如下:
梯度消失现象
在神经网络里,将经过反向传播之后,梯度值衰减到接近于零的现象称作梯度消失现象。
从上面的函数曲线可以看出,当x为较大的正数的时候,Sigmoid函数数值非常接近于1,函数曲线变得很平滑,在这些区域Sigmoid函数的导数接近于零。当x为较小的负数时,Sigmoid函数值也非常接近于0,函数曲线也很平滑,在这些区域Sigmoid函数的导数也接近于0。只有当x的取值在0附近时,Sigmoid函数的导数才比较大。对Sigmoid函数求导数,结果如下所示:
从上面的式子可以看出,Sigmoid函数的导数
d
y
d
x
\frac{dy}{dx}
dxdy最大值为
1
4
\frac{1}{4}
41。前向传播时,y=Sigmoid(x);而在反向传播过程中,x的梯度等于y的梯度乘以Sigmoid函数的导数,如下所示:
使得x的梯度数值最大也不会超过y的梯度的
1
4
\frac{1}{4}
41。由于最开始是将神经网络的参数随机初始化的,x的取值很有可能在很大或者很小的区域,这些地方都可能造成Sigmoid函数的导数接近于0,导致x的梯度接近于0;即使x取值在接近于0的地方,按上面的分析,经过Sigmoid函数反向传播之后,x的梯度不超过yyy的梯度的
1
4
\frac{1}{4}
41,如果有多层网络使用了Sigmoid激活函数,则比较靠后的那些层梯度将衰减到非常小的值。
ReLU函数则不同,虽然在x<0的地方,ReLU函数的导数为0。但是在x≥0的地方,ReLU函数的导数为1,能够将y的梯度完整的传递给x,而不会引起梯度消失。
批归一化(Batch Normalization)
批归一化方法(Batch Normalization,BatchNorm)是由Ioffe和Szegedy于2015年提出的,已被广泛应用在深度学习中,其目的是对神经网络中间层的输出进行标准化处理,使得中间层的输出更加稳定。
通常我们会对神经网络的数据进行标准化处理,处理后的样本数据集满足均值为0,方差为1的统计分布,这是因为当输入数据的分布比较固定时,有利于算法的稳定和收敛。对于深度神经网络来说,由于参数是不断更新的,即使输入数据已经做过标准化处理,但是对于比较靠后的那些层,其接收到的输入仍然是剧烈变化的,通常会导致数值不稳定,模型很难收敛。BatchNorm能够使神经网络中间层的输出变得更加稳定,并有如下三个优点:
1、使学习快速进行(能够使用较大的学习率)
2、降低模型对初始值的敏感性
3、从一定程度上抑制过拟合
BatchNorm主要思路是在训练时以mini-batch为单位,对神经元的数值进行归一化,使数据的分布满足均值为0,方差为1。具体计算过程如下:
1. 计算mini-batch内样本的均值
其中
x
(
i
)
x^{(i)}
x(i)表示mini-batch中的第iii个样本。
例如输入mini-batch包含3个样本,每个样本有2个特征,分别是:
对每个特征分别计算mini-batch内样本的均值:
则样本均值是:
2. 计算mini-batch内样本的方差
上面的计算公式先计算一个批次内样本的均值
μ
B
\mu_{B}
μB 和方差
σ
B
2
\sigma_B^2
σB2,然后再对输入数据做归一化,将其调整成均值为0,方差为1的分布。
对于上述给定的输入数据
x
(
1
)
,
x
(
2
)
,
x
(
3
)
x^{(1)}, x^{(2)}, x^{(3)}
x(1),x(2),x(3),可以计算出每个特征对应的方差:
则样本方差是:
3. 计算标准化之后的输出
其中
ϵ
\epsilon
ϵ是一个微小值(例如1e−7),其主要作用是为了防止分母为0。
对于上述给定的输入数据
x
(
1
)
,
x
(
2
)
,
x
(
3
)
x^{(1)}, x^{(2)}, x^{(3)}
x(1),x(2),x(3),可以计算出标准化之后的输出:
如果强行限制输出层的分布是标准化的,可能会导致某些特征模式的丢失,所以在标准化之后,BatchNorm会紧接着对数据做缩放和平移。
其中γ和β是可学习的参数,可以赋初始值γ=1,β=0,在训练过程中不断学习调整。
- 预测时使用BatchNorm
上面介绍了在训练过程中使用BatchNorm对一批样本进行归一化的方法,但如果使用同样的方法对需要预测的一批样本进行归一化,则预测结果会出现不确定性。
例如样本A、样本B作为一批样本计算均值和方差,与样本A、样本C和样本D作为一批样本计算均值和方差,得到的结果一般来说是不同的。那么样本A的预测结果就会变得不确定,这对预测过程来说是不合理的。解决方法是在训练过程中将大量样本的均值和方差保存下来,预测时直接使用保存好的值而不再重新计算。实际上,在BatchNorm的具体实现中,训练时会计算均值和方差的移动平均值。
丢弃法(Dropout)
丢弃法(Dropout)是深度学习中一种常用的抑制过拟合的方法,其做法是在神经网络学习过程中,随机删除一部分神经元。训练时,随机选出一部分神经元,将其输出设置为0,这些神经元将不对外传递信号。
下图是Dropout示意图,左边是完整的神经网络,右边是应用了Dropout之后的网络结构。应用Dropout之后,会将标了×的神经元从网络中删除,让它们不向后面的层传递信号。在学习过程中,丢弃哪些神经元是随机决定,因此模型不会过度依赖某些神经元,能一定程度上抑制过拟合。
在预测场景时,会向前传递所有神经元的信号,可能会引出一个新的问题:训练时由于部分神经元被随机丢弃了,输出数据的总大小会变小。比如:计算其L1范数会比不使用Dropout时变小,但是预测时却没有丢弃神经元,这将导致训练和预测时数据的分布不一样。