卷积网络是近年来非常流行的网络结构,常用于处理图像(2维卷积)和自然语言(1维卷积)。在不明觉厉的情况下使用卷积层,往往使用别人设计的结构,凑参数往模型里塞,同复杂的原理书中的内容又有点对不上号。本篇从应用场景,具体用法入手,深入到每一个重要参数:具体用途、用法及计算方法。
为什么使用卷积网络
先来看看全连接网络的参数:
网络有三个输入x1,x2,x3和两个输出y1,y2,具体计算方法是:
如果y1与x3没有什么关系,通过训练两者之间的参数w31可能趋近于0,但该连接仍然存在。对于输入为3输出为2的网络需要训练32=6个参数。图片数据非常庞大,如果训练一张10001000像素,RGB三通道的图片,则输入元素有3M个,如果第一个隐藏层有1000个元素,则需要训练100010003*1000个参数,使得网络非常庞大,同时需要非常大量的数据才能避免过拟合。
卷积网络的优势在于共享参数,比如一个3x3的检测物体边缘卷积核训练好后,即可用于整个图片,此时需要学习的参数只有3x3=9个,如果在一层中训练20个3x3的卷积核用于检测不同特征,也只需要训练3x3x20=60个参数。
需要注意的问题是使用同样一组参数(卷积核)处理层的多个输入特征时,每组输入特征需要具有共同的性质,才能被处理,比如图片中的每个小区域边缘都具有相同的性质,可使用同种方法检测;但是预测汽车价格时,各个特征都代表完全不同的含义,就无法将不同意义的特征分组后使用同样的方法处理。
具体用法
卷积网络常用于图像处理,它常与池化层同时使用。以Pytorch为例,在模型的init初始化中创建层:
self.conv=torch.nn.Conv2d(3,16,2,stride=1,padding=1)
self.pool=torch.nn.MaxPool2d(4,4)
在forward前向传播时使用层:
out = self.conv(X)
out = self.pool(out)
传入卷积层的一般是4维数据[m,w,h,c]。此处创建的卷积层可以处理任意大小的图片。构造该层的参数如下:
torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
其中前五个参数最为重要:
- in_channel:输入通道数
- out_channel:输出通道数
- kernel_size:卷积核大小
- stride:步长
- padding:扩边
下面从原理角度介绍各个参数的功能。
重要概念
卷积网络的原理如下图所示,一个3x3的卷积核作用于6x6的图片之上,通过每个相邻的3x3区域计算一个值,具体方法是对应点做元素乘法之后相加,映射成右图中的一个点,然后逐步移动(Stride)遍历左图中所有区域,计算出右图中所有点作为输出。
kernel卷积核
卷积核kernel,有时也叫作filter,具体的计算方法下:
可以看到乘加的方法与全连接非常相似,不同的是使用卷积核之后,输出y中的每个元素只与部分x元素连接,计算量更小,即稀疏连接;使用同一组f与各个位置的x计算,参数更少,即共享参数。
padding扩边
上图中使用3x3的卷积核对6x6的图片做卷积操作,输出是3x3的图片,输出明显变小了,如果经过多次卷积图片会越变越小,另外,在卷积过程中边缘像素也被弱化了(边缘像素只对少数输出点起作用,使各像素点贡献不均衡)。为解决这一问题,一般工具都提供padding扩边功能,在卷积处理之前,延着边扩一圈(或者几圈),元素填充为0,如图片大小为6x6,padding为1时,图片大小变成8x8。目标是使得输出与处理之前的输入大小一致。
卷积可分为Valid Convolutions(带padding)和Same Convolutions(不带padding)两种。padding的大小一般设置为p=(f+1)/2。其中f是卷积核大小,由于上/下,左/右都需要加边,所以除2,可以看到如果卷积核为偶数,则p有小数部分,因此,一般卷积核心都为奇数,以免造成不对称;另外,奇数填充有一个中心点,这样也能更好地描述卷积操作的位置。
stride步长
步长是卷积核在原图上每一步移动的距离。下图是步长为2时,第一步和第二步的卷积操作。
简单地讲就是每次移动几个像素,stride一般设置为1或2,如何设置stride与卷积核大小相关,如果卷积核为3,步长为1,则各个卷积之间有一定的重叠;如果设置为4,则会漏掉一些像素。另外,如果stride设置较大会使输出图片变小,这也起到了一定的池化作用。
输出图片的大小可按以下公式计算:
其中n是图片大小,f是卷积核大小,pad为扩边大小,stride为步长,注意当除法结果非整数时向下取整。
channel通道
通道变换是相对较难以理解的问题,从使用角度看,输入卷积层的是一张256x256x3,即长宽各为256像素,包含RGB三个颜色通道的图片,输出为32x32x16,即大小为32x32,提取了16组特征。下面来看看如何从3通道变成16通道。
如图所示,对三维数据进行三维卷积后,输出为二维(27次元素乘法之后做加法),如果使用16个三维卷积核做卷积,输出则是4x4x16个特征。可以说训练了16个卷积核,用于提取不同的16种特征。对照初始化时的操作,无需指定图片大小,只需指定输入通道数i,输出通道数o,即可提取o种特征。
数据格式
传入conv2d的数据格式一般是4维数组[m,w,h,c],后三维分别是图片的宽高和输入通道数,第一维用于处理多张图片。需要注意的是forward时的输入通道数需要与初始化conv2d时设置的输入通道数一致。
池化层
池化层的主要作用是缩小图片,以及提高鲁棒性。上面提到,通过卷积的步长也能达到使输出变小的效果,池化层的另一个重要功能提高鲁棒性。
池化层一般放置在卷积层之后,用于将某一种特征中的多个值用单个值表示,这里只讨论最简单普通的用法:与卷积层配合使用的池化层。最常见的两种池化分别是最大池化和平均池化,又以最大池化为主。
池化的主要参数也是:大小f,步长s,以及具体的池化方法max/mean。最常见的数池化是f=2,s=2,type=maxpool,如下图所示:
maxpool可看作是提取了最显著的特征,另外,需要注意:
- 池化可能有两个区域或者多个区域有重叠部分。
- 池化层没有需要训练的weight参数,也因为这个原因,在描述一个模型的层数时,一般不将池化层计算在内,只作为卷积层的附属。
- 对于三维数据,池化只对其中的wxh操作,输出的channel大小不变。
瓶颈层
瓶颈层的概念源于GoogleNet中提出inception,inception对输入图像并行执行多个卷积运算和池化操作,并将所有结果拼接为一个非常深地特征图(特征很多)。可以想见,这会引发大量计算,需要很多网络参数。
因此引入了瓶颈层,用于减少计算量,瓶颈指网络中最窄的位置。它常借助1x1的卷积层实现,上面介绍了创建一个卷积层至少需要指定输入通道数,输出通道数,以及卷积核大小,瓶颈层的卷积核大小为1x1,输入通道由输入数据决定,输出通道数人工指定,如果需要将192种特征压缩到16种,则输出通道设为16。因此,可以通过池化层缩减图片宽高,通过瓶颈层缩减通道数。
加入瓶颈层的方法不仅用于图像处理,在自然语言处理及其它功能的网络中也是常用技巧,比如某层输入为10000个元素,输出为1000个元素,则参数数量为10000x1000=10000000,如果加入100元素的瓶颈层,则计算参数为10000x100+1000x100=1100000,计算缩减到之前的十分之一。
卷积网络常规用法
- 尽量使用论文中的超参数,不要自己从头设置。
- 网络结构中,一般wxh越来越小,channel越来越大,且常常是成倍减少或增加。
- 前边是若干卷积层,后面是一个或几个全连接层,最后是softmax。
- 层间数据越来越少,但不要下降太快。