用 CNN 做物体检测


发布于

|

分类

物体检测从来都是个比较热门的研究方向,和其他研究方向一样,它也经历了传统的人工提取特征+浅层分类器的框架,到基于大数据和深度神经网络的端到端的框架的发展过程。

我们先来一句话提一下传统方法:传统方法主要将整个检测过程划分成了三个阶段:检测窗口的选择、特征的设计和最后分类器的设计。本次和大家分享的是几个基于CNN的方法,在这些方法中,有一些是对上述三个阶段分别进行替换和改进,有些是进行融合,成为一个统一的端到端的框架。

本文主要分享5篇文章,从开山鼻祖RCNN,到Fast RCNN,再到Faster RCNN,然后是思想简单巧妙的YOLO,最后是黑魔法满满但我却看不懂多少的YOLO9000 。由于时间原因,我跳过了SPP-NET和SSD这两个同样很有名的文章。

RCNN

我们先来看一下RCNN,这篇用CNN进行物体检测的开山之作。他第一个将CNN用到物体检测问题,同时提出了用CNN进行物体检测的pipeline:首先提取候选框,意思是“这里面可能会有物体”,然后对每个框框进行特征提取,拿到特征之后用分类器进行分类。

它的想法是,“既然CNN在分类问题上表现那么好,同时在特征提取方面表现也不错,那么我们为什么不用CNN来做一个特征提取试试呢?”这时候,我们的pipeline就变成了这样:首先是输入图片,提取了一大堆候选框,然后对于每一个候选框,当成一幅完整的图片,进行特征提取和分类。也就是说,把 物体检测 任务变成了 分类 任务。

我们先来看一下候选框是怎么生成的:它这里面用到了Selective Search算法,个人感觉和Super Pixel有些相似。首先,用一种过分割的方法,将图像分割成小的区域,然后将小区域按照某种规则进行合并,直到整张图像合并成一个区域为止。最后,输出所有曾经存在过的区域

那么,合并规则是什么样的呢?我们来看一下:这里面规则有,合并颜色、纹理比较相近的区域,其中颜色是用颜色直方图来表示的,纹理是用提督直方图来表示的。然后,合并后总面积最小,这个有点像哈夫曼树的想法。然后,合并后总面积在其BBox中所占比例最大的(这一句不是特别明白)。最后还要保证合并后形状尽量规则,避免“特别长的长条”之类的形状。这里面都是使用的传统方法,可以说是“有理有据”。

框框有了,那么下一步就是将它们从原图中裁出来了。把所有框框从图片中裁出来,然后缩放到$227*227$的大小。这里作者提出了几种不同的裁剪方式,我们从后往前看:第一种是最容易想到的,直接裁出来,然后暴力拉伸。第二种是裁出来,然后补足成正方形。第三种是取框框的最长边的长度,然后直接裁出来一个正方形。同时作者还尝试了在框框外面加一圈padding。

图片有了,下一步就是对所有图片进行提取特征和分类了。在这里,作者是用了AlexNet,取Pooling5层的输出作为特征。在这里要说的是,我们选出来的框框有将近3000个,而一张图片上的GroundTruth只有那么几个,这会导致很严重的正负样本比例失衡的问题。为此,作者做了两个限定:首先是正负样本比例为1:3,然后是如果某个框框与某个人GroundTruth的IoU大于等于0.5,就把它当作正样本,这样扩大了正样本的范围。

这样经过上述步骤,我们就拿到了每一个框框的特征,就能直接用SVM或者其他分类算法进行分类了。在这里,作者重新选取了一下正负样本:只有GroundTruth才是正样本,其他框框,如果和GroundTruth的IoU都小于0.3,那么它才是负样本。这样同样是减少了负样本的数量。

这样,我们就能做物体检测任务了。但是作者又做了一个后续工作:用SelectiveSearch提出来的框框可能位置不准确,能不能对框框的位置再进行一次修正呢?于是,作者又训练了一个回归模型,用于修正框框,输出的结果是“这个框框应该向哪里平移多少,同时应该缩放多少,这样就和GroundTruth更加接近了”。

我跑了一下这篇文章提供的代码,然后得到了下面的使用感受:
1. 不是端到端的框架,里面每个步骤都需要人工运行一下。即使写了一键脚本也是非常非常麻烦。
2. 速度慢。整体用时比较长。检测一张800*600的图片大概在4~5秒(GTX1060)。
3. 精度较低。可能是我哪里设置不对,二分类问题精度在50%左右徘徊。

