卷积神经网络(Convolutional Neural Network,CNN)
- 受生物学上感受野机制的启发而提出。
- 一般是由卷积层、汇聚层和全连接层交叉堆叠而成的前馈神经网络
- 有三个结构上的特性:局部连接、权重共享、汇聚。
- 具有一定程度上的平移、缩放和旋转不变性。
- 和前馈神经网络相比,卷积神经网络的参数更少。
- 主要应用在图像和视频分析的任务上,其准确率一般也远远超出了其他的神经网络模型。
- 近年来卷积神经网络也广泛地应用到自然语言处理、推荐系统等领域。
目录
5.1 卷积
考虑到使用全连接前馈网络来处理图像时,会出现如下问题:
(1)模型参数过多,容易发生过拟合。 在全连接前馈网络中,隐藏层的每个神经元都要跟该层所有输入的神经元相连接。随着隐藏层神经元数量的增多,参数的规模也会急剧增加,导致整个神经网络的训练效率非常低,也很容易发生过拟合。
(2)难以提取图像中的局部不变性特征。 自然图像中的物体都具有局部不变性特征,比如尺度缩放、平移、旋转等操作不影响其语义信息。而全连接前馈网络很难提取这些局部不变性特征。
卷积神经网络有三个结构上的特性:局部连接、权重共享和汇聚。这些特性使得卷积神经网络具有一定程度上的平移、缩放和旋转不变性。和前馈神经网络相比,卷积神经网络的参数也更少。因此,通常会使用卷积神经网络来处理图像信息。
卷积是分析数学中的一种重要运算,常用于信号处理或图像处理任务。本节以二维卷积为例来进行实践。
5.1.1 二维卷积运算
在机器学习和图像处理领域,卷积的主要功能是在一个图像(或特征图)上滑动一个卷积核,通过卷积操作得到一组新的特征。在计算卷积的过程中,需要进行卷积核的翻转,而这也会带来一些不必要的操作和开销。因此,在具体实现上,一般会以数学中的互相关运算来代替卷积。
在神经网络中,卷积运算的主要作用是抽取特征,卷积核是否进行翻转并不会影响其特征抽取的能力。特别是当卷积核是可学习的参数时,卷积和互相关在能力上是等价的。因此很多时候,为方便起见,会直接用互相关来代替卷积。
对于一个输入矩阵和一个滤波器,它们的卷积为:
下图给出了卷积计算的示例。
经过卷积运算后,最终输出矩阵大小则为
M′=M−U+1,(5.2)
N′=N−V+1.(5.3)
可以发现,使用卷积处理图像,会有以下两个特性:
1、在卷积层(假设是第层)中的每一个神经元都只和前一层(第层)中某个局部窗口内的神经元相连,构成一个局部连接网络,这也就是卷积神经网络的局部连接特性。
2、由于卷积的主要功能是在一个图像(或特征图)上滑动一个卷积核,所以作为参数的卷积核对于第层的所有的神经元都是相同的,这也就是卷积神经网络的权重共享特性。
5.1.2 二维卷积算子
在本章后面的实现中,算子都继承torch.nn.Module,并使用支持反向传播的pytorchAPI进行实现,这样我们就可以不用手工写backword()的代码实现。
根据公式(5.1),我们首先实现一个简单的二维卷积算子,代码实现如下:
import torch
import torch.nn as nn
import numpy as np
class Conv2D(nn.Module):
def __init__(self, kernel_size, weight_attr=torch.tensor([[0., 1.], [2., 3.]])): # 类初始化,初始化权重属性为默认值,weight_attr为卷积核
super(Conv2D, self).__init__() # 继承torch.nn.Module中的Conv2D卷积算子
self.weight = torch.nn.Parameter(weight_attr)
def forward(self, X):
u, v = self.weight.shape
output = torch.zeros([X.shape[0], X.shape[1] - u + 1, X.shape[2] - v + 1])
for i in range(output.shape[1]):
for j in range(output.shape[2]):
output[:, i, j] = torch.sum(X[:, i:i + u, j:j + v] * self.weight, dim=[1, 2])
return output
# 随机构造一个二维输入矩阵
torch.manual_seed(100)
inputs = torch.tensor([[[1., 2., 3.], [4., 5., 6.], [7., 8., 9.]]])
conv2d = Conv2D(kernel_size=2)
outputs = conv2d(inputs)
print("input: {}, \noutput: {}".format(inputs, outputs))
运行结果:
input: tensor([[[1., 2., 3.],
[4., 5., 6.],
[7., 8., 9.]]]),
output: tensor([[[25., 31.],
[43., 49.]]], grad_fn=<CopySlices>)
5.1.3 二维卷积的参数量和计算量
参数量
由于二维卷积的运算方式为在一个图像(或特征图)上滑动一个卷积核,通过卷积操作得到一组新的特征。所以参数量仅仅与卷积核的尺寸有关,对于一个输入矩阵和一个滤波器,卷积核的参数量为。
假设有一幅大小为32 × 32 的图像,如果使用全连接前馈网络进行处理,即便第一个隐藏层神经元个数为1,此时该层的参数量也高达1025个,此时该层的计算过程如图所示。
可以想象,随着隐藏层神经元数量的变多以及层数的加深,使用全连接前馈网络处理图像数据时,参数量会急剧增加。如果使用卷积进行图像处理,当卷积核为3×3时,参数量仅为9相较于全连接前馈网络,参数量少了非常多。
计算量
在卷积神经网络中运算时,通常会统计网络总的乘加运算次数作为计算量(FLOPs,floating point of operations),来衡量整个网络的运算速度。对于单个二维卷积,计算量的统计方式为:
FLOPs=M′×N′×U×V。
其中M′×N′表示输出特征图的尺寸,即输出特征图上每个点都要与卷积核进行U×V次乘加运算。对于一幅大小为32×32的图像,使用3×3的卷积核进行运算可以得到以下的输出特征图尺寸:
M′=M−U+1=30M′=M−U+1=30
N′=N−V+1=30N′=N−V+1=30
此时,计算量为:
FLOPs=M′×N′×U×V=30×30×3×3=8100
5.1.4 感受野
输出特征图上每个点的数值,是由输入图片上大小为U×V的区域的元素与卷积核每个元素相乘再相加得到的,所以输入图像上U×V区域内每个元素数值的改变,都会影响输出点的像素值。我们将这个区域叫做输出特征图上对应点的感受野。感受野内每个元素数值的变动,都会影响输出点的数值变化。比如3×3卷积对应的感受野大小就是3×3,如下图所示。
而当通过两层3×3的卷积之后,感受野的大小将会增加到5×5,如下图所示。
因此,当增加卷积网络深度的同时,感受野将会增大,输出特征图中的一个像素点将会包含更多的图像语义信息。
5.1.5 卷积的变种
在卷积的标准定义基础上,还可以引入卷积核的滑动步长和零填充来增加卷积的多样性,从而更灵活地进行特征抽取。
5.1.5.1 步长(Stride)
在卷积运算的过程中,有时会希望跳过一些位置来降低计算的开销,也可以把这一过程看作是对标准卷积运算输出的下采样。在计算卷积时,可以在所有维度上每间隔S个元素计算一次,S称为卷积运算的步长(Stride),也就是卷积核在滑动时的间隔。此时,对于一个输入矩阵对于一个输入矩阵和一个滤波器