11.3 Faster R-CNN

Author

jshn9515

Published

April 5, 2026

在上一节中,我们介绍了 Fast R-CNN。它通过共享整张图像的卷积特征,成功解决了 R-CNN 中对每个 proposal 做重复做卷积的低效问题;同时,它还把分类和边界框回归统一到了同一个网络中,使目标检测开始走向端到端训练。

但是,Fast R-CNN 仍然有一个关键瓶颈没有解决:候选区域(Region Proposals)仍然依赖外部算法生成。也就是说,虽然卷积特征可以共享,后续分类和回归也都放进了神经网络里,但 proposal 这一步仍然要依赖 Selective Search 这样的传统方法预先完成。而 Selective Search 本身速度较慢,无法通过反向传播进行学习,也不能和后面的检测网络联合优化。结果就是,Fast R-CNN 虽然检测变快了,但整个系统依然不够快。

所以,Faster R-CNN (Ren et al. 2016) 要解决的问题也就变得非常明确:我们能不能连 proposal 也不要交给外部算法,而是直接让神经网络自己来生成?这正是 Faster R-CNN 的核心思想。它在共享卷积特征图之上,进一步引入了一个专门负责生成候选框的模块,也就是 RPN (Region Proposal Network)。这样一来,候选区域生成和目标检测第一次真正被整合进了同一个统一框架中。

图 1:Faster R-CNN 网络结构
图 1:Faster R-CNN 网络结构 (Zhang et al. 2023, fig. 13.8.4)

在接下来的内容中,我们就来详细看看,Faster R-CNN 是如何通过 Region Proposal Network 把候选框生成也纳入神经网络内部的,以及它为什么能够在提升速度的同时,继续保持很高的检测精度。

11.3.1 Faster R-CNN 的核心思路

Fast R-CNN 已经通过共享卷积特征显著减少了重复计算,但它仍然依赖外部的候选区域生成算法,例如 Selective Search。也就是说,检测网络本身已经变得更统一了,可 proposal 这一步依然游离在神经网络之外。于是,整个系统虽然比 R-CNN 快了很多,但还不够完整。

Faster R-CNN 的关键改进,就是把候选区域的生成也纳入网络内部。它不再先用传统算法单独产生 proposal,再送进检测器,而是直接在共享特征图上增加一个专门负责生成候选区域的模块。这个模块就是 Region Proposal Network (RPN)

这样一来,Faster R-CNN 的主线就变得很清楚了:

  1. 先对整张图像提取共享特征;
  2. 再基于这些共享特征生成候选区域;
  3. 最后对候选区域做分类和边界框回归。

从这个角度看,Faster R-CNN 相比 Fast R-CNN 最本质的变化,就是把 proposal 从一个外部的传统算法,变成了网络内部一个可以学习的模块。不过,这里先不要急着把 RPN 想得太复杂。我们暂时只需要把它理解成一个在特征图上寻找“哪里可能有目标”的网络模块。至于它到底如何生成候选框、为什么需要 anchor、训练时又是如何打标签的,我们放到后面两小节再展开。

有了这个整体主线之后,下面我们就先来看 Faster R-CNN 中最关键的新模块:Region Proposal Network。

import matplotlib.pyplot as plt
import PIL.Image as Image
import PIL.ImageDraw as ImageDraw
import torch
import torchvision.models.detection as models
import torchvision.transforms.v2 as v2

%config InlineBackend.figure_format = 'retina'
print('PyTorch version:', torch.__version__)
PyTorch version: 2.11.0+xpu

11.3.2 Region Proposal Network (RPN)

Region Proposal Network (RPN) 是 Faster R-CNN 中的一个重要创新模块。它的主要作用是直接在卷积特征图上生成候选区域,从而取代了传统的 Selective Search 等外部算法。

从本质上说,RPN 做的是一个“前景性判断 + 边界框回归”的任务。也就是说,对于特征图上的许多位置,它要回答两个问题:

  1. 这个位置附近是否可能有目标?
  2. 如果有目标,那么这个目标的边界框应该是什么样子的?

这听起来其实和最终检测头的工作有点像。区别在于,RPN 不关心目标的具体类别,它只关心这里有没有物体。所以,RPN 的分类任务不是多分类,而是一个二分类:前景 or 背景。

RPN 的做法就是,在共享特征图上,用一个很小的网络窗口不断滑动。每滑到一个位置,就基于当前位置附近的特征,预测若干个可能的候选框。