Fast RCNN

然后呢,就出现了Fast RCNN。看名字就知道是为了解决速度问题的。这里将速度提高的同时,整合了一些步骤,向着端到端模型迈进了一步。

这篇文章主要解决这三个问题:

  1. 对每个框框都从头进行一遍特征提取,太浪费计算资源了
  2. 我们为什么不把分类这件事儿也交给CNN来做呢?
  3. 既然分类交给了CNN来做了,就顺带着怎么把最后的微调工作也交给它吧

这篇文章里,pipeline变成了这样的:

首先,仍旧是使用SelectiveSearch来生成一些框框,之后对整幅图像提取特征,而不是对每一个框框分别提取特征,然后使用一个叫做RoI Pooling 的层从整幅图像的特征图中将每个框框的特征切出来,这样就避免了对每个框框从头过CNN进行特征提取,大大减少时间消耗。最后,使用Multi Task对候选框同时进行分类和坐标回归。

我们一步一步来。最前面的Selective Search没有任何变化,特征提取,也没有什么变化,跳过。然后就是RoI Pooling了。这个Pooling和正常Pooling的唯一区别在于,这里指定的是Pooling结果的大小,正常Pooling指定的是Pooling窗口的大小。也就是说,这里需要把Pooling窗口每次重新算一下(做个除法)就行了。这样能把大小不一致的图像全都Pooling成大小统一的数据。

有了特征,同时有了框框的坐标,下面开始进行分类和框框坐标的回归。这里作者用了Multi Task,同时完成这两个事情。先看上面一路:上面是对框框进行分类的,输出框框属于某个类的概率,然后再看下面一路,是用于调整框框坐标的,输出当属于某一类的时候,框框应该怎么进行调整。上面和下面使用的Loss并不是特别相同,下面使用了一个作者自创的$Smooth_{L1}$ Loss。作者说$Smooth_{L1}$ Loss能表现得更平滑。最后,整体的Loss是两者的加权和:如果为物体,那么就是分类Loss+定位Loss,否则如果是背景,那么就只要分类Loss。

训练过程是从2张图片随机进行水平翻转,然后选出128个候选框,分一下正负样本,后进行训练。这里正负样本的选取同样又一个比较奇怪的规定:首先是数量规定,比例为1:3,然后是分类规定,与GroundTruth的IoU在某个范围是正样本,在某个范围是负样本,其他扔掉。

到这里,我们就能用Fast RCNN进行物体检测了。作者又说,最后那个FC层还有一定速度提升空间,将一个大的矩阵进行SVD分解,拆成两个矩阵,通过降低计算复杂度来进行加速。但是作者的代码中并没有进行实现。

最后,作者还进行了一系列的实验,得出了下面的一些结论。但是个人觉得这些结论只是目前可能是对的,不排除以后被推翻的可能,例如AlexNet说“前面用比较大的卷积核后面用比较小的卷积核”,但是GoogleNet和VGG立刻就把这个给推翻了。

Faster RCNN

然后呢,这个框架仍然不是端到端的,同时我们发现,SelectiveSearch所用的时间比后面特征提取+分类+框框坐标回归的时间还要长。所以,Faster RCNN解决了这几个问题。

对整幅图像进行特征提取,然后使用一个网络生成一些框框,然后后面的事情和RCNN相同,将框框和特征输入给网络,让网络对每一个框框进行判别和坐标回归。

前面后面几乎相同,我们把重点放到这个框框的生成网络上来。前面对一幅图像进行了特征提取,可以得到一个51x39x256维的特征,或者说是一个51×39的图片,每个位置都是一个256维的特征。传统方法是在RGB图或者是灰度图上做滑动窗口,这里是在这个256维的特征图上做滑动窗口;传统方法是对图像进行多尺度变换,得到不同大小、不同长宽比例的窗口,这里是对窗口进行变换,得到不同大小、不同比例的框框。对于特征图上的每一个位置,做滑动窗口,生成9个框框,他们总共有三种面积和三种长宽比。得到坐标之后,网络还计算了一下每个框框里面包含物体的可能性。于是,这个RPN的输出为,xy坐标,hw长宽,里面含有物体和不包含物体的可能性。注意这里是“含有“和“不含有“,而不是“含有某类“和“不含有某类“。我们会发现,这里面提出来的框框有 51x39x9=17901个框框,比之前SelectiveSearch得到的框框要多得多。这时候就要进一步去掉一些不太好的框框了:首先,去掉大小太小的框框,然后去掉超出图像边界的框框,然后做一次非极大值抑制,根据上面的概率取大约12000个框框,然后根据IoU取正样本(IoU>0.7)和负样本(IOU<0.3),剩余的框框扔掉,如果这时候框框还是太多,那么再根据上面的概率,取前2000个。实际测试中我们发现,每张图片能得到600~1800个框框。

