前言
之前我所在的公司「七月在线」开设的深度学习等一系列课程经常会讲目标检测,包括R-CNN、Fast R-CNN、Faster R-CNN,但一直没有比较好的机会深入「但当你对目标检测有个基本的了解之后,再看这些课程你会收益很大」
- 但目标检测这个领域实在是太火了,经常会看到一些写的不错的通俗易懂的资料,加之之前在京东上掏了一本书看了看,就这样耳濡目染中,还是开始研究了
- 今年(2018)五一,从保定回京,怕高速路上堵 没坐大巴,高铁又没抢上,只好选择哐当哐当好几年没坐过的绿皮车,关键还不断晚点
在车站,用手机做个热点,修改题库,顺便终于搞清R-CNN、fast R-CNN、faster R-CNN的核心区别
有心中热爱 何惧任何啥
为纪念这心中热爱,故成此文
再后来,25年8.7日,在解读VideoMimic的过程中,注意到了Grounded SAM2,于此,引发了要修订一系列文章的计划,包括本文
- 比如,计划把《SAM(分割一切)》一文完善
- 把《自监督ViT与目标检测:从基于ViT的DINO、DINOv2,到最新目标检测器Grounding Dino、DINO-X》一文分成两篇文章
一篇侧重Meta发布的自监督ViT——从DINO到DINOv2
一篇侧重IDEA-Research推出的一系列检测、分割模型
在解读后者的过程中发现,值得把本文涉及的Faster R-CNN/RPN等模型的介绍 精进完善下
如此,便将本文的前三部分做了重要修订,且部分不够高清的配图 全部换成了超高清配图- 再之后,考虑到此文中《图像生成发展起源:从VAE、扩散模型DDPM、DDIM到DETR、ViT、Swin transformer》的DETR与该文前半部分的内容DDPM并无很大的联系
但DETR与本文目标检测的定位 完全一致,故把DETR的部分 迁移到本文的第五部分中- 8.12日中午,完成本文第一轮的全面修订....
第一部分 目标检测常见算法
object detection,就是在给定的图片中精确找到物体所在位置,并标注出物体的类别。所以,object detection要解决的问题就是物体在哪里以及是什么的整个流程问题
然而,这个问题可不是那么容易解决的,物体的尺寸变化范围很大,摆放物体的角度,姿态不定,而且可以出现在图片的任何地方,更何况物体还可以是多个类别
目前学术和工业界出现的目标检测算法分成3类
1.1 传统的目标检测算法
传统目标检测主要是Cascade + HOG/DPM + Haar/SVM以及上述方法的诸多改进、优化,其核心流程为:
- 区域选择(穷举策略:采用滑动窗口,且设置不同的大小,不同的长宽比对图像进行遍历,时间复杂度高)
- 特征提取(SIFT、HOG等;形态多样性、光照变化多样性、背景多样性使得特征鲁棒性差)
- 分类器分类(主要有SVM、Adaboost等)
具体而言,如下表格所示
方法及其流程简介 (区域选择-特征提取-分类器分类) | 技术亮点 | 对后续检测框架的影响 |
Viola-Jones(2001) 滑动窗口 + Haar 特征 + 级联 AdaBoost 强分类器 | 1) 积分图快速计算 Haar 特征 2) 级联结构实现毫秒级人脸检测 | 启发了「级联网络」思想(如 Cascade CNN、MTCNN) |
HOG + SVM(2005) | 1) HOG 特征成为通用视觉基元 2) 首次系统提出「特征+分类器」两阶段检测范式 | 特征金字塔、多尺度检测、SVM→Softmax 过渡 |
DPM(2008) | 1) 部件弹性模型 → 解决姿态/形变 2) 难例挖掘、bounding-box 回归、混合模型(mixture model) | 后续演变为 Faster R-CNN 的 anchor 与 RoI 思想 |
总之
- Viola-Jones 把「实时」带进检测
- HOG+SVM 把「手工特征 + 分类器」范式标准化
- DPM 在传统框架内把「形变建模」做到极致
三者共同为 2012 以后「端到端 CNN 检测器」奠定了问题定义、评价指标、后处理(NMS、回归)等全部基础设施
1.2 候选区域/窗 + 深度学习分类
通过提取候选区域,并对相应区域进行以深度学习方法为主的分类的方案,如:
- R-CNN(Selective Search + CNN + SVM)
- SPP-net(ROI Pooling)
- Fast R-CNN(Selective Search + CNN + ROI)
- Faster R-CNN(RPN + CNN + ROI)
- R-FCN
等系列方法;
1.3 基于深度学习的回归方法
YOLO/SSD/DenseBox 等方法;以及最近出现的结合RNN算法的RRC detection;结合DPM的Deformable CNN等
第二部分 传统的目标检测(2001-2010):滑动窗口 + 特征提取 + SVM分类
2.1 图像识别的任务:一是图像识别,二是定位
这里有一个图像任务:既要把图中的物体识别出来,又要用方框框出它的位置
这个任务本质上就是这两个问题:一:图像识别,二:定位
- 图像识别(classification):
输入:图片
输出:物体的类别
评估方法:准确率 - 定位(localization):
输入:图片
输出:方框在图片中的位置(x,y,w,h)
评估方法:检测评价函数 intersection-over-union,比如IOU
卷积神经网络CNN已经帮我们完成了图像识别(判定是猫还是狗)的任务了,我们只需要添加一些额外的功能来完成定位任务即可
「关于什么是IOU,请参看七月在线APP题库大题查看深度学习分类下第55题:https://www.julyedu.com/question/big/kp_id/26/ques_id/2138」
在目标检测的评价体系中,有一个参数叫做 IoU ,简单来讲就是模型产生的目标窗口和原来标记窗口的交叠率。具体我们可以简单的理解为: 即检测结果(DetectionResult)与 Ground Truth 的交集比上它们的并集,即为检测的准确率 IoU:
如下图所示:GT = GroundTruth; DR = DetectionResult;
- 黄色边框框起来的是:
GT⋂DR- 绿色框框起来的是:
GT⋃DR当然最理想的情况就是 DR 与 GT 完全重合,即IoU=1
原图则如下
定位的问题的解决思路有哪些?
思路一:看做回归问题
看做回归问题,我们需要预测出(x,y,w,h)四个参数的值,从而得出方框的位置
- 步骤1
先解决简单问题, 搭一个识别图像的神经网络
在AlexNet VGG GoogleLenet上fine-tuning一下「关于什么是微调fine-tuning,请参看:https://www.julyedu.com/question/big/kp_id/26/ques_id/2137」 - 步骤2:分成两个头,分别处理识别问题(分类)、定位问题(回归)
在上述神经网络的尾部展开(也就说CNN前面保持不变,我们对CNN的结尾处作出改进:加了两个头:“分类头”和“回归头”)
成为classification + regression模式 - 步骤3:
定位目标下的Regression那个部分用欧氏距离损失
使用SGD训练 - 步骤4:
预测阶段把2个头部拼上
完成不同的功能
这里需要进行两次fine-tuning
第一次在ALexNet上做,第二次将头部改成regression head,前面不变,做一次fine-tuning
Regression的部分加在哪?
有两种处理方法:
- 加在最后一个卷积层后面(如VGG)
- 加在最后一个全连接层后面(如R-CNN)
由于regression不好做,故一般想方设法转换为classification问题
regression的训练参数收敛的时间要长得多,所以上面的网络采取了用classification的网络来计算出网络共同部分的连接权值
2.1.2 解决定位问题的思路二:取图像窗口——弄个框框 框住
思路二:取图像窗口
还是刚才的classification + regression思路,咱们取不同的大小的“框”,让框出现在不同的位置,得出这个框的判定得分,最终取得分最高的那个框
- 左上角的黑框:得分0.5
- 右上角的黑框:得分0.75
- 左下角的黑框:得分0.6
- 右下角的黑框:得分0.8
根据得分的高低,我们选择了右下角的黑框作为目标位置的预测。
注:有的时候也会选择得分最高的两个框,然后取两框的交集作为最终的位置预测
疑惑:框要取多大?
取不同的框,依次从左上角扫到右下角。非常粗暴啊
总结一下思路:
对一张图片,用各种大小的框(遍历整张图片)将图片截取出来,输入到CNN,然后CNN会输出这个框的得分(classification)以及这个框图片对应的x,y,h,w(regression)![]()
但这方法实在太耗时间了,故可以做个优化。
- 原来网络是这样的:
- 故可以优化成这样:把全连接层改为卷积层,这样可以提提速
2.2 物体检测(Object Detection)
当图像有很多物体怎么办的?难度可是一下暴增啊,那任务就变成了:多物体识别+定位多个物体
那把这个任务看做分类问题?
看成分类问题有何不妥?
- 你需要找很多位置, 给很多个不同大小的框
- 你还需要对框内的图像分类
- 当然, 如果你的GPU很强大, 恩, 那加油做吧…
所以,传统目标检测的主要问题是:
- 基于滑动窗口的区域选择策略没有针对性,时间复杂度高,窗口冗余
- 手工设计的特征对于多样性的变化没有很好的鲁棒性
看做classification, 有没有办法优化下?我可不想试那么多框那么多位置啊!
第三部分 迈入DL时代:候选区域/窗 + 深度学习分类(2013-2015)
3.1 2013年11月,R-CNN横空出世
3.1.1 提取候选框(Region Proposal):选择性搜索
有人想到一个好方法:预先找出图中目标可能出现的位置,即候选区域(Region Proposal)。利用图像中的纹理、边缘、颜色等信息,可以保证在选取较少窗口(几千甚至几百)的情况下保持较高的召回率(Recall)
所以,问题就转变成找出可能含有物体的区域/框(也就是候选区域/框,比如选2000个候选框),这些框之间是可以互相重叠互相包含的,这样我们就可以避免暴力枚举的所有框了
- 大牛们发明好多选定候选框Region Proposal的方法,比如Selective Search和EdgeBoxes,下图是各种选定候选框的方法的性能对比
- R-CNN最终采用的selective search
那提取候选框用到的算法“选择性搜索”到底怎么选出这些候选框的呢?具体可以看一下PAMI2015的:What makes for effective detection proposals?
有了候选区域,剩下的工作实际就是对候选区域进行图像分类的工作(特征提取+分类)
对于图像分类,不得不提的是2012年ImageNet大规模视觉识别挑战赛(ILSVRC)上,机器学习泰斗Geoffrey Hinton教授带领学生Krizhevsky,使用卷积神经网络将ILSVRC分类任务的Top-5 error降低到了15.3%,而使用传统方法的第二名top-5 error高达 26.2%
此后,卷积神经网络CNN占据了图像分类任务的绝对统治地位
3.1.2 R-CNN:Region Proposal + CNN提取特征 + SVM分类
2013年11月,UC伯克利的Ross Girshick、Jeff Donahue、Trevor Darrell、Jitendra MalikUC Berkeley等人使用Region Proposal + CNN代替传统目标检测使用的滑动窗口+手工设计特征,设计了R-CNN框架,使得目标检测取得巨大突破,并开启了基于深度学习目标检测的热潮
R-CNN的简要步骤如下
- 输入测试图像
- 利用选择性搜索Selective Search算法在图像中从下到上提取2000个左右的可能包含物体的候选区域(Region Proposal)
- 因为取出的区域大小各自不同,所以需要
先将每个Region Proposal缩放(warp)成统一的227x227的大小
PS,至于为何需要这么做,简言之,是为了和下面的CNN兼容——其结构要求输入为固定的227 × 227 像素大小,更具体的下文马上会解释
然后再输入到CNN
作者用 Caffe [24] 实现的 Krizhevsky等人提出的卷积神经网络CNN[25-ImageNet clas-sification with deep convolutional neural networks],从每个区域建议中提取一个 4096 维的特征向量。特征的计算方式是将一个均值已减的 227×227 RGB 图像,通过五个卷积层和两个全连接层进行前向传播
最终将CNN的fc7层的输出作为特征
- 将每个Region Proposal提取到的CNN特征输入到SVM进行分类
再进一步解释一下在计算候选区域的特征时,为何必须首先将该区域内的图像数据转换为与CNN 兼容的形式
- 众所周知,CNN一般都含有卷积部分和全连接部分,其中,卷积层不需要固定尺寸的图像,而全连接层是需要固定大小的输入
- 所以当全连接层面对各种尺寸的输入数据时,就需要对输入数据进行
crop(crop就是从一个大图扣出网络输入大小的patch,比如227×227)
或
warp(把一个边界框bounding box的内容resize成227×227)
等一系列操作以统一图片的尺寸大小,比如224*224(ImageNet)、32*32(LenNet)、96*96等
具体步骤则如下
- 步骤一:训练(或者下载)一个分类模型(比如AlexNet)
- 步骤二:对该模型做fine-tuning
将分类数从1000改为20,比如20个物体类别 + 1个背景
去掉最后一个全连接层 - 步骤三:特征提取
提取图像的所有候选框(选择性搜索Selective Search)
对于每一个区域:修正区域大小以适合CNN的输入,做一次前向运算,将第五个池化层的输出(就是对候选框提取到的特征)存到硬盘 - 步骤四:训练一个SVM分类器(二分类)来判断这个候选框里物体的类别
每个类别对应一个SVM,判断是不是属于这个类别,是就是positive,反之nagative
比如下图,就是狗分类的SVM - 步骤五:使用回归器精细修正候选框位置:对于每一个类,训练一个线性回归模型去判定这个框是否框得完美
比如上面的左图把猫完全框出来了,很完美,中图 框的偏左了,右图 框画的太大了 空白太多
3.1.3 2000个候选框都要进行CNN提特征 + SVM分类,如何提速
细心的同学可能看出来了问题
- R-CNN虽然不再像传统方法那样穷举,但R-CNN流程的第一步中对原始图片通过Selective Search提取的候选框region proposal多达2000个左右
而这2000个候选框每个框都需要进行CNN提特征+SVM分类,计算量很大,导致R-CNN检测速度很慢,一张图都需要47s - 有没有方法提速呢?
答案是有的,这2000个region proposal不都是图像的一部分吗,那么我们完全可以对图像提一次卷积层特征,然后只需要将region proposal在原图的位置映射到卷积层特征图上
这样对于一张图像只需要提一次卷积层特征,然后将每个region proposal的卷积层特征输入到全连接层做后续操作 - 但现在的问题是每个region proposal的尺度不一样,而全连接层输入必须是固定的长度,所以直接这样输入全连接层肯定是不行的
巧的是,SPP Net恰好可以解决这个问题
3.2 SPP Net(2014/6):Spatial Pyramid Pooling(空间金字塔池化)
3.2.1 卷积层最后加入SSP Net,使得后面全连接层得到的输入变成固定
2014年6月,一堆CV牛人们Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun通过发表在IEEE上的论文《Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition》提出了SPP-Net
- 如你在上文中看到的,在R-CNN中,“因为取出的区域大小各自不同,所以需要将每个Region Proposal缩放(warp)成统一的227x227的大小并输入到CNN”
- 但warp/crop这种预处理,导致的问题要么被拉伸变形、要么物体不全,限制了识别精确度。没太明白?
说句人话就是,一张16:9比例的图片你硬是要Resize成1:1的图片,你说图片失真不?
SPP Net的作者Kaiming He等人逆向思考
i) 既然由于全连接FC层的存在,普通的CNN需要通过固定输入图片的大小来使得全连接层的输入固定
ii) 那借鉴卷积层可以适应任何尺寸,为何不能在卷积层的最后加入某种结构,使得后面全连接层得到的输入变成固定的呢?
这个“化腐朽为神奇”的结构就是spatial pyramid pooling layer
3.2.2 和R-CNN检测流程的比较:SPPNet实现CNN多尺度输入且只需要计算一次卷积
具体而言,SPP Net的特点有两个:
- 结合空间金字塔方法实现CNNs的多尺度输入
从上已知,在普通的CNN结构中,输入图像的尺寸往往是固定的(比如224*224像素),输出则是一个固定维数的向量
为使得深度网络能够适应任意尺寸的图像,SPP Net的第一个贡献就是在最后一个卷积层后,作者将最后一个池化层(ROI Pooling,例如,在最后一个卷积层之后的 pool5)替换为空间金字塔池化层(spatial pyramid pooling layer)
从而保证传到下一层全连接层的输入固定(即SSP是一个固定维数的向量,然后给到全连接FC层)
如此,CNN原本只能固定输入、固定输出,CNN加上SSP之后,便能任意输入、固定输出。神奇吧?
下图便是R-CNN和SPP Net检测流程的比较
下图图3则是包含空间金字塔池化层的网络结构。这里256表示conv5层的滤波器数量,conv5是最后一个卷积层 - 只对原图提取一次卷积特征
在R-CNN中,每个候选框先resize到统一大小,然后分别作为CNN的输入,这样是很低效的
而SPP Net根据这个缺点做了优化:只对原图进行一次卷积计算,便得到整张图的卷积特征feature map,然后找到每个候选框在feature map上的映射patch,将此patch作为每个候选框的卷积特征输入到SPP layer和之后的层,完成特征提取工作
如此这般,R-CNN要对每个区域计算卷积,而SPPNet只需要计算一次卷积,从而节省了大量的计算时间,比R-CNN有一百倍左右的提速
如下图图5所示,在特征图的任意窗口中进行特征池化。特征图是从整个图像计算得到的,池化操作则在候选窗口中进行
3.3 2015年4月:Fast R-CNN推出
3.3.1 Fast R-CNN:R-CNN的基础上采纳SPP Net方法
SPP Net真是个好方法,R-CNN的进阶版Fast R-CNN就是在R-CNN的基础上采纳了SPP Net方法,对R-CNN作了改进,使得性能进一步提高
下图便是Fast R-CNN架构『输入图像及多个感兴趣区域(RoI)被输入到一个CNN中。每个RoI被池化为固定大小的特征图,然后通过全连接层(FCs)映射为特征向量。网络针对每个RoI输出两个向量:softmax概率和每类的边界框回归偏移量。该架构通过多任务损失进行端到端训练 』
R-CNN与Fast R-CNN的区别有哪些呢?
- 先说R-CNN的缺点:即使使用了Selective Search等预处理步骤来提取潜在的bounding box作为输入,但是R-CNN仍会有严重的速度瓶颈,原因也很明显,就是计算机对所有region进行特征提取时会有重复计算,Fast-RCNN正是为了解决这个问题诞生的
- 与R-CNN框架图对比,可以发现主要有两处不同:
一是最后一个卷积层后加了一个ROI pooling layer
二是损失函数使用了多任务损失函数(multi-task loss),将边框回归Bounding Box Regression直接加入到CNN网络中训练
「关于什么是边框回归,请参看七月在线APP题库大题查看深度学习分类下第56题:https://www.julyedu.com/question/big/kp_id/26/ques_id/2139」
具体而言
- ROI pooling layer实际上是SPP-NET的一个精简版
SPP-NET对每个proposal使用了不同大小的金字塔映射
而ROI pooling layer只需要下采样到一个7x7的特征图
对于VGG16网络conv5_3有512个特征图,这样所有region proposal对应了一个7*7*512维度的特征向量作为全连接层的输入
换言之,这个网络层可以把不同大小的输入映射到一个固定尺度的特征向量
虽然conv、pooling、relu等操作都不需要固定size的输入,因此,在原始图片上执行这些操作后,虽然输入图片size不同导致得到的feature map尺寸也不同,不能直接接到一个全连接层进行分类
但是,可以加入这个神奇的ROI Pooling层,对每个region都提取一个固定维度的特征表示,再通过正常的softmax进行类型识别 - R-CNN训练过程分为了三个阶段,而Fast R-CNN直接使用softmax替代SVM分类,同时利用多任务损失函数边框回归也加入到了网络中,这样整个的训练过程是端到端的(除去Region Proposal提取阶段)
也就是说,之前R-CNN的处理流程是先提proposal,然后CNN提取特征,之后用SVM分类器,最后再做bbox regression
而在Fast R-CNN中,作者巧妙的把bbox regression放进了神经网络内部,与region分类和并成为了一个multi-task模型,实际实验也证明,这两个任务能够共享卷积特征,并相互促进
所以,Fast-RCNN很重要的一个贡献是成功的让人们看到了Region Proposal + CNN这一框架实时检测的希望,原来多类检测真的可以在保证准确率的同时提升处理速度,也为后来的Faster R-CNN做下了铺垫
3.3.2 Fast R-CNN相比R-CNN在性能上的提升
画一画重点:
- R-CNN有一些相当大的缺点(把这些缺点都改掉了,就成了Fast R-CNN)
大缺点:由于每一个候选框都要独自经过CNN,这使得花费的时间非常多
解决:共享卷积层,现在不是每一个候选框都当做输入进入CNN了,而是输入一张完整的图片,在第五个卷积层再得到每个候选框的特征 - 原来的方法:许多候选框(比如两千个)→ CNN → 得到每个候选框的特征 → 分类+回归
现在的方法:一张完整图片 → CNN → 得到每张候选框的特征 → 分类+回归
所以容易看见,Fast R-CNN相对于R-CNN的提速原因就在于:不像R-CNN把每个候选区域给深度网络提特征,而是整张图提一次特征,再把候选框映射到conv5上,而SPP只需要计算一次特征,剩下的只需要在conv5层上操作就可以了
在性能上提升也是相当明显的:
3.4 Faster R-CNN(2015/6)
3.4.1 Faster R-CNN:把Fast R-CNN中的Selective Search换成RPN
Fast R-CNN存在的问题:存在瓶颈:选择性搜索,找出所有的候选框,这个也非常耗时。那我们能不能找出一个更加高效的方法来求出这些候选框呢?
解决:加入一个提取边缘的神经网络,也就说找到候选框的工作也交给神经网络来做了
所以,还是咱们熟悉的CV牛人们Shaoqing Ren, Kaiming He, Ross Girshick, and Jian Sun等人
- 在Fast R-CNN中引入Region Proposal Network(RPN)替代Selective Search,相当于RPN模块指示后续的「CNN + ROI池化」模块应该关注的位置
同时引入anchor box应对目标形状的变化问题(anchor就是位置和大小固定的box,可以理解成事先设置好的固定的proposal) - 具体做法分两步:1 将RPN放在最后一个卷积层的后面,2 RPN直接训练得到候选区域
如此得到了Faster R-CNN,其对应的论文为《Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks》
至于什么是RPN呢?简言之:
• 在feature map上滑动窗口
• 建一个神经网络用于物体分类 + 框位置的回归——即在规则网格的每个位置同时计算目标性得分和回归区域边界,cls 层输出 2k 个分数,用于估计每个候选框为目标或非目标的概率,而reg 层有 4k 个输出,用于编码 k 个框的坐标
• 滑动窗口的位置提供了物体的大体位置信息
• 框的回归提供了框更精确的位置
如此,一种网络,四个损失函数
- RPN calssification(anchor good.bad)
- RPN regression(anchor->propoasal)
- Fast R-CNN classification(over classes)
- Fast R-CNN regression(proposal ->box)
在速度对比上
Faster R-CNN的主要贡献就是设计了提取候选区域的网络RPN,代替了费时的选择性搜索selective search,使得检测速度大幅提高
3.4.2 三大算法的对比:R-CNN、Fast R-CNN、Faster R-CNN
最后总结一下各大算法的步骤:
R-CNN | Fast R-CNN | Faster R-CNN |
1.在图像中确定约1000-2000个候选框 (使用选择性搜索Selective Search) | 1.在图像中确定约1000-2000个候选框 (使用选择性搜索Selective Search) | 1.对整张图片输进CNN,得到feature map |
2.每个候选框内图像块缩放至相同大小,并输入到CNN内进行特征提取 | 2.对整张图片输进CNN,得到feature map 3.找到每个候选框在feature map上的映射patch,将此patch作为每个候选框的卷积特征输入到SPP layer和之后的层 | 2.卷积特征输入到RPN,得到候选框的特征信息 |
3.对候选框中提取出的特征,使用分类器判别是否属于一个特定类 | 4.对候选框中提取出的特征,使用分类器判别是否属于一个特定类 | 3.对候选框中提取出的特征,使用分类器判别是否属于一个特定类 |
4.对于属于某一类别的候选框,用回归器进一步调整其位置 | 5.对于属于某一类别的候选框,用回归器进一步调整其位置 | 4.对于属于某一类别的候选框,用回归器进一步调整其位置 |
也如下图所示
简言之,即如本文开头所列
- R-CNN(Selective Search + CNN + SVM)
- SPP-net(ROI Pooling)
- Fast R-CNN(Selective Search + CNN + ROI)
- Faster R-CNN(RPN + CNN + ROI)
总的来说,从R-CNN, SPP-NET, Fast R-CNN, Faster R-CNN一路走来,基于深度学习目标检测的流程变得越来越精简,精度越来越高,速度也越来越快
可以说基于Region Proposal的R-CNN系列目标检测方法是当前目标检测技术领域最主要的一个分支
第四部分 基于深度学习的回归方法(2015-2020)
4.1 YOLO (2015/6)
4.1.1 统一检测流程
Faster R-CNN的方法目前是主流的目标检测方法,但是速度上并不能满足实时的要求
YOLO『其对应的论文为:You Only Look Once: Unified, Real-Time Object Detection,中稿CVPR2016, oral』一类的方法慢慢显现出其重要性,这类方法使用了回归的思想,利用整张图作为网络的输入,直接在图像的多个位置上回归出这个位置的目标边框,以及目标所属的类别
我们直接看上面YOLO的目标检测的流程图,如下图所示「系统将检测建模为回归问题。它将图像划分为一个S×S的网格,并且对于每个网格单元预测B个边界框、这些边界框的置信度以及C个类别的概率。这些预测被编码为一个S×S×(B∗5+C)的张量」
- 给个一个输入图像,首先将图像划分成S×S——比如7*7的网格
如果某个物体的中心落在某个网格单元内,则该网格单元负责检测该物体 - 对于每个网格,我们都预测
个(比如2个)边框:包括每个边框是目标的置信度
相当于边框是否框对了对应物体,而置信度分数反映了模型对该边界框包含物体的信心,以及模型认为该边界框预测的准确程度「These confidence scores reflect how confident the model is that the box contains an object andalso how accurate it thinks the box is that it predicts」
形式上,将置信度定义为
如果该单元格中不存在目标,则置信度分数应为零。否则,我们希望置信度分数等于预测框与真实框之间的交并比(IOU)
每个边界框由5个预测值组成:x、y、w、h和置信度。其中,(x, y)坐标表示边界框中心相对于网格单元边界的位置。宽度和高度是相对于整个图像进行预测的。最后,置信度预测表示预测框与任意真实框之间的IOU(交并比)
毕竟在训练时,一般只希望每个目标由一个边界框预测器负责。可根据当前与真实值的IOU(交并比)最高的预测,将其中一个预测器分配为负责预测该目标的预测器
以及每个边框区域在多个类别上的概率
即每个网格单元还预测个条件类别概率,
。这些概率以网格单元包含一个物体为条件。无论框的数量
有多少,我们每个网格单元仅预测一组类别概率
在测试时,将条件类别概率与各个框的置信度预测相乘
这为我们每个框提供了类别特定的置信度分数。这些分数同时编码了该类别出现在该框中的概率,以及预测框与目标物体的匹配程度 - 根据上一步可以预测出7*7*2个目标窗口,然后根据阈值去除可能性比较低的目标窗口,最后通过可消除重复检测的NMS去除冗余窗口即可「关于什么是非极大值抑制NMS,请参看七月在线APP题库大题查看深度学习分类下第58题:https://www.julyedu.com/question/big/kp_id/26/ques_id/2141」
可以看到整个过程非常简单,不再需要中间的Region Proposal找目标,直接回归便完成了位置和类别的判定
4.1.2 网络设计:基于CNN
作者将该模型实现为一个卷积神经网络,并在PASCAL VOC 检测数据集 [9] 上进行评估。网络的初始卷积层用于从图像中提取特征,而全连接层则用于预测输出的概率和坐标
- 具体而言,网络架构受到了用于图像分类的GoogLeNet 模型[34] 的启发。且网络包含24 个卷积层,后接2个全连接层
与GoogLeNet 中使用的inception 模块不同,作者仅仅采用1 × 1 降维层,后接3 × 3 卷积层,这与Lin 等人[22] 的方法类似 - 此外,作者还训练了一个快速版的YOLO,旨在突破快速目标检测的极限。Fast YOLO采用了更少的卷积层(9层而非24层)以及每层更少的滤波器
除了网络规模不同外,YOLO和FastYOLO在训练和测试参数上完全一致
小结:YOLO将目标检测任务转换成一个回归问题,大大加快了检测的速度,使得YOLO可以每秒处理45张图像。而且由于每个网络预测目标窗口时使用的是全图信息,使得false positive比例大幅降低(充分的上下文信息)
但是YOLO也存在问题:没有了Region Proposal机制,只使用7*7的网格回归会使得目标不能非常精准的定位,这也导致了YOLO的检测精度并不是很高
4.2 SSD(2015/12)
上面分析了YOLO存在的问题,使用整图特征在7*7的粗糙网格内回归对目标的定位并不是很精准。那是不是可以结合Region Proposal的思想实现精准一些的定位?
SSD『其对应的论文为:SSD: Single Shot MultiBox Detector』结合:YOLO的回归思想以及Faster R-CNN的anchor机制做到了这点
下图便是SSD的框架图
在训练过程中,SSD 只需输入一张图像及每个目标的真实边界框
- 通过卷积方式,在多个不同尺度的特征图「如(b)和(c)中的8×8和4×4」上的每个位置评估一组(例如4个)具有不同纵横比的默认框
对于每个默认框,会预测所有目标类别(如(c1, c2, · · ·,cp))的形状偏移量和置信度- 在训练时,首先将这些默认框与真实边界框进行匹配。例如,将两个默认框与猫匹配,一个与狗匹配,这些被视为正样本,其余则为负样本。模型损失为定位损失(如SmoothL1[6])与置信度损失(如Softmax)的加权和
总之,SSD获取目标位置和类别的方法跟YOLO一样,都是使用回归,但是YOLO预测某个位置使用的是全图的特征,SSD预测某个位置使用的是这个位置周围的特征
- 那么如何建立某个位置和其特征的对应关系呢?
可能你已经想到了,使用Faster R-CNN的anchor机制。如SSD的框架图所示,假如某一层特征图(图b)大小是8*8,那么就使用3*3的滑窗提取每个位置的特征,然后这个特征回归得到目标的坐标信息和类别信息(图c) - 不同于Faster R-CNN,这个anchor是在多个feature map上,这样可以利用多层的特征并且自然的达到多尺度(不同层的feature map 3*3滑窗感受野不同)
小结:SSD结合了YOLO中的回归思想和Faster R-CNN中的anchor机制,使用全图各个位置的多尺度区域特征进行回归,既保持了YOLO速度快的特性,也保证了窗口预测的跟Faster R-CNN一样比较精准。SSD在VOC2007上mAP可以达到72.1%,速度在GPU上达到58帧每秒
第五部分 CNN+Transformer端对端解决方案的新时代(2020及以后)
5.1 DETR整体结构:backbone,encoder,decoder和FFN
注,本小节的内容主要参考科技猛兽此文的相关部分
一般目标检测的任务是预测一系列的Bounding Box的坐标以及Label,而大多数检测器的具体做法是
- 要么基于proposal,比如R-CNN系列的工作,类似Faster R-CNN、Mask R-CNN
- 要么基于anchor,比如YOLO
- 把问题构建成为一个分类和回归问题来间接地完成这个任务,但最后都会生成很多个预测框(确定框的坐标及框内是什么物体),从而不可避免的出现很多冗余的框
- 而要去除这些冗余的框,则都需要做一个NMS(non-maximum suppersion,非极大值抑制)的后处理(使得最后调参不易、部署不易)
所以如果要是有一个端对端的模型,不需要做NMS之类的后处理 也不需要太多先验知识则该有多好
而通过发布于2020年5月这篇论文《End-to-End Object Detection with Transformers》提出的DETR则满足了大家这个期待:首次通过结合CNN+Transformer端对端解决object detection,其取代了现在的模型需要手工设计的工作,效果不错且可扩展性强——在DETR上加个专用的分割头便可以做全景分割
其解决的方法是把检测问题看做是一个集合预测的问题(即set prediction problem,说白了,各种预测框本质就是一个集合),其基本流程如下图所示
- CNN抽特征且拉直
- 全局建模,给到transformer-encoder去进一步学习全局信息
通过借助Transformer中的的self-attention机制,可以显式地对一个序列中的所有elements两两之间的interactions进行建模或交互,如此就知道了图片中哪块是哪个物体,从而对于同一个物体只需出一个预测框即可 - 接着通过不带掩码机制的transformer-decoder生成很多预测框
注意是并行预测(即并行出框,而不是像原始transformer预测下一个token时一个一个往外蹦)
相当于一次性生成个box prediction,其中
是一个事先设定的远远大于image中object个数的一个整数(比如100)
- 预测框和真实框做二分图匹配
最后通过bipartite matching loss的方法,基于预测的100个boxex和ground truth boxes的二分图做匹配,计算loss的大小,从而使得预测的box的位置和类别更接近于ground truth
当然,这第4步更多是做模型训练的时候用,如果模型训练好了去做推理时,该第4步就不需要了,可以直接在预测的100个框中设定个阈值,比如置信度大于0.7的预测框保留下来,其他视为背景物体而 舍弃
5.1.1 DETR结构之前两部分:backbone与encoder
更细致的讲,DETR整体结构可以分为四个部分:backbone,encoder,decoder和FFN,如下图所示
对于前两部分——backbone和encoder而言
由于一开始的backbone面对的是 维的图像
首先把它转换为维的feature map,一般来说,通道数
或256,
,
然后,由于encoder的输入是维的feature map,故正式输入encoder之前还需要依次进行以下过程(图源:科技猛兽):
- 通道数压缩(其实就是降维操作)
用 1×1 convolution处理将通道数channels数量从压缩到
,即得到
维的新feature map
- 转化为序列化数据
将空间的维度(高和宽
)压缩为一个维度
,即把上一步得到的
(
)维的feature map通过reshape成(
)维的feature map
这步相当于把编码矩阵的维度是,序列化成维度为
维的张量
- 位置编码
在上一步得到了维的feature map之后,再对
维的feature map做positional encoding
最后也做下reshape:高和宽
压缩为一个维度
,使得其与上面input embedding维度是一致的
对于上面第3步的位置编码,再好好解释说明下
首先,通过此文《一文通透位置编码:从标准位置编码、旋转位置编码RoPE到ALiBi、LLaMA 2 Long(含NTK-aware简介)》可知,原始transformer中的Positional Encoding的表达式为:
其中
就是这个
维的feature map的第一维
表示token在sequence中的位置,sequence的长度是
,例如第一个token 的
,第二个token的
,或者准确意义上是
和
表示了Positional Encoding的维度,
的取值范围是:
所以当为1时,对应的Positional Encoding可以写成(注意到
):
其次,DETR与原版transformer中的位置编码有两点不同
- 第一点不同的是,原版Transformer只考虑
方向的位置编码,但是DETR考虑了
方向的位置编码,因为图像特征是2-D特征:即虽然采用的依然是 sin cos 模式,但是需要考虑
两个方向
换言之,DETR不是类似vision transoformer的做法——简单的将其拉伸为,然后从
进行长度为256的位置编码,而是考虑了
方向同时编码,每个方向各编码128维向量,这种编码方式更符合图像特点
Positional Encoding的输出张量是:,
,其中
代表位置编码的长度,
代表张量的位置
意思是说,这个特征图上的任意一个点有个位置编码,这个编码的长度是256,其中,前128维代表
的位置编码,后128维代表
的位置编码
假设你想计算任意一个位置,
,
的Positional Encoding
先把
代入上面4个公式中的
式和
式可以计算得到128维的向量,它代表
的位置编码
再把
代入上面4个公式中的
式和
式可以计算得到128维的向量,它代表
的位置编码
把这2个128维的向量拼接起来,就得到了一个256维的向量,它代表
的位置编码
从而计算所有位置的编码,就得到了的张量,代表这个batch的位置编码
- 第二点不同的是,原版Transformer只在Encoder之前使用了Positional Encoding,而且是在输入上进行Positional Encoding,再把输入经过transformation matrix变为Query,Key和Value这几个张量
但是DETR在Encoder的每一个Multi-head Self-attention之前都使用了Positional Encoding,且只对Query和Key使用了Positional Encoding——即只把维度为维的位置编码与维度为
维的Query和Key相加,而不与Value相加
上图为DETR的Transformer的详细结构,读者可以对比下原版Transformer的结构 可以发现,除了Positional Encoding设置的不一样外,Encoder其他的结构是一致的。每个Encoder Layer包含一个multi-head self-attention 的module和一个前馈网络Feed Forward Network所以,了解了DETR的位置编码之后,你应该明白了其实input embedding和位置编码维度其实是一样的,从而也就可以直接相加,使得Encoder最终输出的是
维的编码矩阵Embedding,按照原版Transformer的做法,把这个东西给Decoder
5.1.2 DETR结构的后两部分:decoder和FFN
通过上面或下面的对比图,可知DETR的Decoder和原版Transformer的decoder也是不太一样的
- 对于原版Transformer,其decoder的最后一个框(上面左图右上角所示):output probability,代表我们一次只产生一个单词的softmax,根据这个softmax得到这个单词的预测结果,即:predicts the output sequence one element at a time
- 不同的是,DETR的Transformer Decoder是一次性处理全部的object queries(上面右图右上角所示),即一次性输出全部的predictions(而不像原始的Transformer是auto-regressive的,从左到右一个词一个词地输出),即:decodes the N objects in parallel at each decoder layer
至于DETR的Decoder主要有两个输入:
- 第一个输入是Transformer Encoder输出的Embedding与 position encoding(在上图右侧第二个multi-head self-attention处)相 + 之后 给到
其中的Embedding就是上文提到的的编码矩阵
- 第二个输入是Object queries
所谓Object queries是一个维度为维的张量,数值类型是nn.Embedding(意味着是可以学习的)
Object queries矩阵内部通过学习建模了100个物体之间的全局关系,例如房间里面的桌子旁边(A类)一般是放椅子(B类),而不会是放一头大象(C类),那么在推理时候就可以利用该全局注意力更好的进行解码预测输出
关于上图右侧第1个multi-head self-attention的Q K V
如上图所示,它的Q K是这么来的:Decoder原本的输入一开始初始化成维度为维的全部元素都为0的张量,然后和Object queries加在一起之后充当第1个multi-head self-attention的Query和Key,至于Value则是Decoder原本的输入,也就是全0的张量
关于上图右侧第2个multi-head self-attention的Q K V
它的Key和Value来自Encoder的输出张量,维度为,其中Key值还进行位置编码(正如上面第一个输入所述)
至于其Query值的一部分来自第1个Add and Norm的输出,维度为的张量,另一部分来自Object queries,充当可学习的位置编码
所以,第2个multi-head self-attention的Key和Value的维度为,而Query的维度为
每个Decoder的输出维度为,送入后面的前馈网络
到这里你会发现:Object queries充当的其实是位置编码的作用,只不过它是可以学习的位置编码,所以,我们对Encoder和Decoder的每个self-attention的Query和Key的位置编码做个归纳,如下图所示,Value没有位置编码
5.1.3 损失函数部分解读
得到了Decoder的输出以后,如前文所述,应该是输出维度为的张量
接下来要送入2个前馈网络FFN得到class和Bounding Box(如再度引用的下图所示),它们会得到 个预测目标 包含类别和Bounding Box(当然这个100肯定是大于图中的目标总数的,如果不够100,则采用背景填充)
所以,DETR输出张量的维度为,和
『对应COCO数据集来说:
, 4 指的是每个预测目标归一化的
,归一化就是除以图片宽高进行归一化』
对于这两个维度
- 前者代表分类分支,指100个预测框的类型
- 后者代表回归分支,指100个预测框的Bounding Box(仅仅计算有物体位置,背景集合忽略)
但是读者可能会有疑问:预测框和真值是怎么一一对应的?换句话说:你怎么知道第47个预测框对应图片里的狗,第88个预测框对应图片里的车?..
- 这就需要用到经典的双边匹配算法了,也就是常说的匈牙利算法,该算法广泛应用于最优分配问题
一幅图片,我们把第个物体的真值表达为
——其中,
表示它的 class ,
表示它的 Bounding Box
然后定义为网络输出的
个预测值
对于第个真值
,
为匈牙利算法得到的与真值
对应的预测值prediction的索引,举个例子,比如
,
,意思就是:与第3个真值对应的预测值是第18个
- 那如何根据匈牙利算法找到与每个真值对应的预测值到底是哪个呢?
对于某一个真值,假设已经找到这个真值对应的预测值
,这里的
是所有可能的排列(代表从真值索引到预测值索引的所有的映射),然后用
最小化
和
的距离
- 这个
具体是:
意思是:假设当前从真值索引到预测值索引的所有映射为,对于图片中的每个真值
先找到对应的预测值
,再看看分类网络的结果
,取反作为
的第1部分
再计算回归网络的结果
与真值的 Bounding Box 的差异,即
,作为
的第2部分
所以,可以使得最小的排列
就是我们要找的排列,即:对于图片中的每个真值
来讲,
就是这个真值所对应的预测值的索引
上述这个匈牙利算法的过程与Anchor或Proposal有异曲同工的地方,只是此时我们找的是一对一匹配 - 接下来就是使用上一步得到的排列
,计算匈牙利损失
式中的具体为:
最常用的loss对于大小 Bounding Box 会有不同的标度,即使它们的相对误差是相似的。为了缓解这个问题,作者使用了
loss和广义IoU损耗
的线性组合,它是比例不变的
Hungarian意思就是匈牙利,也就是前面的,上述意思是需要计算
个 GTBounding Box 和
个输预测出集合两两之间的广义距离,距离越近表示越可能是最优匹配关系,也就是两者最密切,广义距离的计算考虑了分类分支和回归分支
主要参考及扩展阅读
- 斯坦福Fei-Fei Li & Andrej Karpathy & Justin Johnson,1 Feb 2016
Lecture 8: Spatial Localization and Detection - https://www.cnblogs.com/skyfsm/p/6806246.html,by @Madcola
- https://mp.weixin.qq.com/s?__biz=MzI1NTE4NTUwOQ==&mid=502841131&idx=1&sn=bb3e8e6aeee2ee1f4d3f22459062b814#rd
- https://zhuanlan.zhihu.com/p/27546796
- https://blog.youkuaiyun.com/v1_vivian/article/details/73275259
- https://blog.youkuaiyun.com/tinyzhao/article/details/53717136
- Spatial Pyramid Pooling in Deep Convolutional
- Networks for Visual Recognition,by Kaiming He等人
- https://zhuanlan.zhihu.com/p/24774302
- 知乎专栏作者何之源新书《21个项目玩转深度学习——基于TensorFlow的实践详解
- YOLO:https://blog.youkuaiyun.com/tangwei2014/article/details/50915317,https://zhuanlan.zhihu.com/p/24916786