用 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维的特征,或者说是一个51x39的图片,每个位置都是一个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 是在哪里加的魔法,有空再进行研究。

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

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

《用 CNN 做物体检测》上有1条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注