FCN阅读心得(上)

        最近由于专业课上要学生上台将自己读过的论文,便重新拜读了一下语义分割的开山之作,发现经过三个月的学习,自己竟能读懂这篇当时看来晦涩难懂的文章,结合自己读论文的见解以及大神们的博客来详细解释一下这篇论文,希望自己的一些见解能够帮助大家更好的理解这篇文章。

        首先我们知道这篇文章它是用来解决语义分割问题的,那么什么是语义分割呢?语义分割要是要解决分类问题,只不过它的这个分类问题相比于目标检测等分类问题要精细得多,像人脸识别、目标检测等分类问题它的分类对象是整张图片或者是图片中的某个区域,而语义分割它的分类对象是像素,这样一来,经过语义分割处理后的图片各分类实例之间会有精密的边界,好像就是将实例从图片中仔细地割出来一样,分割一词大概就是这样来的吧。

                       

        语义分割就是将像素进行分类,既然谈到分类,我们可以回顾一下图像处理领域中的分类神器CNN,CNN是采用“局部感受野”和“参数共享”这两个策略从bp神经网络中改变而来,使得我们可以利用神经网络来处理图像而不用担心图像中繁多的像素点带来的参数过多的问题,我们知道神经网络之所以能够有十分好的分类效果,得益于它的多层结构,它可以将原始特征逐层进行抽象,获得带有更多信息量的高级特征,利用这些特征可以轻易地进行分类,CNN也是一样,浅层的特征具有较小的感受野,所以它包含的信息量较少,深层的特征是以上一层特征为基础进行抽象,所以深层的特征具有较大的感受野,其包含的信息也比较多,越是深层的特征就越能用来分类。

        为了更好地比较图片分类和像素分类的区别,我们先总结一下图片分类的大致流程:

图一

        上图便是利用CNN对整张图片进行分类的例子,输入为整张图片,经过conv和pooling操作,feature map(就是经过抽象后的特征图)的大小逐层减小,但是它们的通道数会增加,经过若干抽象之后,便可利用所得到的高级特征进行分类了,经典的CNN网络的最后一般都是将feature map拉直成一个向量,利用原始的全连接网络进行最后的降维,因为最后的feature map中的特征数量相比于原始图片已经变得很少了,此时进行全连接时就不会有参数太多无法实现的问题,具体做法便是在后面添加三个全连接层,每一层的特征数量分别是4096、4096和1000,最后输出一个1000维(假定共有1000类,若只将原始对象分为两类,最后一层可改为2维)的向量,每一维上的值不再称为特征,此时我们将其称之为属于该维度对应class的分数score,我们再通过softmax函数处理这些score,便可获取1000个概率值,表示属于相应类别的概率,利用这些概率值进行loss计算反向传播,在测试的时候,选取概率值最大的那一类为预测结果。

        上述便是经典的CNN神经网络对图片分类进行处理的大概流程,文章中提到的比较厉害的CNN网络有AlexNetVGGGoogLeNet ,当我们需要做图片级别的分类任务时,可以直接在这些网络的基础上进行少许修改,并加载其预训练模型,再在本地数据集上进行微调,便可达到不错的效果。但是对于像素级别的分类来说,对于这些经典的CNN网络的应用就显得有些棘手,因为这些经典的CNN网络是通过对原始特征的层层抽象得到高级特征来进行分类的,对于整张图像,他的原始特征就是众多的像素,有很大的抽象空间,而对于一个像素来说,他是最基本的特征,一个RGB像素就只包含三个灰度值,基本上没有什么抽象空间,而且像素分类过程中还会碰到一种尴尬的境地(如图二所示),比如我们要对下图中的像素进行前背景分类,下图中车为前景对象,其他的像素都为背景,我们可以看到,背景和前景的主要色调都是黄色,这样很可能存在两个像素,它们的像素值相同,但它们确实是属于不同的类别,就比如图中红点位置的像素值和绿点位置的像素值相同,但是绿点位置的像素是前景像素,而红点位置的像素是背景像素,这就表明即使我们强行对单个像素特征进行抽象然后分类,也还是不能得到理想的分类效果,因为它是最低级的特征,通过像素值本身特征是不能将其区分开。(写到这里感觉对分类有了一个重新的认识,感觉分类的难易程度是跟分类对象的级别有关系,越是高级的对象越容易通过其本身的特征进行分类,越是低级的对象根据其本身的特征是越不容易分类的。)由此可知对于像素这种低级别的对象,仅仅通过其本身的特征不能将其分类,需要结合它的上下文信息进行分类,这也是我们人类进行分类的方法,我们可以很容易将下图中两个位置的像素进行分类,因为我们知道红点位置像素就是叶子的像素,而绿点位置的像素是车的像素,通过这种认识很容易将其进行分类,也就是我们对像素分类只要让分类网络认识到这个像素是属于什么的像素,便可实现对像素的分类,所以我们分类网络的输入便不是单个像素,而是要分类像素周围若干像素组合成的像素块,对下图的两个像素进行分类,分别将框中的像素块送入网络便可(如图三所示)。

