与yolov3对比,yolov5主要进行了下面4方面的改进:(其实很多在yolov4中已经存在了,这里再仔细研究一下)。以5.0版本来介绍,6.0版本将Focus去掉了。结构图见3.1
- 输入端:Mosaic数据增强,自适应锚框计算,自适应图片缩放
- Backbone:Focus + C3
- Neck:FPN+PAN
- Prediction:GIOU_Loss
下面分别详细解说:
一,Mosaic数据增强(https://zhuanlan.zhihu.com/p/143747206)
Yolov4/5中使用的Mosaic是参考2019年底提出的CutMix数据增强的方式,但CutMix只使用了两张图片进行拼接,而Mosaic数据增强则采用了4张图片,随机缩放、随机裁剪、随机排布的方式进行拼接。
这里首先要了解为什么要进行Mosaic数据增强呢?
在平时项目训练时,小目标的AP一般比终目标和大目标低很多。而Coco数据集中也包含大量的小目标,但比较麻烦的是小目标的分布并不均匀。首先看下小、中、大目标的定义:
2019年发布的论文《Augmentation for small object detection》对此进行了区分:
可以看到小目标的定义是目标框的长宽0*0~32*32之间的物体
但在整体的数据集中,小、中、大目标的占比并不均衡。如上表所示,Coco数据集中小目标占比达41.4%,数量比中目标和大目标都要多。但在所有的训练集图片中,只有52.3%的图片有小目标,而终目标和大目标的分布相对来说更加均匀一些。针对这种情况,YOLOV4/5的作者采用了Moscaic数据增强的方式。主要有几个优点:
- 丰富数据集:随机使用4张图片,随机缩放,再随机分布进行拼接,大大丰富了检测数据集,特别事随机缩放增加了很多小目标,让网络的鲁棒性更好。
- 减少GPU:可能有人会说,随即缩放,普通的数据增强也可以做,但作者考虑到很多人可能只有一个GPU,因此Mosaic增强训练时,可以直接计算4张图片的数据,使得Mini-batch大小并不需要很大,一个GPU就可以达到比较好的效果。
次外,发现另一研究者的训练方式也值得借鉴,采用的数据增强和Mosaic比较类似,也是使用4张图片(不是随机分布),但训练计算loss时,采用”缺啥补啥“的思路:如果上一个iteration中,小物体产生的loss不足(比如小于某一阈值),则下一个iteration就用拼接图;否则就用正常图片训练。
二, Focus模块(摘自:https://blog.csdn.net/qq_39056987/article/details/112712817)
1.1 Focus模块的原理
Focus模块在v5中是图片进入backbone前,对图片进行切片操作,具体操作是在一张图片中每隔一个像素拿到一个值,类似于邻近下采样,这样就拿到了四张图片,四张图片互补,长的差不多,但是没有信息丢失,这样一来,将W、H信息就集中到了通道空间,输入通道扩充了4倍,即拼接起来的图片相对于原先的RGB三通道模式变成了12个通道,最后将得到的新图片再经过卷积操作,最终得到了没有信息丢失情况下的二倍下采样特征图。
其实就是yolov2里面的ReOrg+Conv操作,也就是亚像素卷积的反向操作版本,简单来说就是把数据切分成4份,每份数据都是相当于2倍下采样得到的,然后再channel维度进行拼接,最后进行卷积操作。可以最大程度的减少信息损失而进行下采样操作。
以yolov5s为例,原始的640 × 640 × 3的图像输入Focus结构,采用切片操作,先变成320 × 320 × 12的特征图,再经过一次卷积操作,最终变成320 × 320 × 32的特征图。切片操作如下:
1.2 Focus模块的作用
先引用yolov5官方解答:
Focus() 是用来降低FLOPS的,跟mAP无关,但接下来你可能看到使用Focus() 之后对比普通卷积层FLOPS反而增加了,那作者为什么说可以降低呢?作者又回答了:一个Focus层可以代替3个yolov3/4的层。层数减少了,FLOPS肯定是降低啦。
在讨论Focus的作用之前,先了解两个概念:
- 参数数量(params):关系到模型大小,单位通常是M,通常参数用float32表示,所以模型大小是参数数量的4倍。
- 计算量(FLOPs):即浮点运算数,可以用来衡量算法/模型的复杂度,这关系到算法速度,大模型的单位通常为G,小模型单位通常为M;通常只考虑乘加操作的数量,而且只考虑Conv和FC等参数层的计算量,忽略BN和PReLU等,一般情况下,Conv和FC层也会忽略仅纯加操作的计算量,如bias偏置加和shoutcut残差加等,目前技术有BN和CNN可以不加bias。
- params计算公式:Kh × Kw × Cin × Cout
- FLOPs计算公式:Kh × Kw × Cin × Cout × H × W = 即(当前层filter × 输出的feature map)= params × H × W
总所周知,图片在经过Focus模块后,最直观的是起到了下采样的作用,但是和常用的卷积下采样有些不一样,可以对Focus的计算量和普通卷积的下采样计算量进行做个对比:在yolov5s的网络结构中,可以看到,Focus模块的卷积核是3 × 3,输出通道是32:
那么做个对比:
普通下采样:即将一张640 × 640 × 3的图片输入3 × 3的卷积中,步长为2,输出通道32,下采样后得到320 × 320 × 32的特征图,那么普通卷积下采样理论的计算量为:
FLOPs(conv) = 3 × 3 × 3 × 32 × 320 × 320 = 88473600(不考虑bias情况下)
params参数量(conv) = 3 × 3 × 3 × 32 +32 +32 = 928 (后面两个32分别为bias和BN层参数)
Focus:将640 × 640 × 3的图像输入Focus结构,采用切片操作,先变成320 × 320 × 12的特征图,再经过3 × 3的卷积操作,输出通道32,最终变成320 × 320 × 32的特征图,那么Focus理论的计算量为:
FLOPs(Focus) = 3 × 3 × 12 × 32 × 320 × 320 = 353894400(不考虑bias情况下)
params参数量(Focus)= 3 × 3 × 12 × 32 +32 +32 =3520 (为了呼应上图输出的参数量,将后面两个32分别为bias和BN层的参数考虑进去,通常这两个占比比较小可以忽略)
可以明显的看到,Focus的计算量和参数量要比普通卷积要多一些,但是~实际上是代替了三层,经过改进之后参数量还是变少了,也达到了提速的效果
1.3 其它几个有趣问题(https://github.com/ultralytics/yolov5/issues/847)
- Q1: 有个疑惑,在Focus降低计算量的同时,“步长”采样会不会对边框回归有影响呢?
- A1:Focus(在v6.0中已经被替代)是将空间信息堆叠到通道信息中,可能在边框回归时会有1个像素的减少,但你在回归的时候本来就不可能预测的一个像素都不差。
- Q2:Focus为什么只用了一次,并且是用在整个网络的最开始阶段?
- A2:对分类模型,可能在主干网上使用更好一点;但对检测模型来说,(用在主干网)很难保证不影响mAP@0.5:0.95(mAP@0.5可能影响不大)
三, CSPDarkNet53(摘自:https://blog.csdn.net/moxibingdao/article/details/107438085,https://zhuanlan.zhihu.com/p/143747206)
Yolov4和v5都使用CSPDarknet作为Backbone,从输入图像中提取丰富的信息特征。CSPNet全称是Cross Stage Partial Networks,也就是跨阶段局部网络。CSPNet解决了其他大型卷积神经网络框架Backbone中网络优化的梯度信息重复问题,将梯度的变化从头到尾地集成到特征图中,因此减少了模型的参数量和FLOPS数值,既保证了推理速度和准确率,又减小了模型尺寸。CSPNet实际上是基于Densnet的思想,复制基础层的特征映射图,通过dense block 发送副本到下一个阶段,从而将基础层的特征映射图分离出来。这样可以有效缓解梯度消失问题(通过非常深的网络很难去反推丢失信号) ,支持特征传播,鼓励网络重用特征,从而减少网络参数数量。
CSPDarknet53是在yolov3主干网络Darknet53的基础上,借鉴2019年的CSPNet的经验,产生的Backbone结构,其中包含了5个CSP模块。
每个CSP模块前面的卷积核大小都是3*3,stride=2,因此可以起到下采样的作用。因为Backbone有5个CSP模块,输入图象是608*608,所以特征图变化的规律是:608-->304-->152-->76-->38-->19。经过5次CSP模块后得到19*19大小的特征图。而且作者只在Backbone中采用了Mish激活函数,网络后面仍然采用了Leaky_relu激活函数。为啥要采用CSP模块?CSPNet全称是Cross Stage Paritial Network,主要从网络结构设计的角度解决推理中从计算量很大的问题。CSPNet的作者认为推理计算过高的问题是由于网络优化中的梯度信息重复导致的。因此采用CSP模块先将基础层的特征映射划分为两部分,然后通过跨阶段层次结构将它们合并,在减少了计算量的同时可以保证准确率。因此Yolov4在主干网络Backbone采用CSPDarknet53网络结构,主要有三个方面的优点:
- 优点一:增强CNN的学习能力,使得在轻量化的同时保持准确性。
- 优点二:降低计算瓶颈
- 优点三:降低内存成本
这篇文章的CSP思想介绍的很详细。
Mish激活函数(https://zhuanlan.zhihu.com/p/263555912)
激活函数是为了提高网络的学习能力,提升梯度的传递效率。CNN常用的激活函数也在不断地发展,早期网络常用的有ReLU, LeakyReLU, softplus等,后来又有了Swish,Mish等。Mish激活函数的计算复杂度比ReLU要高不少,如果你的计算资源不是很够,可以考虑使用LeakyReLU代替Mish。在介绍之前,需要先了解softplus和tanh函数。
softplus激活函数的公式如下:$zeta (x)=log(1+e^x)$
上图是其输出曲线,softplus和ReLU的曲线具有相似性,但是其比ReLU更为平滑。目前的普遍看法是,平滑的激活函数允许更好的信息深入神经网络,从而得到更好的准确性和泛化。
tanh的公式如下:$tanh(x)=frac{e^x-e^{-x}}{e^x+e^{-x}}$
Mish激活函数的公式为:$Mish(x)=x imes tanh(zeta (x))$
上图为Mish的曲线。首先其和ReLU一样,都是无正向边界的,可以避免梯度饱和;其次Mish函数是处处光滑的,并且在绝对值较小的负值区域允许一些负值。
CSP结构
CSP结构,其初衷是减少计算量并且增强梯度的表现。主要思想是:在输入block之前,将输入分为两个部分,其中一部分通过block进行计算,另一部分直接通过一个shortcut进行concatenate。作者基于Dense block(对DenseNet网络不熟悉的同学可以查看这里)讲述了CSP的结构,如下图:
上图中,上者为原始的Dense Block,下者为Partial Dense Block。假设将输入按照part_ratio=0.5的比例分成两部分,并且假设一个Dense Block的输入为w *h* c,growth rate为 d,Dense block的layer数量为m,则:对于原始的DenseBlock,其CIO为(Convolutional input/Output)为cm + [(mm+m)d]/2;而对于使用了CSP结构的结构来说,其CIO下降为[cm + (mm+m)d]/2。
作者阐述了CSP结构的优点:
- 加强CNN的学习能力
- 减少计算瓶颈,现在的网络大多计算代价昂贵,不利于工业的落地
- 减少内存消耗
讨论(https://zhuanlan.zhihu.com/p/263555912):
按照CSP论文中的思路,我开始认为的CSP结构应该是这样的——特征输入之后,通过一个比例将其分为两个部分(CSPNet中是二等份),然后再分别输入block结构,以及后面的Partial transition处理。这样符合CSPNet论文中的理论思路。
但是实际上,我参考了一些源码以及darknet配置文件中的网络参数,得到的结构是这样的:
和我所理解不同的是,实际的结构在输入后没有按照通道划分成两个部分,而是直接用两路的1x1卷积将输入特征进行变换。 可以理解的是,将全部的输入特征利用两路1x1进行transition,比直接划分通道能够进一步提高特征的重用性,并且在输入到resiudal block之前也确实通道减半,减少了计算量。虽然不知道这是否吻合CSP最初始的思想,但是其效果肯定是比我设想的那种情况更好的。性能是王道。
3.1 新版v5模块解析(https://blog.csdn.net/Mr_Clutch/article/details/119912926)
先将yolov5-5.0结构图贴上:(https://blog.csdn.net/Q1u1NG/article/details/107511465)
3.1.1 CONV模块
作者在CONV模块(CBL模块)中封装了三个功能:包括卷积(Conv2d)、BN以及Activate函数(在新版yolov5中,作者采用了SiLU函数作为激活函数),同时autopad(k, p)实现了padding的效果。
总的说来Conv实现了将输入特征经过卷积层,激活函数,归一化层,得到输出层。
3.1.2 Bottleneck模块(Resunit)
- 先将channel 数减小再扩大(默认减小到一半),具体做法是先进行1×1卷积将channel减小一半,再通过3×3卷积将通道数加倍,并获取特征(共使用两个标准卷积模块),其输入与输出的通道数是不发生改变的。
- shortcut参数控制是否进行残差连接(使用ResNet)。
- 在yolov5的backbone中的Bottleneck都默认使shortcut为True,在head中的Bottleneck都不使用shortcut。
- 与ResNet对应的,使用add而非concat进行特征融合,使得融合后的特征数不变。
3.1.3 C3模块,v5.0用C3模块代替了CSP。
C3模块
BottleNeckCSP模块
- 在新版yolov5中,作者将BottleneckCSP(瓶颈层)模块转变为了C3模块,其结构作用基本相同均为CSP架构,只是在修正单元的选择上有所不同,其包含了3个标准卷积层以及多个Bottleneck模块(数量由配置文件.yaml的n和depth_multiple参数乘积决定)
- 从C3模块的结构图可以看出,C3相对于BottleneckCSP模块不同的是,经历过残差输出后的Conv模块被去掉了,concat后的标准卷积模块中的激活函数也由LeakyRelu变味了SiLU。
3.1.4 SPP模块(v6.0里新增了SPPF层)
- SPP是空间金字塔池化的简称,其先通过一个标准卷积模块将输入通道减半,然后分别做kernel-size为5,9,13的maxpooling(对于不同的核大小,padding是自适应的)
- 对三次最大池化的结果与未进行池化操作的数据进行concat,最终合并后channel数是原来的2倍。
作用:SPP是由微软研究院的何凯明大神提出,主要为了解决两个问题:
- 有效避免了对图像区域剪裁、缩放操作导致的图像失真等问题;
- 解决了卷积神经网络对图像重复特征提取的问题,大大提高了产生候选框的速度,且节省了计算成本。
但在yolo中不是为解决这两个问题的,yolo版本的SPP实际上只是借鉴了空间金字塔的思想,通过SPP模块实现了局部特征和全部特征,这也是为什么SPP模块中的最大池化核大小要尽可能地接近或者等于需要池化的特征图的大小,特征图经过局部特征与全矩特征相融合后,丰富了特征图的表达能力,有利于待检测图像中目标大小差异较大的情况,尤其是对于yolo这种复杂的多目标检测,对检测的精度上有了很大的提升。
SPPF():先贴上官方介绍:
可以用来代替SPP,结果是一样的,但是可以降低FLOPS,运行的更快。
四,PANet(https://www.cnblogs.com/winslam/p/14486803.html, https://zhuanlan.zhihu.com/p/143747206)
4.1 FPN 结构和yolov3中的FPN结构
FPN就是这样,自顶向下的模式,将高层特征传下来。而底层特征却无法影响高层特征,并且FPN中的这种方法中,顶部信息流往下传,是逐层地传,计算量比较大。PAN解决了这一问题。先看下YOLOV3中未加PAN的结构图:
可以看到经过几次下采样,三个紫色箭头指向的地方,输出分别是76*76, 38*38,19*19.以及最后prediction中用于预测的三个特征图①19*19*255、②38*38*255、③76*76*255。[注:255表示80类别(1+4+80)×3=255] ,我们将Neck部分用立体图画出来(右),更直观的看下两部分之间是如何通过FPN结构融合的。
4.2 PANet
PANet引入了自底向上的路径,使得底层信息更容易传递到高层顶部。左边图(a),V3中讲过,特征融合,featureMap越小,感受野越大;可能要走例如100层,才能遍历玩ResNet。现在加入要将底层特征传递到特征图顶部,如图a红线,如果是采用上述FPN算法,可能要走很多层,增加了很多计算量。而PANet采用的是:在进行自顶向下的特征融合后(实际上,PANet无论是bottom-top,还是top-bottom进行特征信息传递,都是在“从原始网络中提取低、中、高层特征图之后”,进行的),然后再进行自下向顶(这里遍历的最终的特征图P2-5,而不是原始100多层特征图)的特征融合,如图(b)绿色线条,这样特征传递需要“穿越”的特征图数量大大减少,几层就到了。
而Yolov4中Neck这部分除了使用FPN外,还在此基础上使用了PAN结构:
前面CSPDarknet53中讲到,每个CSP模块前面的卷积核都是3*3大小,步长为2,相当于下采样操作。因此可以看到三个紫色箭头处的特征图是76*76、38*38、19*19。以及最后Prediction中用于预测的三个特征图:①76*76*255,②38*38*255,③19*19*255。我们也看下Neck部分的立体图像,看下两部分是如何通过FPN+PAN结构进行融合的。
我们都知道,深层的feature map携带有更强的语义特征,较弱的定位信息。而浅层的feature map携带有较强的位置信息,和较弱的语义特征。FPN就是把深层的抑郁特征传到前蹭,从而增强多个尺度上的语义表达。而PAN则相反把前蹭的定位信息传导到深层,增强多个尺度上的定位能力。
PANet 结构是在FPN的基础上引入了 Bottom-up path augmentation 结构。FPN主要是通过融合高低层特征提升目标检测的效果,尤其可以提高小尺寸目标的检测效果。Bottom-up path augmentation结构可以充分利用网络浅特征进行分割,网络浅层特征信息对于目标检测非常重要,因为目标检测是像素级别的分类浅层特征多是边缘形状等特征。PANet 在 FPN 的基础上加了一个自底向上方向的增强,使得顶层 feature map 也可以享受到底层带来的丰富的位置信息,从而提升了大物体的检测效果。
这样结合操作,FPN层自顶向下传达强语义特征,而特征金字塔则自底向上传达强定位特征,两两联手,从不同的主干层对不同的检测层进行参数聚合,这样的操作确实很皮。FPN+PAN借鉴的是18年CVPR的PANet,当时主要应用于图像分割领域,但Alexey将其拆分应用到Yolov4中,进一步提高特征提取的能力。
五,损失函数(https://zhuanlan.zhihu.com/p/335345293)
YOLOv5损失函数包括:
- classification loss, 分类损失
- localization loss, 定位损失(预测框与GT框之间的误差)
- confidence loss, 置信度损失(框的目标性;objectness of the box)
总的损失函数:classification loss + localization loss + confidence loss (组成部分从yolov1~yolov5都是这三类,但具体损失的计算方法有变)
- YOLOv5使用二元交叉熵损失函数计算类别概率和目标置信度得分的损失。(大多数分类器假设输出标签是互斥的。如果输出是互斥的目标类别,则确实如此。因此YOLO应用softmax函数将得分转换为总和为1的概率。而YOLOv3/v4/v5使用多标签分类。例如,输出标签可以是“行人”和“儿童”,他们不是排他性的。所以输出得分的总和可以大于1)YOLOv3/v4/v5使用多个独立的逻辑(logistic)分类器替换softmax函数,以计算输入属于特定标签的可能性。在计算分类损失进行训练时,YOLOV3/V4/V5对每个标签使用儿园交叉熵损失。这也避免使用softmax函数而降低了计算复杂度。
- YOLOv5使用CIOU Loss作为bounding box回归的损失。边界框回归是许多2D/3D计算机视觉任务中最基本的组件之一,一个改进机会是根据IOU计算的度量损失取代回归损失(例如$l_1$和$l_2-norms$),但是IOU直接用作性能度量和损失函数有两个问题:1)如果两个物体不重叠,则IOU值将为零,并且不会反应两个形状彼此之间的距离。2)在物体不重叠的情况下,如果将IOU用作损失,则其梯度将为零并且无法进行优化。想法:推广到IOU到非重叠清醒,并且确保:a)遵循与IoU相同的定义,即将比较对象的形状属性编码为区域(region)属性;b)维持IoU的尺寸不变性;c) 在重叠对象的情况下确保与IoU的强相关性。
YOLOv5 采用了BECLogits 损失函数计算objectness score的损失,class probability score采用了交叉熵损失函数(BCEcls loss),bounding box采用了GIOU(DIOU, CIOU) Loss。GIoU Loss 用来计算bounding box的 Loss, GIOU 是在CVPR2019中,论文https://arxiv.org/pdf/1902.09630.pdf中提出。GIOU直接把IoU设为回归的 Loss。
$GIoU = IoU-frac{|C(AUB)|}{|C|}$
上面公式的意思是将两个任意框A,B,我们找到一个最小的封闭形状C,让C可以把A,B包含在内,接着计算C种没有覆盖A和B的面积占C总面积的比值,然后用A与B的IoU减去这个比值。与IoU类似,GIoU也可以作为一个距离,loss可以用
$L_GIoU = 1-GIoU$
六,YOLOV5的训练技巧
最后引入B站上大神的yolov5训练技巧:
6.1 训练预热 Warmup
刚开始训练时,模型的权重是随机初始化的,此时若选择一个较大的学习率,可能带来模型的不稳定(振荡)。选择Warmup预热学习率的方式可以使得开始训练的几个epoches或者一些steps内学习率较小,在预热的小学习率下,模型可以慢慢趋于稳定,等模型相对稳定后再选择预先设置的学习率进行训练,使得模型收敛速度变得更快,模型效果更佳。
余弦退火调整学习率CosineAnnealingLR:https://arxiv.org/pdf/1608.03983.pdf
6.2 自动计算锚框 Autoanchor
Anchor给出了目标宽高的初始值,需要回归的是目标真实宽高与初始宽高的偏移量,而不适用anchor的做法需要回归宽高的绝对量。Autoanchor only runs when the best possible recall(BPR, 最大可能召回率) is under threshold. yolov5的threshold是98%。
自动计算锚框是采用聚类方法,You can disable autoanchor with python train.py -- noautoanchor. AutoAnchor will attach anchors automatically to your model.pt file (i.e. last.pt or best.pt)
6.3 超参数进化
Hyperparameter evolution is a method of Hyperparameter Optimization using a Genetic Algorithm(GA) for optimization. 好吧,还是看官网吧(https://github.com/ultralytics/yolov5/wiki/Tips-for-Best-Training-Results)
6.4 断点续训
You use --resume by itself with no arguments, or by pointing to a last.pt to resume from:
python train.py --resume # resume from most recent last.pt python train.py --resume runs/exp0/weights/last.pt # resume from specific weights
6.5 多GPU训练
- DataParallel
- Distriubted DataParallel
更多内容请看:https://github.com/ultralytics/yolov5/issues/607, https://github.com/ultralytics/yolov5/issues/475