yolov1详细讲解
前言
当我们谈起计算机视觉时,首先想到的就是图像分类,没错,图像分类是计算机视觉最基本的任务之一,但是在图像分类的基础上,还有更复杂和有意思的任务,如目标检测,物体定位,图像分割等,见图1所示。其中目标检测是一件比较实际的且具有挑战性的计算机视觉任务,其可以看成图像分类与定位的结合,给定一张图片,目标检测系统要能够识别出图片的目标并给出其位置,由于图片中目标数是不定的,且要给出目标的精确位置,目标检测相比分类任务更复杂。目标检测的一个实际应用场景就是无人驾驶,如果能够在无人车上装载一个有效的目标检测系统,那么无人车将和人一样有了眼睛,可以快速地检测出前面的行人与车辆,从而作出实时决策。
近几年来,目标检测算法取得了很大的突破。比较流行的算法可以分为两类,一类是基于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网络直接预测不同目标的类别与位置。第一类方法(RCNN系列)是准确度高一些,但是速度慢,但是第二类算法是速度快,但是准确性要低一些(yolo、SSD系列)。本文介绍的是Yolo算法,其全称是You Only Look Once: Unified, Real-Time Object Detection,其实个人觉得这个题目取得非常好,基本上把Yolo算法的特点概括全了:You Only Look Once说的是只需要一次CNN运算(一步就完成),Unified指的是这是一个统一的框架(end-to-end框架),提供end-to-end的预测,而Real-Time体现是Yolo算法速度快(可以实时了,确实非常快)(这句话非常棒)。这里我们谈的是Yolo-v1版本算法,其性能是差于后来的SSD算法的,但是Yolo后来也继续进行改进,产生了Yolo9000算法。本文主要讲述Yolo-v1算法的原理,特别是算法的训练与预测中详细细节,最后将给出如何使用TensorFlow实现Yolo算法。(有代码哦!)
滑动窗口与CNN
在介绍Yolo算法之前,首先先介绍一下滑动窗口技术,这对我们理解Yolo算法是有帮助的。(滑动窗口是很重要的)采用滑动窗口的目标检测算法思路非常简单,它将检测问题转化为了图像分类问题。其基本原理就是采用不同大小和比例(宽高比)的窗口在整张图片上以一定的步长进行滑动,然后对这些窗口对应的区域做图像分类,这样就可以实现对整张图片的检测了,如下图3所示,如DPM就是采用这种思路。(其实就和Selective Search差不多,一个窗口一个窗口的做图像分类)但是这个方法有致命的缺点,就是你并不知道要检测的目标大小是什么规模,所以你要设置不同大小和比例的窗口去滑动,而且还要选取合适的步长。但是这样会产生很多的子区域,并且都要经过分类器去做预测,这需要很大的计算量,所以你的分类器不能太复杂,因为要保证速度。解决思路之一就是减少要分类的子区域,这就是R-CNN的一个改进策略,其采用了selective search方法来找到最有可能包含目标的子区域(Region Proposal),其实可以看成采用启发式方法过滤掉很多子区域,这会提升效率。
如果你使用的是CNN分类器,那么滑动窗口是非常耗时的。但是结合卷积运算的特点,我们可以使用CNN实现更高效的滑动窗口方法。这里要介绍的是一种全卷积的方法,简单来说就是网络中用卷积层代替了全连接层,如图4所示。输入图片大小是16x16,经过一系列卷积操作,提取了2x2的特征图,但是这个2x2的图上每个元素都是和原图是一一对应的,如图上蓝色的格子对应蓝色的区域,这不就是相当于在原图上做大小为14x14的窗口滑动,且步长为2,共产生4个子区域。最终输出的通道数为4,可以看成4个类别的预测概率值,这样一次CNN计算就可以实现窗口滑动的所有子区域的分类预测。这其实overfeat算法的思路。之所可以CNN可以实现这样的效果是因为卷积操作的特性,就是图片的空间位置信息的不变性,尽管卷积过程中图片大小减少,但是位置对应关系还是保存的。(原来空间位置不变性是这个意思)说点题外话,这个思路也被R-CNN借鉴,从而诞生了Fast R-cNN算法。
上面尽管可以减少滑动窗口的计算量,但是只是针对一个固定大小与步长的窗口,这是远远不够的。 Yolo算法很好的解决了这个问题,它不再是窗口滑动了,而是直接将原始图片(注意哦,这里是原始图片)分割(隐式的)成互不重合的小方块,然后通过卷积最后生成这样大小的特征图,基于上面的分析,可以认为特征图的每个元素也是对应原始图片的一个小方块(对应回去),然后用每个元素来可以预测那些中心点在该小方格内的目标(比如一只狗的中心点在(3, 4)这个隐式的格子内,那最后生成的特征图的(3,4)就用来预测这条狗的置信度、坐标点、20分类),这就是Yolo算法的朴素思想。下面将详细介绍Yolo算法的设计理念。
设计概念
整体来看,Yolo算法采用一个单独的CNN模型实现end-to-end的目标检测,整个系统如图5所示:
第一步:将输入图片resize到448x448,然后送入CNN网络
第二步:得到处理网络预测结果得到检测的目标
具体来说,Yolo的CNN网络将输入的图片分割成7*7网格,然后每个单元格负责去检测那些中心点落在该格子内的目标,如图6所示:
上图中狗的中心点落入了(1,4)这个中心点,所以最后的特征图(1, 4)用来预测狗的多种信息(两个框的坐标及置信度,以及20分类),其中置信度由两部分组成,一个是Pr(object),当该边界框是背景时Pr(object)=0,而当该边界框包含目标时,Pr(object)=1,另一部分是:
所以置信度定义为:
边界框的大小与位置可以用4个值来表征,(x,y,w,h),还有一点要注意,中心坐标预测值是相对于每个单元格左上角坐标点的偏移值(即取值范围是该格子偏移值加上[0, 1]的数,这样理论上4个元素的大小应该在[0, 1] 范围。)那么对分类问题,对于每一个单元格其还要给出预测出C个类别的概率值,其表征的是由该单元格负责预测的边界框其目标属于各个类别的概率(每个上边都有数(概率))。但是这些概率值其实是在各个边界框置信度下的条件概率,即Pr(classi|object),值得注意的是不管一个单元格预测多少个边界框,其只预测一组类别概率值(也就是说这一个网格预测的多个格子都是同一类),这是Yolo算法的一个缺点,在后来的改进版本中,Yolo9000是把类别概率预测值与边界框是绑定在一起的(这个暂时不了解)。同时,我们可以计算出各个边界框类别置信度(class-specific confidence scores):
边界框类别置信度表征的是该边界框中目标属于各个类别的可能性大小以及边界框匹配目标的好坏。后面会说,一般会根据类别置信度来过滤网络的预测框(注意这句话)。
总结一下,每个单元格需要预测 (B*5 + C)个值。如果将输入图片划分为S*S网格,那么最终预测值为 S*S*(B*5 + C)大小的张量。整个模型的预测值结构如下图所示。对于PASCAL VOC数据,其共有20个类别,如果使用 S=7, B=2,那么最终的预测结果就是7*7*30 小的张量。在下面的网络结构中我们会详细讲述每个单元格的预测值的分布位置。
网络设计
Yolo采用卷积网络来提取特征,然后使用全连接层来得到预测值。网络结构参考GooLeNet模型,包含24个卷积层和2个全连接层,如图8所示。对于卷积层,主要使用1x1卷积来做channle reduction,然后紧跟3x3卷积。对于卷积层和全连接层,采用Leaky ReLU激活函数:max(x, 0.1x)。但是最后一层却采用线性激活函数。
可以看到网络的最后输出为7*7*30大小的张量。这和前面的讨论是一致的。这个张量所代表的具体含义如图9所示。对于每一个单元格,前20个元素是类别概率值,,然后2个元素是边界框置信度,两者相乘可以得到类别置信度(注意这句话:两者相乘可以得到类别置信度),最后8个元素是边界框的(x,y,w,h)。大家可能会感到奇怪,对于边界框为什么把置信度c和(x,y,w,h)都分开排列,而不是按照(x,y,w,h,c)这样排列(相信你也有这样的疑问),其实纯粹是为了计算方便,因为实际上这30个元素都是对应一个单元格,其排列是可以任意的(毛线啊,就是用numpy操作时容易)。
网络训练
在训练之前,先在ImageNet上进行了预训练,其预训练的分类模型采用图8中前20个卷积层,然后添加一个average-pool层和全连接层。预训练之后,在预训练得到的20层卷积层之上加上随机初始化的4个卷积层和2个全连接层。由于检测任务一般需要更高清的图片,所以将网络的输入从224x224增加到了448x448。整个网络的流程如下图所示(注意看图,最后是一个全连接层,然后reshape成7*7*30):
下面是训练损失函数的分析,Yolo算法将目标检测看成回归问题(所谓回归问题就是,给定输入出来的输出),所以采用的是均方差损失函数。但是对不同的部分采用了不同的权重值。首先区分定位误差和分类误差。对于定位误差,即边界框坐标预测误差,采用较大的权重 λcoord=5, 然后其区分不包含目标的边界框与含有目标的边界框的置信度,对于前者,采用较小的权重值λnoobj=0.5,其它权重值均设为1。然后采用均方误差,其同等对待大小不同的边界框,但是实际上较小的边界框的坐标误差应该要比较大的边界框要更敏感(理解这句话)。为了保证这一点,将网络的边界框的宽与高预测改为对其平方根的预测,即预测值变为 (x,y,$\sqrt{w}$, $\sqrt{h}$)。
另外一点时,由于每个单元格预测多个边界框。但是其对应类别只有一个。那么在训练时,如果该单元格内确实存在目标,那么只选择与ground truth的IOU最大的那个边界框来负责预测该目标(卧槽,!!!注意),而其它边界框认为不存在目标(也就是置信度为0了)。这样设置的一个结果将会使一个单元格对应的边界框更加专业化(牛逼),其可以分别适用不同大小,不同高宽比的目标,从而提升模型性能。大家可能会想如果一个单元格内存在多个目标怎么办,其实这时候Yolo算法就只能选择其中一个来训练,这也是Yolo算法的缺点之一。要注意的一点时,对于不存在对应目标的边界框,其误差项就是只有置信度,坐标项误差是没法计算的。而只有当一个单元格内确实存在目标时,才计算分类误差项,否则该项也是无法计算的。
综上讨论,最终的损失函数计算如下:
其中第一项是边界框中心坐标的误差项, 指的是第 i个单元格存在目标,且该单元格中的第 j个边界框负责预测该目标。第二项是边界框的高与宽的误差项。第三项是包含目标的边界框的置信度误差项。第四项是不包含目标的边界框的置信度误差项。而最后一项是包含目标的单元格的分类误差项,
指的是第i个单元格存在目标。这里特别说一下置信度的target值 Ci,如果是不存在目标,此时由于Pr(object)=0,那么Ci=0。如果存在目标,Pr(object)=1,此时需要确定
当然你希望最好的话,可以将IOU取1,这样Ci=1,但是在YOLO实现中,使用了一个控制参数rescore(默认为1),当其为1时,IOU不是设置为1,而就是计算truth和pred之间的真实IOU。不过很多复现YOLO的项目还是取Ci=1,这个差异应该不会太影响结果吧(这里还是没看懂)。
网络预测
在说明Yolo算法的预测过程之前,这里先介绍一下非极大值抑制算法,(non maximum suppression, NMS),这个算法不单单是针对Yolo算法的,而是所有的检测算法中都会用到。NMS算法主要解决的是一个目标被多次检测的问题,如图11中人脸检测,可以看到人脸被多次检测,但是其实我们希望最后仅仅输出其中一个最好的预测框,比如对于美女,只想要红色那个检测结果。那么可以采用NMS算法来实现这样的效果:首先从所有的检测框中找到置信度最大的那个框,然后挨个计算其与剩余框的IOU,如果其值大于一定阈值(重合度过高),那么就将该框剔除;然后对剩余的检测框重复上述过程,直到处理完所有的检测框。Yolo预测过程也需要用到NMS算法。下面就来分析Yolo的预测过程,这里我们不考虑batch,认为只是预测一张输入图片。根据前面的分析,最终的网络输出是 7*7*30,但是我们可以将其分割成三个部分:类别概率部分为[7, 7, 20], 置信度部分为[7, 7, 2],而边界框部分为[7, 7, 2, 4],(对于这部分不要忘记根据原始图片计算出其真实值)。然后将前两项相乘(矩阵[7, 7, 20]乘[7, 7, 2]可以各补一个维度来完成[7, 7, 1, 20]×[7, 7, 2, 1])可以得到类别置信度值为 [7, 7, 2, 20], 这里总共预测了7*7*2=98个边界框(每个框分20类)。
所有的准备数据已经得到了,那么我们先说第一种策略来得到检测框的结果,我认为这是最正常与自然的处理。首先,对于每个预测框根据类别置信度选取置信度最大的那个类别作为其预测标签,经过这层处理我们得到各个预测框的预测类别(两个)及对应的置信度值,其大小都是[7,7,2]。一般情况下,会设置置信度阈值,就是将置信度小于该阈值的box过滤掉,所以经过这层处理,剩余的是置信度比较高的预测框。最后再对这些预测框使用NMS算法,最后留下来的就是检测结果。一个值得注意的点是NMS是对所有预测框一视同仁,还是区分每个类别,分别使用NMS。上面的预测方法应该非常简单明了,但是对于Yolo算法,其却采用了另外一个不同的处理思路(至少从C源码看是这样的),其区别就是先使用NMS,然后再确定各个box的类别。其基本过程如图12所示。对于98个boxes,首先将小于置信度阈值的值归0,小于某个数的都是背景,然后分类别地对置信度值采用NMS,这里NMS处理结果不是剔除,而是将其置信度值归为0。这个策略不是很直接,但是貌似Yolo源码就是这样做的。Yolo论文里面说NMS算法对Yolo的性能是影响很大的,所以可能这种策略对Yolo更好。但是我测试了普通的图片检测,两种策略结果是一样的。(这一段及上一段还是不太清楚)
算法性能分析
这里看一下Yolo算法在PASCAL VOC 2007数据集上的性能,这里Yolo与其它检测算法做了对比,包括DPM,R-CNN,Fast R-CNN以及Faster R-CNN。其对比结果如表1所示。与实时性检测方法DPM对比,可以看到Yolo算法可以在较高的mAP上达到较快的检测速度,其中Fast Yolo算法比快速DPM还快,而且mAP是远高于DPM。但是相比Faster R-CNN,Yolo的mAP稍低,但是速度更快。所以。Yolo算法算是在速度与准确度上做了折中。
为了进一步分析Yolo算法,文章还做了误差分析,将预测结果按照分类与定位准确性分成以下5类:
Correct:类别正确,IOU>0.5;(准确度)Localization:类别正确,0.1 < IOU<0.5(定位不准);Similar:类别相似,IOU>0.1;Other:类别错误,IOU>0.1;Background:对任何目标其IOU<0.1。(误把背景当物体)
可以看到,Yolo的Correct的是低于Fast R-CNN。另外Yolo的Localization误差偏高,即定位不是很准确。但是Yolo的Background误差很低,说明其对背景的误判率较低。Yolo的那篇文章中还有更多性能对比,感兴趣可以看看。
优点
Yolo采用一个CNN网络来实现检测,是单管道策略,其训练与预测都是end-to-end,所以Yolo算法比较简洁且速度快。第二点由于Yolo是对整张图片做卷积,所以其在检测目标有更大的视野,它不容易对背景误判。另外,Yolo的泛化能力强,在做迁移时,模型鲁棒性高。
缺点
首先Yolo各个单元格仅仅预测两个边界框,而且属于一个类别。对于小物体,Yolo的表现会不如人意。这方面的改进可以看SSD,其采用多尺度单元格。也可以看Faster R-CNN,其采用了anchor boxes。Yolo对于在物体的宽高比方面泛化率低,就是无法定位不寻常比例的物体。当然Yolo的定位不准确也是很大的问题。
yolov1详细讲解的更多相关文章
- head标签详细讲解
head标签详细讲解 head位于html网页的头部,后前的标签,并以开始以结束的一html标签. Head标签位置如图: head标签示意图 head包含标签 meta,title,link,bas ...
- 详细讲解nodejs中使用socket的私聊的方式
详细讲解nodejs中使用socket的私聊的方式 在上一次我使用nodejs+express+socketio+mysql搭建聊天室,这基本上就是从socket.io的官网上的一份教程式复制学习,然 ...
- iOS KVC详细讲解
iOS KVC详细讲解 什么是KVC? KVC即NSKeyValueCoding,就是键-值编码的意思.一个非正式的 Protocol,是一种间接访问对象的属性使用字符串来标识属性,而不是通过调用存取 ...
- Android webservice的用法详细讲解
Android webservice的用法详细讲解 看到有很多朋友对WebService还不是很了解,在此就详细的讲讲WebService,争取说得明白吧.此文章采用的项目是我毕业设计的webserv ...
- 详细讲解Android对自己的应用代码进行混淆加密防止反编译
1.查看项目中有没有proguard.cfg. 2.如果没有那就看看这个文件中写的什么吧,看完后将他复制到你的项目中. -optimizationpasses 5 -dontusemixedcasec ...
- 详细讲解Hadoop源码阅读工程(以hadoop-2.6.0-src.tar.gz和hadoop-2.6.0-cdh5.4.5-src.tar.gz为代表)
首先,说的是,本人到现在为止,已经玩过. 对于,这样的软件,博友,可以去看我博客的相关博文.在此,不一一赘述! Eclipse *版本 Eclipse *下载 Jd ...
- [iOS]数据库第三方框架FMDB详细讲解
[iOS]数据库第三方框架FMDB详细讲解 初识FMDB iOS中原生的SQLite API在进行数据存储的时候,需要使用C语言中的函数,操作比较麻烦.于是,就出现了一系列将SQLite API进行封 ...
- jquery插件分类与编写详细讲解
jquery插件分类与编写详细讲解 1. 插件种类 插件其实就是对现有的方法(或者叫函数)做一个封装,方便重用提高开发效率. jQeury主要有2种类型 1)实例对象方法插件 开发能让所有的j ...
- [VC++]用CTime类得到当前日期、时间、星期,格式化(详细讲解)
用CTime类得到当前日期.时间.星期,格式化(详细讲解)2009/05/12 09:48 A.M.① 定义一个CTime类对象 CTime time; ② 得到当前时间 time = CTime:: ...
随机推荐
- git第一次上传push失败解决
第一次上传有可能会遇到push失败的情况,那是因为跟SVN一样,github上有一个README.md 文件没有下载下来 .我们得先 git pull --rebase origin master ...
- Django框架(十二)-- 中间件、CSRF跨站请求伪造
中间件 一.什么是中间件 请求的时候需要先经过中间件才能到达django后端(urls,views,templates,models) 响应的时候也需要经过中间件才能到达web服务网关接口 djang ...
- 【实战经验】--Xilinx--Chipscope使用
1)在工程右键点击New Source 新建Chioscope,在File name 填写名称: 2)新建完成后,工程里会出现你建立的chipscope文件(如下图chip_ddr3.cdc)双击打开 ...
- linux下nginx部署以及配置详解
1.下载源码包解压编译 启动多个,请看:在linux系统下安装两个nginx以及启动 查看nginx包路径:http://nginx.org/download/,两种下载方式: 1.在官网下载使用Xf ...
- SnowflakeIdWorker
/** * Twitter_Snowflake<br> * SnowFlake的结构如下(每部分用-分开):<br> * 0 - 0000000000 0000000000 0 ...
- 2019 央视网java面试笔试题 (含面试题解析)
本人3年开发经验.18年年底开始跑路找工作,在互联网寒冬下成功拿到阿里巴巴.今日头条.央视网等公司offer,岗位是Java后端开发,最终选择去了央视网. 面试了很多家公司,感觉大部分公司考察的点都差 ...
- aria2 cmd set chmod, and others..
import 'package:flutter/material.dart'; import 'dart:io'; import 'dart:async'; import 'package:rxdar ...
- 【转载】C#中使用double.TryParse方法将字符串转换为double类型
在C#编程过程中,将字符串string转换为double类型过程中,时常使用double.Parse方法,但double.Parse在无法转换的时候,会抛出程序异常,其实还有个double.TryPa ...
- 如何查看服务器对外的IP
开发的时候经常会被IP受限,这是由于数据源方限制了IP,所以需要报备一下IP白名单,怎么查看自己的网络对外的IP呢? 用下面的方式最为准确: Windows上操作: 直接再浏览器访问 http://h ...
- mysql数据库查询缓存总结
概述 查询缓存(Query Cache,简称QC),存储SELECT语句及其产生的数据结果.闲来无事,做一下这块的总结,也做个备忘! 工作原理 查询缓存工作原理如下: 缓存SELECT操作的结果集和S ...