查看全课大纲(第 58 / 93 节)
- 1.计算机视觉概述
- 2.计算机视觉应用场景
- 3.困难与挑战
- 4.开源库介绍
- 5.图像显示与存储
- 6.CLAHE
- 7.腐蚀膨胀与开闭运算
- 8.滤波与边缘填充
- 9.各种滤波
- 10.图像金字塔
- 11.傅里叶变换
- 12.短时傅里叶变换与小波变换
- 13.颜色特征与颜色直方图
- 14.几何特征_边缘
- 15.几何特征-角点与斑点
- 16.局部特征-SIFT特征描述子
- 17.局部特征-SURF特征描述子
- 18.局部特征-ORB特征描述子
- 19.其他特征描述子
- 20.传统图像分割技术
- 21.GraphCut
- 22.GrabCut
- 23.人脸检测的传统方法
- 24.行人检测的传统方法HOG+SVM
- 25.行人检测DPM
- 26.人工神经网络的架构
- 27.目标函数与梯度下降
- 28.误差反向传播
- 29.可手算的神经网络
- 30.改进目标函数与克服梯度消散
- 31.神经网络之传统神经网络与深度网络的差异
- 32.各种梯度下降算法
- 33.如何选择合适的梯度下降算法
- 34.避免过适应
- 35.CNN的架构
- 36.CNN的误差反向传播_卷积层
- 37.CNN的误差反向传播_池化层
- 38.分类所用的数据集
- 39.Alexnet详解
- 40.VGG详解
- 41.GoogLeNet_v1
- 42.BatchNormalization
- 43.GoogLeNet_v3v4
- 44.ResNet
- 45.ResNeXt
- 46.CNN设计准则
- 47.图像检索与推土机距离
- 48.基于颜色相似性
- 49.基于形状相似性
- 50.基于纹理相似性
- 51.基于局部特征相似性
- 52.DL的二值哈希编码
- 53.高维空间中检索KDTree
- 54.高维空间中检索LSH
- 55.目标识别任务综述
- 56.RCNN
- 57.SPP-Net
- 58.Fast R-CNN
- 59.Faster R-CNN
- 60.R-CNN系列模型回顾
- 61.R-FCN的设计思路
- 62.R-FCN的实现
- 63.YOLOv1
- 64.YOLOv2_9000
- 65.YOLOv3
- 66.图像分割应用场景与数据集
- 67.上采样的实现方式
- 68.FCN网络架构的由来
- 69.FCN构建细节
- 70.DeepLabv1
- 71.CRF
- 72.DeepLabv2
- 73.DeepLabv3
- 74.特定的医学影像分割任务
- 75.U-Net
- 76.3D U-Net
- 77.V-Net
- 78.DenseNet
- 79.病理切片分析任务及数据集
- 80.病理切片分析
- 81.图说任务及其传统处理方法
- 82.深度语言模型与RNN
- 83.LSTM及其变种
- 84.NIC模型
- 85.SAT模型
- 86.图说的性能指标
- 87.判别模型与生成模型
- 88.KL散度与JS散度
- 89.VAE
- 90.GAN的基本原理
- 91.DCGAN与CGAN
- 92.Wasserstein GAN
- 93.SRGAN
Fast R-CNN
小象实战讲义 · 基于深度学习的计算机视觉
在上一节我们学习了SPP-Net,它通过共享卷积计算和空间金字塔池化,大幅提升了R-CNN的检测速度。然而,SPP-Net仍然存在训练流程复杂、无法端到端优化等问题。本节将介绍目标检测发展史上的又一里程碑——Fast R-CNN。它通过引入感兴趣区域池化(RoI Pooling) 和多任务损失函数(Multi-task Loss),首次在R-CNN系列中实现了真正的端到端训练,在速度与精度上均取得了显著提升。
💡 核心导读
- 核心改进:Fast R-CNN在SPP-Net基础上,用RoI Pooling替代SPP层,并设计多任务损失函数将分类与回归统一。
- 端到端训练:首次在R-CNN系列中把分类与回归并入同一个网络,一次前向、一次反向即可完成训练,所有网络层参数都能参与微调。
- 性能飞跃:相比R-CNN,训练速度提升近9倍,单图检测速度提升146倍,同时平均精度(mAP)还有所提高。
- 关键技术:理解RoI Pooling如何将不同尺寸的候选区域映射为固定尺寸特征,以及多任务损失函数如何平衡分类与定位误差。
从SPP-Net到Fast R-CNN的演进
Fast R-CNN的提出,旨在解决R-CNN和SPP-Net遗留的几个关键问题:
- 多阶段训练:R-CNN/SPP-Net需要独立训练卷积网络、SVM分类器和边界框回归器,流程繁琐。
- 特征存储开销大:需要将中间特征(如fc7层特征)写入磁盘,供SVM和回归器训练,耗费大量存储空间和时间。
- SPP-Net的微调限制:由于SPP层的特殊结构,SPP-Net无法对SPP层之前的卷积层参数进行有效的反向传播和微调(fine-tuning)。
- 速度仍有瓶颈:尽管SPP-Net共享了卷积计算,但特征提取和后处理流程仍不够高效。
Fast R-CNN的作者Ross Girshick针对这些问题,提出了两个核心创新,一举将目标检测推向了端到端训练的新阶段。
核心创新一:感兴趣区域池化(RoI Pooling)
RoI Pooling可以看作是SPP(Spatial Pyramid Pooling)的一个单层特例。它继承了SPP的核心思想——将任意尺寸的输入转换为固定尺寸的输出,但设计更为简洁高效。
RoI Pooling的工作原理
假设我们有一个候选区域(Region of Interest, RoI),在卷积特征图上的对应区域尺寸为 h × w(例如 5×7,即5行7列)。我们的目标是将它池化为一个固定尺寸 H × W 的输出(在Fast R-CNN的VGG16实现中,常设为 7×7)。
RoI Pooling的步骤如下:
- 网格划分:将
h × w的特征区域划分为H × W个大小近似相等的子窗口(bin)。每个子窗口的高度约为h/H,宽度约为w/W。由于h/H和w/W可能不是整数,实际划分时会取整,允许子窗口大小有细微差异。 - 最大池化:对每个子窗口内的所有特征值进行最大池化(Max Pooling)操作。
- 输出:得到
H × W的固定尺寸输出。
import numpy as np
# 模拟一个RoI在特征图上的数据 (假设为单通道,便于演示)
# 尺寸为 5行 x 7列
roi_feature_map = np.array([
[1, 2, 5, 2, 3, 1, 0],
[8, 4, 6, 7, 2, 1, 3],
[3, 5, 9, 2, 1, 4, 6],
[4, 6, 8, 1, 3, 7, 2],
[2, 3, 4, 5, 6, 2, 1]
], dtype=np.float32)
print("原始RoI特征图 (5x7):")
print(roi_feature_map)
print()
# 目标池化尺寸 H=2, W=2
H, W = 2, 2
h, w = roi_feature_map.shape
# 计算每个bin的大致尺寸
bin_h = h / H # 2.5
bin_w = w / W # 3.5
# 进行RoI Pooling
output = np.zeros((H, W), dtype=np.float32)
for i in range(H):
for j in range(W):
# 计算当前bin的起始和结束行列索引 (使用取整)
h_start = int(np.floor(i * bin_h))
h_end = int(np.ceil((i + 1) * bin_h))
w_start = int(np.floor(j * bin_w))
w_end = int(np.ceil((j + 1) * bin_w))
# 确保索引不越界
h_start, h_end = max(h_start, 0), min(h_end, h)
w_start, w_end = max(w_start, 0), min(w_end, w)
# 提取当前bin的区域
bin_region = roi_feature_map[h_start:h_end, w_start:w_end]
# 执行最大池化
output[i, j] = np.max(bin_region)
print(f"经过RoI Pooling (目标尺寸 {H}x{W}) 后的输出:")
print(output)原始RoI特征图 (5x7):
[[1. 2. 5. 2. 3. 1. 0.]
[8. 4. 6. 7. 2. 1. 3.]
[3. 5. 9. 2. 1. 4. 6.]
[4. 6. 8. 1. 3. 7. 2.]
[2. 3. 4. 5. 6. 2. 1.]]
经过RoI Pooling (目标尺寸 2x2) 后的输出:
[[9. 7.]
[9. 7.]]与SPP的对比:
- SPP:使用多个尺度的网格(如
4×4,2×2,1×1),然后将所有尺度的池化结果拼接起来,形成一个固定长度的特征向量(如16+4+1=21个空间bins)。 - RoI Pooling:只使用单一尺度的网格(如
7×7),结构更简单。在这个框架下单尺度网格已经够用:Fast R-CNN 用 RoI Pooling 取得了 66.9% 的 mAP,高于 SPP-Net 的 63.1%;论文把这一提升主要归因于多任务联合训练与卷积层可微调,而不是池化网格的复杂度差异。
RoI Pooling的反向传播
在训练时,RoI Pooling层也需要进行误差反向传播。一个关键细节是:当两个不同的RoI在特征图上存在重叠区域时,该重叠区域的特征点会参与两个RoI的池化计算。
在反向传播时,该特征点将接收来自所有相关RoI的梯度。具体实现中,梯度是累加的。即,对于特征图上的某个点,其梯度等于所有以其作为最大池化响应来源的RoI的梯度之和。
假设特征图上点(x,y)是RoI_A中Bin1的最大值,同时也是RoI_B中Bin2的最大值。
前向传播:
RoI_A_Bin1的输出 = 值(x,y)
RoI_B_Bin2的输出 = 值(x,y)
反向传播:
设RoI_A_Bin1传回的梯度为g_a,RoI_B_Bin2传回的梯度为g_b。
那么特征图上点(x,y)收到的总梯度 = g_a + g_b。核心创新二:多任务损失函数(Multi-task Loss)
这是Fast R-CNN实现端到端训练的关键。它不再使用独立的SVM分类器和边界框回归器,而是将分类任务和边界框回归任务统一到一个网络框架下,并用一个联合损失函数进行优化。
边界框回归(Bounding Box Regression)回顾
在R-CNN中我们已介绍过,边界框回归的目的是对Selective Search提出的候选框进行微调,使其更接近真实标注框(Ground Truth)。
对于一个候选框 和其对应的真实框 ,我们学习一个变换,使得预测框 。
变换通过四个参数 实现:
- 平移:,
- 缩放:,
需要注意的是,这四个变换是按类别分别学习的:飞机的机翼很长、长颈鹿的脖子很长,不同类别从候选框到真实框的偏移规律并不相同,因此每个类别都要单独训练一组 。
我们需要学习的是这四个函数 。按论文的记号,网络对每个类别输出的预测偏移量记为 ,它就是上面四个变换参数在该类别上的取值;而回归目标 由真实框 与候选框 直接算出:
训练的目标就是让网络对真实类别的预测 逼近 。
这里取对数是为了保证缩放因子 为正数,使预测的宽度和高度始终为正。
多任务损失函数定义
Fast R-CNN的损失函数由两部分加权求和而成:
让我们逐一解析:
:分类损失
- 是网络预测的 Softmax 概率分布,共 个类(K个目标类 + 1个背景类)。
- 是该RoI的真实类别标签( 代表背景, 代表目标类别)。
- 分类损失采用交叉熵损失(Cross-Entropy Loss),即负对数似然: 当预测正确( 接近1)时,损失接近0;预测错误( 接近0)时,损失很大。
:定位损失
- 是网络对真实类别 预测的偏移量,即上面 这四种变换。
- 是由真实框 与候选框 按上面公式算出的回归目标。
- 定位损失采用 Smooth L1 损失。对于每个坐标 :
- 当预测值与目标值相差不大时(),使用类似L2损失(平方项),梯度稳定。
- 当相差较大时(),使用L1损失(线性项),对异常值(outliers)不敏感,防止梯度爆炸。
- 总定位损失是四个坐标 Smooth L1 损失的和:
:指示函数
- 这是一个艾弗森括号(Iverson bracket),当条件为真时值为1,否则为0。
- 只有当前RoI是目标()时,定位损失才被计入总损失。 对于背景RoI(),定位损失被忽略,因为背景不需要定位。
:平衡权重
- 用于平衡分类损失和定位损失的量级。在论文中,。
import numpy as np
def smooth_l1_loss(x):
"""计算Smooth L1损失值"""
abs_x = np.abs(x)
return np.where(abs_x < 1, 0.5 * abs_x ** 2, abs_x - 0.5)
# 模拟数据
# 假设一个RoI的真实类别是第2类 (u=2,背景为0)
u = 2
K = 20 # 假设有20个目标类别
# 网络预测的Softmax概率 (随机生成,仅演示)
np.random.seed(42)
p = np.random.rand(K + 1)
p = p / p.sum() # 归一化为概率
print(f"预测概率分布 p (前5个): {p[:5]} ...")
print(f"真实类别 u={u} 对应的概率 p_u = {p[u]:.4f}")
# 计算分类损失
L_cls = -np.log(p[u])
print(f"分类损失 L_cls = -log({p[u]:.4f}) = {L_cls:.4f}")
print()
# 定位部分
# 网络预测的偏移量 t^u (对应四个变换参数)
t_u = np.array([0.15, -0.05, 0.4, 0.1])
# 由真实框与候选框算出的回归目标 v
v = np.array([0.2, -0.1, 0.3, -0.05])
print(f"预测偏移量 t^u: {t_u}")
print(f"回归目标 v: {v}")
print(f"差值 (t^u - v): {t_u - v}")
# 计算Smooth L1损失
loc_loss_per_coord = smooth_l1_loss(t_u - v)
L_loc = loc_loss_per_coord.sum()
print(f"各坐标Smooth L1损失: {loc_loss_per_coord}")
print(f"定位损失 L_loc = {L_loc:.4f}")
print()
# 计算总损失 (lambda=1)
lambda_param = 1
# 指示函数: u>=1 为 True,背景(u=0)为False
indicator = 1 if u >= 1 else 0
total_loss = L_cls + lambda_param * indicator * L_loc
print(f"指示函数 [u>=1] = {indicator}")
print(f"总损失 L = {L_cls:.4f} + {lambda_param} * {indicator} * {L_loc:.4f} = {total_loss:.4f}")预测概率分布 p (前5个): [0.03835183 0.09735042 0.07495408 0.06130091 0.01597586] ...
真实类别 u=2 对应的概率 p_u = 0.0750
分类损失 L_cls = -log(0.0750) = 2.5909
预测偏移量 t^u: [ 0.15 -0.05 0.4 0.1 ]
回归目标 v: [ 0.2 -0.1 0.3 -0.05]
差值 (t^u - v): [-0.05 0.05 0.1 0.15]
各坐标Smooth L1损失: [0.00125 0.00125 0.005 0.01125]
定位损失 L_loc = 0.0188
指示函数 [u>=1] = 1
总损失 L = 2.5909 + 1 * 1 * 0.0188 = 2.6096Fast R-CNN网络架构与训练细节
整体架构
Fast R-CNN的整体流程清晰体现了端到端的思想:
- 输入整图:将整张图像输入卷积网络(如VGG16)。
- 生成候选区域:使用Selective Search在原始图像上提取约2000个候选区域(RoIs)。Selective Search 是类别无关的,RoI 本身只带坐标;类别标签 与回归目标 是训练时另外提供的监督信号。
- 共享特征提取:图像经过卷积网络,得到共享的卷积特征图。
- RoI Pooling:对于每个候选区域,根据其在原始图像上的位置,映射到卷积特征图上对应的区域,然后通过RoI Pooling层得到固定尺寸(如
7×7×512)的特征块。 - 全连接与输出:将固定尺寸的特征块展平,送入一系列全连接层。网络最后有两个并行的输出层:
- Softmax分类层:输出
K+1个类别的概率。 - 边界框回归层:为每个目标类别各输出4个偏移量 ,共 个。注意,计算损失时只采用真实类别 对应的那4个值。
- Softmax分类层:输出
输入图像
|
v
卷积网络 (VGG16 Conv1-5)
| 生成共享特征图
v
卷积特征图 (例如: 512通道)
| Selective Search提取RoIs
|--------------------------------------|
v v
RoI Pooling层 (例如: 7x7) RoI列表 (N个,每个为: 图片索引+4个框坐标)
|
v
展平特征向量
|
v
全连接层 (FC6, FC7, 例如: 4096维)
|
|-----------------------|
v v
Softmax分类层 (K+1维) Bbox回归层 (4K维)
| |
v v
类别概率 各类别的边界框偏移量训练策略
分层采样(Hierarchical Sampling):
- 由于一张图有约2000个RoI,如果全部用于训练,很多区域是高度相关的(重叠)。Fast R-CNN采用小批量(Mini-batch)训练。
- 每个Mini-batch包含 2张图像,每张图像采样 64个RoI(共128个)。
- 这64个RoI中,25%(即16个)是正样本(与某个真实框的IoU ≥ 0.5),75%(即48个)是负样本(IoU在[0.1, 0.5)区间)。设置IoU低阈值0.1是为了挖掘困难负样本(Hard Negative)。
全连接层加速:
- 由于RoI数量多,全连接层的前向计算成为瓶颈。Fast R-CNN论文提出使用截断奇异值分解(Truncated SVD) 来加速大型全连接层。
- 将一个全连接层 (尺寸为 )近似分解为两个更薄的全连接层:,其中 尺寸为 , 尺寸为 ,且 。这样可以将计算复杂度从 降低到 。
微调所有层:
- 与SPP-Net不同,Fast R-CNN可以通过RoI Pooling层将梯度有效地反向传播到之前的卷积层,因此可以对整个网络的所有层进行端到端的微调。
性能表现与意义
Fast R-CNN在PASCAL VOC数据集上取得了突破性的成果:
| 模型 | 训练时间 (小时) | 单图测试时间 (秒) | mAP (%) |
|---|---|---|---|
| R-CNN | 84 | 47.0 | 66.0 |
| SPP-Net | 25 | 2.3 | 63.1 |
| Fast R-CNN | 9.5 | 0.32 | 66.9 |
- 速度:训练速度比R-CNN快8.8倍,测试速度更是快了146倍,达到了接近实时的水平(0.3秒/张)。
- 精度:mAP不仅远超SPP-Net,甚至比原始的R-CNN还高了0.9%,实现了又快又好。
- 存储:无需将特征写入磁盘,节省了大量存储空间。
- 简洁性:统一的模型、端到端的训练流程,大大简化了目标检测系统的开发和训练。
Fast R-CNN的提出,标志着两阶段目标检测器在速度和精度上达到了一个完美的平衡点,并为后续Faster R-CNN的诞生铺平了道路。
📝 动手练一练
- 概念辨析:Fast R-CNN中的RoI Pooling与SPP-Net中的SPP层主要区别是什么?为什么RoI Pooling这种简化设计在实验中并没有导致精度下降?
👉 点击查看参考答案
主要区别:
- 结构复杂度:SPP层使用空间金字塔,即多个尺度的网格(如4×4, 2×2, 1×1),然后将所有结果拼接,形成较长(如21倍通道数)的特征向量。RoI Pooling只使用单一尺度的网格(如7×7),结构更简单。
- 输出维度:SPP的输出维度是固定的,但由多个尺度的bins拼接而成。RoI Pooling的输出维度就是
H × W × C。
精度未下降的原因:
- 信息冗余:深层卷积特征本身具有较高的抽象性和空间不变性。单一尺度的精细网格(如7×7)可能已经足够捕捉到RoI内目标的关键空间信息,多尺度金字塔带来的信息增益有限。
- 训练方式带来的增益大于池化网格的差异:Fast R-CNN 让整个网络(含卷积层)参与微调,并用多任务损失联合训练分类与回归。SPP 与 RoI Pooling 本身都是无参数的池化操作,并不存在“参数更少”的区别,精度提升主要来自训练范式的改变。
- 训练更稳定:端到端的训练方式使得网络可以自适应地学习如何从固定尺寸的RoI Pooling输出中提取最有效的特征,以完成分类和回归任务。
需要说明的是,Fast R-CNN 论文并没有做“SPP 结构 vs RoI Pooling”的消融实验;它的实测结果是 66.9% mAP,高于 SPP-Net 的 63.1%,论文把差距归于多任务训练与卷积层微调。可以说单尺度网格在这个框架下已经够用,而 RoI Pooling 在速度和简洁性上的优势是确定的。
- 损失函数计算:假设在一个Fast R-CNN训练批次中,有一个RoI被标记为背景(
u=0)。网络为其预测的Softmax概率分布为p = [0.8, 0.1, 0.1](假设共3类:背景、猫、狗)。同时,网络还预测了回归偏移量t^u = [0.1, 0.2, 0.3, 0.4](虽然背景不需要)。请计算该RoI对总损失函数的贡献。设平衡权重 。
👉 点击查看参考答案
已知:
- 真实类别
u = 0(背景) - 预测概率
p = [0.8, 0.1, 0.1],因此p_u = p[0] = 0.8 - 网络预测的回归偏移量
t^u在计算背景损失时不被考虑。
计算:
分类损失:
定位损失: 由于
u = 0,指示函数 。 因此,无论回归损失 是多少,其贡献都为 。总损失:
结论:该背景RoI对总损失的贡献仅为分类损失,约为0.2231。回归损失被忽略,这与“背景区域无需精确定位”的直觉一致。
本章小结
Fast R-CNN是目标检测发展史上承前启后的关键模型。它吸收了SPP-Net共享卷积计算的思想,并通过RoI Pooling和多任务损失函数两大创新,成功解决了之前模型训练流程繁琐、存储开销大、无法端到端优化等问题。
- RoI Pooling:将任意尺寸的候选区域特征转换为固定尺寸,结构比SPP更简洁,且便于梯度反向传播。
- 多任务损失函数:将分类(交叉熵损失)和边界框回归(Smooth L1损失)统一到一个框架下,通过指示函数和平衡权重协调两者,实现了真正的端到端联合训练。
- 性能飞跃:在PASCAL VOC 2007数据集上,Fast R-CNN在训练速度、测试速度和检测精度上全面超越了R-CNN和SPP-Net,为实时目标检测奠定了基础。
用今天的眼光看,Fast R-CNN(2015)已是两阶段检测器演进史上的一个阶段:RoI Pooling的坐标量化取整会带来特征错位,2017年被Mask R-CNN的RoI Align取代;检测主流也早已转向YOLO这类单阶段模型与DETR这类Transformer架构。它真正留下的,是“共享卷积 + RoI池化 + 多任务损失”这套训练范式。
Fast R-CNN的成功证明了端到端深度学习在复杂视觉任务中的强大潜力。然而,其候选区域生成模块(Selective Search)仍然是独立、耗时的。这引出了下一个问题:能否用神经网络来生成候选区域,实现完全的端到端? 这正是我们下一节要学习的Faster R-CNN所要回答的。
📋 行动清单
- 理解RoI Pooling如何将不同大小的区域映射为固定大小的特征,并手动演算一个小例子。
- 掌握Fast R-CNN多任务损失函数的构成,能明确写出分类损失和定位损失的计算公式,并解释指示函数的作用。
- 对比R-CNN、SPP-Net和Fast R-CNN在训练流程、速度、精度上的差异,梳理出目标检测模型向端到端演进的清晰脉络。
—— 小象教研组