一文读懂 Faster RCNN

date
Sep 20, 2021
Last edited time
Mar 27, 2023 08:48 AM
status
Published
slug
一文读懂Faster_RCNN
tags
DL
CV
summary
type
Post
Field
Plat
经过 R-CNN 和 Fast RCNN 的积淀,Ross B. Girshick 在 2016 年提出了新的 Faster RCNN,在结构上,Faster RCNN 已经将特征抽取 (feature extraction),proposal 提取,bounding box regression(rect refine),classification 都整合在了一个网络中,使得综合性能有较大提高,在检测速度方面尤为明显。
notion image
依作者看来,如图 1,Faster RCNN 其实可以分为 4 个主要内容:
  1. Conv layers。作为一种 CNN 网络目标检测方法,Faster RCNN 首先使用一组基础的 conv+relu+pooling 层提取 image 的 feature maps。该 feature maps 被共享用于后续 RPN 层和全连接层。
  1. Region Proposal Networks。RPN 网络用于生成 region proposals。该层通过 softmax 判断 anchors 属于 positive 或者 negative,再利用 bounding box regression 修正 anchors 获得精确的 proposals。
  1. Roi Pooling。该层收集输入的 feature maps 和 proposals,综合这些信息后提取 proposal feature maps,送入后续全连接层判定目标类别。
  1. Classification。利用 proposal feature maps 计算 proposal 的类别,同时再次 bounding box regression 获得检测框最终的精确位置。
所以本文以上述 4 个内容作为切入点介绍 Faster R-CNN 网络。
下图展示了 python 版本中的 VGG16 模型中的 faster_rcnn_test.pt 的网络结构,可以清晰的看到该网络对于一副任意大小 PxQ 的图像:
notion image
  • 首先缩放至固定大小 ,然后将 图像送入网络;
  • 而 Conv layers 中包含了 13 个 conv 层 + 13 个 relu 层 + 4 个 pooling 层;
  • RPN 网络首先经过 3x3 卷积,再分别生成 positive anchors 和对应 bounding box regression 偏移量,然后计算出 proposals;
  • 而 Roi Pooling 层则利用 proposals 从 feature maps 中提取 proposal feature 送入后续全连接和 softmax 网络作 classification(即分类 proposal 到底是什么 object)。

1 Conv layers

Conv layers 包含了 conv,pooling,relu 三种层。以 python 版本中的 VGG16 模型中的 faster_rcnn_test.pt 的网络结构为例,如图 2,Conv layers 部分共有 13 个 conv 层,13 个 relu 层,4 个 pooling 层。这里有一个非常容易被忽略但是又无比重要的信息,在 Conv layers 中:
  1. 所有的 conv 层都是:kernel_size=3,pad=1,stride=1
  1. 所有的 pooling 层都是:kernel_size=2,pad=0,stride=2
为何重要?在 Faster RCNN Conv layers 中对所有的卷积都做了扩边处理( pad=1,即填充一圈 0),导致原图变为 大小,再做 3x3 卷积后输出 MxN 。正是这种设置,导致 Conv layers 中的 conv 层不改变输入和输出矩阵大小。如图 3:
notion image
类似的是,Conv layers 中的 pooling 层 kernel_size=2,stride=2。这样每个经过 pooling 层的 矩阵,都会变为 大小。综上所述,在整个 Conv layers 中,conv 和 relu 层不改变输入输出大小,只有 pooling 层使输出长宽都变为输入的
那么,一个 大小的矩阵经过 Conv layers 固定变为 . 这样 Conv layers 生成的 feature map 中都可以和原图对应起来。

2 Region Proposal Networks(RPN)

经典的检测方法生成检测框都非常耗时,如 OpenCV adaboost 使用滑动窗口 + 图像金字塔生成检测框;或如 R-CNN 使用 SS(Selective Search) 方法生成检测框。而 Faster RCNN 则抛弃了传统的滑动窗口和 SS 方法,直接使用 RPN 生成检测框,这也是 Faster R-CNN 的巨大优势,能极大提升检测框的生成速度。
notion image
上图展示了 RPN 网络的具体结构。可以看到 RPN 网络实际分为 2 条线,上面一条通过 softmax 分类 anchors 获得 positive 和 negative 分类,下面一条用于计算对于 anchors 的 bounding box regression 偏移量,以获得精确的 proposal。而最后的 Proposal 层则负责综合 positive anchors 和对应 bounding box regression 偏移量获取 proposals,同时剔除太小和超出边界的 proposals。其实整个网络到了 Proposal Layer 这里,就完成了相当于目标定位的功能。

