在看yolov3之前,建议大家先看下我总结的前两个:
Yolo入门:Yolo系列- - - - 2度冲击:yolov2_0824搞算法的博客-CSDN博客
Yolo入门:Yolo系列- - - - 1度冲击:yolov1_0824搞算法的博客-CSDN博客
然后下面就是正题了,下面这张图是借用别人的
DBL:代码中的Darknetconv2d_BN_Leaky,是yolov3的基本组件.就是卷积+BN+Leaky relu.
resn:n代表数字,有res1,res2, … ,res8等等,表示这个res_block里含有多少个res_unit.
concat:张量拼接.将darknet中间层和后面的某一层的上采样进行拼接.拼接的操作和残差层add的操作是不一样的,拼接会扩充张量的维度,而add只是直接相加不会导致张量维度的改变.
yolov3对之前的算法既有保留又有改进。先分析一下yolov3上保留的东西:
--“分而治之”,从yolo_v1开始,yolo就通过划分单元格来做检测,只是划分的数量不一样;
--采用"leaky ReLU"作为激活函数;
--端到端进行训练。一个loss function搞定训练,只需关注输入端和输出端;
--从yolo_v2开始,yolo就用batch normalization作为正则化、加速收敛和避免过拟合的方法,把BN层和leaky relu层接到每一层卷积层之后;
--多尺度训练。在速度和准确率之间tradeoff。想速度快点,可以牺牲准确率;想准确率高点儿,可以牺牲一点速度;
1.Bounding Box Prediction()
YOLO v3的Bounding Box由YOLOV2又做出了更好的改进。在yolo_v2和yolov3中,都采用了对图像中的object采用k-means聚类。 feature map中的每一个cell都会预测3个边界框(bounding box) ,每个bounding box都会预测三个东西:
(1)每个框的位置(4个值: 中心坐标tx和ty,框的高度bh和宽度bw)
(2)一个objectness prediction
(3)N个类别,coco数据集80类,voc20类。
三次检测,每次对应的感受野不同:
32倍降采样的感受野最大,适合检测大的目标,所以在输入为416×416时,每个cell的三个anchor box为(116 ,90); (156 ,198); (373 ,326);
16倍适合一般大小的物体,anchor box为(30,61); (62,45); (59,119);
8倍的感受野最小,适合检测小目标,因此anchor box为(10,13); (16,30); (33,23);
所以当输入为416×416时,实际总共有(52×52+26×26+13×13)×3=10647个proposal box。
注意:(下面这张图看着是不是很熟悉,yolov2论文中也有这张图)
2.BackBone
整个yolov3结构里面,是没有池化层和全连接层的。前向传播过程中,张量的尺寸变换是通过改变卷积核的步长来实现的,比如stride=(2, 2),这就等于将图像边长缩小了一半(即面积缩小到原来的1/4)。在yolo_v2中,要经历5次缩小,会将特征图缩小到原输入尺寸的1/2^5,即1/32。输入为416x416,则输出为13x13(416/32=13)。
yolov3也和yolov2一样,backbone都会将输出特征图缩小到输入的1/32。所以,通常都要求输入图片是32的倍数。可以对比v2和v3的backbone看看:(DarkNet-19 与 DarkNet-53)
yolo_v2中对于前向过程中张量尺寸变换,都是通过 最大池化来进行,一共有5次。而v3是通过卷积核 增大步长来进行,也是5次。(darknet-53最后面有一个全局平均池化,在yolo-v3里面没有这一层,所以张量维度变化只考虑前面那5次)。
这也是416x416输入得到13x13输出的原因。从图2可以看出,darknet-19是不存在残差结构(resblock,从resnet上借鉴过来)的,和VGG是同类型的backbone(属于上一代CNN结构),而darknet-53是可以和resnet-152正面刚的backbone,看下表:
表1. backbone对比图
从上表也可以看出,darknet-19在速度上仍然占据很大的优势。其实在其他细节也可以看出(比如bounding box prior采用k=9), yolov3并没有那么追求速度,而是在保证实时性(fps>36)的基础上追求performance。不过前面也说了,你要想更快,还有一个 tiny-darknet作为backbone可以替代darknet-53,在官方代码里用一行代码就可以实现切换backbone。搭用tiny-darknet的yolo,也就是tiny-yolo在轻量和高速两个特点上,显然是state of the art级别,tiny-darknet是和squeezeNet正面刚的网络,详情可以看下表:
所以,有了yolo v3,就真的用不着yolo v2了,更用不着yolo v1了。这也是[yolo官方网站](https://pjreddie.com/darknet/),在v3出来以后,就没提供v1和v2代码下载链接的原因了
3.LOSS Function
YOLOv3重要改变之一:No more softmaxing the classes。
YOLO v3现在对图像中检测到的对象执行多标签分类。logistic回归用于对anchor包围的部分进行一个目标性评分(objectness score),即这块位置是目标的可能性有多大。这一步是在predict之前进行的,可以去掉不必要anchor,可以减少计算量。
如果模板框不是最佳的即使它超过我们设定的阈值,我们还是不会对它进行predict。
不同于faster R-CNN的是,yolov3只会对1个prior进行操作,也就是那个最佳prior。而logistic回归就是用来从9个anchor priors中找到objectness score(目标存在可能性得分)最高的那一个。logistic回归就是用曲线对prior相对于 objectness score映射关系的线性建模。
4. predictions across scales
yolov3输出了3个不同尺度的feature map,如上图所示的y1, y2, y3。这也是v3论文中提到的为数不多的改进点:predictions across scales,这个借鉴了FPN(feature pyramid networks),采用多尺度来对不同size的目标进行检测,越精细的grid cell就可以检测出越精细的物体。
y1,y2和y3的深度都是255,边长的规律是13:26:52
对于COCO类别而言,有80个种类,所以每个box应该对每个种类都输出一个概率。
yolo v3设定的是每个网格单元预测3个box,所以每个box需要有(x, y, w, h, confidence)五个基本参数,然后还要有80个类别的概率。所以3*(5 + 80) = 255。这个255就是这么来的。(还记得yolo v1的输出张量吗? 7x7x30,只能识别20类物体,而且每个cell只能预测2个box,和v3比起来就像iphone5s和iphone13一样,指的是性能的提升,并不是说iphone5s不好)
v3用上采样的方法来实现这种多尺度的feature map,可以结合图1和图2右边来看,图1中concat连接的两个张量是具有一样尺度的(两处拼接分别是26x26尺度拼接和52x52尺度拼接,通过(2, 2)上采样来保证concat拼接的张量尺度相同)。作者并没有像SSD那样直接采用backbone中间层的处理结果作为feature map的输出,而是和后面网络层的上采样结果进行一个拼接之后的处理结果作为feature map。为什么这么做呢? 我感觉是有点玄学在里面,一方面避免和其他算法做法重合,另一方面这也许是试验之后并且结果证明更好的选择,再者有可能就是因为这么做比较节省模型size的。
总结:
对于yolov3输出的输出是至关重要的。
第一点, 9个anchor会被三个输出张量平分的。根据大中小三种size各自取自己的anchor。
第二点,每个输出y在每个自己的网格都会输出3个预测框,这3个框是9除以3得到的,这是作者设置的,我们可以从输出张量的维度来看,13x13x255。255是怎么来的呢,3*(5+80)。80表示80个种类,5表示位置信息和置信度,3表示要输出3个prediction。在代码上来看,3*(5+80)中的3是直接由num_anchors//3得到的。
第三点,作者使用了logistic回归来对每个anchor包围的内容进行了一个目标性评分(objectness score)。根据目标性评分来选择anchor prior进行predict,而不是所有anchor prior都会有输出。