![3cda8b4a771c2b9beffe8bea8596f922.png](https://i-blog.csdnimg.cn/blog_migrate/5cc766f6b14506b8a6d4ea5b7c01f8ee.jpeg)
一、序言
一直想写一些东西,但一直没有动手,拖延症泛滥,直到现在才对过往做一下总结,希望微薄的见解能够对读者有些许启发,也就不枉这一通码字。这里写的是一个系列文章,有比较基础的,也有一些稍微深入的,目的是能够浅显而不失内涵地呈现出深度学习的概貌。在这系列文章中,将会介绍到深度学习、目标检测、Caffe框架、论文阅读、代码调试等内容,会涉及数学、计算机理论、软件工程、甚至是文学艺术哲思(当然这是吹牛皮咯),有理论也有实践,在此希望各位看官能够择之精华,弃之糟粕,疏漏之处,还望多多包涵,也恳请能够指出错误,以求共同进步。
那么为什么要从SSD目标检测讲起呢?
首先,计算机视觉的首要任务就是检测出目标,然后才能在此基础上理解事物,解释世界。它是其他计算机视觉任务的基础。虽然现在的检测算法已经很强大,然而它们在小目标的检测精度以及算法速度等指标上依旧亟待提升,因此目标检测是一个比较热门的研究方向。其次,目标检测是一个相对比较难的问题,涉及的范围也比识别任务更宽泛,以一个复杂的任务来学习作为节点,拓展开来更容易了解深度学习的全貌。最后,我接触目标检测任务就是从SSD框架开始的,并且目前工作中,用到最多的也是这个框架,所以从SSD来讲更符合我个人的思路。因此,各路仙家大神就将就一下,勉为其难地顺着这个思路来读下去。
二、目标检测发展概述
首先区分两个概念,检测与识别,识别是识别出图片中的目标属于哪个类别,是一个分类任务,通常在同一个属性范畴内,这张图片只可能属于一个类别,假如图片中出现同一个属性内的两个对立的类别,比如猫和狗同时出现,识别模型也只是把这张图片分类为它认为最像的一个类别。这种情况往往不能够满足现实情况的要求。于是检测模型这时候就发挥了作用,它需要先将两个目标抠出来,再分别判断它们的类别。因此,一个检测模型往往包含定位和分类,即目标在哪里,它是什么类别。不难看出,识别任务基于定位出来的目标这个先验知识的。
在计算机中,一张彩色图片是以RGB三通道顺序来存储的,相当于一个三维数组,数组中数值的大小和位置决定了这张图片的形态,而计算机对这些数字规律是一无所知的,检测算法就相当于一个数学公式,它告诉计算机,经过这样的计算就能够得到正确的结果,这就注定了检测算法要跟数学打交道,差分、积分、卷积、求导、概率论、线性代数、优化、空间变换以及一些其他的更复杂更深奥的数学理论,算法研究的实质就是以一种怎样的形态更好地展现这些数字的规律。这里不对数学知识展开,后续我会专门几篇文章讨论这些数学理论,有兴趣的同学可持续关注。为了使读者有一个更加流畅的理解,下面对目标检测的几个比较重要的方法做简要概述。
初期的检测算法都是人工设计的,比如,模板匹配就假设人脸是由轮廓和五官等相互连接的几个局部块组成,然后人工设计出一套模板作为人脸模式,检测人脸时,首先用边缘检测方法,如Roberts算子、Sobel算子、Canny算子等提取图像的边缘特征,然后提取人脸各个局部块并使之相互关联,最终将其与设计好的模板做匹配。这些方法更多的是关注人的正脸,这样的人工设计模板往往耗费很大的精力,并且带有很大的主观性。也有学者引入神经网络、SVM等机器学习算法来自动学习人脸模板,但这并未从本质上改变该方法的缺陷。
直到2001年,Paul Viola和Michael Jones提出了VJ检测算法,它最初是用于人脸检测,在保证精度的同时,将人脸检测速度提升了上百倍,使得人脸检测的实际应用成为可能。VJ检测算法有三个关键步骤:积分图快速计算方法、AdaBoost学习算法、级联结构分类策略。积分图是Haar特征的一种快速计算方法,所谓的Haar特征就是在图像某个位置取矩形小块儿,该小块儿包含黑白相间等间距的两部分,黑色小块的像素值之和减去白色小块的像素值之和即为Haar特征的值,它表示的是人脸局部的明暗关系。从计算过程来看,每个Haar特征的值,都会涉及其局部像素点的加减运算,相邻部分具有相当大的计算量。Viola和Jones则采用如图 2-1 所示的方式计算,他们将原始图像每个像素值转化为该像素到左上角像素点形成的矩形框的一个和值,得到的积分图与原始图像大小相同,在积分图上计算Haar特征只需几次加减法运算即可得到,与矩形小块儿大小无关。AdaBoost学习方法是将几个弱分类器聚合成强分类器,一般来说,弱分类器比较简单,参数量和计算量相对少得多,在同样的精度要求下,几个弱分类器的组合往往比一个强分类器的计算量要小得多,检测速度也会相应提高。最后,VJ 检测器将多个分类器级联到一起,前面的分类器比较简单,能过滤掉大部分不是人脸的滑动窗口区域,后面的分类器比较精细,对前面分类器选择出的滑动窗口区域进行更加准确的分类,保证整个网络最终的精度。AdaBoost方法和级联策略的不同之处在于前者是并行地合并模型,而后者是串行地合并模型。
![d51c5c5b842e12f439e6c40dbac4a376.png](https://i-blog.csdnimg.cn/blog_migrate/4e3f6df9f3f5d4de284b87c37749ba18.jpeg)
VJ检测算法是目标检测的一个里程碑,之后许多算法都借鉴了它的思想,在它的基础上进行改进。其中一种改进方法是对特征提取方法进行改进,如 Haar-like、HOG、SIFT、LBP、SURF、积分通道、聚合通道、PICO、NPD和DPM等特征来代替简单的Haar特征,这些特征往往是根据经验来设计的,提出一个优秀的特征提取方法往往需要耗费相当大的经历,直到现在仍有许多学者对特征提取方法进行孜孜不倦的研究。另一类改进方法是对分类器的改进,其中具有代表性的有FloatBoost、链式Boosting和嵌套式Boosting方法等。多任务级联的检测方法也是一种改进策略,如JDA可以将人脸检测任务和人脸特征点定位任务结合在一起,交替级联人脸检测的分类器和特征点位置回归,人脸检测的分类器以特征点为中心的局部区域为检测区域,而人脸的特征点位置不断地被检测为人脸的局部区域更新,两种任务交替进行,迭代更新,最终检测出人脸并回归出人脸特征点位置。除此之外,针对多姿态的金字塔式级联结构和树形分支级联结构检测器也被提出来,这类结构每一层被设计为一种姿态的分类器,一个滑动窗口只有在经过所有姿态分类器后都被分类为非目标,才会将此此窗口排除,因此它能够适应多种姿态,但检测时间也会相应的增加。
另外一种里程碑式的目标检测方法是基于卷积神经网络的检测算法,近年来,深度学习的研究取得了巨大的成功,特别是卷积神经网络,在图像领域引起了巨大的变革。2013年,Ross Girshick等人用卷积神经网络处理目标检测任务,提出了R-CNN算法,为之后的SPP-NET、Fast-RCNN,Faster-RCNN、Mask RCNN等一系列算法奠定了基础。与VJ人脸检测器等使用的滑动窗口方法不同,R-CNN使用一种候选框生成机制来减小对目标的搜索范围,在候选框中精确检测目标,是一种两步检测算法。之后,Yolo和SSD等单步检测算法出现,加快了卷积神经网络的检测速度,而SSD正是本文今天的主角。卷积神经网络使得检测精度大大提高,但巨大的计算量使得检测速度远远低于传统算法,模型压缩和加速是深度学习领域另外一个重要的研究课题。
三、相关基础概述
初始版本是想对深度学习基础知识做一个浅显讲解,以便没有深度学习基础的同学也能够大致明白本文在讲什么,但对深度学习理论的概述不是一篇文章能够讲得清楚的,因此为了使本文不显得拖沓,本节不对深度学习理论做介绍,有需要的同学可以参见本系列文章之深度学习基础篇,这里只对下文需要用到的知识做一下陈述。看不懂的同学,没有关系,在心里留下一抹回忆就行,日后学的多了,自然会懂。
首先是感受野,这在MTCNN网络中也是非常重要的一个概念。如图3-1所示为一个简单的三层神经网络,一个5×5图像区域与一个3×3的卷积核做步长为1,边界填充为0的卷积操作后,得到一个3×3的特征图,再经过一个相同的卷积层后,得到的特征图上只有一个神经元。显然,不管输入图片有多大,这个神经元只与第一层的这个5×5的区域相关,也只与第二层的这个3×3的特征区域相关。那么我们称该神经元在第一层特征图上的感受野是这个5×5大小的区域,在第二层特征图上的感受野是3×3大小的区域。感受野反映了一个神经元是由哪些像素点计算得到的。当有池化层,或者卷积步长大于1时,神经元的感受野会扩大,而边界填充会影响感受野中心在整个图像中的位置。对于方形的卷积核,感受野也是一个规则的正方形区域,对于不是方形的卷积核如3×1的卷积核,或者可变形卷积,神经元的感受野就是一个不规则的区域。感受野直接关系到了特征的产生区域,因此,如何更好的改进神经元的感受野以提高模型精度,是一个值得思考的方向。至于怎么计算感受野的大小和位置,我会在MTCNN中讲解,这里不再累述。
![e6c437c0dc0e55e5904ad25bf19341c4.png](https://i-blog.csdnimg.cn/blog_migrate/8fa5988a2d57e56f4262ac0a144f0751.jpeg)
四、SSD基本原理
本节只求讲通基本原理,不细究SSD的参数设置和某些细节。首先给出SSD的网络结构如图4-1所示,为了方便描述,将VGG基础网络和扩展网络,即图4-1中上面一排卷积层,包括VGG up to conv3_3到Avg Pooling层,称为特征网络,用于生成卷积特征;特征网络箭头下方的这些卷积层称为预测网络(称呼而已,其实他们是一个个独立的卷积层,像葫芦娃一样一个个挂在特征网络这根主瓜蔓上),称两种网络的连接处的特征层为预测输入层,它输入到预测网络,生成的特征图就是预测的目标得分和边界框位置,称之为预测输出特征图。
![850648e5e6e6d7d8c668c985d778ca6f.png](https://i-blog.csdnimg.cn/blog_migrate/96520418143f6d587b8669b9218f9a81.jpeg)
- 预设框
这里提出目标检测的两个基本问题。一是,一张图片中目标的位置和个数是不确定的;二是,目标的大小和形状是多变的,解决目标检测问题,都绕不开这两个基本问题。最简单直接的方法就是滑动窗口法,这是一种在全图范围内密集搜索的方法。传统算法中很多是采用这种策略,对于每一个滑动窗口内的局部图像区域,通过模型提取特征,进行分类检测,输出结果,然后再滑动到下一个窗口,如此往复,最后取大于阈值的一些结果作为检测到的目标。滑动窗口的大小和形状,对目标检测精度有很大的影响。一般使用小的窗口来搜索小目标,滑动的次数比较多,输出的结果值也相应比较多,导致了模型对小目标检测较慢,而大目标只需要使用大的窗口遍历几次就可以了。
SSD采用了类似滑动窗口的方法,预先在输入图片上划定一些窗口,称之为预设框(代码中的prior box,论文中的default box)。划定这些预设框的目的是,在训练阶段。但这种机制又不同于滑动窗口方法,因为框的位置和大小形状是由特征图和预设框参数共同决定的,而不是随机选取的。SSD设置了两个超参数来规定预设框的大小和形状,一个是尺度(scales),一个宽高比(aspect ratios)。由这两个参数可以计算出宽和高:
Width * Height = Scale 2
Width / Height = Aspect ratio
如图4-2,假设尺度Scale = 168,宽高比Aspect ratio = {1(红色), 2(绿色),1/2(蓝色)},可以计算出各个框的宽高情况。
![c672369d6b73b02674889a60de37e6ba.png](https://i-blog.csdnimg.cn/blog_migrate/7ff0fff5984b1a9449be95cf3c0bcc04.jpeg)
预设框的位置和数目则由预测输入层来确定的。SSD设计预测层的每个平面位置对应一个预设框,而每个通道预测一个属性。这种形式在MTCNN和可变形卷积(Deformable Convolution)中均有体现。举个例子,如图4-3,假设一个这个检测网络设置了{100,120}两种尺度,{1, 2, 3}三种宽高比的预设框,需要检测20个目标分类,那么每个预设框需要预测4个坐标值和20+1个置信度,+1是因为要多一个背景得分。所有类型的框总共有2×3×(4+20+1)=150个属性,因此预测网络的通道个数就是150。假设预测输入层是一个256通道的5×5特征层,那么可以得到总共需要5×5×150=3750个预设框。
![e64563f1c3fa4ea543c7e8e8af2222ce.png](https://i-blog.csdnimg.cn/blog_migrate/a31461997497c4ccd56864460eca409a.jpeg)
预设框的位置由神经元在特征图中的位置来确定,假设输入图片宽是img_width,高是img_height,预测输入层特征图的宽是width,高是height。可以计算得到(i, j)处的预设框中心点的在输入图片的位置是(i*(img_width / width), j*(img_height / height))。实际的代码实现中,会有一个偏移值0.5加上去,使得中心点坐标位于预设框的正中间。但是由于卷积操作时边界填充的存在,这样得到的预设框中心点的位置是与感受野的中心点位置是有差别的。
在卷积网络中,不同的特征层具有不同大小的感受野,浅层的特征图比较大,但感受野比较小,深层的特征图比较小,感受野却比较大。SSD使用浅层的特征图来检测小目标,使用深层特征图来检测大目标,这也比较符合直观上的感受。不同特征图上产生的预设框是不相同的。大部分预设框是前面几层产生的。
这里需要指出一点,虽然使用的特征层、尺度和宽高比是可以随意设定的,但是感受野是这个特征对应计算的像素区域,设置的预设框往往和这个区域是不一致的。举个栗子,如果我们在高层特征中使用一个小的尺度来预测,它本身也不能预测出一个小目标,因为它的感受野太大,参与计算的像素点的范围要远远大于设置的这个预设框。
因此,只包含一个SSD预测输入层的网络结构如图4-4所示。了解MTCNN的同学可能会注意到,SSD单独的一层预测层,和MTCNN第一个级联网络PNet很相似,不同之处在于,PNet是一个独立的网络,输入图片大小不固定,是以感受野作为窗口的,而SSD前后特征层是相互联系的,输入图片大小固定,是通过一组预设框作为窗口的。
![96df8ed3e23b3d3213b4ddf177ced42d.png](https://i-blog.csdnimg.cn/blog_migrate/711e7b562e314f0121f6612242cabc31.png)
2. 匹配策略
首先要区分三个框,预设框就是提前设置的一些用于监督训练的框;预测框就是对每个预设框的预测情况,和预设框比较产生损失值;真实框是我们标注人员所标注的框,是准确的目标位置。
预设框的直观理解是,假设在这张图片的这个位置有一个某个大小某个形状的目标,需要用这个网络来计算出这个假设的准确性,并调整这个框的位置。既然要验证假设是否正确,就应该有一个真实的信息作为判断的标准,在训练阶段,我们会把这些预设框提前标定为目标(正样本)或背景(负样本),有目标的预设框还要给出目标的类别和目标框的位置,这些标定的信息就是监督学习的标签。为了保持训练网络时损失值的稳定,一般将目标框的位置编码为目标框相对于预设框的坐标偏移,并进行归一化,这在下面编码部分会细讲。在测试阶段,只需要预设框的信息用来解码出真实的坐标,而不必关心样本正负的问题。怎么去假设这个框是正还是负呢?SSD使用预设框与真实框的重叠率(jaccard overlap)来决定它的正负。定义:
![95fd869060c9d86a47429e967bb933e0.png](https://i-blog.csdnimg.cn/blog_migrate/9d4f10c7bead0f60fa50acb2d492ea3a.jpeg)
+ 开始
+++ 遍历 预设框列表:
++++++ 遍历 真实框列表:
+++++++++ 计算并存储预设框与真实框两两之间的交并比。
+++ 遍历 真实框列表:
++++++ 遍历 预设框列表:
+++++++++ 找到交并比最大的一个,作为匹配到的正样本;
+++++++++ 设置这个预设框的类别为真实框的类别;
+++++++++ 将这个预设框从其列表中去除;
+++ 遍历 预设框列表:
++++++ 遍历 真实框列表:
+++++++++ 如果 交并比 > 阈值 && is_max(交并比):
+++++++++ 预设框为正样本;
+++++++++ 设置该预设框的类别为最大交并比的类别;
+ 设置所有的未匹配预设框为负样本;
+ 结束
可以看到,SSD采用了两次匹配,第一次匹配保证了所有的真实框都能有一个预设框与之匹配,但这样匹配得到的正样本太少,第二步使得剩余的未匹配框中可以有更多的预设框匹配为正样本。也就是说,一张图片中,一个真实框可以匹配多个预设框,且最少匹配到一个;一个预设框最多只能匹配一个真实框。图4-5是一个匹配示意图,其中绿色的绿色框是标签框,红色框是预设框,TP表示正样本,FP表示负样本。
![d398458c66565f2100506be6f6adb44a.png](https://i-blog.csdnimg.cn/blog_migrate/cb8df31f24ca544a9f25b6ded0a0034b.jpeg)
3. 难分负样本挖掘
尽管两步匹配策略可以增加正样本数量,但是一张图片目标和背景的比例往往是悬殊的,正样本的比例还是远远低于负样本的比例。如果样本比例严重失衡,那么训练的模型就会向样本多的那一方倾斜,这个很容易理解,我们在教一个小孩子认识苹果的时候,成千上万副图片都是各种各样的苹果,只有几张是梨,那么在小孩子的认知中,你给他拿一个水果,他几乎以1的概率推测为苹果,跟在平衡样本中长大的在座诸位有很大的认知区别。为避免这个问题,就需要挖掘难分负样本。匹配只是把预设框分类为正负样本,而难分负样本挖掘是从负样本中找出哪些是和正样本比较相近,比较难以区分的负样本,从经验上来看,挖掘后的正负样本的比例设置为1:3到1:5比较合适。它的计算流程如下:
1). 计算置信度损失;
2). 如果需要,计算定位损失;
3). 下面就有两种方式来选取难分负样本:
a). 如果需要去除一些非最优框,那么就将损失值作为得分值进行非极大值抑制,然后从保留的序列中选取负样本。
b). 否则将负样本按照损失值降序排列,选择损失值最大的前面一些数值。
4. 损失函数。
SSD的损失函数是多损失函数,即置信度损失和定位损失,置信度损失使用的是Softmax损失函数,定位损失使用的是Smooth L1损失函数。函数形式如(1)所示。其中N是匹配到的预设框的数量。如果N=0,那么就将损失值设置为0。α是加权系数,用于平衡两个损失的重要程度,一般设置为1。截图代替了公式,不是我懒,是不会在知乎上编辑公式~~!。
![89f411def8177b62a94b5b128547dec3.png](https://i-blog.csdnimg.cn/blog_migrate/395135a58d84745998fcdcc04db426c0.png)
其中定位损失如(2)所示,这时候负样本是不起作用的,只对匹配为正样本的那些预设框做运算。其中,
![93bc5202b9623c916cb7bb42a278f106.png](https://i-blog.csdnimg.cn/blog_migrate/69ac5e7c8154433341891723f6a6c413.png)
表示第 个预设框匹配第 个真实框时为1,否则为0。
![0c1283808ae2efa6e7822b47329b8061.png](https://i-blog.csdnimg.cn/blog_migrate/bf65edd932c1f130b337d76508edbfc3.jpeg)
Smooth L1损失函数的形式如式3-1:
![4451361f49454dd4d11f4638e3f72167.png](https://i-blog.csdnimg.cn/blog_migrate/48ca48eac64da2615e35fecf3e6bfc8c.png)
其中置信度损失函数形式如(3)所示,这时候负样本也是要参与计算的,但此时的负样本是经过样本挖掘之后的那些匹配框。
![7e1bfc980e34fb7503bbdda4e014975b.png](https://i-blog.csdnimg.cn/blog_migrate/cae52c41c76c57d5f8e5b0eff6a0a893.png)
整理一下损失函数的计算流程,如图4-6所示。
![4ef212391f6c428d958da9aea9582f0b.png](https://i-blog.csdnimg.cn/blog_migrate/33094aeabffcac5aad6b0f8883c02d6a.jpeg)
5. 编码和解码。
SSD使用中心点坐标、边界框的宽和高来计算定位损失值,它们都是整数,产生的损失值会特别大,并且相当不稳定,因此需要对这些值进行归一化操作。编码的含义就是使用相对坐标来代替绝对坐标。在训练时,计算预设框与真实框之间的相对偏移作为预设框的位置信息,下标为 (x, y, w, h) 分别表示 (中心点横坐标,中心点纵坐标,宽,高) :
标签框x = (真实框x – 预设框x) / 预设框w
标签框y = (真实框y – 预设框y) / 预设框h
标签框w = log (真实框w / 预设框w)
标签框h = log (真实框h / 预设框h)
编码后的四个值用来计算定位损失,监督模型训练。不难看出,由于模型学习的是编码后的标签值,因此,预测出来的也必然是四个相对的坐标。在计算NMS等或者传递给应用层时都要将其解码为真实的坐标值,这个过程就是上面过程的逆向过程,它相当于已知 标签框 = 预测框,带入上面的公式,求 真实框。
6. 数据预处理以及数据增强。
在训练模型的过程中,很大一部分工作都用在了对数据的处理上,数据的质量直接关系到模型的好坏。数据增强是一种增强模型鲁棒性的方法。对每张训练图片按照下列方式之一随机截取:
1). 使用整个原始图片
2). 从原始图片截取一个块,它和真实框的最小重叠率是 0.1、0.3、0.5、0.7或者0.9。
3). 从原始图片中随机截取一块。
块的大小占原始图片大小的比例为0.1和1之间的某个数值,宽高比取0.5和2之间的某个数值。如果真实人脸框的中心在截取的小块之内,则保留重叠部分作为该小块的真实目标框。每个截取块被缩放到300×300的尺寸并按照0.5的概率进行水平翻转。
另外一类增强方法是使图像失真变形,具体的做法是随机的改变图像的对比度、亮度、饱和度、色调以及随机地打乱RGB三通道。数据增强后,训练图片具有更加丰富的特征,可以增强模型的鲁棒性。作者证明了这样的策略能够有效地提高模型的检测精度。
7.非极大值抑制NMS
非极大值抑制( Non Maximum Suppression,NMS)在计算机视觉领域中有非常重要的作用,它的主要思想是搜索局部最大值,抑制极大值,在边缘检测、目标检测等领域有着广泛的应用。在目标检测中,它的主要作用是在众多的检测框中,去除多余的框,保留最优的一个,从而找到检测目标的最佳位置。SSD 中,同一目标的位置上会产生大量的预测边框,它们之间会存在重叠,每一个候选框有一个目标置信度得分,通过引入非极大值抑制方法精确定位目标的位置而又不影响对临近位置其他目标的预测。非极大值抑制的流程如下:
1). 将各个预测边框按照置信度从大到小排序;
2). 选择置信度最高的预测边框作为输出边框,并将其从预测边界框列表中删除;
3). 计算所有预测边框的面积;
4). 计算该输出边框与其他预测边框的 IoU( Intersection over Union),即两个边框的交集面积除以它们的并集面积;
5). 删除 IoU 大于指定阈值的预测边界框;
6). 重复上述过程,直至预测边界框列表为空。
如图4-7所示为采用非极大值抑制方法的人脸检测结果。可见,左边人脸输出 3个预测框,右边输出 2个预测框,按照得分对所有框排序结果为{0.98,0.83,0.81,0.75,0.67},以 0.98作为检测到的人脸框,计算其余的框与该框的IoU,这样可以排除得分0.83和0.75 两个预测框,另外两个IoU面积小于设置的阈值,重新进行排列,结果为{0.81,0.67},以0.81得分的作为人脸框,并通过 IoU排除0.67的预测框,得到最终的检测结果。
![6f8d54b55623ff64c550e4671f5b82e6.png](https://i-blog.csdnimg.cn/blog_migrate/9c16e60a1cff480545b3d346b35ca492.jpeg)
五、总结
SSD的理解稍微有些复杂,很多细节没有讲,如果能够结合代码去理解,会更加容易些,当然这是对刚接触目标检测的同学而言。因此,建议在此基础上,理解一下SSD的代码实现。本文讲解知识点的顺序和论文不太一致,我主要是按照我们思考问题和解决问题的思维方式来阐述的,有些地方写的比较啰嗦,有些地方写的比较简略,希望读者给出宝贵的建议,在此谢过了。
参考:
[1]. SSD: Single Shot MultiBox Detector
[2].