2.1 anchors

提到 RPN 网络,就不能不说 anchors。所谓 anchors,实际上就是一组由 rpn/generate_anchors.py 生成的矩形。直接运行作者 demo 中的 generate_anchors.py 可以得到以下输出:
其中每行的 4 个值 表矩形左上和右下角点坐标。9 个矩形共有 3 种形状,长宽比为大约为 三种,如图 6。实际上通过 anchors 就引入了检测中常用到的多尺度方法。
notion image
注:关于上面的 anchors size,其实是根据检测图像设置的。在 python demo 中,会把任意大小的输入图像 reshape 成 (即图 2 中的 M=800,N=600)。再回头来看 anchors 的大小,anchors 中长宽 1:2 中最大为 352x704,长宽 2:1 中最大 ,基本是 cover 了 800x600 的各个尺度和形状。
那么这 9 个 anchors 是做什么的呢?借用 Faster RCNN 论文中的原图,如下图,遍历 Conv layers 计算获得的 feature maps,为每一个点都配备这 9 种 anchors 作为初始的检测框。这样做获得检测框很不准确,不用担心,后面还有 2 次 bounding box regression 可以修正检测框位置。
notion image
解释一下上面这张图的数字。
  1. 在原文中使用的是 ZF model 中,其 Conv Layers 中最后的 conv5 层 num_output=256,对应生成 256 张特征图,所以相当于 feature map 每个点都是 256-dimensions
  1. 在 conv5 之后,做了 rpn_conv/3x3 卷积且 num_output=256,相当于每个点又融合了周围 3x3 的空间信息(猜测这样做也许更鲁棒?反正我没测试),同时 256-d 不变(如图 4 和图 7 中的红框)
  1. 假设在 conv5 feature map 中每个点上有 k 个 anchor(默认 k=9),而每个 anhcor 要分 positive 和 negative,所以每个点由 256d feature 转化为 cls=2•k scores;而每个 anchor 都有 (x, y, w, h) 对应 4 个偏移量,所以 reg=4•k coordinates
  1. 补充一点,全部 anchors 拿去训练太多了,训练程序会在合适的 anchors 中随机选取 128 个 postive anchors+128 个 negative anchors 进行训练(什么是合适的 anchors 下文 5.1 有解释)
注意,在本文讲解中使用的 VGG conv5 num_output=512,所以是 512d,其他类似。
其实 RPN 最终就是在原图尺度上,设置了密密麻麻的候选 Anchor。然后用 cnn 去判断哪些 Anchor 是里面有目标的 positive anchor,哪些是没目标的 negative anchor。所以,仅仅是个二分类而已!
那么 Anchor 一共有多少个?原图 800x600,VGG 下采样 16 倍,feature map 每个点设置 9 个 Anchor,所以:
其中 ceil() 表示向上取整,是因为 VGG 输出的 feature map size= 50*38。
notion image

2.3 softmax 判定 positive 与 negative

一副 MxN 大小的矩阵送入 Faster RCNN 网络后,到 RPN 网络变为 (M/16)x(N/16),不妨设 W=M/16,H=N/16。在进入 reshape 与 softmax 之前,先做了 1x1 卷积,如图 9:
notion image
该 1x1 卷积的 caffe prototxt 定义如下:
可以看到其 num_output=18,也就是经过该卷积的输出图像为 WxHx18 大小(注意第二章开头提到的卷积计算方式)。这也就刚好对应了 feature maps 每一个点都有 9 个 anchors,同时每个 anchors 又有可能是 positive 和 negative,所有这些信息都保存 WxHx(9*2) 大小的矩阵。为何这样做?后面接 softmax 分类获得 positive anchors,也就相当于初步提取了检测目标候选区域 box(一般认为目标在 positive anchors 中)。
那么为何要在 softmax 前后都接一个 reshape layer?其实只是为了便于 softmax 分类,至于具体原因这就要从 caffe 的实现形式说起了。在 caffe 基本数据结构 blob 中以如下形式保存数据:
对应至上面的保存 positive/negative anchors 的矩阵,其在 caffe blob 中的存储形式为 [1, 2x9, H, W]。而在 softmax 分类时需要进行 positive/negative 二分类,所以 reshape layer 会将其变为[1, 2, 9xH, W] 大小,即单独 “腾空” 出来一个维度以便 softmax 分类,之后再 reshape 回复原状。贴一段 caffe softmax_loss_layer.cpp 的 reshape 函数的解释,非常精辟:
综上所述,RPN 网络中利用 anchors 和 softmax 初步提取出 positive anchors 作为候选区域(另外也有实现用 sigmoid 代替 softmax,输出 [1, 1, 9xH, W] 后接 sigmoid 进行 positive/negative 二分类,原理一样)。