首先,先对特征图施加一个小的卷积层(例如 3x3 卷积),让每个空间位置都融合一点局部上下文信息。然后,在这个位置上接两个并行分支,一个分支输出前景和背景分数,一个分支输出边界框回归参数。但是这里会有一个问题:既然我们不用外部算法(例如 Selective Search)来生成 proposal,那么我们怎么知道每个位置应该预测哪些候选框呢?

Faster R-CNN 的解决办法是引入 锚框(Anchor)

我们在特征图的每一个位置,预先放置一组参考框。这些参考框的数量和尺寸是人为确定的。比如,在同一个位置上,我们可以预先定义 3 个尺度、3 个宽高比的锚框,那么这个位置就对应 9 个锚框。这样一来,RPN 就可以对每个 anchor 预测一个前景分数,表示这个 anchor 是否可能包含目标;然后预测一个 4 维回归向量,用来对这个 anchor 的位置和大小进行微调。

所以,RPN 不是从零开始凭空生成框,而是从一组预设 anchor 出发,对它们进行打分和修正。这种做法让候选框生成问题变得更容易学习,因为网络不需要直接回归绝对坐标,只需要学习如何在参考框基础上做偏移就行。这也继承了 R-CNN 中边界框回归的思想。

假设特征图大小为 \(H \times W\),每个位置有 \(k\) 个候选框,那么总共就有 \(H \times W \times k\) 个候选框。对于每个候选框,RPN 都会输出 2 个分类分数(前景 / 背景)和 4 个回归参数(框位置修正)。

所以,整个 RPN 的输出张量规模大致可以写成:

  • 分类分支:\(H \times W \times 2k\)
  • 回归分支:\(H \times W \times 4k\)

这些预测结果经过解码之后,就会得到一大批候选框。然后,再通过排序、筛选和 NMS,保留其中质量最高的一部分 proposal,送给后面的检测头。

从这个角度看,RPN 其实就是把“候选区域生成”这个原本靠人工设计的传统步骤,变成了一个可以通过数据学习的神经网络模块。这一步非常关键,因为它意味着目标检测中的 proposal 终于可以和后续分类、定位共同建立在深度特征之上,而不再依赖传统算法。当然,候选框的设计还是靠人工确定,所以如何取消人工设计的候选框也是后续研究的一个重要方向,我们在后面会继续介绍。

11.3.3 Anchor 是怎么工作的

虽然我们刚才已经引入了 anchor 的概念,但如果第一次接触 Faster R-CNN,很多人还是会觉得这个设计有点抽象:为什么非要先放一堆参考框?直接让网络预测真实框不行吗?

要理解 anchor,最重要的是先认识到一个事实:图像中的目标尺度变化非常大,长宽比也差别很大。同样是一个特征图位置,它在原图上可能对应的是一只猫,也可能是一辆车,还可能是一根很细长的路灯。如果我们只让这个位置预测一个固定形状的框,那么它显然很难同时适应这么多不同形态的目标。

Anchor 的作用,就是提前考虑到各种常见情况。

例如,在某个特征图位置上,我们可以设置 3 种尺度:128x128,256x256,512x512 和 3 种长宽比:1:1,1:2,2:1。这样组合起来,就会得到 9 个 anchor。它们都以当前位置为中心,但大小和形状不同。于是,网络在这个位置上就不是只面对一种框,而是同时面对 9 种候选框。

这样做的好处是,真实目标通常总能和某个 anchor 比较接近。网络只需要学习在这个 anchor 的基础上,往左一点、往右一点、变宽一点、变高一点,就能把 anchor 修正成更准确的目标框。相比直接从零预测一个任意框,这显然是更容易学习的。

当然,anchor 也不是完美的。它依赖人为设定尺度和长宽比,如果这些设计和数据集中的真实目标分布差异太大,效果就可能受影响。后来很多检测器也尝试过 anchor-free 的方法,直接避免这类先验设计。但在 Faster R-CNN 这个阶段,anchor 是一个非常成功且影响深远的机制,它让候选框生成第一次变得既高效又可学习。

那么,既然 anchor 是一个预设的参考框,我们在训练 RPN 的时候,怎么给它打标签呢?也就是说,如何确定哪些 anchor 是正样本(包含目标),哪些是负样本(背景)?

一种常见的做法是基于 IoU 来打标签:

  • 如果某个 anchor 与某个真实框的 IoU 很高,比如大于 0.7,那么把它标记为正样本;
  • 如果某个 anchor 与所有真实框的 IoU 都很低,比如小于 0.3,那么把它标记为负样本;
  • 落在中间区间的 anchor 通常忽略,不参与训练。

