【目标检测(一)】RCNN详解——深度学习目标检测的开山之作
【目标检测(二)】SPP Net——让卷积计算共享
【目标检测(三)】Fast RCNN——让RCNN模型能端到端训练
【目标检测(四)】Faster RCNN——RPN网络替代selective search
【目标检测(五)】YOLOv1——开启one-stage目标检测的篇章
【目标检测(六)】YOLOv2——引入anchor, Better, Faster, Stronger
【目标检测(七)】YOLOv3——准确率的全面提升
【目标检测(八)】一文吃透目标检测回归框损失函数——IoU、GIoU、DIoU、CIoU原理及Python代码
【目标检测(九)】FPN详解——通过特征金字塔网络进行多尺度特征融合
【目标检测(十)】RetinaNet详解——Focal Loss将one-stage算法推向巅峰
【目标检测(十一)】CenterNet——Anchor Free, NMS Free
【目标检测(十二)】FCOS——用实例分割的思想做Anchor Free的目标检测
1. 目标检测的前世今生
目标检测作为计算机视觉中最基本、最具挑战性的问题之一,近年来受到了广泛的关注。它在过去20年的发展可以说是计算机视觉历史的缩影。如果我们把今天的目标检测看作是深度学习力量下的技术美学,那么让时光倒流20年,我们将见证冷兵器时代的智慧。
上图形象的表示了目标检测的发展历史,在RCNN问世以前,目标检测处于“冷兵器”时代,需要使用传统计算机视觉的方法,手工设计更强的特征,然后使用经典机器学习算法,如下图所示。
随着CNN网络的出现,目标检测进入了深度学习时代,目标检测技术越来越倾向于网络结构、损失函数和优化方法的设计,人们更加关注使用CNN网络自动提取出图像特征,代替了原来的手工设计特征。目标检测从“冷兵器”时代,过渡到“热兵器”时代,从两阶段目标检测算法到单阶段目标检测算法,再到现在的Anchor Free算法趋势,伴随着硬件计算水平的不断提高,目标检测技术的发展进入了“快车道”。本篇文章主要介绍开山之作RCNN算法。
2. RCNN原理
2.1 Pipeline
整体的思路非常简洁明了,作者认为不需要使用滑动窗口的方式在整张图像上滑动来获取Region Proposal(候选框),而只选择一部分候选框,然后在这些窗口上跑CNN网络就可以了。
- 输入一张图片。
- 使用selective search的方法选出来约2000个Region Proposal,这种方法得到的候选框数量比传统方法少的多。大致是采用图像分割的算法得到的图像块,分割得到候选框,后面将进一步介绍、
- 将每一个候选框图片块resize为227*227的大小,然后输入到一个AlexNet CNN网络中,每个候选框图片都能得到一个4096维的特征。
- 为每一类都构建一个SVM分类器,例如你要分十类,就会有10个SVM分类器。将上一步中CNN提取到的特征,输入到这些SVM分类器中,可以得到每一类的分数,从而得到分类结果。(nms算法进行框去重,后面会详述)
- 同时将第三步中CNN输出的特征向量做回归,纠正Bounding Box框左上右下四个坐标的位置。
2.2 Selective Search
selecttive search论文链接:www.huppelen.nl/publication…
目标检测任务比图像分类任务复杂很多,因为目标检测任务可能有很多物体的分类和定位任务,我们说目标检测,一般是暗含classification和localization两个任务的。在训练分类器之前,通常我们需要通过一些方法得到Region Proposal,也就是候选框,在这些候选框中进行分类和回归,找到我们要的目标信息。
在RCNN以前的Region Proposal Algorithms主要采用穷举或者分割的方式,搜索范围很大,候选框数量非常多,会给后面的训练带来非常大的压力。RCNN作者选用selective search的方法,这个方法计算速度比以往的方法快很多,召回率高,综合考虑了颜色、大小、形状、纹理等特征,对图像区域进行分组。这个方法主要包含两个部分:Hierarchical Grouping Algorithm、Diversification Strategies。
2.2.1 Hierarchical Grouping Algorithm
大体思路是作者使用Felzenszwalb and Huttenlocher的分割方法得到图像的分割区域作为初始区域,然后使用多样性的策略,从多个角度特征来表征不同的分割区域,结合区域的相似性使用贪心算法对区域进行迭代分组。原文中是这么表述的:
- 先使用Felzenszwalb and Huttenlocher方法获取初始区域R。
- 构建区域相似度集合,计算所有pair区域的相似度,放进相似集合中。
- 寻找相似度最大的两个区域,将这两个区域合并为新区域,添加进R。
- 从S中移除所有与这两个合并区域有关的项,并计算新区域S与其他区域的相似度,添加进S。
- 跳至step 2,直到S为空。
2.2.2 Diversification Strategies
作者从多个维度去衡量不同图像区域的相似度,进行加权平均,主要有如下几个维度:
- 颜色
- 纹理
- 大小
- 尺寸
即使使用了
Selective Search
等预处理步骤来提取潜在的bounding box
作为输入,但是仍会有严重的速度瓶颈,原因也很明显,就是对所有region
进行特征提取时会有重复计算,需要对所有区域两两之间求相似度,所得到的的候选框数量也偏多。
详细的尺度衡量方式可查看原论文,因为现在selective search几乎已经不用了,这里不做过多赘述了。
2.3 训练过程
- 网络输入预处理:将所有的候选框都resize到227*227的尺寸,但是在resize之前,用到一个trick,就是对Region Proposal进行区域膨胀,保证resize之后正好周围有16个像素的原图像上下文信息。
- CNNC网络:作者使用了AlexNet分类网络作为特征提取器,网络输入为预处理好的227227图像块,经过5个卷积层和2个全连接层能得到固定4096维的向量特征(卷积层最后得到的66*256的特征,每个像素点的感受野是195),Alexnet网络如下图所示:
这部分作者使用AlexNet对RP进行区域块的图像分类,IOU>=0.5的认为是正例,反之为负例。
- 预训练模型:先试用Imagenet(1000类)训练,再用PASCAL VOC(21)类来fine-tune。使用这种方式训练能够提高8个百分点。
- batch设置:32正样本+96负样本,现实情况中负样本会比正样本数量多得多。
- 类别设置:分C + 1类,包括C个目标的类别和1类background。
- Loss: classification loss+bbox regression loss,后者在原文中设置超参数权重λ=1000。
- 分类器:需要训练(C + 1)个SVM分类器,每一类训练一个分类器。这里正负样本划分规则与CNNC网络分类不一样,与Ground Truth IOU值大于0.5认为是正样本,小于0.3的认为是负样本,其他框忽略掉。
- Bounding Box回归:忽略掉分类不正确框的bbox框回归。得到分类器SVM的分数后,需要在feature map上预测一个新的bbox,对Region Proposal进行微调。获取CNN网络提取到的特征之后,把特征向量放进一个线性回归模型里,对框的位置、宽和高进行拟合,主要拟合bbox左上角坐标的xy坐标和框的宽高。方法是在已有的预测框P基础上,学习一个从P到GT的映射,得到更加准确的框。学习的四个偏移量参数如下图所示,分别是左上角坐标的xy偏移量和宽高的偏移量。
- Learning Rate Schedule: 初始学习率设置为0.001 + SGD。
2.4 后处理NMS
测试过程基本和训练过程一样,也是先生成近2000个Region Proposal,然后进行分类和框回归。但是可能会发生检测框(Region Proposal)和目标框多对一的匹配,即几个检测框与一个ground truth相匹配,几种不同的检测框可能与同一个ground truth有较大的IoU。在推理过程中,它们可以回归到具有高分类置信度的同一对象。因此,为了删除重复检测框,NMS后处理是必要的。
NMS是非极大值抑制(Non-maximum suppression)算法,算法流程如下:
Input: 所有检测框,检测框分数,NMS阈值
Output: 经过NMS后的检测框和它对应的分数
- 找到某一类检测框中分数最大的那个,记录下来,放在NMS新检测框集合中。
- 计算记录的分数最大框与其余的bbox的IOU值,IOU表示交并比,两个box区域的交集比上并集,如下图所示:
- 如果其IOU>NMS阈值,那么就舍弃这个bbox(很大概率这两个检测框是同一个目标),从检测框集合和阈值集合中移除这个bbox的信息。
- 从最后剩余的检测框集合中,再找出最大scores的那个,如此循环往复,直到原始检测框集合为空。
Python实现代码如下:
import numpy as np
def nms(dets, thresh):
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
scores = dets[:, 4]
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
order = scores.argsort()[::-1]
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
xx1 = np.maximum(x1[i], x1[order[1:]])
yy1 = np.maximum(y1[i], y1[order[1:]])
xx2 = np.minimum(x2[i], x2[order[1:]])
yy2 = np.minimum(y2[i], y2[order[1:]])
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
ovr = inter / (areas[i] + areas[order[1:]] - inter)
inds = np.where(ovr <= thresh)[0]
order = order[inds + 1]
return keep
3. RCNN缺点
- 多阶段训练: 预训练(ImageNet) + Selective Search + CNN特征提取器(VOC) + 分类器(SVMs) + 边界框回归器(LR)。
- 从每个建议框中提取的特征向量(1x4096)存储于硬盘,消耗大量时间空间成本。
- 每个warped region都需要重新送往CNN提取特征,多次重复卷积计算。
- Inference一张图片耗时太长,GPU: 13 s/frame,CPU: 53 s/frame。