3.3 卷积神经网络

1.图像跟普通数据不一样:它有空间结构

这一章我们正式进入卷积神经网络(Convolutional Neural Network,简称CNN)。说起来,前面几章我们讲的内容,都没限定输入长什么样。可一旦要处理图像,事情就变得很特别了,因为图像里的数(也就是像素)不是一盘散沙,它们有自己的地盘关系。一个像素跟它紧挨着的几个像素关系最紧密,凑到一起就能组成一条边、一个角、一小片纹理,相隔很远的两个像素,关系通常就淡了。图像的这种空间结构,是它最值钱的特性,也是我们下手的一个特别好的切入点。

那么能不能直接拿前面讲的多层感知机去处理图像呢?理论上能,实战中问题会不少。我们算笔账就明白了。假设有一张不算大的彩色图片,长宽各224个像素,三个颜色通道,那拉成一个向量就有 224×224×3=150528224\times224\times3=150528 个数。要是网络第一层有1000个神经元,光这一层的权重就有 150528×10001.5×108150528\times1000\approx1.5\times10^8 个,一亿五千万。一张小图就一亿多参数,训练起来又慢又吃显存,而且这些参数还学不到什么空间上的门道,纯属蛮力。所以处理图像,我们得换个专门利用空间结构的工具,这就是CNN。

我之前翻过一本讲计算机视觉的书,里面提到一个挺贴切的看法:人眼认东西其实也是先抓局部边缘,再一点点拼出整体轮廓,这和CNN的思路是相通的。后来做医学影像的朋友也跟我说,他们拍出来的CT片子里,病灶和周围正常组织的纹理差异往往就藏在小块区域里,远处的像素帮忙不大,这同样要用到空间结构。

2.卷积到底在做什么:一盏滑动的小灯

CNN的核心操作叫二维卷积(convolution)。要理解它,最好的办法是想象你手里有一盏只照亮一小块地方的探照灯。你拿着这盏灯,从图像的左上角开始,一格一格地往右、往下慢慢扫,每照到一个位置,就把那一小块区域仔仔细细看一遍,归纳出一个数,记到输出图上。扫完整张图,你就得到一张全新的图,这张新图叫特征图(feature map)。

我们把这盏灯翻译成数学。设输入图像是 XX,那盏灯本身叫卷积核(kernel),记作 KK,它就是一个小小的权重方块。卷积核的高度记作 hh,宽度记作 ww。灯每停在一个位置,就把卷积核盖住的那一小块图像,跟卷积核里对应位置的权重相乘,然后全加起来,得到输出图上的一个数。输出图上位置 (i,j)(i,j) 那个数的算法是:

Yi,j=a=0h1b=0w1Ka,bXi+a,j+bY_{i,j}=\sum_{a=0}^{h-1}\sum_{b=0}^{w-1}K_{a,b}X_{i+a,j+b}

这里头的 aabb 表示卷积核内部的位置(aa 从0数到 h1h-1bb 从0数到 w1w-1),iijj 表示卷积核当前停在输入图像的哪个位置,Ka,bK_{a,b} 是卷积核里的一个权重,Xi+a,j+bX_{i+a,j+b} 是窗口盖住的那个像素。

不妨举个例子,带点数字的。假设图像是一个 3×33\times3 的小方块,卷积核是个 2×22\times2 的小方块:

X=[120013211],K=[1001]X=\begin{bmatrix}1 & 2 & 0 \\ 0 & 1 & 3 \\ 2 & 1 & 1\end{bmatrix},\quad K=\begin{bmatrix}1 & 0 \\ 0 & 1\end{bmatrix}

当卷积核停在最左上角,盖住左上角那四个数(依次是1、2、0、1)的时候,按对应位置相乘再求和,得到 1×1+2×0+0×0+1×1=21\times1+2\times0+0\times0+1\times1=2。这个2就是输出特征图左上角那个数。然后卷积核往右挪一格、往下挪一格,依此类推,把每个位置都算一遍,最后拼出一张完整的特征图。卷积核里头的权重,跟普通网络一样,也是在训练里慢慢学出来的。

