Yolo算法--从原理到实现(⼀)
YOLO算法:从v1到v3
yolo是⽬前⽐较流⾏的⽬标检测算法,速度快结构简单。其他的⽬标检测算法也有RCNN,faster-RCNN, SSD等。
近⼏年来,⽬标检测算法取得了很⼤的突破。⽐较流⾏的算法可以分为两类,⼀类是基于Region Proposal的R-CNN系算法(R-
CNN,Fast R-CNN, Faster R-CNN),它们是two-stage的,需要先使⽤启发式⽅法(selective search)或者CNN⽹络(RPN)产⽣Region Proposal,然后再在Region Proposal上做分类与回归。⽽另⼀类是Yolo,SSD这类one-stage算法,其仅仅使⽤⼀个CNN⽹络直接预测不同⽬标的类别与位置。第⼀类⽅法是准确度⾼⼀些,但是速度慢,但是第⼆类算法是速度快,但是准确性要低⼀些。本⽂⾸先介绍的是Yolo1算法,其全称是You Only Look Once: Unified, Real-Time Object Detection,题⽬基本上把Yolo算法的特点概括全了:You Only Look Once说的是只需要⼀次CNN运算,Unified指的是这是⼀个统⼀的框架,提供end-to-end的预测,⽽Real-Time体现是Yolo算法速度快,达到实时。这个题⽬是Yolo-v1论⽂的,其性能是差于后来的SSD算法的,但是Yolo后来也继续进⾏改进,产⽣了
Yolo9000算法,和Yolov3。
多一点就不准
检测对于计算机视觉中就是检测到⽬标物的位置,⽽识别则是说,对检测到的这个东西,你告诉我这是属于哪⼀类,⽐如说检测到⼀只狗,你要告诉我这只狗是京巴还是藏獒。那么⽬标检测到底有什么⽤途呢?举个例⼦:⽐如在⽆⼈车上安装⼀个障碍物检测系统,那么⽆⼈车的摄像头就相当于⼈的眼睛,当捕捉到画⾯前⽅有障碍物时,要得到障碍物的具体位置和类别,以便及时作出决策。
为了理解yolo,⾸先先介绍⼀下滑动窗⼝技术。采⽤滑动窗⼝的⽬标检测算法思路⾮常简单,它将检测问题转化为了图像分类问题。其基本原理就是采⽤不同⼤⼩和⽐例(宽⾼⽐)的窗⼝在整张图⽚上以⼀定的步长进⾏滑动,然后对这些窗⼝对应的区域做图像分类(应该是在学习这个东西是⽬标物还是背景)如下图所⽰。但是你并不知道要检测的⽬标⼤⼩是什么规模,所以你要设置不同⼤⼩和⽐例的窗⼝去滑动,⽽且还要选取合适的步长,这样会产⽣很多的⼦区域,并且都要经过分类器去做预测,这需要很⼤的计算量,所以你的分类器不能太复杂,因为要保证速度。解决思路之⼀就是减少要分类的⼦区域,这就是R-CNN(基于区域提名的CNN)的⼀个改进策略,其采⽤了selective search⽅法来到最有可能包含⽬标的⼦区域(Region Proposal),其实可以看成采⽤启发式⽅法过滤掉很多⼦区域,这会提升效率。
yolo设计理念
整体来看,Yolo算法采⽤⼀个单独的CNN模型实现end-to-end的⽬标检测,⾸先将输⼊图⽚resize到448x448,然后送⼊CNN⽹络,最后处理⽹络预测结果得到检测的⽬标。其速度更快,⽽且Yolo的训练过程也是端到端的。与滑动窗⼝不同的是,yolo先将图⽚分成S*S个块。每个单元格会预测B个边界框(bounding box)以及边界框的置信度(confidence score)。所谓置信度其实包含两个⽅⾯,⼀是这个框中⽬标存在的可能性⼤⼩,⼆是这个边界框的位置准确度。前者我们把它记做Pr(obj),若框中没有⽬标物,则Pr(obj)=0,若含有⽬标物则Pr(obj)=1 。那么边界框的位置的准确度怎么去判断呢?我们使⽤了⼀种叫做IOU(交并⽐)的⽅法,意思就是说我预测的框与你真实的框相交的⾯积,和预测的框与真实框合并的⾯积的⽐例。我们可以记做IOU(pred),那么置信度就可以定义为这两项相乘。so,现在有了另⼀个问题,我每个格⼦预测的边界框应该怎么表⽰呢?  边界框的⼤⼩和位置可以⽤四个值来表⽰,(x,y,w,h)注意,不要凭空想象是⼀个矩形对⾓两个点的位置坐标,这⾥⾯的x,y是指预测出的边界框的中⼼位置相对于这个格⼦的左上⾓位置的偏移量,⽽且这个偏移量不是以像素为单位,⽽是以这个格⼦的⼤⼩为⼀个单位。如果不明⽩可以⽤下⾯这张图去举个例⼦。下⾯这个框的中⼼点所在的位置,相对于中⼼点所在的这个格⼦的x,y差不多是0.3, 0.7,  (不明⽩的话就仔细琢磨⼀下)。⽽这个w,h指的是这个框的⼤⼩,占整张图⽚⼤⼩的宽和⾼的相对⽐例,想⼀下,有了中⼼点的位置,有了框的⼤⼩,画出⼀个框是很容易的对吧。(x,y,w,h,c)这五个值理论上
都应该在[0,1]区间上。最后⼀个c是置信度的意思。⼀般⼀个⽹格会预测多个框,⽽置信度是⽤来评判哪⼀个框是最准确的,我们最想得到的框。
框预测好了,接下来就是分类的问题,每个单元格预测出(x,y,w,h,c)的值后,还要给出对于C个类别的概率值。现在重新提⼀下字母的含义,B是边界框的个数,C是我有多少个类别要分类。S是我怎么
划分单元格。那么我每个单元格要预测B*5+C个值。如果将输⼊图⽚划分为S×S⽹格,那么最终预测值为S×S×(B∗5+C)⼤⼩的张量。
⽹络模型:
对于卷积层和全连接层,采⽤Leaky ReLU激活函数:max(x,0.1x)max(x,0.1x)。最后⼀层采⽤线性激活函数。
可以看到⽹络的最后输出为7×7×30⼤⼩的张量。这和前⾯的讨论是⼀致的。这个张量所代表的具体含义如下图所⽰。对于每⼀个单元格,前20个元素是类别概率值,然后2个元素是边界框置信度,两者相乘可以得到类别置信度,最后8个元素是边界框的(x,y,w,h)。⼤家可能会感到奇怪,对于边界框为什么
把置信度c和(x,y,w,h)都分开排列,⽽不是按照(x,y,w,h,c)这样排列,其实纯粹是为了计算⽅便,因为实际上这30个元素都是对应⼀个单元格,其排列是可以任意的。但是分离排布,可以⽅便地提取每⼀个部分。这⾥来解释⼀下,⾸先⽹络的
预测值是⼀个⼆维张量PP,其shape为[batch,7×7×30]。采⽤切⽚,那么就是类别概率部分,⽽是置信度部分,最后剩余部分是边界框的预测结果。这样,提取每个部分是⾮常⽅便的,这会⽅⾯后⾯的训练及预测时的计算。
下⾯是训练损失函数的分析,Yolo算法将⽬标检测看成回归问题,采⽤的是均⽅差损失函数。但是对不同的部分造成的误差采⽤了不同的权重值。还有⼀点就是较⼩的边界框的坐标误差应该要⽐较⼤的边界框要更敏感。为了保证这⼀点,将⽹络的边界框的宽与⾼预测改为对其平⽅根的预测,即预测值变为,为什么呢?因为你想⼀下⽐如w和h为0.1或者更⼩,那么我稍微⼀点点的幅度,我这个框变化就很明显。⽐如我从0.1到0.15,这个变化相对于0.1增加很多,但是同样的我预测有误差,⽽误差是从0.4到0.45,这个增⼤⼏乎是看不出来的。因此对于⼀个⼩框,如果我偏移⼤的话,会更加影响框的准确性,有可能因为我这⼀个⼩⼩的偏移,导致我这个物体不再框⾥⾯了,所以这就是为什么要让⼩得边界框的误差⽐⼤的边界框误差要更加敏感。通过开根号可以达到这个效果,因为⼀个⼩于⼀的数,开平⽅会被放⼤,⽽且越⼩,开平⽅后相⽐原来的数差别就更⼤。(如果不理解这⼀点可以慢慢思考⼀下,或者跳过也没关系)
其中第⼀项是边界框中⼼坐标的误差项,指的是第 i 个单元格存在⽬标,且该单元格中的第 j 个边界框负责预测该⽬标。第⼆项是边界框的⾼与宽的误差项。第三项是包含⽬标的边界框的置信度误差项。第四项是不包含⽬标的边界框的置信度误差项。⽽最后⼀项是包含⽬标的单元格的分类误差项,指的是第 i 个单元格存在⽬标。
当然这其中还⽤到了另⼀种机制,叫做NMS(non maximum suppression)⾮极⼤值抑制算法。NMS算法主要解决的是⼀个⽬标被多次检测的问题。这个机制不是针对YOLO的,⽽是对于各种检测算法都有⽤到。就⽐如说YOLO,yolo-v1预测了两个边界框,但是我检测的时候总不能把两个框都画上去吧,因此我只取概率值最⼤的那个框,这就叫做⾮极⼤值抑制,除了极⼤概率的那个框,别的我丢弃。
YOLO的缺点
1.YOLO对相互靠的很近的物体,还有很⼩的体 检测效果不好,这是因为⼀个⽹格中只预测了两个框,并且只属于⼀类。
2.同⼀类物体出现的新的不常见的长宽⽐和其他情况时,泛化能⼒偏弱。
3.由于损失函数的问题,定位误差是影响检测效果的主要原因。尤其是⼤⼩物体的处理上,还有待加强。
yolo-v2的改进
Batch Normalization
CNN在训练过程中⽹络每层输⼊的分布⼀直在改变, 会使训练过程难度加⼤,但可以通过normalize每层的输⼊可以解决这个问题。新的YOLO⽹络在每⼀个卷积层后添加batch normalization,通过这⼀⽅法,mAP(精度)获得了2%的提升。batch normalization 也有助于规范化模型,可以在舍弃dropout优化后依然不会过拟合。
High Resolution Classifier
⽬前的⽬标检测⽅法中,基本上都会使⽤ImageNet预训练过的模型(classifier)来提取特征,如果⽤
的是AlexNet⽹络,那么输⼊图⽚会被resize到不⾜256 * 256,导致分辨率不够⾼,给检测带来困难。为此,新的YOLO⽹络把分辨率直接提升到了448 * 448,这也意味之原有的⽹络模型必须进⾏某种调整以适应新的分辨率输⼊。
对于YOLOv2,作者⾸先对分类⽹络(⾃定义的darknet)进⾏了微调,分辨率改成448 * 448,在ImageNet数据集上训练10轮(10 epochs),训练后的⽹络就可以适应⾼分辨率的输⼊了。然后,作者对检测⽹络部分(也就是后半部分)也进⾏微调。这样通过提升输⼊的分辨率,mAP(精度)获得了4%的提升。
Convolutional With Anchor Boxes
之前的YOLO利⽤全连接层的数据完成边框的预测,导致丢失较多的空间信息,定位不准。作者在这⼀版本中借鉴了Faster R-CNN中的anchor思想,回顾⼀下,anchor是RPN⽹络中的⼀个关键步骤,说的是在卷积特征图上进⾏滑窗操作,每⼀个中⼼可以预测9种不同⼤⼩的建议框。
为了引⼊anchor boxes来预测bounding boxes,作者在⽹络中果断去掉了全连接层。剩下的具体怎么操作呢?⾸先,作者去掉了后⾯的⼀个池化层以确保输出的卷积特征图有更⾼的分辨率。然后,通过缩减⽹络,让图⽚输⼊分辨率为416 * 416,这⼀步的⽬的是为了让后⾯产⽣的卷积特征图宽⾼都为奇数,这样就可以产⽣⼀个center cell。作者观察到,⼤物体通常占据了图像的中间位置, 就可以只⽤中⼼的⼀个cell来预测这些物体的位置,否则就要⽤中间的4个cell来进⾏预测,这个技巧可稍稍提升效
率。最后,YOLOv2使⽤了卷积层降采样(factor为32),使得输⼊卷积⽹络的416 * 416图⽚最终得到13 * 13的卷积特征图(416/32=13)。
加⼊了anchor boxes后,可以预料到的结果是查全率上升,准确率下降。我们来计算⼀下,假设每个cell预测9个建议框,那么总共会预测13 * 13 * 9 = 1521个boxes,⽽之前的⽹络仅仅预测7 * 7 * 2 = 98个boxes。具体数据为:没有anchor boxes,模型查全率为81%,mAP为69.5%;加⼊anchor boxes,模型查全率为88%,mAP为69.2%。这样看来,准确率只有⼩幅度的下降,⽽查全率则提升了7%,说明可以通过进⼀步的⼯作来加强准确率,的确有改进空间。
⽽yolo-v3则是将格⼦划分的更细了⼀些从13*13细化到了19*19,每个框预测3个框,在⼀定程度上缓解了⼩⽬标检测不到的问题以及多个框重叠在⼀起只能画出⼀个框的问题。细节请去阅读论⽂。
对于yolo,官⽅给出了使⽤darknet框架训练的压缩包。但是使⽤darknet框架训练,难以理解其中的细节了,调试起来⾮常困难,对于darknet,我做到的只能是做出训练数据,然后把数据丢进⽹络进⾏训练,完全没有tensorflow的那种运算框架⾮常清晰地感觉。(甚⾄不到darknet训练⽹络的时候损失函数定义在哪⾥。)之后我可以给出⼀个⽤darknet训练yolo的具体步骤,但是我还是想去⽤tensorflow 或者keras训练⾃⼰的数据。在tensorflow或者keras的train.py⽂件中可以清晰地看出逻辑关系。