一、RPN的提出背景
在当时(2016年),先进的目标检测网络如SPPnet、Fast R-CNN提出后,减少了目标识别任务的计算时间,使得区域候选成为了计算瓶颈。当时已有的区域候选算法:Selective Search, SS仅能做到2s完成一张图像的区域候选;EdgeBoxes虽然更快,能做到0.2s完成,但是这是以牺牲计算质量为代价的。
RPN(Region Proposal Network,区域建议网络)的提出背景主要源于传统目标检测算法在候选区域生成方面的不足。传统的候选区域生成方法,如Selective Search,虽然在一定程度上推动了目标检测的发展,但其计算量大、速度慢,成为目标检测实时性的瓶颈。因此,研究者们开始探索如何通过深度学习的方法,利用卷积神经网络(CNN)来高效地生成候选区域,从而提高目标检测的速度和精度。
Region Proposal Network,直接翻译是“区域生成网络”,通俗讲是“筛选出可能会有目标的框”。其本质是基于滑窗的无类别object检测器,输入是任意尺度的图像,输出是一系列矩形候选区域。
传统检测方法提取候选区域都非常耗时,如OpenCV adaboost使用滑动窗口+图像金字塔,或R-CNN使用SS(Selective Search)。而Faster RCNN直接使用RPN生成检测框,能极大提升检测框的生成速度,而RPN是用一个全卷积网络来实现的,可以与检测网络共享整幅图像的卷积特征,从而产生几乎无代价的区域推荐。
二、时间及作者
RPN网络由任少卿、何凯明等人在2015年提出,作为Faster R-CNN框架的重要组成部分。Faster R-CNN论文的发表标志着RPN网络的正式问世,该论文在目标检测领域引起了广泛关注,为后续的目标检测算法研究提供了重要的思路和方法。
在正式详细说明RPN之前,我想先总结下Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks这篇文章的几点精华,提炼要点:
- RPN是一个全卷积网络,能同时预测候选区域的边界和置信度;
- RPN的提出目的是为了提升目标检测的效率(速度),使用RPN后与检测任务相比,候选区域的选择几乎是不耗费计算资源(nearly cost-free);
- RPN能提升目标检测效率的原因是RPN与分类器共用要检测的原图的卷积特征图;
- 首次提出锚框(anchor)的概念,在锚框提出之前候选框的选择只能通过各种尺寸的图像(pyramids of images)或者各种尺寸的算子(pyramids of filters),这也是为了提升目标检测的效率。
三、主要贡献
RPN网络的主要贡献体现在以下几个方面:
- 高效生成候选区域:RPN通过全卷积网络的方式,在特征图上滑动窗口并预测每个位置的锚框(anchor boxes),从而高效地生成候选区域。这种方法相比传统的Selective Search等方法,计算量更小,速度更快。
- 提升目标检测性能:RPN生成的候选区域质量更高,能够更准确地覆盖目标区域,从而提高后续目标检测网络的分类和回归精度。
- 实现端到端训练:RPN与后续的目标检测网络(如Fast R-CNN)共享卷积特征,使得整个检测框架可以实现端到端的训练和优化,进一步提升了检测性能。
四、优缺点
优点:
- 高效性:RPN通过全卷积网络的方式生成候选区域,计算效率高,速度快。
- 准确性:生成的候选区域质量高,能够更准确地覆盖目标区域。
- 灵活性:RPN可以与其他目标检测网络结合使用,形成端到端的检测框架。
缺点:
- 对硬件要求高:RPN及后续的目标检测网络需要较大的计算量和存储空间,对硬件资源的要求较高。
- 参数调整复杂:RPN中涉及的参数较多(如锚框的尺度、长宽比等),需要仔细调整以获得最佳性能。
五、网络结构
RPN网络主要由以下几个部分组成:
- 特征提取层:输入图像经过预训练的卷积神经网络(如VGGNet、ResNet等),提取出丰富的特征图。
- 滑动窗口与锚框:在特征图上滑动窗口,并在每个位置上预测多个锚框。这些锚框作为候选区域的初始位置。
- 分类与回归层:对于每个锚框,RPN输出两个分数(前景和背景的概率)和四个坐标偏移量(用于调整锚框的位置)。
- 非极大值抑制:通过非极大值抑制算法去除冗余的候选区域。
RPN是一个全卷积网络,模型计算过程如下:
- 使用一个n×n滑动窗口(sliding window)在共享的卷积特征图上滑动并提取特征。每个滑动窗口的中间位置都对应有k个锚框(anchors),在本文中使用3种缩放比例×3种长宽比,共k=9种锚框;
- 对于每一个滑动窗口,通过卷积计算将其转换为一个固定维度向量(例如基础CNN为ZFnet时,该向量长度为256;基础CNN为VGG时,该向量长度为512),通常称为"中间层"或"隐藏层";
- 然后使用这个中间层作为输入,分别经过两个孪生全连接层分支(two sibling fully connected layers):分类层和回归层;
- 分类层输出的是每个格子属于各个类别(如人、车等)的概率分布,需要注意的是这里输出的是有物体和无物体,即正样本(前景)和负样本(背景)两个概率,因此输出长度为2k;而回归层则输出的是每个格子中心点相对于真实边界框偏移的距离值,中心坐标偏移加上长宽偏移共4个值,因此输出长度为4k。
六. 基于PyTorch框架的RPN
注意:由于RPN与其他网络模型关系十分密切,难易独立分割开来。以下代码只是一个非常基础的示意,并未包含诸如锚框生成、前向传播中的空间尺寸变换、损失函数定义以及后处理步骤(如非极大值抑制NMS)等内容。在实际项目中,请参考Faster R-CNN论文或其他开源实现来完善整个RPN模块的功能。
import torch
import torch.nn as nn
from torchvision.models import vgg16
import torch.nn.functional as F
# RPN网络定义
class RPN(nn.Module): #softmax??? 长宽计算方法??
def __init__(self, in_channels=512):
super(RPN, self).__init__()
self.conv1 = nn.Conv2d(in_channels, 512, kernel_size=3, padding=1)
self.relu = nn.ReLU(inplace=True)
self.cls_score = nn.Conv2d(512, 2 * 9, kernel_size=1) #k=9
self.bbox_pred = nn.Conv2d(512, 4 * 9, kernel_size=1)
def forward(self, x):
x = self.relu(self.conv1(x))
rpn_cls_score = self.cls_score(x) #生成预测正负样本(有无物体的概率)[pos_label, neg_label]
rpn_cls_score_softmax = F.softmax(rpn_cls_score)
rpn_bbox_pred = self.bbox_pred(x) #生成bounding box的[tx, ty, tw, th]
return rpn_cls_score_softmax, rpn_bbox_pred
# 使用预训练的VGG16作为特征提取器
backbone = vgg16(pretrained=True).features[:-1] # 模型微调,去掉最后一个池化层
rpn = RPN(in_channels=512) # VGG16最后一层输出维度为512
# 定义损失函数和优化器
cls_loss_func = nn.CrossEntropyLoss()
bbox_loss_func = nn.SmoothL1Loss()
optimizer = torch.optim.SGD(rpn.parameters(), lr=0.001, momentum=0.9)
# 假设我们有一个训练数据加载器,格式为images, (gt_boxes, gt_labels)
data_loader = ...
# 训练过程,这里仅说明RPN的训练过程,即交替训练的第一步!
num_epochs = 10
for epoch in range(num_epochs):
for images, (gt_boxes, gt_labels) in enumerate(data_loader):
# 前向传播
features = backbone(images)
rpn_cls_scores_softmax, rpn_bbox_preds = rpn(features)
# 数据预处理,将预测结果调整到与ground truth相匹配的格式
# 这部分会根据你的具体实现有所不同,这里仅作示例
rpn_cls_scores_view = rpn_cls_scores_softmax.permute(0, 2, 3, 1).contiguous().view(-1, 2)
rpn_bbox_preds_view = rpn_bbox_preds.permute(0, 2, 3, 1).contiguous().view(-1, 4)
gt_labels_view = gt_labels.view(-1)
# 计算损失
rpn_cls_loss = cls_loss_func(rpn_cls_scores_view, gt_labels_view)
rpn_bbox_loss = bbox_loss_func(rpn_bbox_preds_view, gt_boxes)
# 总损失,这里暂时忽略Ncls,Nreg,λ
loss = rpn_cls_loss + rpn_bbox_loss
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 打印训练信息
if (images + 1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{images+1}/{len(data_loader)}], Loss: {loss.item()}')
# 训练结束
print('Training finished.')
七、应用场景
RPN网络广泛应用于各种目标检测任务中,包括但不限于人脸检测、车辆检测、行人检测等。此外,RPN还可以作为其他图像处理任务(如实例分割、关键点检测等)的预处理步骤,为后续任务提供高质量的候选区域。在实际应用中,RPN已经成为许多先进目标检测算法的重要组成部分,推动了目标检测技术的快速发展。