训练完之后你会发现一件特别有意思的事:不同的卷积核会自发地学会盯不同的东西,有的专门对横着的边有反应,有的对竖着的边敏感,还有的对某种斑点纹理来劲。你什么都没教它,它自己就学出了一整套找边缘、找纹理的招数,颇为巧妙。记得有一次小张看一个公开课,讲师把训练好的卷积核做了可视化,他盯着屏幕看了半天,感慨说原来这些小方块自己就能学会认边缘,有点像小孩子学画画,先会描线条,慢慢才会涂色块。

3.步长、填充和特征图怎么摞起来

刚才说的是最朴素的滑法,还有两个小配件得认识一下。

一个是步长(stride),说的就是卷积核每次挪几格。步长是1,就一格一格慢慢挪,看得很细。步长是2,就跳着挪,输出特征图直接缩成原来的一半,相当于一边卷积一边就把图给缩小了,常用来代替池化做下采样。

另一个是填充(padding)。我们会注意到,按前面的滑法,图像边缘的像素被卷积核盖到的次数,比中间的像素少得多,存在感偏低。为了弥补这一点,可以在图像四周补一圈零(这个补零的动作就叫填充),让边缘像素也能被公平地多看几次。补多少圈、步长取多少,都会决定输出特征图有多大。

还有一个关键点:卷积核是一大摞,不止一个。一个卷积核扫一遍只能产出一张特征图,可一张图里的模式千千万,一个核哪里够用。所以一层通常会甩出几十上百个卷积核,每个都把输入扫一遍,各自产出一张特征图,这些特征图摞在一起,就成了一个厚厚的多通道方块。下一层又拿这个厚方块当输入,再用一摞新的卷积核去扫它,特征图就这么一层层地变厚、一层层地把抽象程度往上提。层与层之间,一般还会塞一个ReLU激活函数,把负的响应削掉,只留下有反应的部分。

4.池化:把图浓缩一下

卷积层负责找特征,可特征图越攒越多、越攒越大,算起来扛不住。这时候就需要池化(pooling)层出场了。池化做的事其实特别简单,它把特征图切成一小块一小块的,每块只留一个代表。比如最常用的最大池化(max pooling),就是把相邻的四个格子划成一组,只挑里头数值最大的那个留下,剩下三个扔掉。这么一来,特征图的长宽直接缩成一半,数据量明显减少。而且因为只留了最显眼的那个响应,模型对小范围内的位置挪动也就不那么敏感了,一只猫稍微往左挪两像素,模型照样认得出来。

这道理有点像看报纸,一整版密密麻麻的字,我们眼睛其实只挑几个标题和关键句来记,大致内容就有了,不必每个字都盯着不放。小明之前做毕业设计,把池化层去掉想看看会怎样,结果显存一下子就吃满了,跑一个epoch比原来慢了三四倍,这才体会到池化省下来的计算量有多可观。

5.为什么卷积这么省参数、又这么适合图像

CNN之所以能在视觉领域称霸好些年,靠的是两个特别会过日子的好习惯。

第一个叫参数共享。回想一下,同一个卷积核在整张图的左边和右边是被反复使用的,相当于这个核带着同一套权重,在图的各个角落寻找同一种局部模式。这么一来,不管图像多大,一个卷积核只需要学一套权重就够了。跟前面那个一亿多参数的多层感知机一比,CNN的参数量大幅下降。这有点像一位有经验的质检员,不管流水线上过来多少件产品,他手上那套检验手法是通用的,不必每件产品都重新学一套标准。

第二个叫局部连接。卷积核每次只盯着图像的一小块区域看,这正好踩中了图像里近距离像素关系紧密的事实。它不会被远处的无关像素干扰,专注得很。某部很有名的侦探小说里写过,破案的关键往往就藏在案发现场方圆几米的小细节里,远处的风声虫鸣多半无关紧要,卷积核的脾气也差不多,只关心眼前这一小片。

把这两个好习惯加到一起,CNN既能扛得住大图,又能抓住空间上的门道,参数还少,训练起来也轻快,简直像是给图像量身定做的。

6.层层抽象:从像素到物体

把卷积、激活、池化这些积木摞起来,你会发现一件特别有意思的事:网络学会了一层一层地把图像往抽象里提。