图二
图三

        所以有一种传统的将CNN网络运用在像素分类中的方法便是选取待分类像素周围的若干像素组成像素块送入分类网络,以滑动窗口的形式扫描图片上的所有像素,这种方式一看就十分暴力,不能体现出CNN网络的优雅性,而且这种分类方式还有一下三种缺点:

1、存储开销很大 例如对每个像素使用的图像块的大小为15x15,然后不断滑动窗口,每次滑动的窗口给CNN进行判别分类,因此则所需的存储空间根据滑动窗口的次数和大小急剧上升。

2、计算效率低下。相邻的像素块基本上是重复的,针对每个像素块逐个计算卷积,这种计算也有很大程度上的重复。

3、像素块大小的限制了感知区域的大小。通常像素块的大小比整幅图像的大小小很多,只能提取一些局部的特征,从而导致分类的性能受到限制。

        三种上述缺点使得传统的CNN应用于像素分类的方法显得可行性不高,而且此方法不容易提高分类精度,主要是输入的像素块太小,不能充分地利用CNN地层层抽象的优势,得到的特征也不具有很好的分类性能,而且这种方式不能利用经典的CNN网络如AlexNet、VGG、GoogLeNet等,因为输入太小,需要重新设计小型的CNN网络,这样又得重新训练(因为不能利用预训练模型),耗费时间长,而且还达不到很好的效果。

        鉴于上述缺点,本篇论文的作者想出了一条妙计,可有效规避上述缺点而优雅地将经典CNN网络运用到像素分类上来,从此开辟了CNN在语义分割上的一片疆土。这条妙计便是将串行扫描改成并行扫描(我自己认为是这样的,论文中并没有这样说),传统的方法是每取一个像素块,将其通过一次CNN网络,结合滑动窗口我们可以理解成串行扫描图片中的所有像素所对应的像素块。而FCN是运用经典CNN网络直接将整张图像输入到CNN网络中,由于层层抽象的进行,feature map的尺寸会越来越小,对靠后层的feature map分类后在放大到原始图像尺寸,最后得到score map和原始图像的像素一一对应,通过与label map的对比进行训练(如下图所示)。

