和Faster R-CNN相比,R-FCN具有更深的共享卷积网络层,这样可以获得更加抽象的特征
抽象特征的捕获能否通过“sketch/conceptual...” - guided 来进行?想法还是说把逻辑推理和深度学习暴力计算相结合,各自发挥各自的优势~
4. R-FCN网络的设计动机
Faster R-CNN是首个利用CNN来完成proposals预测的,从此之后很多的目标检测网络都开始使用Faster R-CNN的思想。而Faster R-CNN系列的网络都可以分成2个部分:ROI Pooling之前的共享全卷积网络和ROI Pooling之后的ROI-wise子网络(用来对每个ROI进行特征提出,并进行回归和分类)。第1部分就是直接用普通分类网络的卷积层,用来提取共享特征,然后利用ROI Pooling在最后一层网络形成的feature map上面提取针对各个RoIs的特征向量,然后将所有RoIs的特征向量都交给第2部分来处理(即所谓的分类和回归),而第二部分一般都是一些全连接层,在最后有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)。而ResNet论文中为了解决这个问题,做出了一点让步,即将RoI Pooling层不再放置在ResNet-101网络的最后一层卷积层之后而是放置在了“卷积层之间”,这样RoI Pooling Layer之前和之后都有卷积层,并且RoI Pooling Layer之后的卷积层不是共享计算的,它们是针对每个RoI进行特征提取的,所以这种网络设计,其RoI Pooling层之后就具有了位置敏感性translation-variance,但是这样做会牺牲测试速度,因为所有的RoIs都需要经过若干层卷积计算,这样会导致测试速度很慢。R-FCN就是针对这个问题提出了自己的解决方案,在速度和精度之间进行折中。
————————————————
1. Faster R-CNN:全卷积网络(第1部分,共享计算) -> ROI Pooling -> ROI-wise 子网络(第2部分:单独计算,用来对每个ROI进行特征提出,并进行回归和分类)
网络有两种能力:1)位置检测能力;2)分类能力;
问题:第1部分的网络具有“位置不敏感性”,第1部分用来提取特征 + 第2部分则只剩下全连接层 的做法,其(位置)检测精度会较低,也会浪费掉分类网络强大的分类能力
2. Res-Net:将ROI Pooling 层放在了“卷积层之间”,并且 ROI Pooling 层之后的卷积层不是共享计算,而是针对每个RoI进行特征提取,所以会得到“位置敏感性”;缺点:会牺牲测试速度;
3. R-FCN:在速度和精度之间进行折中;
K*K 网格划分,K*K 个 位置敏感-score maps,每一个score map表示原始图像中的哪些位置含有人的某个部位,该score map会在含有对应的人体的某个部位的位置有高的响应值,也就是说每一个score map都是用来描述人体的其中一个部位出现在该score map的何处,而在出现的地方就有高响应值
————————————————
>>> 分类网络:分类精度高、位置不敏感,是个值得好好研究的大课题哇
R-FCN详解
论文题目:R-FCN: Object Detection via Region-based Fully Convolutional Networks
论文链接:论文链接
论文代码:Caffe版本链接地址;Python版本链接地址;Deformable R-FCN版本链接地址
一、R-FCN初探
1. R-FCN贡献
- 提出Position-sensitive score maps来解决目标检测的位置敏感性问题;
- 区域为基础的,全卷积网络的二阶段目标检测框架;
- 比Faster-RCNN快2.5-20倍(在K40GPU上面使用ResNet-101网络可以达到 0.17 sec/image);
2. R-FCN与传统二阶段网络的异同点
图1 R-FCN与传统二阶段网络的异同点
相同点:首先,两者二阶段的检测框架(全卷积子网络+RoI-wise subnetwork); 其次两者最终输出的结果都是相应的类别和对应的BB;
不同点:
如上图所示,我们可以看到和Faster R-CNN相比,R-FCN具有更深的共享卷积网络层,这样可以获得更加抽象的特征;同时,它没有RoI-wise subnetwork,不像Faster R-CNN的feature map左右都有对应的网络层,它是真正的全卷积网络架构;从图中的表格可以看出Faster R-CNN的共享卷积子网络是91层,RoI-wise子网络是10层,而R-FCN只有共享卷积子网络,深度为101层。与R-CNN相比,最大的不同就是直接获得整幅图像的feature map,再提取对应的ROI,而不是直接在不同的ROI上面获得相应的feature map。
3. 分类网络的位置不敏感性和检测网络的位置敏感性
我在很多相关的检测论文中都看到这两个概念,但是一直都没有理解其真正的含义,相信很多朋友们也有同样的困惑,所以我在这里解释一下。
图2 分类网络的位置不敏感性和检测网络的位置敏感性
分类网络的位置不敏感性:简单来讲,对于分类任务而言,我希望我的网络有一个很好地分类性能,随着某个目标在图片中不断的移动,我的网络仍然可以准确的将你区分为对应的类别。如上图左边所示,不管你这只鸟在图片中如何移动,我的分类网络都想要准确的将你分类为鸟。即我的网络有很好地区分能力。实验表明,深的全卷积网络能够具备这个特性,如ResNet-101等。
检测网络的位置敏感性:简单来讲,对于检测任务而言,我希望我的网络有一个好的检测性能,可以准确的输出目标所在的位置值。随着某个目标的移动,我的网络希望能够和它一起移动,仍然能够准确的检测到它,即我对目标位置的移动很敏感。我需要计算对应的偏差值,我需要计算我的预测和GT的重合率等。但是,深的全卷积网路不具备这样的一个特征。
总之,分类网络的位置不敏感性和检测网络的位置敏感性的一个矛盾问题,而我们的目标检测中不仅要分类也要定位,那么如何解决这个问题呢,R-FCN提出了Position-sensitive score maps来解决这个问题;
4. R-FCN网络的设计动机
Faster R-CNN是首个利用CNN来完成proposals预测的,从此之后很多的目标检测网络都开始使用Faster R-CNN的思想。而Faster R-CNN系列的网络都可以分成2个部分:ROI Pooling之前的共享全卷积网络和ROI Pooling之后的ROI-wise子网络(用来对每个ROI进行特征提出,并进行回归和分类)。第1部分就是直接用普通分类网络的卷积层,用来提取共享特征,然后利用ROI Pooling在最后一层网络形成的feature map上面提取针对各个RoIs的特征向量,然后将所有RoIs的特征向量都交给第2部分来处理(即所谓的分类和回归),而第二部分一般都是一些全连接层,在最后有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)。而ResNet论文中为了解决这个问题,做出了一点让步,即将RoI Pooling层不再放置在ResNet-101网络的最后一层卷积层之后而是放置在了“卷积层之间”,这样RoI Pooling Layer之前和之后都有卷积层,并且RoI Pooling Layer之后的卷积层不是共享计算的,它们是针对每个RoI进行特征提取的,所以这种网络设计,其RoI Pooling层之后就具有了位置敏感性translation-variance,但是这样做会牺牲测试速度,因为所有的RoIs都需要经过若干层卷积计算,这样会导致测试速度很慢。R-FCN就是针对这个问题提出了自己的解决方案,在速度和精度之间进行折中。
二、R-FCN架构分析
1. R-FCN算法步骤
图3 R-FCN算法步骤
如图所示,我们先来分析一下R-FCN算法的整个运行步骤,使得我们对整个算法有一个宏观的理解,接下来再对不同的细节进行详细的分析。
- 首先,我们选择一张需要处理的图片,并对这张图片进行相应的预处理操作;
- 接着,我们将预处理后的图片送入一个预训练好的分类网络中(这里使用了ResNet-101网络的Conv4之前的网络),固定其对应的网络参数;
- 接着,在预训练网络的最后一个卷积层获得的feature map上存在3个分支,第1个分支就是在该feature map上面进行RPN操作,获得相应的ROI;第2个分支就是在该feature map上获得一个K*K*(C+1)维的位置敏感得分映射(position-sensitive score map),用来进行分类;第3个分支就是在该feature map上获得一个4*K*K维的位置敏感得分映射,用来进行回归;
- 最后,在K*K*(C+1)维的位置敏感得分映射和4*K*K维的位置敏感得分映射上面分别执行位置敏感的ROI池化操作(Position-Sensitive Rol Pooling,这里使用的是平均池化操作),获得对应的类别和位置信息。
这样,我们就可以在测试图片中获得我们想要的类别信息和位置信息啦。
2. Position-Sensitive Score Map解析
图3是R-FCN的网络结构图,其主要设计思想就是“位置敏感得分图position-sensitive score map”。现在我们来解释一下其设计思路。如果一个RoI中含有一个类别C的物体,我们将该RoI划分为K*K 个区域,其分别表示该物体的各个部位,比如假设该RoI中含有的目标是人,K=3,那么就将“人”划分成了9个子区域,top-center区域毫无疑问应该是人的头部,而bottom-center应该是人的脚部,我们将RoI划分为K*K个子区域是希望这个RoI在其中的每一个子区域都应该含有该类别C的物体的各个部位,即如果是人,那么RoI的top-center区域就应该含有人的头部。当所有的子区域都含有各自对应的该物体的相应部位后,那么分类器才会将该RoI判断为该类别。也就是说物体的各个部位和RoI的这些子区域是“一一映射”的对应关系。
OK,现在我们知道了一个RoI必须是K*K个子区域都含有该物体的相应部位,我们才能判断该RoI属于该物体,如果该物体的很多部位都没有出现在相应的子区域中,那么就该RoI判断为背景类别。那么现在的问题就是网络如何判断一个RoI的 K*K个子区域都含有相应部位呢?前面我们是假设知道每个子区域是否含有物体的相应部位,那么我们就能判断该RoI是否属于该物体还是属于背景。那么现在我们的任务就是判断RoI子区域是否含有物体的相应部位。
这其实就是position-sensitive score map设计的核心思想了。R-FCN会在共享卷积层的最后一层网络上接上一个卷积层,而该卷积层就是位置敏感得分图position-sensitive score map,该score map的含义如下所述,首先它就是一层卷积层,它的height和width和共享卷积层的一样(即具有同样的感受野),但是它的通道个数为K*K*(C+1) 。其中C表示物体类别种数,再加上1个背景类别,所以共有(C+1)类,而每个类别都有 K*K个score maps。现在我们只针对其中的一个类别来进行说明,假设我们的目标属于人这个类别,那么其有 K*K 个score maps,每一个score map表示原始图像中的哪些位置含有人的某个部位,该score map会在含有对应的人体的某个部位的位置有高的响应值,也就是说每一个score map都是用来描述人体的其中一个部位出现在该score map的何处,而在出现的地方就有高响应值”。既然是这样,那么我们只要将RoI的各个子区域对应到属于人的每一个score map上然后获取它的响应值就好了。但是要注意的是,由于一个score map都是只属于一个类别的一个部位的,所以RoI的第 i个子区域一定要到第i张score map上去寻找对应区域的响应值,因为RoI的第i个子区域需要的部位和第i张score map关注的部位是对应的。那么现在该RoI的K*K个子区域都已经分别在属于人的K*K个score maps上找到其响应值了,那么如果这些响应值都很高,那么就证明该RoI是人呀。当然这有点不严谨,因为我们只是在属于人的 K*K个score maps上找响应值,我们还没有到属于其它类别的score maps上找响应值呢,万一该RoI的各个子区域在属于其它类别的上的score maps的响应值也很高,那么该RoI就也有可能属于其它类别呢?是吧,如果2个类别的物体本身就长的很像呢?这就会涉及到一个比较的问题,那个类别的响应值高,我就将它判断为哪一类目标。它们的响应值同样高这个情况发生的几率很小,我们不做讨论。
OK,这就是position-sensitive score map的全部思想了,应该很容易理解了吧。
3. Position-Sensitive Rol Pooling解析
上面我们只是简单的讲解了一下ROl的K*K个子区域在各个类别的score maps上找到其每个子区域的响应值,我们并没有详细的解释这个“找到”是如何找的?这就是位置敏感Rol池化操作(Position-sensitive RoI pooling),其字面意思是池化操作是位置敏感的,下来我们对它进行解释说明。
如图3所示,通过RPN提取出来的RoI区域,其是包含了x,y,w,h的4个值,也就是说不同的RoI区域能够对应到score map的不同位置上,而一个RoI会被划分成K*K个bins(也就是子区域。每个子区域bin的长宽分别是 h/k 和 w/k ),每个bin都对应到score map上的某一个区域。既然该RoI的每个bin都对应到score map上的某一个子区域,那么池化操作就是在该bin对应的score map上的子区域执行,且执行的是平均池化。我们在前面已经讲了,第i个bin应该在第i个score map上寻找响应值,那么也就是在第i个score map上的第i个bin对应的位置上进行平均池化操作。由于我们有(C+1)个类别,所以每个类别都要进行相同方式的池化操作。
图4 Position-Sensitive Rol Pooling解析
图4已经很明显的画出了池化的方式,对于每个类别,它都有K*K个score maps,那么按照上述的池化方式,ROI可以针对该类别可以获得K*K个值,那么一共有(C+1)个类别,那么一个RoI就可以得到K*K*(C+1)个值,就是上图的特征图。那么对于每个类别,该类别的K*K个值都表示该RoI属于该类别的响应值,那么将这K*K个数相加就得到该类别的score,那么一共有(C+1)个scores,那么在这(C+1)个数上面使用简单的softmax函数就可以得到各个类别的概率了(注意,这里不需要使softmax分类器了,只需要使用简答的softmax函数,因为这里就是通过简单的比大小来判断最终的类别的)。
4. Position-Sensitive Regression解析
前面的position-sensitive score map和Position-sensitive RoI pooling得到的值是用来分类的,那么自然需要相应的操作得到对应的值来进行回归操作。按照position-sensitive score map和Position-sensitive RoI pooling思路,其会让每一个RoI得到(C+1)个数作为每个类别的score,那么现在每个RoI还需要 4个数作为回归偏移量,也就是x,y,w,h的偏移量,所以仿照分类设计的思想,我们还需要一个类似于position-sensitive score map的用于回归的score map。那么应该如何设置这个score map呢,论文中给出了说明:即在ResNet的共享卷积层的最后一层上面连接一个与position-sensitive score map并行的score maps,该score maps用来进行regression操作,我们将其命名为regression score map,而该regression score map的维度应当是 4*K*K ,然后经过Position-sensitive RoI pooling操作后,每一个RoI就能得到4个值作为该RoI的x,y,w,h的偏移量了,其思路和分类完全相同。
5. 为什么position-sensitive score map能够在含有某个类别的物体的某个部位的区域上具有高响应值?
这种有高响应值现在只是作者自己设想的啊,如果网络不满足这一点的话,那么我们前面的所有分析都不成立啦。现在我们就大致解释一下为什么训练该网络能够让网络最终满足这一点。首先根据网络的loss计算公式,如果一个RoI含有人这个物体,那么该RoI通过position-sensitive score map和Position-sensitive RoI pooling得到的(C+1)个值中属于人的那个值必然会在softmax损失函数的驱动下变得尽量的大,那么如何才能使得属于人的这个值尽量的大呢?那么我们需要想想属于人的这个预测值是怎么来的?经过前面的分析,我们已经知道它是通过Position-sensitive RoI pooling这种池化操作获得的,那么也就是说使得(C+1)个值中属于人的那个值尽量大,必然会使得position-sensitive score map中属于人的那个score map上的RoI对应的位置区域的平均值尽量大,从而会使得该score map上在该区域上的响应值尽量大,因为只有该区域的响应值大了,才能使得预测为人的概率大,才会降低softmax的loss,整个训练过程才能进行下去。
图5 位置敏感得分映射表现1
图6 位置敏感得分映射表现2
如图5和图6所示,我们同样可以得出以上的结论。如图5所示,我们输入了一张含有一个小孩的图片,图中黄色的BB表示我们的检测到的目标,也就是我们的一个ROI,接下来是9张位置敏感的得分映射图(在这里使用的是3x3的特征映射),这9张图分别表示对人这个目标的top-left、top-center、... bottom-right不同区域敏感的得分映射。对应到图中就是将这个ROI分为9个子区域,每一个子区域其实大致上对应到了小孩的不同部位,而不同的部位一般都会有其独特的特征存在,9个区域敏感得分映射图对不同的区域比较敏感(所谓的敏感就是说如果这个子区域中存在该目标的某个部位特征时,其才会输出较大的响应值,否则的话我会输出较小的响应值)。图5中的9个得分映射对ROI中划分的对应子区域都比较敏感(都有很强的响应值,越白表示响应越大,越黑表示响应越小),即ROI中的9个子区域都有较大的响应值。然后进行位置敏感池化操作,最后进行Vote操作,由于9个区域中基本上都有很高的响应值,最后投票通过,认为这个ROI中的对象是一个person。同理,可以得出图6是一个背景类。(图6的位置敏感ROI池化中有5个区域是黑色的,即表示具有较低的响应值,只有4个区域比较高,即表示具有较高的响应值,根据Vote机制,就将其分类为背景类)。
6. Loss计算及其分析
这个Loss就是两阶段目标检测框架常用的形式。包括一个分类Loss和一个回归Loss。lamdy用来平衡两者的重要性。对于任意一个RoI,我们需要计算它的softmax损失,和当其不属于背景时的回归损失。这很简单,因为每个RoI都被指定属于某一个GT box或者属于背景,即先选择和GT box具有最大重叠率(IOU)的Rol,然后在剩余的Rol中选择与GT box的重叠率值大于0.5Rol进行匹配操作,最后将剩余的Rol都归为背景类。即每个Rol都有了对应的标签,我们就可以根据监督学习常用的方法来训练它啦。
7. online hard example mining
这个方法是目标检测框架中经常会用到的一个tricks,其主要的思路如下所示:首先对RPN获得的候选ROI(正负样本分别进行排序)进行排序操作;然后在含有正样本(目标)的ROI中选择前N个ROI,将正负样本的比例维持在1:3的范围内,基本上保证每次抽取的样本中都会含有一定的正样本,都可以通过训练来提高网络的分类能力。如果不进行此操作的话,很可能会出现抽取的所有样本都是负样本(背景)的情况,这样让网络学习这些负样本,会影响网络的性能。(这完全是我个人的理解,哈哈哈)
8. Atrous algorithm(Dilated Convolutions或者膨胀卷积)
图7 膨胀卷积
这个方法同样也是目标检测中常用的一个tricks,其最主要的目的是可以在减小卷积步长的同时扩大feature map的大小,即同等情况下,通过这个操作,我们可以获得一个更大的feature map,而实验表明,大的feature map会提升检测的性能。具体的解释可以去看这个链接。上图是一个膨胀卷积的操作,通过几次操作,我们可以看到我们的接收场在不断的扩大,具体的解释请看英文吧。
9. 为了过滤背景Rols使用的方法
在测试的时候,为了减少RoIs的数量,作者在RPN提取阶段就对RPN提取的大约2W个proposals进行了过滤,方法如下所示,
- 去除超过图像边界的proposals;
- 使用基于类别概率且阈值IoU=0.7的NMS过滤;
- 按照类别概率选择top-N个proposals;
所以在测试的时候,最后一般只剩下300左右个RoIs,当然这个数量是一个超参数。并且在R-FCN的输出300个预测框之后,仍然要对其使用NMS去除冗余的预测框。
10. 训练细节
R-FCN和Faster R-CNN采取了同样的训练策略,具体的训练策略可以参考这篇博客。
11. 图片中的ROI和特征上的ROI之间的映射关系
如果你不清楚它们是如何映射的,请查看这个链接。
三、R-FCN性能分析
1. 定量结果分析
表1 使用ResNet-101全卷积策略
如上表所示,作者测试了不同大小的ROI对性能的影响(我们使用了预训练的ResNet-101网络,在VOC 07数据集上面进行测试),我们可以看到如果使用1x1的ROI,显示输出失败,具体原因不得而知。当使用7x7的ROI时,能够获得最好的结果,这也是论文中最终使用7x7大小的ROI的原因吧,作者应该是做了很多的验证工作。
表2 Faster R-CNN与R-FCN性能比较
如上表所示,我们比较了Faster R-CNN和R-FCN的性能,从表中我们可以看出与Faster R-CNN相比,R-FCN有更快的运行速度,大概是2.5倍以上。另外,我们可以发现性能稍微有一点点提升,当调整ROI的个数时,我们发现300个ROI时能够获得最好的性能。
表3 预训练网络的深度对性能的影响
如上表所示,随着预训练网络层数的加深,我们的检测性能在不断的得到提高,使用VGG和ResNet网络还是有很大的性能差异,但是过深的网络并没有提高其性能,可能的原因是我们的网络发生了过拟合情况。
表4 COCO数据集的训练结果
如上表所示,我们采用了COCO数据集进行性能验证,与Faster R-CNN相比,R-FCN可以实现3倍的加速,准确率可以提升2个百分点。
2. 定性结果分析
图8 VOC 2007检测结果
图9 COCO检测结果
以上是R-FCN算法在VOC2007和COCO数据集上面的性能表现,总体上看效果还是挺不错的,具体的效果需要你自己去尝试,根据自己的需求去选择合适的算法。
四、总结
总的来讲,和Faster R-CNN相比,R-FCN具有更快的运行速度(2.5倍以上),稍微提高了一点检测精度,在速度和准确率之间进行了折中,提出position-sensitive score map来解决检测的位置敏感性问题。算法中的很多细节值得我们进行深入的研究和分析,希望你从中学到了很多有用的东西。
参考文献:
[1] R-FCN对应的poster,相关链接;
[2] VGG Reading Group - Sam Albanie ,参考链接,密码:hby1;
[3] 详解R-FCN,博客链接;
注意事项:
[1] 该博客是本人原创博客,如果您对该博客感兴趣,想要转载该博客,请与我联系(qq邮箱:[email protected]),我会在第一时间回复大家,谢谢大家。
[2] 由于个人能力有限,该博客可能存在很多的问题,希望大家能够提出改进意见。
[3] 如果您在阅读本博客时遇到不理解的地方,希望可以联系我,我会及时的回复您,和您交流想法和意见,谢谢。
[4] 本人业余时间承接各种本科毕设设计和各种小项目,包括图像处理(数据挖掘、机器学习、深度学习等)、matlab仿真、python算法及仿真等,有需要的请加QQ:1575262785详聊!!!
说的是干话
Motivation
虽然fast rcnn共享了每个roi的feature map, faster rcnn利用rpn也使proposal的生成共享了feature map,已经比之前的rcnn减少了大量计算方法,快了很多。但是roi pooling后面的多个fc,每个roi之间是没有共享计算的,而且fc的参数巨多,在之前的faster rcnn源码解析中也都讲过,train时有128个送到后面,test时有300个送到后面,大量的重复计算浪费了很多时间。
观察到ResNet和GoogLeNet,他们只在最后一层f是全连接的,不像vgg有隐层fc6 fc7,所以很自然的方法,我们能不能把roi pooling后面的隐层fc去掉,rpn共享前面所有的卷积,roi pooling后直接跟一个fc,但是效果是不行的,所以kaiming的ResNet附录里A. Object Detection Baselines的做法是共享前91个卷积(相当于vgg的前13个卷积),后面10个卷积依然不共享(相当于vgg的隐层fc)。kaiming为什么这样做呢?因为分类是要求translation invariance,而检测要求translation variance,这就矛盾了,所以需要通过roi pooling这种与位置相关的层打破translation invariance,roi后面的卷积就有了translation-invariance性,但是这样还是降低了速度。(后来的faster rcnn v3时是仅仅把vgg的特征换成resnet的,roi 后面还是fc6 fc7 fc8)
所以这篇论文就想能不能共享所有的计算,并提出了Region-based Fully Convolutional Network (R-FCN)
-------------------------------------------------------------------------------------------------------------
在最后一个卷积res5c后,添加一个1*1的卷积生成k^2(C+1)个channel的位置敏感的分类feature和k^2(C+1)个channel的回归feature。以分类为例,这k^2个score map对应k^2个格子的位置。以k=3,左上角为例,黄色是C+1个channel,每个channel对应一个类别在左上角的score map,经过经过这个pool之后得到(C+1)*k*k,然后vote(avg or max pool),得到(C+1)*1*1,直接softmax,没有fc或者卷积等计算,所有roi都贡献了计算。
R-FCN解读
最近一直做检测,发现检测领域好多好玩的东西啊,R-FCN是msra dai老师和kaiming做的,insight很赞,这次翻出来再学习一下。最近旷视科技又发了light RCNN,检测这领域真是日新月异。
Motivation
虽然fast rcnn共享了每个roi的feature map, faster rcnn利用rpn也使proposal的生成共享了feature map,已经比之前的rcnn减少了大量计算方法,快了很多。但是roi pooling后面的多个fc,每个roi之间是没有共享计算的,而且fc的参数巨多,在之前的faster rcnn源码解析中也都讲过,train时有128个送到后面,test时有300个送到后面,大量的重复计算浪费了很多时间。
观察到ResNet和GoogLeNet,他们只在最后一层f是全连接的,不像vgg有隐层fc6 fc7,所以很自然的方法,我们能不能把roi pooling后面的隐层fc去掉,rpn共享前面所有的卷积,roi pooling后直接跟一个fc,但是效果是不行的,所以kaiming的ResNet附录里A. Object Detection Baselines的做法是共享前91个卷积(相当于vgg的前13个卷积),后面10个卷积依然不共享(相当于vgg的隐层fc)。kaiming为什么这样做呢?因为分类是要求translation invariance,而检测要求translation variance,这就矛盾了,所以需要通过roi pooling这种与位置相关的层打破translation invariance,roi后面的卷积就有了translation-invariance性,但是这样还是降低了速度。(后来的faster rcnn v3时是仅仅把vgg的特征换成resnet的,roi 后面还是fc6 fc7 fc8)
所以这篇论文就想能不能共享所有的计算,并提出了Region-based Fully Convolutional Network (R-FCN)
(以RoIpooling为分界线,分为两部分)
# ResNet101 prototxt
layer {
bottom: "res5c"
top: "pool5"
name: "pool5"
type: "Pooling"
pooling_param {
kernel_size: 7
stride: 1
pool: AVE
}
}
layer {
bottom: "pool5"
top: "fc1000"
name: "fc1000"
type: "InnerProduct"
inner_product_param {
num_output: 1000
}
}
layer {
bottom: "fc1000"
top: "prob"
name: "prob"
type: "Softmax"
}
Approach
在最后一个卷积res5c后,添加一个1*1的卷积生成k^2(C+1)个channel的位置敏感的分类feature和k^2(C+1)个channel的回归feature。以分类为例,这k^2个score map对应k^2个格子的位置。以k=3,左上角为例,黄色是C+1个channel,每个channel对应一个类别在左上角的score map,经过经过这个pool之后得到(C+1)*k*k,然后vote(avg or max pool),得到(C+1)*1*1,直接softmax,没有fc或者卷积等计算,所有roi都贡献了计算。注意还有个
# conv_new_1
# relu1是C1-C5后加了relu后的feature。
# ResNet-101 has 100 convolutional layers followed by global average pooling and a 1000-class fc layer
# R-FCN把global pool和fc都去掉,加了个1*1的卷积降维,然后这101个卷积都贡献参数和计算
conv_new_1 = mx.sym.Convolution(data=relu1, kernel=(1, 1), num_filter=1024, name="conv_new_1", lr_mult=3.0)
relu_new_1 = mx.sym.Activation(data=conv_new_1, act_type=‘relu‘, name=‘relu1‘)
# rfcn_cls/rfcn_bbox
# rfcn_cls: k*k(C+1)个channel rfcn_bbox: k*k*4(C+1)个channe
rfcn_cls = mx.sym.Convolution(data=relu_new_1, kernel=(1, 1), num_filter=7*7*num_classes, name="rfcn_cls")
rfcn_bbox = mx.sym.Convolution(data=relu_new_1, kernel=(1, 1), num_filter=7*7*4*num_reg_classes, name="rfcn_bbox")
psroipooled_cls_rois = mx.contrib.sym.PSROIPooling(name=‘psroipooled_cls_rois‘, data=rfcn_cls, rois=rois, group_size=7, pooled_size=7,output_dim=num_classes, spatial_scale=0.0625)
psroipooled_loc_rois = mx.contrib.sym.PSROIPooling(name=‘psroipooled_loc_rois‘, data=rfcn_bbox, rois=rois, group_size=7, pooled_size=7, output_dim=8, spatial_scale=0.0625)
# vote
cls_score = mx.sym.Pooling(name=‘ave_cls_scors_rois‘, data=psroipooled_cls_rois, pool_type=‘avg‘, global_pool=True, kernel=(7, 7))
bbox_pred = mx.sym.Pooling(name=‘ave_bbox_pred_rois‘, data=psroipooled_loc_rois, pool_type=‘avg‘, global_pool=True, kernel=(7, 7))
cls_score = mx.sym.Reshape(name=‘cls_score_reshape‘, data=cls_score, shape=(-1, num_classes))
bbox_pred = mx.sym.Reshape(name=‘bbox_pred_reshape‘, data=bbox_pred, shape=(-1, 4 * num_reg_classes))
# softmax计算loss
cls_prob = mx.sym.SoftmaxOutput(name=‘cls_prob‘, data=cls_score, label=label, normalization=‘valid‘, grad_scale=1.0)
bbox_loss_ = bbox_weight * mx.sym.smooth_l1(name=‘bbox_loss_‘, scalar=1.0, data=(bbox_pred - bbox_target))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
Position-sensitive RoI pooling
rpn生成的每个proposal对应到这k^2(C+1)个channel的score map上得到每个roi,然后将每个roi划分为k*k个bin,每个bin是位置相关的,比如左上角的bin,只在黄色层(C+1个channel)对应位置做average或max pooling,注意是对应位置,只在黄色层的左上角做,不是在黄色层整个roi上做。比如下面这个只看person这个channel,第一个图是左上角的bin,只在第一个图roi的左上角红色框做pool,不是在这个图的整个roi绿色框做pool。
这里的k相当于roi pooling的pool height/width
//使用CUDA多线程计算
CUDA_KERNEL_LOOP(index, nthreads) { //index为最终score map上所有,共有(C+1)*k*k个值
// The output is in order (n, ctop, ph, pw),类似于图像的BIL逐行扫描
int pw = index % pooled_width; //score map上第i=[0,k-1]列
int ph = (index / pooled_width) % pooled_height; //score map上第j=[0,k-1]行
int ctop = (index / pooled_width / pooled_height) % output_dim; //score map上第ctop个层(class)
int n = index / pooled_width / pooled_height / output_dim; //第n个roi
// ......
int gw = pw;
int gh = ph;
//ctop*group_size*group_size+gh*gh*group_size+gw,计算得到的是第ctop类的(ph,pw)位置索引
//例如,score map上第ctop=1类的第(i,j)=(1,1)位置,c=1*49+1*7+1,对于feature map上第c个颜色层中(实际包含C=21层)的第2(ctop+1)层
int c = (ctop*group_size + gh)*group_size + gw;
// 移动到该层做average pool
// 这是cuda代码,每次计算score map上一个值,如左上角
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
其他细节
- ResNet101的res5c是2048channel,为了减少计算量,先用了个1*1的卷积降维到了1024,然后才有position-sensitive的score map
- 加了洞算法,调整了步长,使最户的feature map为1/16
- 经过position-sensitive pooling后得到了k^2(C+1)的feature(类似于roipooling后的7*7*512的feature),后面两个分支,一个是分支首先average/max pooling到1*1*(C+1),然后softmax得到分类,另一个是坐标回归,生成4k^2-d的向量,然后做average/max pooling到4-d。此处为了简便,做的是类别无关的回归,但是类别相关的回归(生成4k^2C-d的向量)也是适用的
实验
- Naive Faster R-CNN 就是开始说的直接ResNet拿过来,roi pooling后无隐层fc,区别于kaiming的roi pooling后有10个卷积(76.4% mAP)
- Class-specific RPN,之前的rpn只分为是否是物体,这里直把proposal分为具体哪一个类别
- R-FCN without position-sensitivity,即k设置为1,类似于分割直接产生C+1个channel,每个channel代表一个类别的score map。这是最直接的做法了
根据表可知R-FCN和标准的faster rcnn(76.4% mAP)差不多了,在roi后没有用任何带参数的层,证明
ps-RoIpooling可以编码到有用的用于定位的空间信息,ps: Class-specific RPN近似于fast rcnn中sliding window的特殊形式
这两个表说明,R-FCN节省了很多训练和测试时间,且和标准的faster rcnn相比精度还要高一点点
这两个表说明这个策略泛化性很强,无论是不同深度的网络还是不同的proposal的提取方式,该论文的策略都适用。
参考:
msracver/Deformable-ConvNets
R-FCN:
原文地址:https://www.cnblogs.com/cx2016/p/11403432.html