FCN

简介

FCN全称是‘Fully Convolutional Networks’,也就是全卷积网络。这个网络去掉了全连接层,网络结构里只有卷积(池化和反卷积)操作。本文的FCN特指这个语义分割网络,而非广义的全卷积网络。

作者在论文里说,这是第一个可以端到端训练、输出像素级预测(pixels-to-pixels)语义分割网络,它可以处理任意大小的图片输入。本网络使用上采样层进行预测,使用降采样池化进行学习(特征)。

相比patch-wise的方法,本网络的效率更高。本网络最终的输出和图片具有相同的分辨率(意味着不需要进行线性差值恢复到原始分辨率),也不需要proposals预处理,最终输出不需要随机场或者局部分类器(local classifiers)进行进一步处理。总的来说,不需要什么预处理和后处理

语义分割试图解决两个具有矛盾的的问题:语义(分类)和定位。全局(语义)信息告诉我们图片里是什么,定位信息告诉我们它在哪。在论文中作者定义了一种‘跳越连接’结构,将深层的、粗糙的、语义的信息,和浅层的、详细的、外观信的息结合起来。

从分类网络改造为密集的FCN

作者测试用到了AlexNet、GoogleNet和VGG,改造的时候去掉最终的输出层,将全连接层改造为等效的卷积层。然后在网络最后面用1x1网络预测一个21通道的特征图(对应21个分类),再进行反卷积恢复到图片原始大小,发现这样的简单改造下,各个网络在语义分割上的表现已经很好了,特别是VGG,几乎是state-of-the-art水准。可以说基本验证了全卷积在语义分割上的威力。后面讲的网络都是在VGG基础上测试的。

pytorch 语义分割onehot 语义分割 fcn_pytorch 语义分割onehot

这时的网络结构基本是这样滴:

pytorch 语义分割onehot 语义分割 fcn_池化_02

注意这里还没加反卷积、跳跃连接等结构,仅仅在backbone最后加了分类预测和双线性差值。

网络结构:把是啥和在哪结合起来

上面的网络最后一步采用32倍上采样。虽然已经得到了不错的表现(可想而知之前这个领域发展有多差),但是最终预测层的32倍上采样限制了预测输出的精细程度。我们通过添加跳跃连接来解决这个问题。更高层次的特征空间信息较少,但是语义信息多,较浅的特征图语义信息较弱,但是空间特征保留较多。

网络结构如下:

pytorch 语义分割onehot 语义分割 fcn_语义分割_03

可以看到pool5的输出就是前面一节的内容。不同层次的输出分别被称为FCN-32s、FCN-16s、FCN-8s。其效果如下图:

pytorch 语义分割onehot 语义分割 fcn_卷积_04


pytorch 语义分割onehot 语义分割 fcn_pytorch 语义分割onehot_05

上图带fixed的输出是固化分类网络部分,只有最后面的基层网络重新训练了,其他输出都是端到端整体fine-tuning的。由于FCN-8s相对FCN-16s的收益已经很小了,所以作者不再试着融合更底层的特征。

从代码角度补充理解FCN

从上面的网络结构看起来,好像和Unet很像呀,怎么效果这么差呢?
看了下这份实现:https://github.com/MarvinTeichmann/tensorflow-fcn/blob/master/fcn8_vgg.py

代码细节

  • 融合方式:做加法
  • 每次上采样后的通道数:21(20个分类加一个背景类)
  • 左侧的poolX融合前怎么处理?
    poolX通道数并不是21,所以会用一个1x1卷积核映射为21通道再和上采样过来的特征图相加
  • 除了上面说的3条,右侧还有额外的卷积核池化吗:没了

我猜测这里有两个问题可能是FCN效果不佳的原因

  1. 融合部分的通道数太窄,为21

我猜测作者可能是为了方便的上采样输出FCN-32s、FCN-16s和FCN-8s来进行比较。如果不需要FCN-32s、FCN-16s,完全可以在前两次融合的时候保留较多的通道。最后融合的时候再输出21通道。Inception V3论文中总结的一个规则就是“要防止出现特征描述的瓶颈”,这个21通道就是瓶颈。所以后面FCN-8s和FCN-16s比较优势不大,作者也没再增加融合层了。
2. 融合前后没有做进一步的卷积操作