2.4 bounding box regression 原理

如图 9 所示绿色框为飞机的 Ground Truth(GT),红色为提取的 positive anchors,即便红色的框被分类器识别为飞机,但是由于红色的框定位不准,这张图相当于没有正确的检测出飞机。所以我们希望采用一种方法对红色的框进行微调,使得 positive anchors 和 GT 更加接近。
notion image
对于窗口一般使用四维向量 表示,分别表示窗口的中心点坐标和宽高。对于图 11,红色的框 A 代表原始的 positive Anchors,绿色的框 G 代表目标的 GT,我们的目标是寻找一种关系,使得输入原始的 anchor A 经过映射得到一个跟真实窗口 G 更接近的回归窗口 G’,即:
  • 给定 anchor 和
  • 寻找一种变换 F,使得:,其中
notion image
那么经过何种变换 F 才能从图 10 中的 anchor A 变为 G’呢? 比较简单的思路就是:
  • 先做平移
  • 再做缩放
观察上面 4 个公式发现,需要学习的是 这四个变换。当输入的 anchor A 与 GT 相差较小时,可以认为这种变换是一种线性变换, 那么就可以用线性回归来建模对窗口进行微调(注意,只有当 anchors A 和 GT 比较接近时,才能使用线性回归模型,否则就是复杂的非线性问题了)。接下来的问题就是如何通过线性回归获得 了。线性回归就是给定输入的特征向量 , 学习一组参数 , 使得经过线性回归后的值跟真实值 非常接近,即 。对于该问题,输入 cnn feature map,定义为 ;同时还有训练传入 之间的变换量,即 。输出是 四个变换。那么目标函数可以表示为:
其中 是对应 anchor 的 feature map 组成的特征向量, 是需要学习的参数, 是得到的预测值( 表示 ,也就是每一个变换对应一个上述目标函数)。为了让预测值 与真实值 差距最小,设计 L1 损失函数:
函数优化目标为:
为了方便描述,这里以 L1 损失为例介绍,而真实情况中一般使用 soomth-L1 损失。需要说明,只有在 GT 与需要回归框位置比较接近时,才可近似认为上述线性变换成立。说完原理,对应于 Faster RCNN 原文,positive anchor 与 ground truth 之间的平移量 与尺度因子 如下:
对于训练 bouding box regression 网络回归分支,输入是 cnn feature ,监督信号是 Anchor 与 GT 的差距 ,即训练目标是:输入 的情况下使网络输出与监督信号尽可能接近。那么当 bouding box regression 工作时,再输入 时,回归网络分支的输出就是每个 Anchor 的平移量和变换尺度 ,显然即可用来修正 Anchor 位置了。

2.5 对 proposals 进行 bounding box regression

在了解 bounding box regression 后,再回头来看 RPN 网络第二条线路,如图 12。
notion image
先来看一看上图 11 中 1x1 卷积的 caffe prototxt 定义:
可以看到其 num_output=36,即经过该卷积输出图像为 ,在 caffe blob 存储为 ,这里相当于 feature maps 每个点都有 9 个 anchors,每个 anchors 又都有 4 个用于回归的 变换量。
回到图 8,VGG 输出 的特征,对应设置 个 anchors,而 RPN 输出:
  1. 大小为 的 positive/negative softmax 分类特征矩阵
  1. 大小为 的 regression 坐标回归特征矩阵
恰好满足 RPN 完成 positive/negative 分类 + bounding box regression 坐标回归.

2.6 Proposal Layer