作者提出了很多种网络训练的方法,其中他代码中用了最后的这一种“Alt-Opt”(交替优化)的方法。先训练RPN,再用RPN做基础训练FastRCNN,在用第二步做基础,训练RPN,但只更新RPN特有的层,最后用第三步做基础,只更新FastRCNN特有的层。

最后说一下使用感受,比RCNN的速度快,大约每秒7张图片,二分类问题精度在89%左右。

YOLO

所以,速度能不能再提高一点呢?Faster RCNN的Pipeline还是太长了。同时,能不能还用回归的方法来做分类呢?

YOLO的思想是,将整张图片打成格子,然后分成两部分同时来做,第一部分是这个格子里面有没有物体?第二部分是,如果有物体的话,那么它属于某个类的概率是多少?并且它的坐标应该是什么?最后的结果就是“有物体x是哪类”。最最后,做一个简单的非极大值抑制,消除重叠的框框,输出即可。

这个Pipeline相当于是将Faster RCNN的RPN做了简化,或者说是将滑动窗口的步长增大了,同时将每个位置生成的框框的数量大大减少了,少做了很多NMS,同时使用了一个比较简单而高效的网络,使得速度有了很大的提升。

但是它的缺点也很明显,它将图片划分了格子,如果几个物体同时落到一个格子里面,就检测不出来了,或者说是“对小物体不敏感”。我们可以将格子划分得小一点,或者将框框的数量增多,这样可以在一定程度上增加对小物体的敏感程度,但是会降低性能。

YOLO 9000

YOLO的速度已经很惊人了,但有些人就是不知道满足。

YOLO9000主要提升了YOLO的性能,速度更快,精度更高,同时这篇文章中放出了很多黑科技,真的能配得上它使用的“Darknet”这个工具的名字。

先来说说做的改进(有一些我不理解):

  1. Batch Normalize:这个手段最近好像经常能看见。作者说用了它就不用DropOut了,同时可以提高模型的收敛速度,减少过拟合的发生。
  2. High Resolution Classfier: 将图片的分辨率提升一倍,好像能增加对小物体的敏感程度。
  3. Convolutional With Anchor Box: 借鉴Faster RCNN的思想,加入Anchor Box,可以提升Recall。(没看懂……)
  4. Dimension Clusters: 使用聚类的方法来自动选择最佳的初始Boundry Box(同样没看懂……)
  5. Direct Iocation Prediction:和ResNet的思想相似,加入PassThrough Laye r。同时将特征加深。
  6. Fine-Grained Features: 将预测值归一化到0~1
  7. Multi Scale Training:这个看懂了……因为模型只有卷积层和Pooling层,这两种层与输入图片的大小都无关,所以可以任意改变输入图片的尺寸。于是,作者每隔10个iter,就改变一下输入图片的尺寸,尺寸为320+rand(10)*32,这样可以强迫网络接受不同分辨率的图片,提升网络精度。

然后是如何使网络更加强大了。这里又是一堆黑魔法。

  1. 使用分类和检测混合数据集,扩展可以检测物体的种类:分类数据集没有Ground Truth Box,检测数据集有。那么,遇到分类数据集里面的图片,就只用分类部分的Loss,遇到检测数据集里面的图片,就同时用分类Loss和定位Loss。
  2. 层次化预测:参考WordNet,让网络按照树形结构进行预测。(没看懂……)例如,犬类->狗->猎狗->某种类小猎狗。
  3. 通过WordTree,合并多个数据集的数据。

看不懂归看不懂,用还是要用的。实际使用感受是,每秒大约能处理27张图片,二分类问题精度在95%左右。

写在最后

如果说前面的RCNN部分可以说是“很自然”的进化,后面YOLO简直就是黑魔法。由于中间跳过了SSD,不知道SSD是在哪里加的魔法,有空再进行研究。

这里只是对上面几种方法进行一个大概的描述,里面应该是有不少理解错误的吧……欢迎指出。

本文是《数字视频处理》课程报告的讲稿


评论

  1. leo 的头像
    leo

    yolo9000的5和6写反了

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注