右侧上采样和融合操作之后没有额外的卷积变换了,导致右侧几乎不能学习对特征图进行refine,几乎就是干巴巴的上采样。这也是融合部分通道数窄的原因,因为没有额外的变换过程,只能一步到位降到21通道。

总结

FCN有两大明显的优点:一是可以接受任意大小的输入图像,而不用要求所有的训练图像和测试图像具有同样的尺寸。二是更加高效,因为避免了由于使用像素块(patch)而带来的重复存储和计算卷积的问题。

缺点:整体来说FCN的8倍上采样带来的结果还是有点粗糙,而且没有考虑像素之间的关系。

从上面的分析我们也支持,FCN融合部分结果过于简单,没有refine的的卷积部分,导致结果不是很理想。根据SegNet论文对FCN的总结,编码部分参数有134M,解码部分只有0.5M,左右不对称,头重脚轻。

贡献:

  • 将端到端的全卷积网络推广到语义分割中;
  • 使用反卷积层进行上采样;
  • 提出了跳跃连接来改善上采样的粗糙程度。

U-Net

U-Net算是对FCN的改进,应用领域是医学图像处理。作者在FCN基础上做了很多改进。

整体结构如下:

pytorch 语义分割onehot 语义分割 fcn_pytorch 语义分割onehot_06

网络结构整体很简介,标注也很清楚。作者把左侧降采样的部分称为“收缩分支”,右侧上采样的部分称为“扩张分支”。

特征融合方式

FCN做特征融合的方法是做加法,这里是做拼接。

扩张分支上每一层特征图都比收缩分支小

由于网络结构中的所有卷积都没有增加任何padding,所以右侧同一级的特征图比左边小,所以融合的时候要进行crop裁剪之后才能拼接。最终预测出的分割图比输入图片小一定尺寸。按照上面的架构图,长宽各少180。

使用镜像对称扩展边缘的图像

上一条说了,U-Net输出的图像比输入小一个固定的尺寸。那么为了获得图像完整的分割图,需要在边缘增加部分图像。如下图,黄色方框 范围的输出需要蓝色方框那么大的输入。多出来的图片怎么来的呢?作者的方法就是在边缘取图像的镜像图片拼接上去。仔细看可以看出图像是相对白线对称的。这样做的好处还包括,对于多大的输入图片都可以自然而然的适应。

pytorch 语义分割onehot 语义分割 fcn_编码器_07

扩张分支的特征图更宽

本文上面说了,FCN在扩展部分的特征图通道太少。这里做了改进,扩张分支和左则收缩分支通道是对称的,这样就可以把更多的上下文信息传递到更高分辨率的层。

扩张部分还有额外的卷积

扩张部分,特征融合之后,还有两层卷积。这样两个路径的特征可以融合的更彻底。

计算LOSS时相邻的单元间的背景使用更高权重

相邻对象的分离是一个很大的挑战,很容易将它们分为同一个物体。为此作者在计算LOSS的时候,为相邻单元格之间的空隙赋予更高的权重,让网络重点学习区分相邻的物体。

下图C中黑色的是背景,作者为两个单元格中间的部分赋予了更高的权重,权重的分别如d所示。

pytorch 语义分割onehot 语义分割 fcn_池化_08

数据增广

本论文的模型是用于参加医学图像分割比赛的,在医学领域的数据集都很小,所以数据增广很有必要。

数据增广除了增加训练样本数,另一方面也是模拟实际的情况,因为细胞这些东西本来就是会平移、旋转、变形的。

测试结果

ISBI 2015挑战赛中以较大的优势赢了。领先第二名非常多。

pytorch 语义分割onehot 语义分割 fcn_语义分割_09

总结

U-Net基本解决了FCN的缺点,可以支持灵活的大小输入(最小尺寸有限制,最大没有),并且能在小数据集上取得不错的效果。

SegNet

