查看全课大纲(第 56 / 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
RCNN
RCNN:区域卷积神经网络
小象实战讲义 · 基于深度学习的计算机视觉
目标检测是计算机视觉的核心任务之一,它要求我们不仅识别出图像中的物体是什么,还要精确地标出它们的位置。在深度学习时代之前,目标检测依赖于手工特征和复杂的多阶段流程,性能存在瓶颈。RCNN(Regions with CNN features)的出现,首次将强大的卷积神经网络特征应用于目标检测,开创了深度学习目标检测的先河。本节我们将深入剖析RCNN的核心思想、技术细节与完整流程。
💡 核心导读
- 两阶段检测范式:RCNN确立了“区域建议 + 区域分类”的两阶段检测框架,将检测问题转化为分类问题。
- Selective Search:一种基于图像底层特征(颜色、纹理等)的层次合并算法,用于生成约2000个可能包含物体的候选区域。
- 特征提取与微调:使用在ImageNet上预训练的CNN(如AlexNet)提取候选区域特征,并针对检测任务进行微调。
- 多任务头设计:使用SVM进行类别分类,使用边界框回归模型对候选框位置进行精细调整,两者分别使用CNN的不同层特征。
- 性能与局限:RCNN在PASCAL VOC等数据集上大幅提升了检测精度(mAP),但其多阶段、高计算成本的设计也催生了后续一系列改进。
从分类到检测:RCNN的核心思想
目标检测任务需要输出每个检测到的物体的类别标签和其边界框(Bounding Box;坐标形式有多种,如左上角坐标加宽高、两角点坐标,或中心点坐标加宽高——本节按各环节的惯例选用)。RCNN的创始人Ross Girshick等人洞察到一个关键点:如果已经知道物体在图像中的确切区域,那么检测问题就退化成了一个区域分类问题。
因此,RCNN的总体流程可以拆解为两个核心模块:
- 区域建议(Region Proposal):找出图像中所有可能包含物体的区域。这些区域的数量很多(约2000个),且与具体类别无关,但必须保证真实的物体被包含在其中。
- 区域分类与精修(Classification & Refinement):对每一个候选区域,利用卷积神经网络(CNN)提取特征,然后判断它属于哪一类物体(或背景),并对该区域的边界框位置进行精细调整。
import numpy as np
# 模拟一张简单图像(5x5,单通道灰度,值代表强度)
image = np.array([
[10, 10, 10, 80, 80],
[10, 10, 10, 80, 80],
[10, 10, 10, 80, 80],
[90, 90, 90, 20, 20],
[90, 90, 90, 20, 20]
], dtype=np.uint8)
print("模拟图像 (5x5):")
print(image)
regions = {
"区域A": (0, 0, 3, 3),
"区域B": (3, 3, 2, 2),
}
print("\n两个候选区域(边界框)及其像素统计:")
for name, (x, y, w, h) in regions.items():
patch = image[y:y + h, x:x + w]
print(f" {name} [x={x}, y={y}, w={w}, h={h}]: 尺寸 {patch.shape}, 像素均值 {patch.mean():.1f}")
print("\nRCNN的思路:分别对区域A和区域B裁剪、Warp,再送入CNN提取特征并分类。")模拟图像 (5x5):
[[10 10 10 80 80]
[10 10 10 80 80]
[10 10 10 80 80]
[90 90 90 20 20]
[90 90 90 20 20]]
两个候选区域(边界框)及其像素统计:
区域A [x=0, y=0, w=3, h=3]: 尺寸 (3, 3), 像素均值 10.0
区域B [x=3, y=3, w=2, h=2]: 尺寸 (2, 2), 像素均值 20.0
RCNN的思路:分别对区域A和区域B裁剪、Warp,再送入CNN提取特征并分类。关键技术一:Selective Search与区域建议
如何自动生成大量可能包含物体的候选区域?RCNN采用了Selective Search算法。它的核心思想来源于层次聚类:
- 初始化:将图像分割成许多小的初始区域(例如使用Felzenszwalb算法)。
- 计算相似度:计算所有相邻区域之间的相似度。相似度度量可以综合考虑颜色直方图、纹理梯度直方图、区域大小以及合并后区域在其最小外接矩形中的占比。
- 合并区域:不断合并相似度最高的两个区域,形成新的区域,并更新相似度。
- 循环迭代:重复步骤2-3,直到整个图像合并为一个区域。
- 输出建议:在合并过程中产生的所有区域,取其外接矩形作为候选框输出,通常一张图会产生约2000个候选框。
Selective Search的优势在于,它通过多种相似度准则,能够将颜色、纹理不同但属于同一物体的部分合并起来(例如猫的脸和身体),从而生成质量较高的候选区域。
# Selective Search 层次合并示意(伪代码)
输入: 图像 I
输出: 候选边界框列表 proposals
1. 生成初始分割区域 R = {r1, r2, ..., rn}
2. 初始化相似度集合 S = 计算R中所有相邻区域对的相似度
3. while S 非空:
4. 从S中取出相似度最高的区域对 (ri, rj)
5. 合并 rt = ri ∪ rj
6. 从R中移除ri, rj,加入rt
7. 从S中移除所有包含ri或rj的相似度对
8. 计算rt与R中其相邻区域的新相似度,加入S
9. 记录rt的外接矩形,加入proposals(如果满足大小等条件)
10. 返回proposals关键技术二:Warp操作与特征提取
Selective Search产生的候选框形状和大小各异,但CNN(如AlexNet)的输入要求是固定尺寸(例如227x227)。因此,需要将每个候选区域变形(Warp) 到统一尺寸。RCNN论文中尝试了多种Warp方式:
- 各向异性缩放(Anisotropic Scaling):直接将区域拉伸/压缩成正方形,可能导致物体严重形变。
- 各向同性缩放+上下文填充(Isotropic Scaling with Context):先在区域边界外扩展一定像素(如16像素)以提供上下文信息,然后将扩展后的区域缩放成正方形。如果扩展后超出图像边界,则用图像均值填充。
经过Warp后,每个候选区域都变成了一张227x227的“小图片”。接下来,RCNN使用一个在ImageNet上预训练好的CNN(如AlexNet)来提取这些“小图片”的特征。这里有一个关键设计:特征提取是独立进行的,即2000个候选区域需要分别过一遍CNN,这是RCNN速度慢的主要原因之一。
import numpy as np
# 固定随机种子,保证每次运行结果一致
np.random.seed(0)
# 模拟一个候选框和图像
# 假设图像是8x8的“图片”,我们关注其中一个区域
H, W = 8, 8
simulated_image = np.random.randint(0, 256, (H, W, 3), dtype=np.uint8) # 随机生成一个彩色图
# 模拟一个候选框 [x, y, w, h],这里假设是图像中的一个子区域
candidate_box = [1, 1, 4, 6] # 一个非正方形的矩形
def warp_region_isotropic_with_context(region_box, image_shape, target_size=227, context_pad=16):
"""
模拟各向同性缩放+上下文填充的Warp操作(概念演示)。
由于我们无法读取真实图像,这里仅模拟坐标变换逻辑。
"""
x, y, w, h = region_box
H_img, W_img, C = image_shape
# 1. 添加上下文(扩展边界)
x1 = max(0, x - context_pad)
y1 = max(0, y - context_pad)
x2 = min(W_img, x + w + context_pad)
y2 = min(H_img, y + h + context_pad)
# 2. 计算扩展后区域的宽高
context_w = x2 - x1
context_h = y2 - y1
# 3. 确定缩放比例(保持长宽比)
scale = target_size / max(context_w, context_h)
new_w = int(round(context_w * scale))
new_h = int(round(context_h * scale))
# 4. 理论上这里会调用 cv2.resize 进行缩放。
# 由于是模拟,我们返回缩放后的尺寸信息。
return {
'original_box': region_box,
'context_box': [x1, y1, context_w, context_h],
'warped_size': (new_h, new_w, C), # 注意OpenCV是(高,宽,通道)
'target_size': target_size
}
# 执行模拟Warp
warp_info = warp_region_isotropic_with_context(candidate_box, simulated_image.shape)
print("候选框原始信息:", warp_info['original_box'])
print("添加上下文后的框:", warp_info['context_box'])
print("Warp后目标尺寸 (高, 宽, 通道):", warp_info['warped_size'])
print("(注:实际会进一步填充或裁剪至精确的 {}x{} 正方形)".format(warp_info['target_size'], warp_info['target_size']))候选框原始信息: [1, 1, 4, 6]
添加上下文后的框: [0, 0, 8, 8]
Warp后目标尺寸 (高, 宽, 通道): (227, 227, 3)
(注:实际会进一步填充或裁剪至精确的 227x227 正方形)RCNN的详细架构与训练流程
RCNN是一个典型的多阶段训练模型,流程复杂但逻辑清晰。
第一步:CNN预训练与微调(Fine-tuning)
- 预训练:使用AlexNet在ImageNet(1000类)数据集上进行图像分类任务的训练,得到一个强大的特征提取器。
- 微调:
- 数据准备:对每张训练图片,用Selective Search提取约2000个候选区域。如果一个候选区域与任意一个真实标注框(Ground Truth)的交并比(IoU) >= 0.5,则将其视为该物体类别的正样本;如果与所有真实框的IoU < 0.5,则视为负样本(背景)。
- 网络改造:将AlexNet最后的1000类分类层,替换为(N+1)类分类层,其中N是目标检测数据集的类别数(PASCAL VOC为20,得21路;ILSVRC检测任务为200,得201路),额外的1类即背景类。
- 训练:用上述准备好的(N+1)类样本,以较小的学习率对预训练好的CNN进行微调,学习目标检测任务相关的特征。损失函数使用对数损失(Log Loss);在Softmax输出上它就是多分类交叉熵,只是文献里的叫法不同。
交并比(IoU) 是衡量两个边界框重叠程度的重要指标:
第二步:训练类别分类器(SVM)
微调后的CNN主要提升了特征质量,但RCNN发现,直接用其Softmax输出进行分类,效果不如单独训练一个线性支持向量机(SVM)。
- 特征提取:对于每个训练样本(候选区域),使用微调后的CNN,提取其最后一个全连接层(如AlexNet的fc7层)的4096维特征向量。
- 样本定义:这里正负样本的定义与微调阶段略有不同。
- 正样本:每个类别的真实标注框(Ground Truth) 作为该类的正样本。
- 负样本:那些与所有真实框的IoU < 0.3的候选区域(通常是背景或其他类别)。
- 训练:为每个类别训练一个二分类线性SVM(一类对其余)。例如,“猫”分类器,将所有“猫”的真实框作为正样本,将IoU<0.3的区域(可能是背景、狗、车等)作为负样本进行训练。
- 难负样本二次训练(Hard Negative Mining):若分类完全正确,正样本得分应高于0.5、负样本得分应低于0.5;但实际中总有一小部分负样本得分仍高于0.5,这些就是假阳性(False Positives)。把它们收集起来对SVM再训练一轮,分类准确度通常还能有所提升。
第三步:训练边界框回归模型(Bounding Box Regression)
Selective Search给出的候选框通常不够精确。边界框回归模型的目标是学习一个映射,将候选框P微调至更接近真实框G。
- 输入特征:回归模型使用的是pool5特征(conv5 经最大池化后的特征图,AlexNet 中为 6×6×256;课件与讲授中常把它简称为“conv5特征”,论文原文用的是 pool5),而不是全连接层特征。池化后的特征图仍保持二维结构、保留了物体的空间位置信息,而全连接特征把这些几何信息打平丢失了。
- 回归目标:定义四个偏移量参数: 其中是候选框的中心坐标和宽高,是对应的真实框。
- 训练:仅对那些与真实框IoU > 0.6的候选框进行回归训练。为每个类别单独训练一个回归模型(通常是岭回归)。模型输入是pool5特征,输出是预测的偏移量,目标是使其接近计算出的。
import numpy as np
# 模拟计算边界框回归的目标值(偏移量)
def compute_bbox_regression_targets(P, G):
"""
P: 候选框 [P_x, P_y, P_w, P_h] (中心点坐标和宽高)
G: 真实框 [G_x, G_y, G_w, G_h]
返回: 目标偏移量 [t_x, t_y, t_w, t_h]
"""
P_x, P_y, P_w, P_h = P
G_x, G_y, G_w, G_h = G
t_x = (G_x - P_x) / P_w
t_y = (G_y - P_y) / P_h
t_w = np.log(G_w / P_w)
t_h = np.log(G_h / P_h)
return np.array([t_x, t_y, t_w, t_h])
# 示例
P_box = [50, 50, 60, 80] # 候选框 (中心x,中心y,宽,高)
G_box = [55, 52, 58, 85] # 真实框
targets = compute_bbox_regression_targets(P_box, G_box)
print("候选框 P:", P_box)
print("真实框 G:", G_box)
print("回归目标值 [t_x, t_y, t_w, t_h]:")
print(np.round(targets, 4))候选框 P: [50, 50, 60, 80]
真实框 G: [55, 52, 58, 85]
回归目标值 [t_x, t_y, t_w, t_h]:
[ 0.0833 0.025 -0.0339 0.0606]RCNN的测试(推理)流程
测试时,对一张新图片:
- 生成候选区域:运行Selective Search,得到约2000个候选框。
- Warp与特征提取:对每个候选框进行Warp,然后分别通过微调后的CNN,提取出两类特征:用于分类的fc7特征和用于回归的pool5特征。
- 分类与筛选:
- 将每个候选框的fc7特征输入所有类别的SVM分类器,得到每个类别的得分。
- 对每个类别单独应用非极大值抑制(NMS)。NMS的目的是去除冗余框。其步骤为:将所有框按该类得分排序;选中得分最高的框;移除所有与该框IoU超过阈值(如0.5)的其他框;在剩余框中重复此过程。
- 边界框回归:对经过NMS后保留下来的每个候选框,使用其对应类别的边界框回归模型,结合pool5特征,预测偏移量并对框的位置进行精细调整。
- 输出:最终得到每个检测到的物体的类别和调整后的精确边界框。
import numpy as np
# 模拟非极大值抑制 (NMS) 过程
def nms_simulation(boxes, scores, iou_threshold=0.5):
"""
boxes: list of [x1, y1, x2, y2] (左上角,右下角坐标)
scores: list of confidence scores
iou_threshold: 重叠阈值
返回:保留的框的索引
"""
boxes = np.array(boxes)
scores = np.array(scores)
# 按得分降序排序
order = scores.argsort()[::-1]
keep = []
while order.size > 0:
i = order[0] # 当前得分最高的框
keep.append(i)
# 计算当前框 i 与剩余框的IoU
xx1 = np.maximum(boxes[i, 0], boxes[order[1:], 0])
yy1 = np.maximum(boxes[i, 1], boxes[order[1:], 1])
xx2 = np.minimum(boxes[i, 2], boxes[order[1:], 2])
yy2 = np.minimum(boxes[i, 3], boxes[order[1:], 3])
w = np.maximum(0.0, xx2 - xx1)
h = np.maximum(0.0, yy2 - yy1)
inter = w * h
area_i = (boxes[i, 2] - boxes[i, 0]) * (boxes[i, 3] - boxes[i, 1])
area_rest = (boxes[order[1:], 2] - boxes[order[1:], 0]) * (boxes[order[1:], 3] - boxes[order[1:], 1])
union = area_i + area_rest - inter
iou = inter / union
# 保留IoU小于阈值的框(即抑制掉重叠度高的)
inds = np.where(iou <= iou_threshold)[0]
order = order[inds + 1] # +1 因为order[1:]是偏移了的
return keep
# 模拟数据:5个候选框及其得分
sim_boxes = [
[10, 10, 50, 50], # 框0
[12, 12, 52, 52], # 框1,与框0高度重叠
[100, 100, 150, 150], # 框2,远离
[15, 15, 55, 55], # 框3,与框0重叠
[105, 105, 155, 155] # 框4,与框2重叠
]
sim_scores = [0.9, 0.85, 0.8, 0.7, 0.65]
keep_indices = nms_simulation(sim_boxes, sim_scores, iou_threshold=0.5)
print("所有候选框 (x1,y1,x2,y2) 及得分:")
for i, (box, score) in enumerate(zip(sim_boxes, sim_scores)):
print(f" 框{i}: {box}, 得分: {score:.2f}")
print(f"\n应用NMS (IoU阈值=0.5) 后保留的框索引: {keep_indices}")
print("解释:框0得分最高,抑制了与其高度重叠的框1和框3;框2抑制了框4。")所有候选框 (x1,y1,x2,y2) 及得分:
框0: [10, 10, 50, 50], 得分: 0.90
框1: [12, 12, 52, 52], 得分: 0.85
框2: [100, 100, 150, 150], 得分: 0.80
框3: [15, 15, 55, 55], 得分: 0.70
框4: [105, 105, 155, 155], 得分: 0.65
应用NMS (IoU阈值=0.5) 后保留的框索引: [np.int64(0), np.int64(2)]
解释:框0得分最高,抑制了与其高度重叠的框1和框3;框2抑制了框4。性能评价与历史意义
目标检测常用平均精度均值(mAP) 来评价模型性能。其计算过程为:对每个类别,绘制以召回率(Recall)为横轴、准确率(Precision)为纵轴的PR曲线,计算曲线下的面积,得到该类别的平均精度(AP);然后对所有类别的AP取平均,即得mAP。
RCNN在PASCAL VOC 2010数据集上将mAP从传统方法的35.1%提升至53.7%,在ILSVRC数据集上从24.3%提升至31.4%,这是一个里程碑式的突破。
然而,RCNN的缺点也非常明显:
- 训练测试速度极慢:每张图片的2000个候选区域需要独立进行CNN前向传播,特征还需写入磁盘。以VGG16为基础模型时,训练总耗时约84小时,其中63小时都花在候选区域的特征提取上;测试一张图片需47秒。
- 训练流程复杂:需经历预训练、微调、SVM训练、边界框回归训练四个独立阶段。
- 存储开销大:所有候选区域的特征都需要写入磁盘,占用大量磁盘空间,并带来额外的读写耗时。
正是这些缺点,推动了SPP-Net、Fast R-CNN、Faster R-CNN等后续工作的快速发展,目标检测的效率得到了极大提升。
📝 动手练一练
- 概念辨析:RCNN在“微调(Fine-tuning)”阶段和“训练SVM分类器”阶段,对于“正样本”的定义有何不同?为什么要有这样的区别?
👉 点击查看参考答案
- 微调阶段:正样本定义为与任意真实框(Ground Truth)IoU >= 0.5的候选区域。这是因为微调CNN时,我们希望网络能学习到“大概包含物体”的区域的特征,对框的精确度要求相对宽松,侧重于特征表示的学习。
- SVM训练阶段:正样本直接使用真实标注框(Ground Truth) 本身。这是因为SVM作为一个严格的分类器,需要非常纯净的正样本来确立分类边界。使用IoU>=0.5的候选框作为正样本可能包含背景或物体部分,不利于SVM学习到最具有判别力的特征。负样本则定义为与所有真实框IoU < 0.3的候选区域,它们大多是与目标几乎不重叠的普通背景区域;真正意义上的“难负样本”是第二步中得分仍高于0.5的假阳性,两者不要混为一谈。
- 原因:这种差异源于两个阶段的不同目标。微调是为了让CNN提取更好的通用特征,而SVM训练是为了获得一个判别力极强的分类器。论文作者通过实验发现,这样定义能获得最佳性能。
- 特征理解:RCNN中,用于SVM分类的特征来自CNN的fc7层(全连接层),而用于边界框回归的特征来自pool5(conv5 经池化后的卷积特征图)。请解释为什么回归任务要使用卷积特征图而不是全连接层特征?
👉 点击查看参考答案
边界框回归需要预测物体位置的精细偏移量(如中心点移动几个像素,宽高缩放多少)。这要求所使用的特征必须保留物体的空间位置和几何信息。
- 卷积特征图(pool5):特征图(feature map)仍然保持与输入图像空间位置相对应的二维结构。某个位置的特征响应强,往往对应原图中该位置存在特定模式的视觉元素。因此,pool5特征蕴含了丰富的空间信息。
- 全连接层特征(fc7):经过全连接层后,空间结构被完全打平,变成一个一维的高维向量。这个向量是全局特征的抽象表示,擅长表达“是什么”,但已经丢失了“在哪里”的精确空间信息。 因此,为了精确地调整边界框的位置,必须使用保留了空间信息的卷积层特征。
本章小结
RCNN是深度学习目标检测的奠基之作。它创造性地将CNN强大的特征提取能力与传统的区域建议方法相结合,通过“区域建议+分类与精修”的两阶段范式,显著提升了检测精度。尽管其多阶段、高计算成本的架构已被后续更高效的模型所取代,但RCNN所确立的核心思想——使用深度特征、两阶段处理、边界框回归——至今仍在影响着目标检测领域的发展。
需要如实定位:RCNN发表于2014年,属于历史方案。此后Fast/Faster R-CNN沿用两阶段思路并解决了重复卷积的瓶颈,工程实践中已不再直接使用RCNN本身;它在今天的价值,主要在于它是理解“区域建议+分类精修”这一范式的最佳起点。
📋 行动清单
- 理解Selective Search生成区域建议的基本原理,明白其目的是保证召回率。
- 掌握RCNN四阶段训练流程(预训练、微调、SVM训练、BBox回归)及其各自的数据准备方式。
- 能够解释IoU、NMS、mAP等目标检测核心评价指标的概念和计算意义。
- 思考RCNN架构的主要瓶颈,为学习后续Fast/Faster R-CNN的改进思路做准备。
—— 小象教研组