论文名称:《 R-FCN:object detection via region-based fully convolutional networks 》
论文代码:https://github.com/daijifeng001/r-fcn
一、概述:
1、R-FCN创新点:
R-FCN要解决的根本问题是Faster R-CNN检测速度慢的问题。Faster R-CNN速度慢是因为ROI层后的结构对不同的proposal是不共享的,试想下如果有300个proposal,ROI后的全连接网络就要计算300次,耗时惊人。所以本文作者把ROI后的结构往前挪来提升速度,但光是挪动下还不行,ROI在conv5后会引起上节提到的平移可变性问题,必须通过其他方法加强结构的平移可变性,Position-sensitive score map因此而生。
归纳如下:
(1)提出Position-sensitive score maps来解决目标检测的位置敏感性问题;
(2)充分利用全卷积网络来减少总体计算量,提升速度比Faster-RCNN快2.5-20倍;
2、R-FCN与传统二阶段网络的对比:
R-FCN中,所有能共享的层都在ROI Pooling之前做好了,因此在ROI Pooling后基本不会有太多的重复计算。为了要在ROI Pooling之前实现层共享,一方面将conv5_x的计算移到Pooling层之前,但这样依然还存在一些全连接层的重复计算,因此再引入position-sensitive score map和position-sensitive ROI Pooling,使得经过Pooling后简单地执行一些操作就能得到回归和分类结果,而不再像Faster RCNN一样用几个全连接层去得到结果。
相同点:
(1)均为two-stage的目标检测框架;
(2)最终输出的结果均为相应的类别和对应的BB;
不同点:
(1)和Faster R-CNN相比,Faster R-CNN的共享卷积子网络是91层,RoI-wise子网络是10层,而R-FCN只有共享卷积子网络,深度为101层;R-FCN具有更深的共享卷积网络层,这样可以获得更加抽象的特征;
(2)和Faster R-CNN相比,没有RoI-wise subnetwork,不像Faster R-CNN的feature map左右都有对应的网络层,它是真正的全卷积网络架构;
(3)和R-CNN相比,最大的不同就是直接获得整幅图像的feature map,再提取对应的ROI,而不是直接在不同的ROI上面获得相应的feature map。
3、要解决的问题(提出 Position-sensitive score maps):
分类网络的位置不敏感性和检测网络的位置敏感性的一个矛盾问题,而在目标检测中不仅要分类也要定位。
(1)类网络的位置不敏感性(translation-invariance),即平移不变性:
平移不变性则是针对分类任务的。对于分类任务,随着某个目标在图片中不断的移动,希望网络仍然可以准确的将目标区分为对应的类别。如上图左边所示,不管鸟在图片中如何移动,分类网络应将其准确分类为鸟。实验表明,深的全卷积网络能够具备这个特性,如ResNet-101等。举个例子,在用基础的分类结构比如ResNet、Inception给一只猫分类时,无论猫怎么扭曲、平移,最终识别出来的都是猫,输入怎么变形输出都不变这就是平移不变性,网络的层次越深这个特性会越明显。
(2)检测网络的位置敏感性(translation-variance),即平移可变性:
平移可变性则是针对目标检测的。对于检测任务,随着某个目标的移动,希望网络的预测框能够和它一起移动,即仍然能够准确的检测到它,即网络对目标位置的移动很敏感。更改预测框位置需要计算对应的偏差值,以及预测bbbox和GT的重合率等。深的全卷积网路不具备这样的特征。举个例子,一只猫从图片左侧移到了右侧,检测出的猫的坐标会发生变化就称为平移可变性。当卷积网络变深后最后一层卷积输出的feature map变小,物体在输入上的小偏移,经过N多层pooling后在最后的小feature map上会感知不到,这就是所谓“网络变深,平移可变性变差”。
举个例子,以ResNet-101为主干的Faster R-CNN 网络。如果在Faster R-CNN中没有ROI池化层把RPN生成的Proposal映射到原特征图中缩小预测范围,直接对整个feature map进行分类和位置的回归,由于ResNet的结构较深,平移可变性较差,检测出来的坐标会极度不准确。ROI就是Region proposal。
如果在ResNet卷积层中间加个ROI层结果就不一样了,ROI层提取出的proposal中,有的对应目标类别label,有的对应背景label,proposal位置的偏移就有可能造成其label分类(前景和背景分类)的不同。偏移后原来的前景label很有可能变成了背景label,原来的背景很有可能变成了前景,对携带label信息的proposal进行学习和分类,则分类loss对proposal的位置是敏感的,这种情况ROI层给深层网络带来了平移可变性。
如果把ROI加到ResNet的最后一层,前面输出的feature map太小,这时proposal的一个小偏移在conv5输出上很有可能都感知不到,即proposal对应的label没有改变,ROI把proposal对应到原特征图上的类别不准确,所以虽然有ROI也对平移可变性没有什么帮助,以此进行训练学习,识别出来的位置准确度会很差。
二、核心操作:
文中的ROI就是region proposal。另外Faster RCNN中的ROI Pooling和本文的ROI Pooling不是一个意思,前者只是简单将每个region feature变换到统一的尺寸的feature,变换过程中采用Max pooling;而后者则是一种position-sensitive的ROI Pooling。
1、Position-sensitive score map:
ResNet-101应用到R-FCN,把最后的average pool和1000-d fc全连接层都去掉,只保留前100层。后面新加一个1x1x1024的卷积层用来降维(从2048降到1024),再加一个特殊卷积来生成k*k * (C+1)维的Position-sensitive score map_1。其中的C是要分类的类别数,加上1表示加上一个背景分类;k*k是之后的ROI Pooling中对ROI(region proposal)区域要划分的小格数,比如论文中k=3就是对ROI在长宽方向各三等分形成9个小区域。
Position-sensitive score map的值对ROI相对于原图的位置移动很敏感。图中最后一个特殊卷积输出Position-sensitive score map_后,就要对其做ROI Pooling了,和Faster R-CNN中的ROI Pooling一样,要对score map_1上某个ROI对应部分9个小区域分别进行pooling,每个小区域只会在对应的(C+1)个维度上作pooling,得到1*1*(C+1)的向量,即原来的ROI的一个面pool成了一个点,比如ROI左上角的区域就在前C+1个维度上pooling,左中位置的区域就在C+2到2C+2间的维度上作pooling,以此类推。pooling后输出的是C+1维度的k*k个列向量,每个维度(对应一个类别)上的k*k个数据(对应这k*k部分是否都含有目标的一部分)再加到一起(图2的vote过程)形成C+1个单点数据,及对应类别的类别概率。对于目标定位的输出和上面的分类输出过程类似,只是维度不再是k*k * (C+1),而是k*k*4,表示9个小区域的[dx,dy,dw,dh]4个偏移坐标。
除了主网络ResNet以外,还有RPN网络用于生成ROI(region proposal),因此在训练的时候,作者采用RPN网络和R-FCN交替训练的方式来共享特征。
这部分比较抽象,但是是本文精髓,下面再详细分析一下原理:
若一个RoI中含有一个类别C的物体,我们将该RoI划分为K*K 个区域,其分别表示该物体的各个部位,比如假设该RoI中含有的目标是人,K=3,那么就将“人”划分成了9个子区域,top-center区域毫无疑问应该是人的头部,而bottom-center应该是人的脚部,我们将RoI划分为K*K个子区域是希望这个RoI在其中的每一个子区域都应该含有该类别C的物体的各个部位,即如果是人,那么RoI的top-center区域就应该含有人的头部。当所有的子区域都含有各自对应的该物体的相应部位后,那么分类器才会将该RoI判断为该类别。也就是说物体的各个部位和RoI的这些子区域是“一一映射”的对应关系。一个RoI必须是K*K个子区域都含有该物体的相应部位,我们才能判断该RoI属于该物体,如果该物体的很多部位都没有出现在相应的子区域中,那么就该RoI判断为背景类别。这就是为啥要在最后把一个类别的k*k个概率加起来,就是要算k*k个位置的综合得分情况。
position-sensitive score map设计的核心思想就是如何让其能够反映一个RoI的 K*K个子区域都含有相应部位。R-FCN会在共享卷积层的最后一层网络上接上一个卷积层,而该卷积层的输出位置敏感得分图position-sensitive score map,该score map的含义如下所述,首先它就是一层卷积层的输出,这个卷积层卷积核的height和width和前面共享卷积层的一样(即具有同样的感受野),但是它的通道个数(卷积核个数)为K*K*(C+1) 。其中C表示物体类别种数,再加上1个背景类别,所以共有(C+1)类,而每个类别都有 K*K个score map_2。卷积层使得ROI的通道数变为K*K*(C+1)。 而后对ROI三维张量进行pool,变为1*1*(k*k*(C+1))一列向量。对这列向量reshape即可得到 k*k*(C+1)的score map_2三维张量。每一和类别对应的K*K个值,对应其在原图上的ROI的k*k个位置包含该类目标的可能性。
只针对其中的一个类别来进行说明,假设我们的目标属于人这个类别,那么其有 K*K 个score maps,每一个score map表示原始图像中的对应位置含有人的某个部位的概率,该score map会在真实含有对应的人体的某个部位的位置有高的响应值,也就是说每一个score map都是用来描述人体的其中一个部位出现在该score map的何处,而在出现的地方就有高响应值。既然是这样,那么我们只要将RoI的各个子区域对应到属于人的每一个score map(一个1*1*(C+1)的列向量的一个数)上然后获取它对不同类别(C+1)的响应值就可以判断这个位置含有每类物体的某个部位的相对置信程度。但是要注意的是,由于一个score map都是只属于一个类别的一个部位的,所以RoI的第 i个子区域一定要到k*k中的第i张score map上去寻找对应区域的响应值,因为RoI的第i个子区域需要的部位和第i张score map关注的部位是对应的,这种对应关系就是我们要让网络去学习的东西。那么现在该RoI的K*K个子区域都已经分别在属于人的K*K个score maps上找到其响应值了,那么如果这些响应值都很高,那么就证明该RoI是人。我们只是在属于人的 K*K个score maps上找响应值,还没有到属于其它类别的score maps(K*K)上找响应值,万一该RoI的各个子区域在属于其它类别的上的score maps的响应值也很高,那么该RoI就也有可能属于其它类别。这就会涉及到一个比较的问题,哪个类别score maps(K*K)的综合响应值高,我就将它判断为哪一类目标。
PS:Position-sensitive score map为什么会带来平移可变性???
Position-sensitive score map的概念最早来自另一篇实例分割的论文《Instance-sensitive Fully Convolutional Networks 》上图是示意图,中间的9张图对应Position-sensitive score map的9个维度的输出。拿左上角的图说明:它的每一个点反映该点正好出现在目标左上角的概率(更准确的说应该是得分,因为还没做softmax),也可以理解是该点右下方正好是目标的概率,也就是前面说的这个物体的左上部分落在左上角的小框里。要注意的是:“目标左上角的概率”的概念并不局限于图中画的绿色框范围,而是整张图上的每一个点,这是新学习者很容易引起误解的地方。同理其余8张图各自对应了目标正上侧、右上侧、左中侧、正中侧、右中侧、左下侧、正下侧、右下侧的概率。在训练时,一个ROI的9个小区域从每张图的对应区域去Pooling出一个结果,组成新的图(图3右侧的9宫格图),如果ROI刚好覆盖ground truth,这个新的区域就标记为前景(label=1)。
为什么每张图都能携带相对位置信息?因为ROI提取1~9号小方格时,每个小方格在每张图上的位置并不相同,而是在上下左右方向上有偏移,当组合出来的9宫格对应ground truth时,小方格1就对应了 ground truth左上角的位置,小方格2对应了ground truth正上方的位置,依此类推,所以用这种9宫格训练目标时就有了相对目标位置的信息在里面 。
此例子和R-FCN的不同点是,此例中的score map_2中的每个格子的大小和score map_1中分割ROI的大小一样,而在R-FCN中,score map_2中的每个格子是一个点,这个点是score map_1中分割ROI得到的小格子池化得到的值。
2、Position-sensitive ROI Pooling ——先横面划片,再纵向划片,把选中的三维张量平均池化为一个列向量:
怎么使得ROl的K*K个子区域在各个类别的score maps上找到其每个子区域的响应值的?这就是位置敏感Rol池化操作(Position-sensitive RoI pooling),其字面意思是池化操作是位置敏感的。ROI的左上角区域和Score map_左上角的score map对应关系是怎么建立起来的?
通过RPN提取出来的RoI区域,其是包含坐标信息(x,y,w,h)。首先不同的RoI区域能够对应到score map_1的不同位置上,图中标出来的“ROI”(【K*K*(C+1)】*n*n)多个ROI中的一个ROI。每一个RoI会被划分成K*K个bins(也就是子区域。每个子区域bin的长宽分别是 h/k 和 w/k ),此时ROI的维度是(k*k个大小为【K*K*(C+1)*n/k*n/k】的三维张量),每个bin(共k*k个)都对应到score map_2上的对应区域(K*K个(C+1)列向量中的一个)。既然该RoI的每个bin都对应到score map_2上的某一个子区域,那么池化操作就是在该bin对应的score map_1上的子区域((C+1)*n/k*n/k的三维张量)执行,得到score map_2中的一个子区域((C+1)*1*1)且执行的是平均池化。我们在前面已经讲了,第i个bin应该在第i个score map上寻找响应值,那么也就是在第i个score map上的第i个bin对应的位置上进行平均池化操作。由于我们有(C+1)个类别,所以每个类别都要进行相同方式的池化操作。即在原ROI三维张量中,俯视看,9个位置分9个三维张量,每一个位置(共9个)对应一个(k*k*(C+1))*n/k*n/k 的三维张量,只选择它被划分成的9=k*k段中的1段(C+1)*n/k*n/k大小的三维张量,进行池化得到score map_2中的一个列向量((C+1)*1*1)。
上图已经很明显的画出了池化的方式,对于每个类别,它都有K*K个score map,那么按照上述的池化方式,ROI可以针对该类别可以获得K*K个值,那么一共有(C+1)个类别,那么一个RoI就可以得到K*K*(C+1)个值,就是上图的score map_2特征图。那么对于每个类别,该类别的K*K个值都表示该RoI(不同位置)属于该类别的响应值,那么将这K*K个数相加就得到该类别的score,那么一共有(C+1)个scores,那么在这(C+1)个数上面使用简单的softmax函数就可以得到各个类别的概率了(注意,这里不需要使softmax分类器了,只需要使用简答的softmax函数,因为这里就是通过简单的比大小来判断最终的类别的)。
三、网络结构:
整个网络主要就是由全卷积网络(ResNet)和RPN网络构成,前者用于提取特征,后者用于生成ROI。
1、R-CNN系回顾:
Faster R-CNN是首个利用CNN来完成proposals预测的,从此之后很多的目标检测网络都开始使用Faster R-CNN的思想,即用CNN完成第一个stage,生成预选框/候选框/RP。Faster R-CNN系列的网络都可以分成2个部分:ROI Pooling之前的共享全卷积网络和ROI Pooling之后的ROI-wise子网络(用来对每个ROI进行特征提出,并进行回归和分类),具体如下:
(1)first stage:第一部分就是直接用普通分类网络的卷积层,用来提取共享特征,然后利用ROI Pooling在最后一层网络形成的feature map上面提取针对各个RoIs的特征向量,然后将所有RoIs的特征向量都交给第2部分来处理(即所谓的分类和回归)。
(2)second stage:第二部分一般都是一些全连接层,在最后有2个并行的loss函数:softmax和smoothL1,分别用来对每一个RoI进行分类和回归,这样就可以得到每个RoI的真实类别和较为精确的坐标信息(x, y, w, h)。
需要注意的是第1部分通常使用的都是像VGG、GoogleNet、ResNet之类的基础分类网络,这些网络的计算都是所有RoIs共享的,在一张图片上面进行测试的时候只需要进行一次前向计算即可。而对于第2部分的RoI-wise subnetwork,它却不是所有RoIs共享的,主要的原因是因为这一部分的作用是“对每个RoI进行分类和回归”,所以不能进行共享计算。
因此,第1部分的网络具有“位置不敏感性”,而如果我们将一个分类网络比如ResNet的所有卷积层都放置在第1部分用来提取特征,而第2部分则只剩下全连接层,这样的目标检测网络是位置不敏感的translation-invariance,所以其检测精度会较低,而且这样做也会浪费掉分类网络强大的分类能力(does not match the network's superior classification accuracy)。
2、ResNet回顾:
ResNet论文为了解决这个问题,做出了一点让步,即将RoI Pooling层不再放置在ResNet-101网络的最后一层卷积层之后而是放置在了“卷积层之间”,这样RoI Pooling Layer之前和之后都有卷积层,并且RoI Pooling Layer之后的卷积层不是共享计算的,它们是针对每个RoI进行特征提取的,所以这种网络设计,其RoI Pooling层之后就具有了位置敏感性translation-variance,但是这样做会牺牲测试速度,因为所有的RoIs都需要经过若干层卷积计算,这样会导致测试速度很慢。R-FCN就是针对这个问题提出了自己的解决方案,在速度和精度之间进行折中。
3、R-FCN 算法具体步骤:
网络结构:首先输入图像经过一个全卷积网络(比如ResNet),然后一方面在最后一个卷积层后面添加特殊的卷积层(控制feature map的维度)生成position-sensitive score map的备用特征图,另一方面全卷积网络的某个卷积层(可能是最后一个卷积层)输出作为RPN网络的输入,RPN网络最后生成ROI。最后的ROI Pooling层将前面的socre map和ROI作为输入(在备用特征图中找到roil对应区域进行ROI Pooling,其本质是对特选部分平均池化),得到score map,取平均后输出类别信息。另外回归部分和分类部分是并列的。
(1)首先,选择一张需要处理的图片,并对这张图片进行相应的预处理操作;
(2)接着,我们将预处理后的图片送入一个预训练好的分类网络中(这里使用了ResNet-101网络的Conv4之前的网络),固定其对应的网络参数;
(3)接着,在预训练网络的最后一个卷积层获得的feature map后存在3个分支:
1)第1个分支就是在该feature map上面进行RPN操作,获得相应的ROI位置信息;
2)第2个分支是在该ROI对应的feature map上获得一个 K*K*(C+1)*原面积 维的position-sensitive score map;
3)第3个分支在该ROI对应的feature map上获得一个 4*K*K*原面积 维的位置敏感得分映射;
(4)最后,在深度为K*K*(C+1)维的分类score map_1和深度为4*K*K维的定位score map_1上面分别执行Position-Sensitive ROI pooling操作(这里使用的是平均池化操作),分别得到分类和定位的score map_2,再分别进行VOTE取均值化三维张量为列向量,最后通过softmax函数和计算offset分别获得对应的类别和位置信息。
position-sensitive score map中的每个 (k*k*(C+1))*n*n 大小的ROI进行ROI pooling后会得到k*k*(C+1)大小的输出,这个输出进行vote操作得到C+1维的输出,这个vote操作就是一个均值操作。最后再连接一个softmax层输出每一类的概率。回归支路和分类支路类似,只不过接的卷积层的卷积核数量不是k^2(C+1)而是4*k^2(代码中采用的是4*2*k^2),因此在经过position-sensitive Roi Pooling后得到4*k*k维度的输出,再经过vote操作得到4*1*1的输出,表示预测的bbox坐标offset。
4、Loss计算及其分析:
这个Loss就是两阶段目标检测框架常用的形式。包括一个分类Loss和一个回归Loss。lamdy参数作为权重用来平衡两者的重要性。对于任意一个RoI,需要计算它的softmax损失,和当其不属于背景时的回归损失。这很简单,因为每个RoI都被指定属于某一个GT box或者属于背景,即先选择和GT box具有最大重叠率(IOU)的Rol,然后在剩余的Rol中选择与GT box的重叠率值大于0.5Rol进行匹配操作,最后将剩余的Rol都归为背景类。即每个Rol都有了对应的标签,我们就可以根据监督学习常用的方法来训练它,把网络对图片的ROI生成这一步训练的越来越精准。
5、online hard example mining:
这个方法是目标检测框架中经常会用到的一个tricks,其主要的思路如下所示:首先对RPN获得的候选ROI(正负样本分别进行排序)进行排序操作;然后在含有正样本(目标)的ROI中选择前N个ROI,将正负样本的比例维持在1:3的范围内,基本上保证每次抽取的样本中都会含有一定的正样本,都可以通过训练来提高网络的分类能力。如果不进行此操作的话,很可能会出现抽取的所有样本都是负样本(背景)的情况,这样让网络学习这些负样本,会影响网络的性能。
6、Rols预筛选:
为了过滤背景Rols使用的方法在测试的时候,为了减少RoIs的数量,作者在RPN提取阶段就对RPN提取的大约2W个proposals进行了过滤,方法如下所示,去除超过图像边界的proposals;使用基于类别概率且阈值IoU=0.7的NMS过滤;按照类别概率选择top-N个proposals;所以在测试的时候,最后一般只剩下300左右个RoIs,当然这个数量是一个超参数。并且在R-FCN的输出300个预测框之后,仍然要对其使用NMS去除冗余的预测框。
四、实验结果:
采用COCO数据集进行性能验证,与Faster R-CNN相比,R-FCN可以实现3倍的加速,准确率可以提升2个百分点。
测试不同大小的ROI对性能的影响(我们使用了预训练的ResNet-101网络,在VOC 07数据集上面进行测试),我们可以看到如果使用1x1的ROI,显示输出失败,具体原因不得而知。当使用7x7的ROI时,能够获得最好的结果,这也是论文中最终使用7x7大小的ROI的原因吧,作者应该是做了很多的验证工作。
比较了aster R-CNN和R-FCN的性能,从表中我们可以看出与Faster R-CNN相比,R-FCN有更快的运行速度,大概是2.5倍以上。另外,我们可以发现性能稍微有一点点提升,当调整ROI的个数时,我们发现300个ROI时能够获得最好的性能。