我正在参加「掘金·启航计划」
本文是我和好友 @Zee 读论文后整理而来,部分表述可能存在不清晰之处、甚至可能出现错误。欢迎交流指正。
部分图片源于互联网,如有侵权,请联系我删除
R-CNN
R-CNN(Regions with CNN features) 是深度学习领域的经典框架之一,首次将卷积神经网络(CNN)引入了目标检测领域,并取得了优秀的效果。为了实现这个目的,本文解决了两个主要问题:使用深度网络定位目标,以及使用少量有标注的数据集训练出高容量的模型。
本文使用的数据集为 PASCAL VOC ,具体内容可以参考 目标检测数据集PASCAL VOC详解
本文涉及文章:Rich feature hierarchies for accurate object detection and semantic segmentation
结构
此目标检测系包含三个模块
- 生成各自独立的区域候选框
- 较大的卷积网络,用以从各区域中提取定长的特征向量
- 一些用以分类的线性 SVM(支持向量机)
最后实际上还有一个部分,使用回归算法精修候选区域。基本流程如下图:
确定候选区域
文章的这一部分用的是 Selective Search ,一种传统的目标检测算法。文章里仅是一笔带过,故在此不做赘述。具体算法原理可以参考:理解Selective Search - 知乎 (zhihu.com)。
此处使用它生成两千个候选区域。
特征提取
此部分通过五个卷积层、两个全连接层,将传入的 227*227 的 RGB 图片提取为 4096 维的特征向量。卷积网络的结构如下:
对于任意区域大小的图片,这里最终采用的是直接拉伸的方法转为 227*227。不过在拉伸前会对区域框做一个扩大处理,使得拉伸后的图片周围有原图的 p 像素上下文(此处 p = 16 )
文章的支撑材料里探讨了几种不同的 warp 方法。
SVM+NMS
对于每一个类别,作者使用相应的已训练好的 SVM 对已提取出的特征向量进行打分。当所有候选区域都打完分后,对每个类别使用 NMS(非极大值抑制)算法,扔掉那些 IoU 高出 给定阈值的区域。下面具体介绍这个部分:
IoU
Intersection-over-union,正如其名称所言,计算的是两个矩形框的交集区域面积除以并集区域面积
该值的取值范围为 [0, 1],0 代表完全不相交,1 代表二者完全等同
NMS
NMS 用于去除重复框,效果如下
它的算法原理并不复杂,主要是下面这几个步骤:
- 给定n个候选框,根据其分数从大到小排序
- 循环遍历每一个框,计算它与其他框的 IoU ,如果大于特定的阈值,就丢弃对应的框(因为那俩很可能框的是同一个东西)
- 重复,直至所有框处理完成(被保留或丢弃)
对应 Python 代码如下:
import numpy as np
def nms(dets, thresh):
""" :dets 给定的候选框
:thresh 阈值
return 保留的框们
"""
# 获取dets的各种信息
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
# 计算 IoU
ovr = inter / (areas[i] + areas[order[1:]] - inter)
# 保留 IoU 低于 thresh 的那些框
inds = np.where(ovr <= thresh)[0]
order = order[inds + 1]
return keep
训练
预训练
CNN 在 ILSVRC 2012 训练集上做了预训练,该数据集具有图像级别的标注(也就是没有候选框)
微调
为了使 CNN 适应于目标检测的新任务,作者使用缩放后的候选区域微调网络。这一过程中,网络的最后一层(分类输出层)从 1000 维更改为了 21 维 (20 种不同的目标 + 背景),其余层结构不变。作者将所有与人工标注的框的IoU大于 0.5 的区域认为是正类(也就是对应分类)、小于 0.3 的是背景(这俩数值是{0, 0.1, ..., 0.5}以网格搜索的方式整出来的)。
梯度下降的初始学习率为 0.001 (预训练时的10%),每一轮采样 32 个正样本区域和 96 个背景区域组成 size 为 128 的 batch。鉴于相较于背景来说,正类较为稀少,因此采样时会偏向于正类。
Bounding Box Regression
作者使用由 D. Hoiem, Y. Chodpathumwan, and Q. Dai. Diagnosing error in object detectors. In ECCV. 2012.
提出的目标检测分析工具来探究此方法的错误模式,理解微调是怎么起作用的。一顿操作后,提出了 Bounding Box Regression
用于降低定位误差。通过使用从 Selective Search
选出的区域的 特征(CNN 的第五个卷积层后的最大值池化层) ,作者训练了个线性回归模型以预测一个新的候选区域。结果显示,这帮助修复了大量的错误定位,提升 mAP
3到4个百分点。
结果和问题
当时,这一模型在 PASCAL VOC 2012 数据集上得到了 30% 的提升(相较于之前的最好结果),效果可以说是斐然、
但是,R-CNN 也有很多问题:
- 慢。尽管在原论文里,作者一直在强调“我们这个方法很快”;但展示的数据放在今天看却慢的吓人:用(当时的)GPU 检测一张图片需要 13s,而 CPU 甚至达到了 50s+。这样的耗时不难理解:每张图要先用 Selective Search 提取 2000 个候选区域,每个候选区域都需要用 CNN 提取特征,并用 SVM 进行分类,这也是后面改进的重点。
- 大。由于后面的过程需要前面过程产生的特征,保存这些特征需要占用大量的存储空间。
- 图片变形。由于送入 CNN 时图片被强制缩放到 227*227,因此不免变形
正因为有这样的问题,R-CNN 后续有很多改进。下面继续介绍
Fast R-CNN
正如名称所言,Fast R-CNN 是对 R-CNN 的改进,结果不仅能使用较深的卷积神经网络(VGG16),训练上速度也快了后者仅 9 倍,测试上更是快了 200+ 倍,检测单图仅需 0.3s (不包括产生候选区域的时间);它在 VOC 2012 上的 mAP 也达到了 66% ( R-CNN 是 62% )。
不过,在正式介绍它之前,我们先看另一项 R-CNN 的改进:SPPnet
SPPnet
SPPnet,提出于Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition
,通过共享部分计算加速了 R-CNN。简单来说,SPPnet 对整张图进行卷积、计算特征(而不是像 R-CNN 一个候选区域来一次),然后使用对应部分的特征对每个物体进行分类。通过最大池化,将候选框内的特征图转化为固定大小的输出(例如6×6)来提取对应候选框的特征。多输出尺寸被池化后连接成空间金字塔池。SPPnet 在测试时将 R-CNN 加速10到100倍。由于更快的候选框特征提取,训练时间也减少了 3 倍。
简而言之,SPP 能够实现 任意尺寸输入、固定尺寸输出(给全连接层)。SPPnet 基于此实现了对不同输入尺寸的适应性。
但 SPPnet 也有缺点,类似 R-CNN ,它也是多阶段流水线,包含提取特征、微调、训练 SVM 以及最后的 Box Regression,这些特征也得写入到磁盘。所以接下来回到主题,看看 Fast R-CNN 的改进
优点
总的来说,相对 R-CNN 和 SPPnet,它有这几个优点:
- 更高的 mAP
- 单阶段训练,使用了多任务损失(multi-task loss)
- 训练时会更新所有网络层
- 无需将特征存储于磁盘
结构
Fast R-CNN 仍然使用 Selective Search 产生候选区域,并将图片和这些候选区域作为输入。不同于对每个候选都做卷积,Fast R-CNN 对整张图片卷,并产生一个卷积特征图,然后对每个候选 RoI(Region of Interest,感兴趣区域)用 RoI 池化层产生定长的特征向量,丢入全连接层。最后会产生两个同级输出:第一个是分类结果,第二个是精修过的区域位置。
这里能看出比较有趣的另一点是,Fast R-CNN 将 R-CNN 最后的 Box Regression 也巧妙结合到了整个网络中。不过下面我们先看看另一个提到的东西: RoI 池化层
RoI 池化层
此层使用最大值池化,将任意感兴趣的有效区域转成小的、定长的特征图(长度 H*W,比如 7*7,H、W均为超参数)。本文里,RoI 使用一个四元组定义 ,分别对应左、上、高、宽
。
具体的操作时,RoI 池化层将 h*w 的 RoI 窗口划分为 H*W 的网格,然后将每个小格对应位置的值最大值池化、放入对应的网格内。对特征图的每个通道来说,这样的操作都是独立的。
其实这一层就是简化版的 SPP ,就是把金字塔池化缩到了一层。把不同尺寸的输入丢进去,最后都能得到固定维度的特征,能够丢给全连接层。
一个 h*w = 5*5 的输入经过 H*W = 2*2 后,结果示意如下
训练
初始化
本文尝试三个从 ImageNet 训练出来的模型,分别是 R-CNN 用的 CaffeNet(AlexNet 改进版)、VGG_CNN_M_1024 和 VGG16,从小到大分别称为S
、M
、L
。为了适应本文效果,模型做了如下三个变换:
- 最后的最大值池化层改为了 RoI 池化层,对每个模型固定了这一层输出的大小 H*W (比如 L模型就是 7*7)
- 最后的全连接层和 softmax 换成了上面描述的并行层
- 模型的输入被改成了两个部分:图片本身和相应的 RoI 区域
微调
对R-CNN 和 SPPnet 来说,当训练时,它们的某一批候选区域是随机挑的,比如 128 个候选区域可能来自 128 张不同的图片,但 Fast R-CNN 则是从 N 张图片里每张取 R/N 的候选区域出来,比如 N=2,R=128 两张图各取 64 个。更重要的是,由于同一张图的 RoI 可以共享计算,因此相较于之前的两个网络,它能大幅加快计算速度。
多任务损失
由于输出是两个并行的层,Fast R-CNN 的损失函数也就是二者之和。具体定义为
前半部分 即为预测类别(p)和真实类别(u)的对数损失;后半部分则为区域的损失。其中,为二者的平衡系数(在本文里均使用 1 ),[u ≥ 1]
则表示仅当 u
为真实类别时才计算,u = 0 (背景类)时不计算。最后的部分定义如下:
同 R-CNN 使用的 损失()不同,此处的损失更平滑。当 x 较大时,前者的梯度()较大,容易导致梯度爆炸;而此处的式子会保持梯度恒小于等于 1。
Mini-batch 采样
如上面已经展示的那样,Fast R-CNN 用 2*64 个 RoI 组成 mini-batch,类似于 R-CNN ,里面包含 25% 的目标样本(IoU >= 0.5)和 75% 的背景样本(0.1 <= IoU < 0.5)。在训练时,每个图片有 0.5 的概率水平翻转。
其他训练时的一些细节,如超参数的选择等此处略去,感兴趣的可以参考原文。
检测
检测的基本过程已经在结构里概述过,此处不再赘述。说一句,在得到结果的分类框和各自的分数后,Fast R-CNN 也对使用了 NMS,对每个类别的重复框做了处理。
在做检测时,作者发现,有比较多的时间花在了全连接层上。而我们知道,全连接层其实就是在计算矩阵 。为了加速这一过程,作者用到了 SVD(奇异值分解)。
SVD
简单来说,对给定的任意形状矩阵 ,都可以分解成下列形式
假定原始矩阵大小为 n*m,则分解出的三个矩阵大小分别如下
矩阵 仅在对角线上有值,称为奇异值
,按重要程度从左上到右下排序。因此,我们可以仅保留前 个奇异值,同时舍去多余的行和列,就能起到压缩的效果。被保留的区域如下图所示:
压缩后的三个矩阵相乘,大小仍为 m*n,且信息基本保持不变(在k值适当时)。
最终到实现时,相当于把一个全连接层改为两个,第一个不含偏置,第二个才加上偏置。
一些结果
下表来自原文,分别展示了三者在训练和测试上的速度
可以看到,对于 L 模型,Fast R-CNN 在训练上比 R-CNN 快到了 8.8 倍,测试上则达到了 213 倍(加上 SVD 的情况);同时,它的 mAP 也高于 R-CNN。
除此之外,作者还做了不少的对比实验,检验了对微调层的选择、多任务训练是否有帮助、scale 的策略、训练数据增加是否有帮助、softmax 和 SVM 对比、更多的候选框是否有效等,感兴趣的同学可以阅读原文,此处不再赘述。
缺点
尽管 Fast R-CNN 有不少优点,但仍然存在一些缺陷。其中,由于它挑选候选框仍然使用 Selective Search 算法,这成了耗时的主要来源。后文的 Faster R-CNN 就针对此做了改进。
Faster R-CNN
此部分由我的好友 @Zee 撰写的 PPT 整理而来,由衷感谢他的支持。
对应文章:Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks
结构
Faster R-CNN 相较于 Fast R-CNN 的最大改进就是,它提出了 RPN 结构来代替 Selective Search 生成候选框,并且与目标检测的卷积层进行共享来缩短训练时间和测试时间,同时提高目标检测的精度。二者结构的对比如下所示:
观察上图,我们能发现,Faster R-CNN 使用 RPN 结构从特征图中产生候选区域,而其余部分均与 Fast R-CNN 类似。文章里作者使用了两种卷积网络:ZF
和 VGG16
,下图是使用 ZF
网络的详细结构图
所以接下来,咱们就来看看 RPN 是个什么东西?
RPN
对于特征图 𝐻×𝑊×𝑑,我们使用一个 n*n(文章中n=3
) 的滑动窗口, 对每个中心点生成个 𝑘 个 (原文为 3x3 共九个, 三种比例分别为 1:1, 1:2, 2:1
,三个尺寸分别为 128 , 256, 512
) anchors。产生的 anchors 示意如图:
之后对所有的 anchors 做 bounding box regression 回归,这样如果对应的 anchor 有对应目标,就能找到尺寸最匹配的框。
经过 3×3 卷积层后,每个滑动窗口被映射为一个维度为 d 的低纬特征向量 (对于 ZF 是 256, VGG16 则为 512 ) , 它被送入到两个并行的 1×1 的卷积层,分别生成 𝐻×𝑊×2𝑘 和 𝐻×𝑊×4𝑘 的输出。前者 H*W 代表 anchors 的组数,每组包含 k 个 anchor,相邻的两个数字分别代表预测为目标物体和背景的概率;后者其余类似,4 个数字代表最终框的宽高以及位置偏移的相关量。
最后,将两个输出和 reshape 信息一起结合,生成对应于原始图片尺寸的 proposal, 与 feature map 一起送入 RoI pooling 层。后续结构与 Fast RCNN 一致。
损失函数
类似于 Fast R-CNN ,Faster 的损失函数也由两部分组成:
- 分类损失是 log 损失, i 是每一个 minibatch 中每个 anchor 的索引
- 如果 anchor 为 positive, 为 1, 否则为 0; 表示 anchor 为 positive 的概率
- 回归损失函数是 smooth
- 是表示(𝑡𝑥, 𝑡𝑦, 𝑡𝑤, 𝑡ℎ)四个参数的预测向量, 是表示 ground truth 对应的向量
- 最后使用 和 来进行标准化,原文中设置 、 、 ; 代码中 、 、 。因为只有正 anchor 才会被计算回归损失, 而 mini-batch 一般是 128 个正样本和 128 个负样本
训练
在训练时,作者采用了四步训练法来实现 RPN 和 Faster R-CNN 的卷积层参数共享。具体步骤如下:
实验
文章做了大量的实验,以验证各因素对算法效果的影响。
缺点
当然,Faster R-CNN 也存在一些缺点,我们的归纳如下所示
- 卷积提取网络:无论是 VGGNet 还是 ResNet,其特征图仅仅是单层的,分辨率通常也较小,这些都不利于小物体及多尺度的物体检测,因此多层融合的特征图、增大特征图的分辨率等都是可以优化的方向。
- NMS:在 RPN 产生 Proposal 时,为了避免重叠的框,使用了 NMS,并以分类得分为筛选标准。但 NMS 本身的过滤对于遮挡物体不是特别友好,本身属于两个物体的 Proposal 有可能因为 NMS 而过滤为 1 个,造成漏检,因此改进优化 NMS 是可以带来检测性能提升的。
- RoI Pooling:Faster R-CNN 的原始 RoI Pooling 两次取整带来了精度的损失,因此后续 Mask RCNN 针对此 Pooling 进行了改进,提升了定位的精度。
- 全连接:原始 Faster R-CNN 最后使用全连接网络,这部分全连接网络占据了网络的大部分参数,并且 RoI Pooling 后每一个 RoI 都要经过一遍全连接网络,没有共享计算。
- 正负样本:在 RPN 及 RCNN 部分,都是通过超参数来限制正、负样本的数量,以保证正、负样本的均衡。而对于不同任务与数据,这种正、负样本均衡方法是否都是最有效的值得思考,且现实生活中得到均衡的正负样本有时难以实现。
- 两阶网络:Faster R-CNN 的 RPN 与 R-CNN 两个阶段分工明确,带来了精度的提升,但速度相对较慢,实际实现上还没有达到实时。因此,网络阶数也是一个值得思考的问题。
感谢阅读,如有帮助,欢迎不吝点赞。
本文仅发于掘金和个人平台,作者 FunnySaltyFish。