因此,和真实目标高度重叠的 anchor,应该被判为前景;而几乎不和任何目标重叠的 anchor,应该被判为背景。至于为什么忽略那些 IoU 介于 0.3 和 0.7 之间的 anchor,是因为它们既不算是好样本,也不算是坏样本,参与训练可能会引入噪声,反而不利于模型学习。

11.3.4 Faster R-CNN 的整体流程

有了 RPN 和 anchor 的设计,下面我们再来回顾一下 Faster R-CNN 的整体流程:

  1. 输入整张图像;
  2. 使用卷积神经网络提取整张图像的共享特征图;
  3. 在共享特征图上运行 RPN,生成一批候选区域;
  4. 对这些候选区域进行筛选,例如去除过小框、使用 NMS 去掉大量重叠框;
  5. 将保留下来的 proposal 映射到共享特征图上;
  6. 通过 RoI Pooling(或后续版本中的 RoI Align)提取固定大小的区域特征;
  7. 将这些区域特征送入检测头,输出分类结果和边界框回归结果。
image = Image.open('figures/bus.jpg').convert('RGB')
transform = v2.Compose(
    [
        v2.ToImage(),
        v2.ToDtype(torch.float32, scale=True),
    ]
)
weights = models.FasterRCNN_ResNet50_FPN_Weights.DEFAULT
model = models.fasterrcnn_resnet50_fpn(weights=weights)
model.eval()

with torch.inference_mode():
    inputs = transform(image).unsqueeze(0)
    outputs = model(inputs)[0]

draw = ImageDraw.Draw(image)
for box, label, score in zip(outputs['boxes'], outputs['labels'], outputs['scores']):
    if score > 0.5:
        draw.rectangle(box.tolist(), outline='red', width=3)
        draw.text(
            xy=(box[0], box[1]),
            text=f'{weights.meta["categories"][label]}: {score:.2f}',
            fill='red',
            font_size=24,
        )

fig = plt.figure(1, figsize=(6, 6))
ax = fig.add_subplot(1, 1, 1)
ax.imshow(image)
ax.set_title('Faster R-CNN Detection')
ax.axis('off')
plt.show()

可以看到,Faster R-CNN 准确识别出了四个人和一辆汽车,且置信度都在 97% 以上。这个结果已经非常不错了。唯一美中不足的就是,最右边那个人的边界框高度不够,模型可能把这个人的头部误认为是背景。但是整体来说,Faster R-CNN 是第一个能够在保持高精度的同时,真正实现端到端训练的目标检测框架。

11.3.5 Faster R-CNN 的意义与局限性

Faster R-CNN 在目标检测发展史上非常重要,因为它真正把两阶段检测框架统一了起来。

  • 在 R-CNN 中,proposal、CNN 特征提取、SVM 分类器、边界框回归器彼此分离;
  • 在 Fast R-CNN 中,虽然分类和回归已经统一进网络,但 proposal 仍然依赖外部方法;
  • 而到了 Faster R-CNN,proposal 也被整合进了神经网络,并且和检测头共享卷积特征。

这意味着,目标检测第一次形成了一个相对完整的深度学习流水线:从特征提取、候选框生成,到分类和定位,全部在统一框架中完成。 这个思想影响非常深远,后面的很多两阶段检测器,其实都可以看作是在 Faster R-CNN 的基础上不断改进。

不过,Faster R-CNN 也并不是没有局限。

  • 它仍然是两阶段方法。也就是说,它需要先生成 proposal,再对 proposal 逐个做精细处理。这种设计通常有利于精度,但在推理速度上天然不如单阶段检测器简洁。
  • 它继承了 RoI Pooling 带来的对齐误差问题。如果继续使用 Fast R-CNN 中的 RoI Pooling,那么 proposal 映射和网格划分中的离散化仍然会引入量化误差。后来很多版本会用 RoI Align 来改善这个问题。
  • Anchor 的设计依赖人工经验。不同数据集上的目标尺度和长宽比分布不同,anchor 的选择会影响效果。这也促使后来出现了越来越多 anchor-free 的检测方法。

References

Ren, Shaoqing, Kaiming He, Ross Girshick, and Jian Sun. 2016. Faster r-CNN: Towards Real-Time Object Detection with Region Proposal Networks. https://arxiv.org/abs/1506.01497.
Zhang, Aston, Zachary C. Lipton, Mu Li, and Alexander J. Smola. 2023. Dive into Deep Learning. Cambridge University Press. https://D2L.ai.

Reuse