4.7 图像分割FCN、U-Net与DeepLab

1.语义分割要做什么:给每个像素都安排一个类别

说起来,前面我们在3.3 节讲卷积神经网络的时候,做的是图像分类,整张图最后归到一类就好。后来4.3 节讲目标检测,更进一步去框出每个物体的位置。可现实里有些活儿比这还细,光画一个框远远不够。

我们先举个具体的例子。自动驾驶里,车子在路上跑,摄像头拍下来的画面里,哪一片是能开过去的安全路面,哪一片是人行道,哪一片是隔离带,得一一标清楚。要画框的话,框的边是直的,可路面的边界往往是斜的、弯的,框不住。这种时候就需要更精细的做法,把画面里每一个像素都单独判一次类别。这样的任务就叫图像分割,更准确地说是语义分割。

语义分割的输出,是一张和原图同样大小的标注图,每个像素位置上都填着它所属的类别。我们设输入图像的高为 HH、宽为 WWHHWW 分别是图像的行数和列数,再设一共有 CC 个类别,CC 是类别总数。那么输出就是一张 H×WH\times W 的图,每个位置上写一个 11CC 之间的类别编号。这里 ×\times 是乘号,表示 HH 行乘 WW 列的网格。

还记得4.1 节我们提过分割的几种形式吗。语义分割只区分类别,不区分同类的不同个体。比如画面里有两只猫挨着,语义分割都标成猫这一类,两只猫在它眼里就是一回事。要是想把不同的个体也分开,那叫实例分割(4.3 节提过的Mask R-CNN就属于这一路),再往细里走还有把两者合起来的,叫全景分割。这一篇我们重点讲语义分割。

2.FCN:把分类网络改造成像素级预测

要做的活儿清楚了,紧接着的问题是怎么做。最直觉的做法是,把3.3 节里那种分类网络搬过来用,每个像素周围抠一小块,单独过一遍网络判一次类别。听起来简单,可实际跑起来慢得让人难受,而且对每个像素单独处理,也丢掉了整体结构的信息。

2015年有一篇文章,叫FCN(全卷积网络,Fully Convolutional Network),算是把这层窗户纸捅破了。FCN干的事情其实就一句话,把分类网络最后那几层全连接层(fully connected layer,就是把特征拉成一长条再接一层的那种)换成卷积层。这么一改,整张图从头到尾都是卷积,没有全连接层那种非得把输入拍平的限制,网络就能输入任意大小的图片,输出一张和输入大致对应位置的预测图。

这里面还藏着一个关键操作,叫上采样。分类网络里一路卷积加池化下来,特征图会越变越小,分辨率越降越低。等到要做像素级预测的时候,得把这份缩小的特征图再放大回去,恢复到原图的大小。这个放大的过程就叫上采样。最简单的上采样办法是双线性插值,其实就是按位置做线性加权,把缺失的位置补出来。我们记上采样倍数为 rrrr 就是把特征图放大的倍数,比方说 r=2r=2 表示长宽各放大到两倍。

FCN的意义在于它第一次把端到端的像素级预测跑通了,从那以后大家心里都有底了,原来分类网络可以这么改造来做分割。说起来这是个开山之作,后面的方法多多少少都受了它的启发。

3.U-Net:编码解码加跳跃连接,医学图像里的常青树

FCN跑通了路子,但有一个明显的短板,就是边界不够精细。一路下采样再上采样,原图里那些细小的纹理和边缘信息,在中途已经被压缩掉了,最后再放大回来也只能恢复个大概。

这就轮到U-Net登场了。U-Net最早是2015年一篇医学图像分割的文章提出来的,结构画出来像个字母U,左边一半是编码器(encoder),一路下采样提特征,右边一半是解码器(decoder),一路上采样恢复分辨率,所以叫U-Net。它最关键的招数是中间那一根根横着的连线,叫跳跃连接(skip connection)。