Proposal Layer 负责综合所有 变换量和 positive anchors,计算出精准的 proposal,送入后续 RoI Pooling Layer。还是先来看看 Proposal Layer 的 caffe prototxt 定义:
Proposal Layer 有 3 个输入:positive vs negative anchors 分类器结果 rpn_cls_prob_reshape,对应的 bbox reg 的 变换量 rpn_bbox_pred,以及 im_info;另外还有参数 feat_stride=16,这和图 4 是对应的。首先解释 im_info。对于一副任意大小 PxQ 图像,传入 Faster RCNN 前首先 reshape 到固定 MxN,im_info=[M, N, scale_factor] 则保存了此次缩放的所有信息。然后经过 Conv Layers,经过 4 次 pooling 变为 WxH=(M/16)x(N/16) 大小,其中 feature_stride=16 则保存了该信息,用于计算 anchor 偏移量。
notion image
Proposal Layer forward(caffe layer 的前传函数)按照以下顺序依次处理:
  1. 生成 anchors,利用对所有的 anchors 做 bbox regression 回归(这里的 anchors 生成和训练时完全一致)
  1. 按照输入的 positive softmax scores 由大到小排序 anchors,提取前 pre_nms_topN(e.g. 6000) 个 anchors,即提取修正位置后的 positive anchors
  1. 限定超出图像边界的 positive anchors 为图像边界,防止后续 roi pooling 时 proposal 超出图像边界(见文章底部 QA 部分图 21)
  1. 剔除尺寸非常小的 positive anchors
  1. 对剩余的 positive anchors 进行 NMS(nonmaximum suppression)
  1. Proposal Layer 有 3 个输入:positive 和 negative anchors 分类器结果 rpn_cls_prob_reshape,对应的 bbox reg 的 (e.g. 300) 结果作为 proposal 输出
之后输出 proposal=[x1, y1, x2, y2],注意,由于在第三步中将 anchors 映射回原图判断是否超出边界,所以这里输出的 proposal 是对应 MxN 输入图像尺度的,这点在后续网络中有用。另外我认为,严格意义上的检测应该到此就结束了,后续部分应该属于识别了。
RPN 网络结构就介绍到这里,总结起来就是:生成 anchors -> softmax 分类器提取 positvie anchors -> bbox reg 回归 positive anchors -> Proposal Layer 生成 proposals

3 RoI pooling

而 RoI Pooling 层则负责收集 proposal,并计算出 proposal feature maps,送入后续网络。从图 2 中可以看到 Rol pooling 层有 2 个输入:
  1. 原始的 feature maps
  1. RPN 输出的 proposal boxes(大小各不相同)

3.1 为何需要 RoI Pooling

先来看一个问题:对于传统的 CNN(如 AlexNet 和 VGG),当网络训练好后输入的图像尺寸必须是固定值,同时网络输出也是固定大小的 vector or matrix。如果输入图像大小不定,这个问题就变得比较麻烦。有 2 种解决办法:
  1. 从图像中 crop 一部分传入网络
  1. 将图像 warp 成需要的大小后传入网络
notion image
两种办法的示意图如图 14,可以看到无论采取那种办法都不好,要么 crop 后破坏了图像的完整结构,要么 warp 破坏了图像原始形状信息。
回忆 RPN 网络生成的 proposals 的方法:对 positive anchors 进行 bounding box regression,那么这样获得的 proposals 也是大小形状各不相同,即也存在上述问题。所以 Faster R-CNN 中提出了 RoI Pooling 解决这个问题。不过 RoI Pooling 确实是从 Spatial Pyramid Pooling 发展而来,但是限于篇幅这里略去不讲,有兴趣的读者可以自行查阅相关论文。

3.2 RoI Pooling 原理

分析之前先来看看 RoI Pooling Layer 的 caffe prototxt 的定义:
其中有新参数 pooled_wpooled_h,另外一个参数 spatial_scale 认真阅读的读者肯定已经知道知道用途。RoI Pooling layer forward 过程:
  • 由于 proposal 是对应 尺度的,所以首先使用 spatial_scale 参数将其映射回 大小的 feature map 尺度;
  • 再将每个 proposal 对应的 feature map 区域水平分为 pooled_w x pooled_h 的网格;
  • 对网格的每一份都进行 max pooling 处理。
这样处理后,即使大小不同的 proposal 输出结果都是 pooled_w x pooled_h
固定大小,实现了固定长度输出。
notion image

4 Classification

