Notes on Faster RCNN

Faster RCNN 的工作具有里程碑的意义,把 Region Proposal 和具体的检测过程全部通过 CNN 完成,实现了整个目标检测过程的 end to end 的训练。后续的大部分工作,例如 SSD, RFCN,FPN,Mask RCNN 等都延续了 RPN 的思路,因此,充分理解 Faster RCNN 对后续的工作有很多帮助。

整体过程

RPN 的引入使得 Faster RCNN 的网络结构有一点点复杂,尤其是分别有两次 BBox regression 和 classification,导致一眼看上去会有点懵。
整体上来看,Faster RCNN 输入一张图像,期望的输出是:

  1. 图像中所有目标的 BBox;
  2. 对于每一个 BBox,需要输出一个对应的 label,表明该 BBox 里面的是什么目标;
  3. 对于每一个 label 和 BBox 还需要输出概率,表明模型的置信度。

简单来说,要经历以下3个步骤:
第一步,输入图像表示为 \(H \times W \times D\) 的矩阵。图像首先输入到 CNN 中进行前向传播,得到 feature map,即上图中的 F_conv,在 F_conv 之前和普通的 CNN 没有任何区别,主要变化的是 F_conv 之后的部分;
第二步,把 F_conv 输入到 RPN 网络中,RPN 的作用是得到 预先定义好数量的 区域,这里得到的预先定义好数量的区域就是通过 RPN 得到的 Proposal BBox(替代 selective search 的工作)。
第三步,第二步得到了 Proposal BBox,接下去就是在这些 Proposal BBox 的基础上得到最终的精确的 BBox 以及每个 BBox 中的目标类别,过程和 Fast RCNN 基本上是一模一样的。

Stage 1:RPN 获取 Proposal BBox

RPN 的作用是替代 selective search 给出 Proposal BBox。其基本过程是:

  1. 人为并且先验地 给出一些 BBox(文章中称为 Anchor),这些 BBox 很不靠谱,而且数量非常多;
  2. 在 #1 中的 BBox(Anchor) 基础上进行调整和过滤,得到数量数量合理的、并且比 #1 靠谱一些的 BBox,作为 Proposal BBox。

接下来要分别解释上述两个步骤。

给出 Anchor

检测的目的是找到 BBox。每个目标物体的 BBox 通常有不同的大小和长宽比。假设我们知道需要预测的图像中有2个目标,那么,直接让网络输出两组BBox的坐标就好了,即 \(x_{min}\), \(y_{min}\), \(x_{max}\), \(y_{max}\). 但是,这种输出定义至少有两个方面的问题,

  1. 输入的大小和长宽比变化很大,导致从某种意义上说输出的内容不是同一个尺度
  2. 在这种输出中,必须要限制 \(x_{min}<x_{max}\), \(y_{min}<y_{max}\), 这在神经网络中比较难搞定。

因此,通常采用另外一种方式:不去直接学习 BBox 的具体坐标,而是学习 BBox 相对于 reference BBox 的 offset(这种给个 reference 的思想是不是和 ResNet 中的 shortcut 有点类似?)。假设 reference 的位置表示为 \(x_{center}\), \(y_{center}\), \(width\), \(height\), 那么,我们让网络去学习 \(\Delta{x_{center}}\), \(\Delta{y_{center}}\), \(\Delta{width}\), \(\Delta{height}\).

下面说回 Anchor,Anchor 实际上就是上面说的 reference BBox,并且是完全人为地拍脑袋决定的 reference BBox,完全不涉及到学习过程。现在具体解释一下这个拍脑袋的过程:
假设 F_conv 的 channel 数是 \(d\),那么,F_conv 的 shape 应该是 \(W/r \times D/r \times d\),其中 \(r\) 是 subsampling rate。为方便,F_conv 的 shape 记为 \(w \times h \times d\)。RPN 的第一步是在\(w \times h\) 的每一个位置上去生成 \(k\) 个 Anchor,现在的问题是:每一个位置的 \(k\) 个 Anchor 是怎么生成的?答案是:拍脑袋生成的,Anchor 既然是 BBox,那么,就可以有不同的大小和长宽比,原文中选了 3 种不同的大小和 3 种不同的长宽比,组合一下可以得到 \(3 \times 3=9\) 个不同的 Anchor。
因此,一张输入图像生成的 Anchor 数量是 \(w \times h \times k\)。

