写在前面
上次写完Faster RCNN之后,本来想着开始写YOLO系列。后面想起Faster RCNN还有一个多尺度的问题并没有解决,解决多尺度问题的方法叫FPN,也叫特征金字塔网络。因此将FPN和Mask RCNN作为专题的第二篇文章。
特征金字塔网络FPN
FPN的引出
前面讲完了Faster RCNN,其检测速度比最原始的RCNN提高了许多倍,精度也得到了提高。然而,Faster RCNN并不向传说中的那样完美。我们设想一下这种场景,假如一张图片中同时有老虎和猫两种物体,而老虎和猫体型相差太大。如果网络适应了对老虎类型的大的目标检测,在检测猫这种小物体类别时必然会出现一些不准确的情况,反之亦然。这也符合我们的直观印象。
我们可以先自己思考下如何解决这个问题。
我们以图片的形式来直观地看下CNN中特征的利用形式。
上图展示了4种利用特征的形式。
a 图像金字塔(Featurized image pyramid),是解决多尺度问题的传统方法。这里需要重点讲以下。图像金字塔实现方法也非常简单,我们将图像进行缩放等操作可以得到不同尺度的图片,再将这些不同尺度的图片分别进入Faster RCNN进行训练。
图像金字塔存在一些问题:
(1)运行时间会变为原来的数倍,Faster RCNN好不容易积攒起来的速度优势就这样被抹平了,难以做到实时处理。
(2)训练时,对显卡的要求成倍增加,一般显卡比较难适应这种要求。
(3)单尺度训练,多尺度预测,解决了显卡的问题,但是训练和预测的目标并不一致。
b 单特征图,即仅仅采用最后一层特征的形式,如之前讲过的SSP-Net,Fast RCNN,Faster RCNN都是采用这种形式;
c 多尺度特征融合。典型应用是在SSD中,没有上采样过程。即从网络不同层抽取不同尺度的特征做预测,这种方式不会增加额外的计算量。但是这种方式也有缺点,这种算法将底层的语义信息弱的特征图舍弃了,这些底层特征图虽然位置信息强,但由于特征弱,无法进行分类,因此被舍弃。
d 即我们要讲的特征金字塔网络FPN:featuried pyramid network。顶层特征通过上采样和底层特征做融合,而且每层都是独立预测的,这里不多讲,下面依次展开。
FPN的结构
FPN可分成两部分,一个是自底向上的路径(左边),一个是自顶向下的路径(右边),我们牢记这两个概念。
自底向上的路径:自下而上的路径与卷积网络的前馈计算是一样的。每次卷积之后,特征图有两种变化。第一种特征图变小为原来的1/2(这里统一指的是宽和高,面积实际减小为原来的1/4),第二种是特征图大小不变。可以想象,在自底向上的路径中,一路上存在一些尺寸相同的特征图,且他们位置相邻。我们把这些尺寸相同的特征图统称为金字塔的一个级/阶段(stage)。在每一stage中,我们不可能将每张特征图都拿出来进行利用。那么我们该取哪张图片作为这一stage中特征图的代表呢?我们取出最后一层作为这一stage的代表,因为最后一层相对最深,特征图包含的语义信息也最为丰富。这样,我们分别在每个stage中选出最深的那张代表特征图,便得到了多尺度的特征图作为输入。我们可以观察到,如果FPN只有左边自底向上的部分,那么FPN则退化成了“多尺度特征融合”。
这里我们把目光聚集到最开始选择的哪个大尺寸的特征图,即便我们取这一stage中最深的那层,特征信息依然是较弱的。这些大尺寸特征图由于尺寸大,分辨率强,位置信息强,但是特征较弱。假如我们舍不得放弃利用这些大尺寸底层特征图的位置信息,特征弱的问题是否可以想办法解决呢?有人联想到顶层特征强,可以用顶层特征来加强底层特征。那么该如何利用顶层特征来加强底层特征呢?
答案在自顶向下的过程中。
上图是自顶向下过程和中间横向连接过程的示意图。
自顶向下过程和自底向上过程相反,每次卷积后,特征图也有2种变化。第一种是特征图大小变为原来的2倍,第二种是特征图大小不变。将上采样后的特征图和前一级特征图进行连接(其实就是进行相加),使得底层特征图得到加强。这样我们便可得到特征信息强且位置信息强的特征图了,完美解决。
注意,这里横向连接前,对左边的底层特征图进行了1x1的卷积,这样做的目的是改变底层特征图的channel数,使得其与顶层特征图一致,这样方便特征图进行相加操作(即上面讲的横向连接)。
另一个还需注意的问题是,在底层特征图和上采样完的顶层特征图相加之后,还需进行3x3的卷积来得到最后的特征图。这样做的目的是去除上采样的混叠现象。(这里不懂请百度)。
这里在上一张图片总结一下自顶向下的过程。
FPN实例讲解
下面给大家举个例子。这个是利用ResNet进行卷积的结构图,我们看下自顶向上和自顶向下到底是怎样的一个过程。conv2,conv3,conv4,conv5被用作输出。
这张图大家一拿到可能是懵逼的,这里和大家讲解下。
最左边一列是5个卷积层,第一层输出是112x112,其他层输出可以依此类推。这里在进行“自底向上”的过程中,我们不把conv1作为底层,原因是conv1特征图尺寸过大,会占用较多的内存。我们在第二列也能看出,随着卷积的不断进行,特征图尺寸逐渐减小为原来的一半。
从第二stage开始,我们把每个阶段最后一层的激活输出作为金字塔的特征:{C2,C3,C4,C5}。与此同时,我们进行自顶向下上采样的过程:
C5→1x1→P5→上采样→P5大
C4→1x1→P4(1)+P5大→P4→P4大
C3→1x1→P3(1)+P4大→P3→P3大
C2→1x1→P2(1)+P2大→P2
最后产生了[P2,P3,P4,P5]的多尺度特征图。
FPN与Faster RCNN的融合
Faster RCNN是由RPN和Fast RCNN两部分组成的(参考我第一篇文章)。因此,我们也将FPN与Faster RCNN的融合分为这两部分来讲。
我们先上一张基于ResNet的Faster RCNN结构图(未结合FPN之前)。
这里需要注意的是,在这里我们是将conv4作为RPN和ROI Pooling的特征图,在RoI Pooling之后再接的conv5,这个是和基于FPN的Faster RCNN有所不同的地方。
FPN与RPN的融合
大家还记得RPN是什么嘛?我们在第一篇文章中讲过,RPN是Faster RCNN中用来产生候选框的子网络。
因为要产生多尺度的特征图,目前上图中所示的网络结构明显是不符合要求的。为了实现多尺度的效果,我们把**[P2,P3,P4,P5,P6]**作为RPN的输入,如上图所示。我们这里再来回顾一下RPN。
RPN在Faster RCNN中有9种不同尺度的anchors,分为三种ratio和scale,然而对于多尺度的输入来说,scale的意义不大,因此就只保留了三种ratio。[P2,P3,P4,P5,P6]→{32x32, 64x64, 128x128, 256x256, 512x512}+{1:2,1:1,2:1]→共产生15种不同大小的anchors(5种不同的scale和3种不同的ratio)
我们可以具体看下细节:
如上图所示,P2经过3x3卷积得到256维的特征图,再经过1x1得到结果,再结合anchors得到候选框。P3,P4,P5,P6同理。共经过5次RPN操作,每次操作过程都一样,最后得到26188((256x256+128x128+64x64+32x32+16x16)x3=26188)的结果数目。我们再把合适的结果挑出来作为合适的候选框。
FPN被用于RPN网络中用于生成proposal,原来的RPN网络是以主网络的某个卷积层输出的feature map作为输入,简单来说就是只以这一个尺度的feature map。但是现在要将FPN嵌入在RPN网络中,生成不同尺度的特征并融合作为RPN网络的输入。在每一个scale层,都定义了不同大小·的anchor,对于P2,P3,P4,P5,P6这些层,定义anchor的大小为32x32 ,64 x64, 128x128, 256x256, 512x512,另外每个scale都有3个长宽对比度:1:2,1:1,2:1,因此整个特征金字塔网络中会有15种anchor。
为了让尺度信息更加丰富,对P5进行maxpooling操作,产生P6。因此,RPN的输入为[P2,P3,P4,P5,P6]。
FPN与Fast RCNN的结合
我们先上一张图回顾一下Fast RCNN是什么。
在Faster RCNN中,公共特征图是RPN的一个输入。但是RPN如今已经今非昔比了。RPN的输入已变成多尺度输入,已经不再是一张公共feature map的输入了。因此,原图必定不再映射到一张公共feature map上了,必定要映射到多张feature map上。但是,我们如今有[P2,P3,P4,P5]四个公共Feature Map上,那么哪些候选框应该映射到哪个feature map上比较合适呢?(注意,这里由于工程上的原因,我们把P6给删了,感兴趣的同学可以深究,这里就不多讲了)
我们根据候选框的大小,把他们分配到各个尺度的特征图上,大的候选框应该映射到大的特征图上,假设候选框的大小为w x h,它映射到Pk这个特征图上,k是多少呢?
计算k的公式:
224 x 224是ImagNet预训练的图片大小,所以我们基准候选框的大小设置为224,记为k0,由于Faster RCNN中用的是C4(这点可以参考本文后半部分的Mask RCNN结构)作为ROI Pooling的输入,所以k0=4。
根据这个公式我们举个例子。
假设候选框是w x h = 112 x 112的大小,那么k=k0-1=4-1=3,候选框应该映射到P3这个特征图上,然后再做ROI Pooling。
最后上一张基于FPN的Faster RCNN总览图。
总结一下就是,首先我们有一个ResNet网络,通过自底向下的过程得到[C2-C5],再通过自底向下的过程得到[P2-P6】。
再将P2-P6五个特征图分别输入到RPN中作为候选框,再将所有的候选框和P2-P5作为输入进入到ROI Pooling中去。最后得到分类结果和框的回归。这里需要注意的是,此时的Conv5是在ROI Poling之前的,这是和基于ResNet的Faster RCNN的一点不同的地方。因为之前基于ResNet的Faster RCNN中conv5是在Faster RCNN之后(和前文呼应)。
总结一下,FPN仅仅在尺度上得到优化,对于Faster RCNN的速度没有提升。如果想在速度上进行优化,不建议从FPN入手。
FPN到此结束。
Mask RCNN
Mask RCNN的引出
Mask RCNN是ICCV2017的Best Paper。在Faster RCNN的基础上,增加了Mask分支,实现了目标检测和实例分割的双赢。
Mask,可译为面具的意思,在本文中指“刚好跟物体轮廓对齐的区域”。
Mask RCNN除了可用作目标检测外,还可用于实例分割。
关于实例分割的概念,涉及到计算机视觉第三部分的内容:图像分割。这里也简单和大家做个科普。图像分割可分为语义分割和实例分割。举个例子,下图左边是语义分割,右边是实例分割。
语义分割和实例分割的区别在于前者区分的是“大类”,比如图中的羊,被分为一类。实例分割则分的更细一些,图中每只羊都被区分开来,不同的羊之间颜色不相同。
这里再给大家讲下一个很常见的误区。给一张图,经过分割之后可以得到人的轮廓。如下图所示。
现在请大家思考一个问题:分割问题是回归问题吗?像之前的Faster RCNN中的bounding box regression一样?这是很直观的一种想法。然而在这里要告诉大家的是,这种想法是错误的。原因在于轮廓本身是非常复杂且难以确定的,如果利用回归的方式,本身算法太复杂且非常难以执行。
实际上,分割问题(包括语义分割和实例分割)是分类问题,对每个像素点做分类。可以认为每个像素都有个类别,如上图所有红色区域的像素点都是类别“人”,其他的区域像素点类别为“背景”。
说的再详细一些,我们分割的最后,会得到一张和原图大小相同的scores map分数图,这样我们可以通过将score输入sigmoid函数中,通过训练可强行将背景无限逼近于0,将人作为前景逼近于1。在原图中将1的位置设为红色便可得到上图中的右半部分。
Mask RCNN≈FCN+Faster RCNN
什么是FCN
FCN(Fully Convolutional Net),用来解决语义分割的一种方法。Mask RCNN可以理解为使用Faster RCNN检测出框后,再在框上进行语义分割的。因此,
Mask RCNN≈Faster RCNN+Semantic Segmentation Method trained on instance-level dataset。
Mask RCNN既能进行分割,也能进行检测,准确率都很高。
Mask RCNN与Faster RCNN的异同之处。
如上图所示,Mask RCNN与Faster RCNN的不同之处在于两点。一是Mask RCNN使用了ROIAlign而非Faster RCNN使用的 ROI Pooling。第二点在于Mask RCNN在输出时增加了一个分割的结果。下面会重点基于这两点区别之处给大家讲解Mask RCNN。
什么是RoIAlign?
先给大家介绍两个概念,平移不变(translation-invariant)和平移等价(translation-equivariant)。
平移不变:要求物体如何移动,结果都不变。例如分类Classification。
平移等价:要求物体如何移动,结果跟着如何移动。例如实例分割。
目前来说,卷积Convolution和FCN做到了平移等价。
而全连接(Fully-Connected)和global pooling全局池化是平移不变的。因此,分类最后接的往往是全连接或者全局池化。
我们看下MaskRCNN论文中的这张图。
上图中红色虚线框内两部分是平移等价的,前面一个红色虚线框是卷积,后面的红色虚线框是FCN。
现在,RoI Resize 的平移等价性就至关重要了,因为这个也是平移等价的话,那么整个Mask RCNN就都是平移等价的了,就满足我们的要求了。那么RoI Pooling是否具有平移等价性呢?
RoI Pooling共有两次取整的过程,正是这两次取整过程导致RoI Pooling不具有平移等价性。
问题1:从输入图上的RoI到输出特征图上的RoI Feature,RoI Pooling是直接通过四舍五入得到的结果。
问题2:在将每个RoI对应的特征转化为固定大小的维度时,又采用了取整操作。如上图所示。
这种取整操作(在Mask RCNN中被称为quantization)对ROI分类影响不大,但是对逐像素的预测目标是有害的。原因在于每个RoI 所取得的特征与RoI没有对齐。
为了解决RoI Pooling的问题,Mask RCNN中提出了RoI Align,保证了在RoI Resize前后的平移等价性。
RoI Align的主要创新点在于,针对问题1,不再进行取整操作。针对问题2,提出使用双线性插值(这里需要自行百度)来更精确地找到每块对应的特征。总的来说,RoI Align的作用主要是剔除了RoI Pooling的取整操作,并且使得每个RoI 取得的特征能够更好地对齐原图上的RoI 区域。
下图是RoI Align的原理示意图。
再举一个RoI Align的例子。
上图中,上半部分是RoI Pooling的示意图,下半部分是RoI Align示意图。和RoI Pooling不同的是,由于使用了双线性插值,RoI Align中bin的大小可以是小数(这点只要百度下双线性插值就非常好理解)。
Mask RCNN的结构
这里我们上一张基于ResNet-C4的完整的网络结构图。
上图中与Faster RCNN不同的部分主要在于Mask分支和RoI Align部分。有了结构之后,我们便可以开始进行训练。
Mask RCNN的训练。
训练分成两部分,也即是两阶段(two-stage)。
注意,在Mask Loss中我们使用的损失函数是Sigmoid binary cross-entropy loss作为损失函数,这里我们不禁要问,为何不是“Softmax"呢(像FCN一样)?
针对这个问题,Mask RCNN论文作者做了一个实验,对比两种激活函数,发现Sigmoid比Softmax结果大概好5.5AP(average Precision)。造成这个实验结果的原因在于我们在detection分支中已经知道了物体的类别,所以针对Mask,不用再考虑其他类别,只要分类ground truth的那个mask就行,近似二分类,所以更容易训练。在Mask RCNN的训练环节,输出了K个Mask预测图(为每一个类都输出一张,K是类别数)。当然,在训练Mask RCNN时,输出的K个特征图中,也只是对应ground truth类别的那一个特征图对Mask Loss有贡献。
Mask RCNN训练与预测的不同之处
这里说明一下,为何预测时要先运行Fast RCNN部分之后再预测Mask呢?原因也是为了保持平移等价性。只有先运行FastRCNN得到最准确的框之后再进行mask预测,才会是最准确的,而不是直接利用RPN的框。
下图阐述了Mask RCNN的Mask 分支。
在MaskRCNN的RoI Align之后有一个“head"部分,其主要作用是将RoI Align的输出维度扩大,这样在预测Mask RCNN时会更加精确。
在Mask RCNN中,相较于Faster RCNN还有些略微的调整。例如positive RoI被定义为与ground truth的IOU大于0.5的就行(Faster RCNN中设置的阈值是0.7)。除此之外,
最后总结。Mask RCNN的结构相当复杂,主要创新点就是在于RoI Align对RoI Pooling的改良。从RCNN➡Fast RCNN➡FasterRCNN➡MaskRCNN,每一步都是在上一步的基础上进行创新的。随着深度学习的高速发展,单任务模型已逐渐被抛弃,取而代之的是更集成,更综合,更强大的多任务模型。