目录
- 前言
- 语义分割
- 背景介绍
- FCN核心剖析
- [全卷积(Fully Convolution)](#全卷积(Fully Convolution))
- 反卷积(deconvolution)
- [跳跃连接(Skip Connection)](#跳跃连接(Skip Connection))
- 结语
- 参考资料
前言
本篇文章收录于语义分割专栏,如果对语义分割领域感兴趣的,可以去看看专栏,会对经典的模型以及代码进行详细的讲解哦!其中会包含可复现的代码!带大家深入语义分割的领域,将从原理,代码深入讲解,希望大家能从中有所收获,其中很多内容都包含着自己的一些想法以及理解,如果有错误的地方欢迎大家批评指正。
论文名称:《Fully Convolutional Networks for Semantic Segmentation》
作为语义分割专栏系列的第一篇,本文将带大家来学习语义分割领域的开山之作:FCN。

语义分割
首先还是带大家认识下什么是语义分割。

首先我们先来认识下计算机视觉的一些非常重要的研究方向。其实每一个方向都是在前个方向的基础上提出的更高的要求。首先是图像分类 任务,这个大家还是比较熟悉的,就是判别图像中有什么,然后就是目标检测 了,我们希望不仅能够判别图像中有什么,更希望能够确定他在什么位置。然后就到了语义分割 ,我们对每个像素点进行分类,确定其属于哪一类,比如说还是羊,还是背景之类的,其核心就是对要对每个像素点分类,就是每个像素点都有自己的类别。然后就到了实例分割 ,其实就有点类似语义分割和目标检测的融合,我们在目标检测的基础上进行更加精细的划分,就是只对检测到的物体进行语义分割并且分出不同的实例,每个实例都是不同的,比如说小羊1,小羊2。最后到了全景分割,就是融合了语义分割和实例分割,就是对每一个像素都进行划分,每个像素都有自己的类别ID,每个相同的类别是不同的实例。
当然,其他的就是简要讲一下,重要的还是我们的语义分割了。语义分割是一种像素级别 的分类,就是把图像中的每个像素都进行分类,每个像素都有自己的标签,比如说羊,草地,天空。如下这张图就是比较简单的语义分割了,就是将像素划分为是羊的像素和不是羊的像素,是比较简单的0-1分类了。其实在语义分割任务中,会有比较多的类别的,其语义标注的文件就是掩码文件,单通道的,就是每个像素点所对应的语义标签,举个例子就是可能0是草地,1是羊,2是天空,那么其mask掩码标注文件就是由0,1,2组成的了,关于掩码模式的介绍如果有不了解的欢迎去专栏看看数据集的那一篇,有详细的介绍哦。

背景介绍
首先我们来看FCN出现的历史背景,当时的语义分割任务依赖于手工先验加分类器 ,首先通过提取每个像素点或小快区域的手工特征,然后使用分类器(SVM,随机森林,KNN等)来对每个像素进行分类从而得到一张与原图大小一致的标签图。要么就是滑动窗口加CNN,将图像分成一个个小块,每个小块输入CNN提取特征,预测中心像素的类别从而代表整个小块,然后移动窗口滑动整个图像,拼凑预测结果,形成完整的分割图。
那么如果是你,在当时的背景条件下,你希望改革当前的语义分割领域,你会怎么做?首先我们来想,CNN在当时已经在图像分类以及目标检测领域取得了很大的进展,那么对于同样是图片任务的语义分割,能不能也有相同的作用呢?并且当时语义分割任务总是需要训练多个模型,或者经过多个步骤才能得到最终的预测结果,能不能有一种方法,实现端到端的训练以及预测呢?
如果我们希望使用CNN来进行语义分割任务,那有什么难点,看当时常用的CNN网络就是AlexNet、VGG、GoogleNet,主要都是用于图像分类任务的,其架构都是卷积再卷积,最后经过全连接层变成了一维的分类向量,一维向量似乎无法适应于语义分割这种像素级别的任务。并且卷积的组成都是卷积池化激活,其过程会造成shape的改变,不断的下采样(downsample)会造成图片输入输出的shape不匹配,又怎么进行预测呢?
FCN核心剖析
是的,你遇到这些问题你怎么办?希望大家都能够设身处地去思考,我们才能够明白每一项工作的创新意义,同时进行深入思考,很多时候你可能也会有自己的想法,每个创新性的想法都是不经意间的,希望大家都能够有思维的碰撞。好了,话说回来,我们来看看FCN的作者时怎么做的,就是那些问题,看看人家如何进行解决的。
全卷积(Fully Convolution)
什么是全卷积,顾名思义了,就是全都是由卷积结构组成的网络了,当然了这也是FCN名字的由来了。原本的CNN网络最后都会接上全连接层,导致其输出变成一维向量了,那么为何不去掉全连接层呢?这样输出的不就是二维的了吗,这样输出的Heatmap(热力图)不就能够更加的适合语义分割任务了嘛。

这就是FCN的第一个创新点,舍弃了全连接层,构建了一个仅仅只有卷积的网络。同时因为之前有着全连接层的存在,所以我们的输入图片的像素是固定的,对于VGG来说图像必须是(224x224),因为全连接层本质上是将特征图展平进行矩阵乘法运算。什么意思呢?就是只有当输入的图像为224x224时,经过一系列的卷积池化才能得到特征图大小为 7×7×512,会被展平为一个 25088 维的向量送到全连接层进行处理。现在没有了全连接层,那么我们输入的图片大小也将不会在受到限制。
而更关键的是,全连接层会完全打乱输入特征图的空间结构信息 ,也就是说,在进入全连接层后,网络已无法感知哪些特征来自图像的哪个位置。这种结构虽然适合图像级别的分类任务,但对于需要保留像素空间位置信息的语义分割任务而言是致命缺陷,因为我们需要对每一个像素做出精确的类别判断。所以在FCN中,直接舍弃了所有的全连接层,并且将其替换为了三个卷积层。其中注意的是,最后一个卷积层的输出通道 为我们语义分割任务的类别数,为每个类别输出概率,选取最大的作为该像素类别。
反卷积(deconvolution)
解决了CNN最后输出的一维分类向量的问题,现在我们输出的是二维的,但是新的问题又来了,我们的语义分割任务是像素级别的预测任务,每个所对应的都有个类别,但是经过CNN网络之后,会经历一系列的下采样,其shape大小还是无法匹配原来的图片。那么我们要做的就是经过上采样来恢复图片的shape。
好说到这,我们在来看看当时经常用的上采样的方法。
最近邻插值法
这还是很好理解的,就是在上采样的过程中其值是由最近的进行确认。根据如下公式进行确定:
SrcX=dstX\*Scale_factorX
SrcY=dstY\*Scale_factorY
其中SrcX和SrcY代表原图像的位置,dstX和dstY代表上采样后的图像的位置。其根本就是我们目标位置乘以缩放因子的值就是其在原图像的位置的值。举个例子就知道了,上采样后的图像的(3,1)的值为原图像中的(3x(2/4),1x(2/4))=(1.5,0.5),采用四舍五入的规则,故其值为(2,1)也就是原图像中橙色的位置,直接取橙色的位置的值即可。其上采样的原理计算都非常简单,当然这所带来的后果就是生成的图像是非常粗糙的。

双线性插值
在讲双线性插值前,我们先来讲讲什么叫线性插值。

其实就是有两个点(x_0,y_0)和(x_1,y_1),现在我们需要估计其中位于x_0和x_1中的一个点x的值,我们该如何去预测。其实就是将这两点连成一条直线,然后我们将x看作是其直线上的一点。线性插值的原理就是认为其发展是均匀的,线性的,所以我们可以通过直线去预测。

但是线性插值有个很大的弊端,就是我们只是通过单个维度取预测其值,会存在非常大的误差并且偶然性也非常大。所以双线性插值就来了,我们通过两次插值来进行预测,就是通过多个维度来预测从而减小误差。

其方式如上图所示,我们首先通过在x方向上进行插值得到R1和R2,然后在在y方向上进行插值得到最终的值。公式如下:


讲这么多公式可难以理解,直接举个例子说明就好了,比如说我们上采样的目标图像的像素点位置映射到原图像上的位置为(5.6,8,1),具体怎么映射,就是跟前面所讲的最近邻插值法的映射方法相同,乘以缩放因子。那么我们所取的四个位置的值就是(5,8),(5,9),(6,8),(6,9),然后将这个带入上述所列的公式计算即可。
反卷积
**重点来了,注意!!!**刚才所讲的方法都是上采样中常用到的。现在我们来说FCN中用到的上采样的方式是反卷积。
反卷积是什么?其实用原论文的方式来说也叫做去卷积化。我们都知道卷积操作,多个像素点经过卷积核相乘相加得到一个像素点的值,即多对一的形式。而反卷积就是一个像素点的值经过卷积核相乘相加得到多个像素点的值,即一对多的形式,也可以叫做去卷积。
我们为什么不采用刚才说的上采样的方法,当然就是因为我们通过刚才所述的方法去求,难免无法适应较多较复杂的情况,毕竟我们所使用的方法难以去很好的恢复图像的细节。那么能不能有种方法像卷积那样是能够学习的,学习不同数据的细节,从而很好的进行图像恢复。再看我们通过卷积的方式进行了下采样,那么能不能类似于卷积的方式来进行上采样呢?所以反卷积他来了。

具体实现包含三个步骤:首先在空间维度进行零填充,然后在边缘补零(补零数量为kernel_size-padding-1
),最后使用转置后的卷积核执行常规卷积计算。这种设计不仅保留了卷积的参数共享特性,还能通过端到端训练自动学习最优的上采样方式,从而更有效地恢复特征图的空间细节。
上采样的方式总结如下:
方法 | 原理 | 优点 | 缺点 |
---|---|---|---|
最近邻插值 | 使用最接近的像素值来填充新的像素。对于一个要插入的像素,找到离它最近的输入图像中的像素,并将其值赋给该像素。 | 简单、快速、计算量小 | 生成图像质量较低,可能出现块状效应,图像边缘锯齿严重 |
双线性插值 | 使用周围四个最接近的像素值进行线性插值。对于一个要插入的像素,根据其周围四个像素的距离进行加权平均。 | 比最近邻插值生成的图像质量高,计算量适中 | 计算量比最近邻插值大,在图像边缘和细节处可能不够精确 |
反卷积/转置卷积 | 可以看作是卷积的逆过程。通过学习到的滤波器,将特征图上采样到更高的分辨率。通常用于生成更精细的输出特征图。 | 可以学习上采样过程,生成更精细的图像 | 计算量较大,可能引入伪影,需要精心设计网络结构以避免棋盘效应 |
跳跃连接(Skip Connection)
好了,到了FCN的最后一个核心创新点。
首先我们需要明白一个事情就是,我们的网络在进行特征提取的时候是从低级语义信息不断不断的进行提取到最后的输出的高级语义信息的。网络的低层提取的语义信息更多代表了图像的纹理、边缘等一些显性的信息,网络的高层所提取的一些语义信息更多的就是其数据核心的抽象的语义信息了。那我们最后进行语义分割的特征图的语义信息肯定损失了很多关键的细节、边缘信息了,并且最后还会有上采样的过程,这个现象就会更加加剧。我们想要最后进行语义分割也能够有些这些细节信息怎么办?
这就是跳跃连接了。想要低层语义信息,直接把低层的语义信息加回来不就好了,简单粗暴,但是同样的也非常有效。其实我们仔细看看,是不是也就是残差网络的残差连接的形式,但是注意哈,这个时候是还没有残差网络出现的哦。所以FCN也算是残差结构的前身了。具体如何操作,FCN-8s就是将最深层的conv7特征进行2倍上采样,然后与来自pool4的同分辨率特征相加融合;接着对融合后的特征再次进行2倍上采样,与pool3的特征进行二次融合;最后通过8倍上采样得到最终预测结果。

我们再来看看使用了Skip Connection的效果咋样,还是能够很明显的看出来的,对比另外两个,FCN-8s的语义分割效果与GT还是非常接近的。

结语
希望上列所述内容对你有所帮助,如果有错误的地方欢迎大家批评指正!
并且如果可以的话希望大家能够三连鼓励一下,谢谢大家!
如果你觉得讲的还不错想转载,可以直接转载,不过麻烦指出本文来源出处即可,谢谢!
参考资料
本文参考了下列的文章内容,集百家之长汇聚于此,同时包含自己的思考想法
手把手带你从论文出发实战搭建分割FCN网络_fcn模型建立和训练-CSDN博客
上采样(最近邻插值、双线性插值法、反池化、转置卷积)_最近邻上采样-CSDN博客
FCN -- 语义分割的里程碑之作_fully convolutional networks for semantic segmenta-CSDN博客