图四

       那为什么说FCN的做法是并行扫描呢?按照FCN的做法,在原始图片中提取到一个像素块后,并没有急着将其送入到CNN网络中,而是等所有像素块都提取到之后再统一送入CNN网络,CNN网络中对这些像素块的处理并不是独立的,还对这些像素块进行了一定程度的融合,使得各个像素块之间还有一定的联系(根据卷积操作我们可以很容易想到),所以这样做后传统方法中的感受野太小的问题就可以解决了,感受野一旦变大,分类的准确性便有了保障(因为特征很高级),但是FCN这么做之后便有了一个问题,就是由于CNN网络中存在pooling层,它会使feature map一层层地变小,最后分类后得到的score map尺寸比原图要小很多,并不能对应于原图的像素整体分类结果,这是作者就采取某种措施放大score map(上采样)到原始图像的尺寸。这样我们就能看到FCN便是近似地将传统的多次串行扫描同时进行(因为使用了上采样,所以是近似的),可看作是并行的。

       FCN这种并行扫描的方式相对于传统的方法能带来很多优势:

  1. FCN能直接利用经典的CNN网络模型,在经典网络模型后添加自己的上采样层,训练时可直接利用预训练模型再在本地的数据集上进行微调,既能节省时间又能达到很好的效果。
  2. 这是一个端对端(end to end)的网络,搭建好网络后我们只关心input和output,而不用管中间的过程,十分方便,不像目标检测,需要费劲心思去挑选候选区域。
  3. pixel to pixel:像素对像素,之前我们已经讨论过,单独利用像素本身的特征是无法将其进行分类的,因为会存在两个像素的像素值一样但是属于不同类别的情况,所以直接从像素到像素的角度去分类是不可行的,必须用包含该像素的像素块对像素进行分类,但是我们看到这种分类方式需要很大的存储开销,而本文另辟蹊径,从其他途径达到了像素对像素的分类效果,有效地解决了存储开销大的问题,而且不会出现之前的不同类别相同像素不可分的情况,可以说是十分巧妙了。
  4. 可以输入任意大小的图片,这是由于作者将全连接层改成了全卷积层(下文会详细叙述)的优势,为什么改成卷积之后就可以输入任意尺寸大小的图片了呢?之前的有全连接层的CNN网络是不能输入任意大小的图片的,如图一的CNN模型所示,卷积后接上一个输出向量为4096维的全连接层的话,输出维度就已经固定了(是4096),那么权重矩阵的大小也就已经固定了,为4096*n,这个n便是将最后一层卷积层输出的feature map拉直成向量的维数n,可以看到最后一层feature map的通道数是256,假定它的尺寸是7*7,则n=7*7*256,那么4096固定了的话,这个n也就固定了,进而feature map的尺寸就不能变,所以输入的图片尺寸大小就不能便改变。但是当我们将全连接层改成卷积层的时候,后一层的维度也就是通道数只和卷积核的数量有关,跟feature map的尺寸没有关系,所以就可以输入任意尺寸的图片了。

        现在我们来分析一下作者是如何将传统的串行扫面改成并行扫描的:

       假设原始图像的大小是384*384,传统的分类方式是一个像素对应一个15*15的像素块(该像素位于像素块中间),则传统的串行扫描是以15*15为浮窗,步长为1去扫描图像,每扫描一次就将扫描到的15*15像素块送入小型CNN网络,最终得到一个1000维(假设分为1000类)的向量,由这个向量中的1000个score来判断该像素属于哪一类(显然是得分最高的那一类),那么如果将这个过程并行化就是:将扫描到的所有15*15像素块送入CNN网络,最终得到384*384个1000维的向量,384*384便是像素的个数,每个像素都有一个1000维的向量,而且每个像素的1000维向量所在的位置对应于像素在原始图片中的位置。这种简单的并行化各个15*15的像素块的1000维向量的提取是独立的,也就是我们的CNN网络只看到15*15的区域,感受野就比较小了,小的感受野会对分类的性能有很大的影响,所以FCN的并行化需要将所有的15*15的像素块之间建立联系,从而扩大感受野提高分类性能,这时我们的目的和将整张图像送入CNN网络所达到的效果一致,因为CNN网络中后面一层的输入就是其前一层的输出,所以随着层的深入,该层所看到对应原图的感受野会不断增加,所以将整张图片送入CNN网络就相当是传统串行扫描3*3(经典CNN网络的卷积核大多是3*3)的像素块的并行化,之所以不用15*15是因为CNN网络已经拥有其他增加感受野的方法了,用3*3的就够了。但是我们知道,CNN增加感受野的方式就是pooling,pooling在增加感受野的同时也丢失了部分特征信息,使得随着感受野的增加,feature map的尺寸也就越来越小,所以将整张图片送入到CNN网络中最终可能得到的是(384/32)*(384/32)个1000维向量(32是总体的下采样因子),而不是384*384个1000维向量,预测出的图像和原图的大小不匹配,就不能通过于label map比较计算loss值训练参数,但是可以通过上采样的方式将预测图像放大到原始图像,上采样的问题我们放到后面讨论。现在我们着重讨论它这个12*12(12=384/32)个1000维向量是怎么来的,我们知道12*12是CNN并行化的结果,可以看成每个特征在原图中对应的感受野区域进行并行化扫描的结果,比如12*12个特征每个特征对应到原图的感受野是227*227,FCN并行化就相当于以227*227大小为浮窗,以16为步长在原图中扫描,每扫描一次便得到13*13的feature map(记为Fi),一共能扫描到12*12个Fi((384-227)/16+1=12 上取整),然后我们对于每一个Fi,在后面接三个全连接层变成1000维的向量,便有了12*12个1000维的向量,所有Fi的1000维向量对应Fi的感受野在原始图像中的大致位置组合起来,就形成了1000维的向量束,而这种向量束的形成在编程实现起来比较麻烦,因为这1000维的向量是通过连接3个全连接层得到的,feature map从全连接层过渡是直接将整个feature map由矩阵展开成向量,再进行全连接,若单独针对227*227大小的输入,最终可将13*13的feature map全部展开成向量作为全连接层的输入,但是当我们输入整个图像后,缩小16倍后便是24*24的feature map,要通过这个feature map得到12*12的1000维向量束,我们就不能将这24*24的feature map全部展开接上全连接层(因为这样做只会输出1个1000维向量),而是以13*13的滑动窗口扫描,每扫描一次就展开接上全连接层,这样就会有12*12的1000维向量束,但是这样做会很麻烦,在这里作者就采用了全卷积的方式代替了全连接,因为我们可以注意到全连接和全卷积是等价的,全连接可以转化为全卷积(我会在另一篇博客中举个简答的例子来说明),全卷积代替全连接的方式便是用若干(比如4096个)大小等同于feature map尺寸的卷积核去卷积feature map,就会得到卷积核个数(比如4096)个1*1的feature map,最终得到的结果就和全连接的一样,具体到上面的例子,我们要得到13*13的feature map Fi的1000维向量,之前的方法就要连三个全连接层,而现在我们的做法是连三个全卷积层,第一层是4096个13*13的卷积核去卷积13*13的feature map,得到1*1*4096的矩阵,第二层是用4096个1*1的卷积核去卷积前一层的1*1的feature map,得到1*1*4096的矩阵,第三层便是用1000个1*1的卷积核去卷积第二层输出的1*1的feature map得到1*1*1000的矩阵,这个1*1*1000的矩阵就相当于1000维的向量,这就是单独对一个13*13 feature map处理的方法,若将其应用到全图卷积的话,输入384*384的图像,等缩小16倍后得到24*24的feature map,上面我们说到要以13*13的滑动窗口扫描,没扫描一次就连接3个全连接层才会得到12*12的1000维向量束,有了全卷积之后,我们就直接在24*24的feature map上依次进行13*13*4096、1*1*4096、1*1*1000的卷积,依然可以得到12*12的1000维向量束,也就是12*12*1000的矩阵,至此FCN若何进行并行化的方式便讲解清楚了,下面便开始讲FCN是如何上采样将score map恢复到原始图片的大小。

       之前我们了解到,FCN就是采用融合并行的方式来进行整个图片中的像素分类,相比于传统的串行方式,不仅减少可存储开销,还增大了感受野,提高分类性能,但是增大感受野是有代价的,虽然通过pooling舍弃部分特征来增大感受野获取较高的分类性能,但是你却因此丢失了空间信息,虽然能高效地分类,但是不能精准地把分类结果对应到原始图像地每个像素上,所以作者不得已采用上采样来尽量弥补损失的空间信息。那么作者是如何上采样地呢?且听下回分解!

        

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值