卷积网络CNN
卷积网络 CNN
卷积网络是近年来非常流行的网络结构,常用于处理图像(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 初始化中创建层:
1 | self.conv=torch.nn.Conv2d(3,16,2,stride=1,padding=1) |
在 forward 前向传播时使用层:
1 | out = self.conv(X) |
传入卷积层的一般是4维数据 [m,w,h,c]。此处创建的卷积层可以处理任意大小的图片。构造该层的参数如下:
1 | 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。
- 层间数据越来越少,但不要下降太快。