하루하루가 지나간다
2019.01
愿在别人眼里算不上梦想的梦想 成真~
言归正传,记录下之前在ROS下跑yolov3的历程吧:
感觉现在视觉感知领域用yolo的比faster-RCNN多很多了,毕竟在无人驾驶圈子,感知从不能仅靠一个视觉传感器来实现了,所以在实用方面,单一的视觉算法精度从80%提高到90%,其实际用处并不算大:一方面,可以通过其他传感器比如Lidar来弥补,另一方面在决策规划层的应用效果也是一样的,无论怎样,它都是一个障碍物,需要躲避。所以在yolo版本不断更新、既保持了处理效率的同时,也提高了检测精度,yolo的优势也就凸显出来了!
先来看看yolo v1~v3这几个版本的特点吧~
论文名称:You only look once unified real-time object detection
论文链接
YOLO:其实是把物体检测(object detection)问题看作是目标区域预测和类别预测的回归问题,只用一个卷积神经网络结构就可以从输入图像直接预测bounding box和类别概率。
优点:1、YOLO的速度非常快。在Titan X GPU上的速度是45 fps(frames per second),加速版的YOLO差不多是150fps。2、YOLO是基于图像的全局信息进行预测的。这一点和基于sliding window以及region proposal等检测算法不一样,与Fast R-CNN相比,YOLO在误检测(将背景检测为物体)方面的错误率能降低一半多。3、YOLO可以学到物体的generalizable representations。可以理解为泛化能力强。4、准确率高,有实验证明。
缺点:1、位置精确性差,对于小目标物体以及物体比较密集的也检测不好,比如一群小鸟。2、YOLO虽然可以降低将背景检测为物体的概率,但同时导致召回率较低。
结构:
算法:
首先把输入图像划分成S*S的格子,然后对每个格子都预测B个bounding boxes,每个bounding box都包含5个预测值:x,y,w,h和confidence。x,y就是bounding box的中心坐标,与grid cell对齐(即相对于当前grid cell的偏移值),使得范围变成0到1;w和h进行归一化(分别除以图像的w和h,这样最后的w和h就在0到1范围);另外每个格子都预测C个假定类别的概率。在本文中作者取S=7,B=2,C=20(因为PASCAL VOC有20个类别),所以最后一共有7*7*30个tensor。
此时,可以发现,预测的每个bounding box都对应一个confidence score,如果grid cell里面没有object,confidence就是0,如果有,则confidence score等于预测的box和ground truth的IOU值。所以如何判断一个grid cell中是否包含object呢?答案是:如果一个object的ground truth的中心点坐标在一个grid cell中,那么这个grid cell就是包含这个object,也就是说这个object的预测就由该grid cell负责。每个grid cell都预测C个类别概率,表示一个grid cell在包含object的条件下属于某个类别的概率,注意grid cell和bounding box的区别,类别概率是针对grid cell的。
但是,这个方格(grid cell)在包含object的条件下属于某个类别的概率,也就是这个乘法到底是怎么算的呢?每个bounding box的confidence和每个类别的score相乘,得到每个bounding box属于哪一类的confidence score,即得到每个bounding box属于哪一类的confidence score。也就是说最后会得到20*(7*7*2)=20*98的score矩阵,括号里面是bounding box的数量,20代表类别。接下来的操作都是20个类别轮流进行:在某个类别中(即矩阵的某一行),将得分少于阈值(0.2)的设置为0,然后再按得分从高到低排序。最后再用NMS算法去掉重复率较大的bounding box(NMS:针对某一类别,选择得分最大的bounding box,然后计算它和其它bounding box的IOU值,如果IOU大于0.5,说明重复率较大,该得分设为0,如果不大于0.5,则不改;这样一轮后,再选择剩下的score里面最大的那个bounding box,然后计算该bounding box和其它bounding box的IOU,重复以上过程直到最后)。最后每个bounding box的20个score取最大的score,如果这个score大于0,那么这个bounding box就是这个socre对应的类别(矩阵的行),如果小于0,说明这个bounding box里面没有物体,跳过即可。
网络方面主要采用GoogLeNet,卷积层主要用来提取特征,全连接层主要用来预测类别概率和坐标。最后的输出是7*7*30,这个30前面也解释过了,7*7是grid cell的数量。这里注意下实现的细节可能人人都不大一样,比如对inception的改动,最后几层的全连接层的改动等等,但是重点在于最后一层的输出是7*7*30。另外两个小细节:1、作者先在ImageNet数据集上预训练网络,而且网络只采用fig3的前面20个卷积层,输入是224*224大小的图像。然后在检测的时候再加上随机初始化的4个卷积层和2个全连接层,同时输入改为更高分辨率的448*448。2、Relu层改为pRelu,即当x<0时,激活值是0.1*x,而不是传统的0。
损失函数方面,作者采用sum-squared error的方式把localization error(bounding box的坐标误差)和classificaton error整合在一起。在loss function中,前面两行表示localization error(即坐标误差),第一行是box中心坐标(x,y)的预测,第二行为宽和高的预测。这里注意用宽和高的开根号代替原来的宽和高,这样做主要是因为相同的宽和高误差对于小的目标精度影响比大的目标要大。举个例子,原来w=10,h=20,预测出来w=8,h=22,跟原来w=3,h=5,预测出来w1,h=7相比,其实前者的误差要比后者小,但是如果不加开根号,那么损失都是一样:4+4=8,但是加上根号后,变成0.15和0.7。第三、四行表示bounding box的confidence损失,就像前面所说的,分成grid cell包含与不包含object两种情况。这里注意下因为每个grid cell包含两个bounding box,所以只有当ground truth 和该网格中的某个bounding box的IOU值最大的时候,才计算这项。第五行表示预测类别的误差,注意前面的系数只有在grid cell包含object的时候才为1。
流程:
训练的时候:输入N个图像,每个图像包含M个object,每个object包含4个坐标(x,y,w,h)和1个label。然后通过网络得到7*7*30大小的三维矩阵。每个1*30的向量前5个元素表示第一个bounding box的4个坐标和1个confidence,第6到10元素表示第二个bounding box的4个坐标和1个confidence。最后20个表示这个grid cell所属类别。注意这30个都是预测的结果。然后就可以计算损失函数的第一、二 、五行。至于第二三行,confidence可以根据ground truth和预测的bounding box计算出的IOU和是否有object的0,1值相乘得到。真实的confidence是0或1值,即有object则为1,没有object则为0。 这样就能计算出loss function的值了。
测试的时候:输入一张图像,跑到网络的末端得到7*7*30的三维矩阵,这里虽然没有计算IOU,但是由训练好的权重已经直接计算出了bounding box的confidence。然后再跟预测的类别概率相乘就得到每个bounding box属于哪一类的概率。
论文名称: YOLOv2:Better,Faster,Stronger
论文链接:https://arxiv.org/abs/1612.08242
首先YOLO有两个缺点:一个缺点在于定位不准确,另一个缺点在于和基于region proposal的方法相比召回率较低。因此YOLOv2主要是要在这两方面做提升。另外YOLOv2并不是通过加深或加宽网络达到效果提升,反而是简化了网络。大概看一下YOLOv2的表现:YOLOv2算法在VOC 2007数据集上的表现为67 FPS时,MAP为76.8,在40FPS时,MAP为78.6.
那好,就来看看YOLOv2是怎么来进行改进v1,变得Better,Faster,Stronger?
1、Batch Normalization(添加了BN层)
BN(Batch Normalization)层简单讲就是对网络的每一层的输入都做了归一化,这样网络就不需要每层都去学数据的分布,收敛会快点。原来的YOLO算法(采用的是GoogleNet网络提取特征)是没有BN层的,因此在YOLOv2中作者为每个卷积层都添加了BN层。另外由于BN可以规范模型,所以本文加入BN后就把dropout去掉了。实验证明添加了BN层可以提高2%的mAP。
2、High Resolution Classifier(修改了预训练的步骤,解决了模型从分类模型切换到检测模型时还要适应图像分辨率的改变)
首先fine-tuning的作用不言而喻,现在基本跑个classification或detection的模型都不会从随机初始化所有参数开始,所以一般都是用预训练的网络来finetuning自己的网络,而且预训练的网络基本上都是在ImageNet数据集上跑的,一方面数据量大,另一方面训练时间久,而且这样的网络都可以在相应的github上找到。
原来的YOLO网络在预训练的时候采用的是224*224的输入(这是因为一般预训练的分类模型都是在ImageNet数据集上进行的),然后在detection的时候采用448*448的输入,这会导致从分类模型切换到检测模型的时候,模型还要适应图像分辨率的改变。而YOLOv2则将预训练分成两步:先用224*224的输入从头开始训练网络,大概160个epoch(表示将所有训练数据循环跑160次),然后再将输入调整到448*448,再训练10个epoch。注意这两步都是在ImageNet数据集上操作。最后再在检测的数据集上fine-tuning,也就是detection的时候用448*448的图像作为输入就可以顺利过渡了。作者的实验表明这样可以提高几乎4%的MAP。
3、Convolutional With Anchor Boxes(去掉了全连接层和最后一个池化层;缩减网络输入图像尺寸;引入anchor boxes)
原来的YOLO是利用全连接层直接预测bounding box的坐标,而YOLOv2借鉴了Faster R-CNN的思想,引入anchor。首先将原网络的全连接层和最后一个pooling层去掉,使得最后的卷积层可以有更高分辨率的特征;然后缩减网络,用416*416大小的输入代替原来448*448。这样做的原因在于希望得到的特征图都有奇数大小的宽和高,奇数大小的宽和高会使得每个特征图在划分cell的时候就只有一个center cell(比如可以划分成7*7或9*9个cell,center cell只有一个,如果划分成8*8或10*10的,center cell就有4个)。为什么希望只有一个center cell呢?因为大的object一般会占据图像的中心,所以希望用一个center cell去预测,而不是4个center cell去预测。网络最终将416*416的输入变成13*13大小的feature map输出,也就是缩小比例为32。我们知道原来的YOLO算法将输入图像分成7*7的网格,每个网格预测两个bounding box,因此一共只有98个box,但是在YOLOv2通过引入anchor boxes,预测的box数量超过了1千(以输出feature map大小为13*13为例,每个grid cell有9个anchor box的话,一共就是13*13*9=1521个,当然由后面第4点可知,最终每个grid cell选择5个anchor box)。顺便提一下在Faster RCNN在输入大小为1000*600时的boxes数量大概是6000,在SSD300中boxes数量是8732。显然增加box数量是为了提高object的定位准确率。
作者的实验证明:虽然加入anchor使得MAP值下降了一点(69.5降到69.2),但是提高了recall(81%提高到88%)。
4、Dimension Clusters
我们知道在Faster R-CNN中anchor box的大小和比例是按经验设定的,然后网络会在训练过程中调整anchor box的尺寸。但是如果一开始就能选择到合适尺寸的anchor box,那肯定可以帮助网络越好地预测detection。所以作者采用k-means的方式对训练集的bounding boxes做聚类,试图找到合适的anchor box。另外作者发现如果采用标准的k-means(即用欧式距离来衡量差异),在box的尺寸比较大的时候其误差也更大,而我们希望的是误差和box的尺寸没有太大关系。所以通过IOU定义了如下的距离函数,使得误差和box的大小无关:
在分析了聚类的结果并平衡了模型复杂度与recall值,作者选择了K=5,这也就是Figure2中右边的示意图是选出来的5个box的大小,这里紫色和黑色也是分别表示两个不同的数据集,可以看出其基本形状是类似的。而且发现聚类的结果和手动设置的anchor box大小差别显著。聚类的结果中多是高瘦的box,而矮胖的box数量较少。
5、Multi-Scale Training
为了让YOLOv2模型更加robust,作者引入了Muinti-Scale Training,简单讲就是在训练时输入图像的size是动态变化的,注意这一步是在检测数据集上fine tune时候采用的,不要跟前面在Imagenet数据集上的两步预训练分类模型混淆。具体来讲,在训练网络时,每训练10个batch(文中是10个batch,个人认为会不会是笔误,不应该是10个epoch?),网络就会随机选择另一种size的输入。那么输入图像的size的变化范围要怎么定呢?前面我们知道本文网络本来的输入是416*416,最后会输出13*13的feature map,也就是说downsample的factor是32,因此作者采用32的倍数作为输入的size,具体来讲文中作者采用从{320,352,…,608}的输入尺寸。这种网络训练方式使得相同网络可以对不同分辨率的图像做detection。虽然在输入size较大时,训练速度较慢,但同时在输入size较小时,训练速度较快,而multi-scale training又可以提高准确率,因此算是准确率和速度都取得一个不错的平衡。通过multi-scale training的检测模型,在测试的时候,输入图像在尺寸变化范围较大的情况下也能取得mAP和FPS的平衡。
总结:High Resolution Classifier的提升非常明显(近4%),另外通过结合dimension prior+localtion prediction这两种方式引入anchor也能带来近5%mAP的提升。
那以上是将模型精度变的更好,那怎么让模型变的更快呢?
1、Darknet-19
在YOLO v1中,作者采用的训练网络是基于GooleNet,这里作者将GooleNet和VGG16做了简单的对比,GooleNet在计算复杂度上要优于VGG16(8.25 billion operation VS 30.69 billion operation),但是前者在ImageNet上的top-5准确率要稍低于后者(88% VS 90%)。而在YOLO v2中,作者采用了新的分类模型作为基础网络,那就是Darknet-19。
2、Training for Classification
这里的2和3部分在前面有提到,就是训练处理的小trick。这里的training for classification都是在ImageNet上进行预训练,主要分两步:1、从头开始训练Darknet-19,数据集是ImageNet,训练160个epoch,输入图像的大小是224*224,初始学习率为0.1。另外在训练的时候采用了标准的数据增加方式比如随机裁剪,旋转以及色度,亮度的调整等。2、再fine-tuning 网络,这时候采用448*448的输入,参数的除了epoch和learning rate改变外,其他都没变,这里learning rate改为0.001,并训练10个epoch。结果表明fine-tuning后的top-1准确率为76.5%,top-5准确率为93.3%,而如果按照原来的训练方式,Darknet-19的top-1准确率是72.9%,top-5准确率为91.2%。因此可以看出第1,2两步分别从网络结构和训练方式两方面入手提高了主网络的分类准确率。
3、Training for Detection
在前面第2步之后,就开始把网络移植到detection,并开始基于检测的数据再进行fine-tuning。首先把最后一个卷积层去掉,然后添加3个3*3的卷积层,每个卷积层有1024个filter,而且每个后面都连接一个1*1的卷积层,1*1卷积的filter个数根据需要检测的类来定。比如对于VOC数据,由于每个grid cell我们需要预测5个box,每个box有5个坐标值和20个类别值,所以每个grid cell有125个filter(与YOLOv1不同,在YOLOv1中每个grid cell有30个filter,还记得那个7*7*30的矩阵吗,而且在YOLOv1中,类别概率是由grid cell来预测的,也就是说一个grid cell对应的两个box的类别概率是一样的,但是在YOLOv2中,类别概率是属于box的,每个box对应一个类别概率,而不是由grid cell决定,因此这边每个box对应25个预测值(5个坐标加20个类别值),而在YOLOv1中一个grid cell的两个box的20个类别值是一样的)。另外作者还提到将最后一个3*3*512的卷积层和倒数第二个卷积层相连。最后作者在检测数据集上fine tune这个预训练模型160个epoch,学习率采用0.001,并且在第60和90epoch的时候将学习率除以10,weight decay采用0.0005。
论文名称:YOLOv3: An Incremental Improvement
论文地址:https://pjreddie.com/media/files/papers/YOLOv3.pdf
YOLO算法的基本思想是:首先通过特征提取网络对输入图像提取特征,得到一定size的feature map,比如13*13,然后将输入图像分成13*13个grid cell,接着如果ground truth中某个object的中心坐标落在哪个grid cell中,那么就由该grid cell来预测该object,因为每个grid cell都会预测固定数量的bounding box(YOLO v1中是2个,YOLO v2中是5个,YOLO v3中是3个,这几个bounding box的初始size是不一样的),那么这几个bounding box中最终是由哪一个来预测该object?答案是:这几个bounding box中只有和ground truth的IOU最大的bounding box才是用来预测该object的。可以看出预测得到的输出feature map有两个维度是提取到的特征的维度,比如13*13,还有一个维度(深度)是B*(5+C),注:YOLO v1中是(B*5+C),其中B表示每个grid cell预测的bounding box的数量,C表示bounding box的类别数(没有背景类,所以对于VOC数据集是20),5表示4个坐标信息和一个置信度(objectness score)。
bounding box的坐标预测方式还是延续了YOLO v2的做法,类别预测方面主要是将原来的单标签分类改进为多标签分类,因此网络结构上就将原来用于单标签多分类的softmax层换成用于多标签多分类的逻辑回归层。首先说明一下为什么要做这样的修改,原来分类网络中的softmax层都是假设一张图像或一个object只属于一个类别,但是在一些复杂场景下,一个object可能属于多个类,比如你的类别中有woman和person这两个类,那么如果一张图像中有一个woman,那么你检测的结果中类别标签就要同时有woman和person两个类,这就是多标签分类,需要用逻辑回归层来对每个类别做二分类。逻辑回归层主要用到sigmoid函数,该函数可以将输入约束在0到1的范围内,因此当一张图像经过特征提取后的某一类输出经过sigmoid函数约束后如果大于0.5,就表示属于该类。
YOLO v3采用多个scale融合的方式做预测。在多个scale的feature map上做检测,对于小目标的检测效果提升还是比较明显的。前面提到过在YOLO v3中每个grid cell预测3个bounding box,看起来比YOLO v2中每个grid cell预测5个bounding box要少,其实不是!因为YOLO v3采用了多个scale的特征融合,所以boundign box的数量要比之前多很多,以输入图像为416*416为例:(13*13+26*26+52*52)*3和13*13*5相比哪个更多应该很清晰了。关于bounding box的初始尺寸还是采用YOLO v2中的k-means聚类的方式来做。
网络结构(Darknet-53)一方面基本采用全卷积(YOLO v2中采用pooling层做feature map的sample,这里都换成卷积层来做了),另一方面引入了residual结构(YOLO v2中还是类似VGG那样直筒型的网络结构,层数太多训起来会有梯度问题,所以Darknet-19也就19层,因此得益于ResNet的residual结构,训深层网络难度大大减小,因此这里可以将网络做到53层,精度提升比较明显)。Darknet-53只是特征提取层,源码中只使用了pooling层前面的卷积层来提取特征,因此multi-scale的特征融合和预测支路并没有在该网络结构中体现。具体信息可以看源码:https://github.com/pjreddie/darknet/blob/master/cfg/yolov3.cfg。预测支路采用的也是全卷积的结构,其中最后一个卷积层的卷积核个数是255,是针对COCO数据集的80类:3*(80+4+1)=255,3表示一个grid cell包含3个bounding box,4表示框的4个坐标信息,1表示objectness score。模型训练方面还是采用原来YOLO v2中的multi-scale training。
以上借鉴了博客专家AI之路的博客,在此表示钦佩和感谢!
理解了这几个版本的原理以后,就可以实战操作了
https://pjreddie.com/darknet/yolo/
网络训练以及运行,官方的教程已经给的很清楚了,在此不在赘述~
接下来,说一说移植ROS实际应用的事:
两种方法:
1.我自己整理的github,直接down下来即可:https://github.com/I-am-Unique/catkin_darknet
注:①使用的时候因为我是将一个工作空间的src一起上传了,下载下来以后,将darknet_ros和detecting_test两个程序包移到你的catkin_ws/src目录下,然后进入darknet_ros ,有一个darknet.zip的压缩包,将其解压到当前目录下即可,直接后退到你的catkin_ws/目录下,catkin_make 即可。
mkidr -p catkin_ws/src
cd catkin_ws/src
git clone https://github.com/I-am-Unique/catkin_darknet
cd ..
catkin_make
②在catkin_ws/src/darknet_ros/darknet_ros/yolo_network_config/weights目录下应该放你用yolo训练的权重文件,或者直接下载预训练好的权重放这里即可。
③在catkin_darknet/src/darknet_ros/darknet_ros/config/目录下,ros.yaml文件定义了该工程订阅的topic以及它检测完成以后将结果发布出去的topic,见下图:
可以看出,这个yolo_ros节点订阅了/camera/image话题作为输入,发布三个话题作为输出,分别是:
/darknet_ros/found_object 物体类别名称。
/darknet_ros/bounding_boxes 框信息。
/darknet_ros/detection_image 检测结果图片。
④git上,src目录下还有一个程序包detecting_test是测试用的,里面有一个将本地图片读取发布在topic:/camera/image上和订阅检测结果的/darknet_ros/detection_image的可执行程序,具体编译通过以后,使用两个终端分别运行以下命令:
rosrun detecting_test image_publisher test.jpg
rosrun detecting_test image_subscriber
2.参考ROS官方的源码:https://github.com/leggedrobotics/darknet_ros
注:① git clone https://github.com/leggedrobotics/darknet_ros.git 之后,你会发现,darknet文件夹是空的,需要自己手动安装,因为它是@链接到darknet官网的,但是一定注意在去官网:
https://github.com/pjreddie/darknet/tree/508381b37fe75e0e1a01bcb2941cb0b31eb0e4c9下载darknet时,一定不能使用git clone,要使用压缩包下载下来,然后进行提取替换刚才那个空的darknet文件夹,直接后退catkin_make,不要在darknet文件夹里make。
②如果不使用压缩包,会一直编译出问题。
(∩_∩)
各自加油~