给出 Proposal BBox

不靠谱的 BBox(即 Anchor)给出来了,接下来就是如何从这些不靠谱的,数量非常多的 BBox 中给出一些相对来说靠谱一些,并且数量不是那么多的 Proposal BBox。
回顾一下目前的状态,在 F_conv 的 \(w \times h\) 个位置上,每一个位置上的 depth 是 \(d\),相当于一个 \(d\) 维的向量,每一个位置上对应地生成了 \(k\) 个 Anchor。
这里要做的工作就是,对于每一个位置,通过这 \(d\) 维的向量,去调整(和过滤)它对应的 \(k\) 个 Anchor,得到靠谱一些的 Anchor,作为 Proposal BBox。
具体方法其实是比较野蛮粗暴的。直接在这 256 维的向量上引出 2 个 全连接层(FC),其中一个 FC 用来判断 Anchor 中是目标还是背景(二分类任务),另外一个 FC 用来在 Anchor 上回归出如果要得到稍微靠谱的 BBox 要如何进行的,即学习 \(\Delta{x_{center}}\), \(\Delta{y_{center}}\), \(\Delta{width}\), \(\Delta{height}\)。

搞定这一步之后,就完成了对于 Anchor 中是目标还是背景的判定以及如何调整 Anchor 使得其表示的 BBox 变得靠谱一些。
可是,上面只是完成了对于 F_conv 上一个位置的判定,F_conv 有 \(w \times h\) 个位置,因此要对每一个位置进行相同的判定。实际工程实现中是使用 \(1 \times 1\) 的卷积操作完成的。

接下来是一些后处理工作,包括:

  1. 对于上面被判断为背景的 Anchor,直接过滤掉;
  2. Anchor 中有一些是 overlap 的,要通过 nms 进行合并。

至此,完成了 Proposal BBox 的工作。接下来,就是和 Fast RCNN 完全相同的精修 BBox 以及识别 BBox 中具体是哪类目标的过程。

Stage 2:精修 BBox 以及识别目标的具体类别

Stage 1 的 RPN 给了还是不太精确的 Proposal BBox。那么,对于 Stage 2 的精修 BBox 和识别具体目标这个任务,一个简单的想法是把 Proposal BBox 从原图中抠出来,resize 到固定长宽尺寸,然后输入到一个 CNN 中去做具体类别识别和 BBox 精修。但是,这种方法又要过一遍 CNN 提取特征的过程,该过程计算量很大。在上面的 RPN 过程中,F_conv 已经有提取的特征了,是否可以复用 F_conv 的特征呢?
首先考虑一下直接使用 F_conv 的阻力。在 RCNN 中之所以把 Proposal BBox resize 到 fixed size 的 image 然后 feed 到 CNN 中,是因为在 classification 和 regression 的两个 branch 中均有 FC 操作,而 FC 操作要求输入的 shape 必须是 fixed 的。如果要复用 F_conv 的结果,必须保证在输入到 classification 和 regression 的所有的 patch 必须是 fixed size 的。
一种方法是,从 F_conv crop 出 Proposal BBox 对应的 feature map 然后 resize 到 fixed size 输入到后续网络中。另一种方法,也是 Faster RCNN 采用的方法是对 Proposal BBox 对应的 F_conv 区域进行 ROIPooling,然后把 ROIPooling 的结果 feed 到后续网络中。(ROIPooling 可以确保输入不同尺寸的 feature map,输出固定尺寸的 feature map)
后续过程和 RPN 类似,只不过 classification 部分不是判断背景/目标的二分类任务,而是判断具体是什么目标的多分类任务(N+1,N 是类别数,1 是背景)。然后是去除被判定为背景的 BBox 以及 NMS 过程。

后注

Anchor 的选择其实也并不是完全拍脑袋,还是要根据具体检测的目标特点去选择,例如,检测行人,可以选一些瘦长的 BBox。同时还可以根据数据的统计特定来确定,例如,需要检测的目标大小均在 \(50 \times 50\) 像素以上,那么就没必要选太小的尺度。
为容易理解,上面的 RPN 网络做了简化,实践中使用的要稍微复杂一点,但是思路不变。

0%