Classification 部分利用已经获得的 proposal feature maps,通过 full connect 层与 softmax 计算每个 proposal 具体属于那个类别(如人,车,电视等),输出 cls_prob 概率向量;同时再次利用 bounding box regression 获得每个 proposal 的位置偏移量 bbox_pred,用于回归更加精确的目标检测框。Classification 部分网络结构如图 16。
notion image
从 RoI Pooling 获取到 7x7=49 大小的 proposal feature maps 后,送入后续网络,可以看到做了如下 2 件事:
  1. 通过全连接和 softmax 对 proposals 进行分类,这实际上已经是识别的范畴了
  1. 再次对 proposals 进行 bounding box regression,获取更高精度的 bbox_pred.

5 Faster RCNN 训练

Faster R-CNN 的训练,是在已经训练好的 model(如 VGG_CNN_M_1024,VGG,ZF)的基础上继续进行训练。实际中训练过程分为 6 个步骤:
  1. 在已经训练好的 model 上,训练 RPN 网络,对应 stage1_rpn_train.pt
  1. 利用步骤 1 中训练好的 RPN 网络,收集 proposals,对应 rpn_test.pt
  1. 第一次训练 Fast RCNN 网络,对应 stage1_fast_rcnn_train.pt
  1. 第二训练 RPN 网络,对应 stage2_rpn_train.pt
  1. 再次利用步骤 4 中训练好的 RPN 网络,收集 proposals,对应 rpn_test.pt
  1. 第二次训练 Fast RCNN 网络,对应 stage2_fast_rcnn_train.pt
可以看到训练过程类似于一种 “迭代” 的过程,不过只循环了 2 次。至于只循环了 2 次的原因是应为作者提到:“A similar alternating training can be run for more iterations, but we have observed negligible improvements”,即循环更多次没有提升了。接下来本章以上述 6 个步骤讲解训练过程。
下面是一张训练过程流程图,应该更加清晰:

5.1 训练 RPN 网络

在该步骤中,首先读取 RBG 提供的预训练好的 model(本文使用 VGG),开始迭代训练。来看看 stage1_rpn_train.pt 网络结构,如图 19。
notion image
与检测网络类似的是,依然使用 Conv Layers 提取 feature maps。整个网络使用的 Loss 如下:
上述公式中 表示 anchors index, 表示 positive softmax probability, 代表对应的 GT predict 概率(即当第 个 anchor 与 GT 间 IoU>0.7,认为是该 anchor 是 positive;反之 IoU<0.3 时,认为是该 anchor 是 negative;至于那些 0.3<IoU<0.7 的 anchor 则不参与训练);
代表 predict bounding box, 代表对应 positive anchor 对应的 GT box。可以看到,整个 Loss 分为 2 部分:
  1. cls loss,即 rpn_cls_loss 层计算的 softmax loss,用于分类 anchors 为 positive 与 negative 的网络训练
  1. reg loss,即 rpn_loss_bbox 层计算的 soomth L1 loss,用于 bounding box regression 网络训练。注意在该 loss 中乘了 ,相当于只关心 positive anchors 的回归(其实在回归中也完全没必要去关心 negative)。
由于在实际过程中, 差距过大,用参数λ平衡二者,使总的网络 Loss 计算过程中能够均匀考虑 2 种 Loss。这里比较重要是 使用的 soomth L1 loss,计算公式如下:

5.2 通过训练好的 RPN 网络收集 proposals

在该步骤中,利用之前的 RPN 网络,获取 proposal rois,同时获取 positive softmax probability,如图 20,然后将获取的信息保存在 python pickle 文件中。该网络本质上和检测中的 RPN 网络一样,没有什么区别。
notion image

5.3 训练 Faster RCNN 网络

读取之前保存的 pickle 文件,获取 proposals 与 positive probability。从 data 层输入网络。然后:
  1. 将提取的 proposals 作为 rois 传入网络,如图 21 蓝框
  1. 计算 bbox_inside_weights+bbox_outside_weights,作用与 RPN 一样,传入 soomth_L1_loss layer,如图 21 绿框
这样就可以训练最后的识别 softmax 与最终的 bounding box regression 了。
notion image
之后的 stage2 训练都是大同小异,不再赘述了。Faster R-CNN 还有一种 end-to-end 的训练方式,可以一次完成 train,有兴趣请自己看作者 GitHub 吧。

© Lazurite 2021 - 2024