最开头那几层,卷积核学到的东西还很简单,基本都是些横边、竖边、色块边界之类的基础件。再往中间几层走,这些基础件开始拼搭,组合出角、纹理、简单的几何形状。到了最后几层,形状又拼成了物体的部件,比如一只眼睛、一个轮子、一张狗脸。整个过程,就像是从一团乱码像素,一步步提炼出有意义的概念,层数越深,概念越高级。这其实就是深度学习里表示学习在视觉上的具体模样,也是CNN最迷人的地方。有个很有名的说法,把网络比作一位从摹拓临摹起步的画师,先学会勾最简单的线条,再慢慢能画出动人的肖像,深度网络的抽象过程大抵也是这么一层层长进的本事。

7.一小段历史:从手写数字到视觉革命

CNN可不是一夜之间冒出来的。早在上世纪八十年代末到九十年代,有位叫杨立昆(Yann LeCun)的学者就搞出了一个叫LeNet的卷积网络,专门用来认银行卡上的手写数字,那时候就已经在银行里实际跑了起来。可受限于当时的算力和数据,CNN在更难的图像任务上一直没能大放异彩,被不少人当成冷板凳选手。

转折发生在2012年。那一年有个叫ImageNet的图像识别大赛,数据集大得吓人,类别上千。一个叫AlexNet的深层卷积网络横空出世,靠着GPU的算力和海量的数据,把识别错误率生生砍掉了一大截,把一众传统方法远远甩在身后。这一战直接引爆了深度学习,从此CNN成了计算机视觉的绝对主力,杨立昆本人后来也拿了图灵奖。这段历史特别能说明一件事:很多技术其实早就不差,差的只是算力和数据没攒够而已。

CNN这块就先聊到这儿,它是后面视觉任务的地基,建议大家把卷积那个滑窗的过程在脑子里多过几遍,想通了再看后面的内容就轻松了。

下一章见。

练习

Q1. 为什么不用多层感知机直接处理图像,而要用 CNN?

一张 224×224×3224\times224\times3 的彩图拉成向量有 15 万多个数,MLP 第一层若有 1000 个神经元,光这层权重就约 1.5 亿个,训练又慢又吃显存,还学不到空间结构。CNN 专门利用图像的局部空间关系(邻近像素组成边、角、纹理),靠参数共享和局部连接大幅省参数,又能抓住空间门道,所以更适合图像。

Q2.2×22\times2 卷积核 K=[1001]K=\begin{bmatrix}1 & 0 \\ 0 & 1\end{bmatrix}3×33\times3 图像 X=[120013211]X=\begin{bmatrix}1 & 2 & 0 \\ 0 & 1 & 3 \\ 2 & 1 & 1\end{bmatrix} 的左上角,手算输出值。

卷积核盖住左上角四个数 1、2、0、1,对应位置相乘再求和:1×1+2×0+0×0+1×1=21\times1+2\times0+0\times0+1\times1=2。这就是输出特征图左上角那个数。卷积核逐格右移、下移,每个位置都这样算一遍拼出整张特征图。

Q3. 步长(stride)和填充(padding)分别解决什么问题?池化层又起什么作用?

步长控制卷积核每次挪几格,步长 2 直接让输出特征图缩成一半,可代替池化做下采样。填充在图像四周补一圈零,让边缘像素也能被公平地多看几次(否则边缘像素被卷积核盖到的次数远少于中间)。池化把特征图切块只留代表(最大池化留最大值),缩小长宽省计算量,还让模型对小范围位置挪动更不敏感。

Q4.(面试题) CNN 凭什么两个"会过日子的好习惯"就能在视觉领域称霸?请展开参数共享和局部连接。

一是参数共享:同一个卷积核带着同一套权重在整张图各个角落反复使用,不管图像多大,一个核只学一套权重,参数量相比 MLP 大幅下降。二是局部连接:卷积核每次只盯图像的一小块区域看,正好踩中"邻近像素关系紧密、远处像素关系淡"的事实,专注不被远处无关像素干扰。两者合起来,CNN 既扛得住大图、又抓住空间门道、参数还少、训练轻快,像是给图像量身定做的,所以长期称霸视觉。

相关标签
深度学习卷积计算机视觉