我看arxiv.org上这篇论文是2016年10月份的,挺早了。本篇论文提出了一种基于“编码器-解码器”结构的语义分割网络,可以认为是对FCN的某种改进。这是我第一次看到“编码器-解码器”这种叫法,但是这种叫法也适用于这篇论文之前的FCN、U-Net等网络。要论论文配图,U-Net论文的配图更能体现“编码器-解码器”结构的精髓。

SegNet并不是为了创造一个大而全的语义分割网络,而是追求内存、推理时间和效果的平衡。作者希望这个网络适用于马路场景的语义分割,所以内存、推理时间不可忽略。本网络的平衡体现在作者用编码器阶段的池化索引给解码器做上采样。所以本网络效果我觉得一般,但是思路值得借鉴。

FCN的不足

SegNet主要对标的就是FCN,所以对FCN的缺点做了较全面的分析:

  • 头重脚轻:编码器参数量和计算量远大于解码器,参数量134M-0.5M,差距明显。
  • 分阶段训练:参数太多,难于训练,作者训练时分了多个阶段来训练不同层次的解码器
  • 占用内存较多:解码器需要复用编码器的特征,所以特征在被解码器使用完毕之前,这些特征要呆在内存里不能释放,占用大量内存
  • 高层特征未复用:由于只融合了三层收益就收敛了,没有使用更高层次的特征进行融合,边缘信息丢失严重

网络结构

本网络神似U-Net,但是没有画成U形的,而是画成一个哑铃的形状。整体结构就是一半编码一半解码,符合“编码器-解码器”风格。

本网络基本改进了上面分析出的FCN的缺点。

pytorch 语义分割onehot 语义分割 fcn_编码器_10

改进头重脚轻的问题

可以看到,网络的编解码部分基本是对称的,这样就不存在头重脚轻的问题了,编码和解码的能力基本对等了。

改进分阶段训练的问题

SegNet使用VGG16的13个卷积层作为编码器,去掉了全连接层,参数从134M降到14.7M。解码器部分最后使用softmax对每个像素做分类,输出原始图像尺寸的分割图。

参数量降低之后网络更易于训练,可以端到端训练整个网络。

改进内存占用较多的问题

占用内存较多是因为需要复用编码阶段的特征。为此作者使用编码阶段的最大池化索引来上采样,而不是把编码阶段的特征拿过来使用(注意:编码阶段的特征没有给解码器用,用的是池化索引)。每个2x2的池化窗口可以用2个bit来记录池化索引,大大减少了内存的占用。和FCN的对比如下:

pytorch 语义分割onehot 语义分割 fcn_语义分割_11

所谓池化索引,就是最大池化降采样的时候最大值所在的位置,解码的时候使用用这个索引直接上采样,其他位置补0。

使用最大池化索引的好处有二:首先是改善了对边界的刻画(我感觉是和没有融合比),其次是减少了参数(这可能才是真正的好处),不需要学习上采样卷积核。

改进高层特征未复用的问题

高分辨率的特征记录了更多细节,但是占用内存。作者使用最大池化索引来恢复空间细节,解决了内存占用的问题。

基于对称的结构,每一阶段的特征记录的信息都得到了有效利用。

一些对比结论

作者在论文里对FCN和SegNet做了一些魔改,做了很多对比测试。这里写下结论:

  1. decode时复用encoder的特征效果更好(比使用索引好)
  2. 当推理阶段模型内存受到制约时,池化索引可以在占用很少内存的情况下改善分割结果
  3. 解码时,更大的卷积核表现更好

其它

作者讨论了mIOU的局限性,认为这个指标挑选的不是人类感觉上的最好效果。作者在实验中输出了F1值作为指标之一。

测试效果

其实作者魔改的FCN很容易就超过SegNet,作者主要验证了自己的改进思路是有效的。看作者挑的图片,效果还不错。

pytorch 语义分割onehot 语义分割 fcn_卷积_12

SegMet小结

SegNet结果不算出彩,但是一个比较工程向、比较实用的网络。在效率、内存占用和效果之间实现了不错的平衡。