跳跃连接做的事情,是把编码器里那些还没被压缩得太狠的高分辨率特征,直接接到解码器对应的位置上。打个比方,这有点像写文章的时候,一边打草稿一边把原稿留着,等到润色的时候把原稿的细节重新贴回去。编码器在每一层都留下了当时的细节,解码器上采样时就把这些细节和刚放大的特征拼到一起,这样恢复出来的边界就精细得多。

U-Net在医学图像分割里几乎封了神。原因有几个。一是医学图像的分割任务往往对边界要求极高,比如分割一个肿瘤的边缘,差几个像素就可能影响诊断,U-Net的跳跃连接正好对路。二是医学数据本来就少,医院里标注一份CT或者病理切片要请资深医生一笔一笔画,几百张就是大工程了,而U-Net结构对称、用了跳跃连接,数据不多也能训出像样的结果。我记得看过一篇文章讲细胞分割的比赛,冠军方案就是U-Net的变体,训练数据只有几十张图,照样比别的队伍领先一截。

4.DeepLab:用空洞卷积在不增加参数的情况下扩大感受野

U-Net解决了精细边界的问题,可还有一件事让人头疼,那就是感受野和分辨率的矛盾。

我们先说说感受野是什么。感受野说的是网络里某一个神经元,能看到原图上多大的范围。层数越深、池化越多,感受野就越大,能看到越大的物体,可代价是特征图越来越小,分辨率越来越低,细节丢得越来越多。反过来要是少下采样,分辨率保住了,感受野又不够大,看不见整个物体。这对矛盾,在分割那些大小不一的物体时格外难受。

DeepLab给了一个很巧妙的答案,叫空洞卷积。空洞卷积也叫膨胀卷积,英文是atrous convolution。普通的卷积,卷积核是密密地挨在一起扫过去的。空洞卷积做的事情,是在卷积核的元素之间人为地塞进一些空隙,让卷积核撑开,这样扫过原图的时候一次就能覆盖更大的范围,可卷积核里真正参与运算的参数数量并没有变多。

我们记膨胀率为 dddd 就是卷积核元素之间塞空隙的间距。比方说 d=1d=1 的时候,空洞卷积就退化成普通卷积,元素之间不留空。d=2d=2 的时候,相邻两个元素之间隔一个空位,原本 3×33\times3 的卷积核会撑开覆盖一个 5×55\times5 的区域,九个权重分散在四角、各边中点和中心位置,但参与运算的权重数量仍是九个。这样在不增加参数、也不增加计算量的前提下,感受野就被撑大了。这里参数和计算量要分开讲,参数是网络里要学的权重数量,计算量是跑一次前向要做的运算次数,通常用FLOPs来衡量,FLOPs就是浮点运算次数(floating point operations)的缩写。

光有空洞卷积还不够。一张图里的物体有大有小,路面上可能同时有小石子和大卡车,用同一个膨胀率很难兼顾。DeepLab又设计了ASPP(atrous spatial pyramid pooling,空洞空间金字塔池化),做法是并行地用几个不同的膨胀率去卷积同一份特征,再把结果拼起来。这样网络就能在多个尺度上同时抓物体,小物体用小膨胀率看,大物体用大膨胀率看。我把这种做法比成手里有几把不同焦距的放大镜,看什么尺寸的东西随手换一把,比只有一把强多了。

DeepLab后来又迭代了好几版,DeepLabv2、DeepLabv3、DeepLabv3+一路下来,到了DeepLabv3+还把U-Net那种编码解码结构又借了回来,分割精度一路往上提。说穿了后来分割网络的设计思路,多是空洞卷积加编码解码这两个法宝在组合着用。

5.怎么算分割得好不好:mIoU和像素准确率

模型训完了,得有个客观的指标说它到底做得好不好。分割里最常用的指标叫mIoU。

我们先说IoU(Intersection over Union,交并比)。设某一类别的预测区域和真实区域分别为两个集合,预测集合记作 AA、真实集合记作 BB,那么IoU就是两个集合的交集大小除以并集大小:

IoU=ABAB\text{IoU}=\frac{|A\cap B|}{|A\cup B|}

这里 AB|A\cap B|AABB 交集里的像素个数,AB|A\cup B|AABB 并集里的像素个数。|\cdot| 这个符号表示集合里元素的多少。\cap 是交集符号,\cup 是并集符号。IoU的值在 0011 之间,越接近 11 说明预测和真实重合得越好。

把每一个类别都算一遍IoU,再取平均,就是mIoU(mean IoU,平均IoU)。mIoU是分割任务里几乎人人用的指标,比赛和论文都拿它当主成绩。比方说自动驾驶里找可行驶区域,可行驶区域这一类的IoU算出来要是高,就说明车子认路面认得准。

还有一个简单些的指标叫像素准确率(pixel accuracy),就是全图所有像素里,预测正确的像素所占的比例。这个指标直观是好直观,可它有个毛病,就是会被大类压住。比方一张图里背景占了九成,前景只占一成,那网络哪怕把前景全认错,像素准确率还能凑出九成,看起来挺高其实什么都没分对。mIoU对每一类都平等看待,所以更靠得住。

我们再举两个实在的例子来收尾。一个是医学影像里分割肝脏这种器官,mIoU能到 0.90.9 以上才算能进手术辅助系统那种程度,因为这类任务边界特别要紧,错一点就影响医生判断。还有一个是工业上的表面缺陷检测,比如手机屏幕玻璃上的划痕分割,缺陷区域往往很小,光看像素准确率会被大片完好的玻璃盖过去,所以工程师基本都盯mIoU,再细一些还看每个类别单独的IoU。

到这里,从FCN开创端到端分割,到U-Net用跳跃连接把边界做精,再到DeepLab用空洞卷积和ASPP对付多尺度,分割这一条线的主干就串起来了。这些方法在今天的实例分割、全景分割,乃至更新的SAM(Segment Anything Model,能做通用的零样本分割)里,都能看到影子。说穿了根上的思想是一脉相承的。今天就先到这儿,下一章见。

练习

Q1. FCN 把分类网络改造成像素级预测,关键改了什么?U-Net 又靠什么把边界做精细?

FCN 把分类网络最后那几层全连接层换成卷积层,整张图从头到尾都是卷积,网络就能输入任意大小的图、输出一张和输入大致对应位置的预测图,再靠上采样把缩小的特征图放大回原图大小。U-Net 靠的是跳跃连接:把编码器里还没被压缩太狠的高分辨率特征直接接到解码器对应位置,解码器上采样时把这些细节和刚放大的特征拼到一起,恢复出来的边界就精细得多,所以它在医学图像(边界要求极高)里几乎封神。

Q2. 空洞卷积取膨胀率 d=2d=2,原本 3×33\times3 的卷积核能覆盖多大区域?参数变多了吗?

d=2d=2 时相邻元素间隔一个空位,原本 3×33\times3 的卷积核会撑开覆盖一个 5×55\times5 的区域,九个权重分散在四角、各边中点和中心位置,但参与运算的权重数量仍是九个。所以它在感受野被撑大的同时,参数和计算量都没有变多。DeepLab 还用 ASPP 并行几个不同膨胀率来同时抓多尺度物体。

Q3. 分割里为什么基本都盯 mIoU,而不用像素准确率当主指标?

因为像素准确率会被大类压住。一张图里背景占九成、前景只占一成,网络哪怕把前景全认错,像素准确率还能凑出九成,看着高其实什么都没分对。mIoU 对每一类都平等看待(先算每类的 IoU 再取平均),大类压不住它,所以更靠得住,比赛和论文都拿它当主成绩。像手机屏幕划痕这种缺陷区域很小的任务,尤其得盯 mIoU 或每个类别单独的 IoU。

相关标签
深度学习图像分割U-NetDeepLab