YOLOv5网络详解

官方源码仓库:https://github.com/ultralytics/yolov5
文章下载地址:无

0 前言

在前面我们已经介绍过了YOLOv1~v4的网络的结构,今天接着上次的YOLOv4再来聊聊YOLOv5,如果还不了解YOLOv4的可以参考之前的博文。YOLOv5项目的作者是Glenn Jocher并不是原Darknet项目的作者Joseph Redmon。并且这个项目至今都没有发表过正式的论文。之前翻阅该项目的issue时,发现有很多人问过这个问题,有兴趣的可以翻翻这个issue#1333。作者当时也有说准备在2021年的12月1号之前发表,并承诺如果到时候没有发表就吃掉自己的帽子。
YOLOv5网络详解

(⊙o⊙)…,emmm,但这都2022年了,也不知道他的帽子是啥味儿。过了他承诺的发表期限后,很多人还去该issue下表示”关怀”,问啥时候吃帽子,下面这位大哥给我整笑了。

YOLOv5网络详解
本来Glenn Jocher是准备要发表论文的,但至于为啥没发成作者并没有给出原因。我个人的猜测是自从YOLOv4发表后,有很多人想发这方面的文章,然后在YOLOv4上进行改动,改动过程中肯定有人把YOLOv5仓库里的一些技术拿去用了(YOLOv4论文4月出的,YOLOv5仓库5月就有了)。大家改完后发了一堆文章,那么YOLOv5的技术就被零零散散的发表到各个文章中去了。Glenn Jocher一看,这也太卷了吧,你们都把我技术写了,那我还写个锤子,直接撂挑子不干了。

当然以上都是我个人yy哈,回归正题,YOLOv5仓库是在2020-05-18创建的,到今天已经迭代了很多个大版本了,现在(2022-3-19)已经迭代到v6.1了。所以本篇博文讲的内容是针对v6.1的,大家阅读的时候注意看下版本号,不同的版本内容会略有不同。前几天我在YOLOv5项目中向作者提了一个issue#6998,主要是根据当前的源码做了一个简单的总结,然后想让作者帮忙看看总结的内容是否有误,根据作者的反馈应该是没啥问题的,所以今天就来谈谈我个人的见解。下表是当前(v6.1)官网贴出的关于不同大小模型以及输入尺度对应的mAP、推理速度、参数数量以及理论计算量FLOPs

Modelsize
(pixels)
mAPval
0.5:0.95
mAPval
0.5
Speed
CPU b1
(ms)
Speed
V100 b1
(ms)
Speed
V100 b32
(ms)
params
(M)
FLOPs
@640 (B)
YOLOv5n64028.045.7456.30.61.94.5
YOLOv5s64037.456.8986.40.97.216.5
YOLOv5m64045.464.12248.21.721.249.0
YOLOv5l64049.067.343010.12.746.5109.1
YOLOv5x64050.768.976612.14.886.7205.7
YOLOv5n6128036.054.41538.12.13.24.6
YOLOv5s6128044.863.73858.23.612.616.8
YOLOv5m6128051.369.388711.16.835.750.0
YOLOv5l6128053.771.3178415.810.576.8111.4
YOLOv5x6
+ TTA
1280
1536
55.0
55.8
72.7
72.7
3136
26.2
19.4
140.7
209.8

1 网络结构

关于YOLOv5的网络结构其实网上相关的讲解已经有很多了。网络结构主要由以下几部分组成:

  • Backbone : New CSP-Darknet53
  • Neck : SPPF , New CSP-PAN
  • Head : YOLOv3 Head

下面是我根据yolov5l.yaml绘制的网络整体结构,YOLOv5针对不同大小(n,s,m,l,x)的网络整体架构都是一样的,只不过会在每个子模块中采用不同的深度和宽度,分别应对yaml文件中的depth_multiplewidth_multiple参数。还需要注意一点,官方除了n,s,m,l,x版本外还有n6,s6,m6,l6,x6,区别在于后者是针对更大分辨率的图片比如1280x1280,当然结构上也有些差异,后者会下采样64倍,采用4个预测特征层,而前者只会下采样到32倍且采用3个预测特征层。本博文只讨论前者。下面这幅图(yolov5l)有点大,大家可以下载下来仔细看一下。

YOLOv5网络详解
通过和上篇博文讲的YOLOv4对比,其实YOLOv5在Backbone部分没太大变化。但是YOLOv5在v6.0版本后相比之前版本有一个很小的改动,把网络的第一层(原来是Focus模块)换成了一个6x6大小的卷积层。两者在理论上其实等价的,但是对于现有的一些GPU设备(以及相应的优化算法)使用6x6大小的卷积层比使用Focus模块更加高效。详情可以参考这个issue#4825。下图是原来的Focus模块(和之前Swin Transformer中的Patch Merging类似),将每个2x2的相邻像素划分为一个patch,然后将每个patch中相同位置(同一颜色)像素给拼在一起就得到了4个feature map,然后在接上一个3x3大小的卷积层。这和直接使用一个6x6大小的卷积层等效。

YOLOv5网络详解

在Neck部分的变化还是相对较大的,首先是将SPP换成成了SPPFGlenn Jocher自己设计的),这个改动我个人觉得还是很有意思的,两者的作用是一样的,但后者效率更高。SPP结构如下图所示,是将输入并行通过多个不同大小的MaxPool,然后做进一步融合,能在一定程度上解决目标多尺度问题。

YOLOv5网络详解
SPPF结构是通过多个5x5大小的MaxPool层串行传递输入。这里需要注意的是,串联的两个5x5大小的MaxPool层与一个9x9大小的MaxPool层是一样的。 MaxPool 层的大小与 13x13 大小的 MaxPool 层相同。

YOLOv5网络详解

让我们做一个简单的实验来比较SPPSPPF的计算结果和速度。代码如下(注意这里去掉SPPF开头和结尾的1x1卷积层,只比较包含MaxPool的部分):

import time
import torch
import torch.nn as nn


class SPP(nn.Module):
    def __init__(self):
        super().__init__()
        self.maxpool1 = nn.MaxPool2d(5, 1, padding=2)
        self.maxpool2 = nn.MaxPool2d(9, 1, padding=4)
        self.maxpool3 = nn.MaxPool2d(13, 1, padding=6)

    def forward(self, x):
        o1 = self.maxpool1(x)
        o2 = self.maxpool2(x)
        o3 = self.maxpool3(x)
        return torch.cat([x, o1, o2, o3], dim=1)


class SPPF(nn.Module):
    def __init__(self):
        super().__init__()
        self.maxpool = nn.MaxPool2d(5, 1, padding=2)

    def forward(self, x):
        o1 = self.maxpool(x)
        o2 = self.maxpool(o1)
        o3 = self.maxpool(o2)
        return torch.cat([x, o1, o2, o3], dim=1)


def main():
    input_tensor = torch.rand(8, 32, 16, 16)
    spp = SPP()
    sppf = SPPF()
    output1 = spp(input_tensor)
    output2 = sppf(input_tensor)

    print(torch.equal(output1, output2))

    t_start = time.time()
    for _ in range(100):
        spp(input_tensor)
    print(f"spp time: {time.time() - t_start}")

    t_start = time.time()
    for _ in range(100):
        sppf(input_tensor)
    print(f"sppf time: {time.time() - t_start}")


if __name__ == '__main__':
    main()

终端输出:

True
spp time: 0.5373051166534424
sppf time: 0.20780706405639648

通过对比可以发现,两者的计算结果完全一样,但是SPPF的计算速度比SPP的快两倍以上,幸福感翻倍。

在Neck部分另外一个不同点就是New CSP-PAN了,在YOLOv4中,Neck的PAN结构是没有引入CSP结构的,但在YOLOv5中作者在PAN结构中加入了CSP。详情见上面的网络结构图,每个C3模块里都含有CSP结构。在Head部分,YOLOv3, v4, v5都是一样的,这里就不讲了。

2 数据增强

在YOLOv5代码里,关于数据增强策略还是挺多的,这里简单罗列部分方法:

  • Mosaic,将四张图片拼成一张图片,讲过很多次了
    YOLOv5网络详解
  • Copy paste,将部分目标随机的粘贴到图片中,前提是数据要有 segments 数据才行,即每个目标的实例分割信息。下面是 Copy paste 原论文中的示意图。
    YOLOv5网络详解
  • Random affine(Rotation, Scale, Translation and Shear),随机进行仿射变换,但根据配置文件里的超参数发现只使用了 Scale 和 Translation 即缩放和平移。
    YOLOv5网络详解
  • MixUp,就是将两张图片按照一定的透明度融合在一起,具体有没有用不太清楚,毕竟没有论文,也没有消融实验。代码中只有较大的模型才使用到了 MixUp ,而且每次只有10%的概率会使用到。
    YOLOv5网络详解
  • Albumentations,主要是做些滤波、直方图均衡化以及改变图片质量等等,我看代码里写的只有安装了 albumentations 包才会启用,但在项目的 requirements.txt 文件中 albumentations 包是被注释掉了的,所以默认不启用。
  • Augment HSV(Hue, Saturation, Value),随机调整色度,饱和度以及明度。
    YOLOv5网络详解
  • Random horizontal flip,随机水平翻转
    YOLOv5网络详解

3 训练策略

在YOLOv5源码中使用到了很多训练的策略,这里简单总结几个我注意到的点,还有些没注意到的请大家自己看下源码:

  • Multi-scale training(0.5~1.5x),多尺度训练,假设设置输入图片的大小为640%20%5Ctimes%20640,训练时采用尺寸是在0.5%20%5Ctimes%20640%20%5Csim%201.5%20%5Ctimes%20640之间随机取值,注意取值时取得都是32的整数倍(因为网络会最大下采样32倍)。
  • AutoAnchor(For training custom data),训练自己数据集时可以根据自己数据集里的目标进行重新聚类生成Anchors模板。
  • Warmup and Cosine LR scheduler,训练前先进行 Warmup 热身,然后在采用 Cosine 学习率下降策略。
  • EMA(Exponential Moving Average),可以理解为给训练的参数加了一个动量,让它更新过程更加平滑。
  • Mixed precision,混合精度训练,能够减少显存的占用并且加快训练速度,前提是GPU硬件支持。
  • Evolve hyper-parameters,超参数优化,没有炼丹经验的人勿碰,保持默认就好。

4 其他

4.1 损失计算

YOLOv5的损失主要由三个部分组成:

  • Classes loss,分类损失,采用的是 BCE loss ,注意只计算正样本的分类损失。
  • Objectness loss, obj 损失,采用的依然是 BCE loss ,注意这里的 obj 指的是网络预测的目标边界框与GT Box的 CIoU 。这里计算的是所有样本的 obj 损失。
  • Location loss,定位损失,采用的是 CIoU loss ,注意只计算正样本的定位损失。

Loss%3D%5Clambda_1%20L_%7Bcls%7D%20%2B%20%5Clambda_2%20L_%7Bobj%7D%20%2B%20%5Clambda_3%20L_%7Bloc%7D
其中,%5Clambda_1%2C%20%5Clambda_2%2C%20%5Clambda_3为平衡系数。

4.2 平衡不同尺度的损失

这里是对三个预测特征层(P3, P4, P5)上的obj损失使用不同的权重。源码中,预测特征层(P3)用于预测小目标的权重为4.0,预测特征层(P4)用于预测中目标的权重为1.0,预测特征层(P5)用于预测中目标预测大目标使用权重为0.4,作者说这是COCO数据集的超参数集。
L_%7Bobj%7D%20%3D%204.0%20%5Ccdot%20L_%7Bobj%7D%5E%7Bsmall%7D%20%2B%201.0%20%5Ccdot%20L_%7Bobj%7D%5E%7Bmedium%7D%20%2B%200.4%20%5Ccdot%20L_%7Bobj%7D%5E%7Blarge%7D

4.3 消除Grid敏感度

在上篇文章YOLOv4中有提到过,主要是调整预测目标中心点相对Grid网格的左上角偏移量。下图是YOLOv2,v3的计算公式。

YOLOv5网络详解

在:

  • t_x是网络预测的目标中心x坐标偏移量(相对于网格的左上角)
  • t_y是网络预测的目标中心y坐标偏移量(相对于网格的左上角)
  • c_x为网格左上角对应的x坐标
  • c_y是网格左上角对应的y坐标
  • %5Csigma是 Sigmoid 激活函数,将预测的偏移量限制在0到1之间,即预测的中心点不会超出对应的 Grid Cell 区域

关于预测目标中心点相对Grid网格左上角%28c_x%2C%20c_y%29偏移量为%5Csigma%28t_x%29%2C%20%5Csigma%28t_y%29。YOLOv4的作者认为这样做不太合理,比如当真实目标中心点非常靠近网格的左上角点(%5Csigma%28t_x%29%5Csigma%28t_y%29应该趋近与0)或者右下角点(%5Csigma%28t_x%29%5Csigma%28t_y%29应该趋近与1)时,网络的预测值需要负无穷或者正无穷时才能取到,而这种很极端的值网络一般无法达到。为了解决这个问题,作者对偏移量进行了缩放从原来的%280%2C%201%29缩放到%28-0.5%2C%201.5%29这样网络预测的偏移量就能很方便达到0或1,故最终预测的目标中心点b_x%2C%20b_y的计算公式为:
b_x%20%3D%20%282%20%5Ccdot%20%5Csigma%28t_x%29%20-%200.5%29%20%2B%20c_x%20%5C%5C%20b_y%20%3D%20%282%20%5Ccdot%20%5Csigma%28t_y%29%20-%200.5%29%20%2B%20c_y
下图是我绘制的y%20%3D%20%5Csigma%28x%29对应before曲线和y%20%3D%202%20%5Ccdot%20%5Csigma%28x%29%20-%200.5对应after曲线,很明显通过引入缩放系数scale以后,yx更敏感了,且偏移的范围由原来的%280%2C%201%29调整到了%28-0.5%2C%201.5%29
YOLOv5网络详解
在YOLOv5中除了调整预测Anchor相对Grid网格左上角%28c_x%2C%20c_y%29偏移量以外,还调整了预测目标高宽的计算公式,之前是:
b_w%20%3D%20p_w%20%5Ccdot%20e%5E%7Bt_w%7D%20%5C%5C%20b_h%20%3D%20p_h%20%5Ccdot%20e%5E%7Bt_h%7D
在YOLOv5调整为:
b_w%20%3D%20p_w%20%5Ccdot%20%282%20%5Ccdot%20%5Csigma%28t_w%29%29%5E2%20%5C%5C%20b_h%20%3D%20p_h%20%5Ccdot%20%282%20%5Ccdot%20%5Csigma%28t_h%29%29%5E2
作者Glenn Jocher的原话如下,也可以参考issue#471:

The original yolo/darknet box equations have a serious flaw. Width and Height are completely unbounded as they are simply out=exp(in), which is dangerous, as it can lead to runaway gradients, instabilities, NaN losses and ultimately a complete loss of training.

作者的大致意思是,原来的计算公式并没有对预测目标宽高做限制,这样可能出现梯度爆炸,训练不稳定等问题。下图是修改前y%20%3D%20e%5Ex和修改后y%20%3D%20%282%20%5Ccdot%20%5Csigma%28x%29%29%5E2(相对Anchor宽高的倍率因子)的变化曲线, 很明显调整后倍率因子被限制在%280%2C%204%29之间。
YOLOv5网络详解

4.4 匹配正样本(Build Targets)

之前在YOLOv4介绍中有讲过该部分内容,其实YOLOv5也差不多。主要的区别在于GT BoxAnchor Templates模板的匹配方式。在YOLOv4中是直接将每个GT Box与对应的Anchor Templates模板计算IoU,只要IoU大于设定的阈值就算匹配成功。但在YOLOv5中,作者先去计算每个GT Box与对应的Anchor Templates模板的高宽比例,即:
r_w%20%3D%20w_%7Bgt%7D%20/%20w_%7Bat%7D%20%5C%5C%20r_h%20%3D%20h_%7Bgt%7D%20/%20h_%7Bat%7D%20%5C%5C
然后统计这些比例和它们倒数之间的最大值,这里可以理解成计算GT BoxAnchor Templates分别在宽度以及高度方向的最大差异(当相等的时候比例为1,差异最小):
r_w%5E%7Bmax%7D%20%3D%20max%28r_w%2C%201%20/%20r_w%29%20%5C%5C%20r_h%5E%7Bmax%7D%20%3D%20max%28r_h%2C%201%20/%20r_h%29
然后算出r_w%5E%7Bmax%7Dr_h%5E%7Bmax%7D之间的最大值,即宽高方向差异最大的值:
r%5E%7Bmax%7D%20%3D%20max%28r_w%5E%7Bmax%7D%2C%20r_h%5E%7Bmax%7D%29
如果GT Boxr%5E%7Bmax%7D与对应的Anchor Template小于阈值anchor_t(源码中默认设置为4.0),即GT Box的高宽比与对应的Anchor Template相差不大,则赋值为GT BoxAnchor Template模板。为了方便大家理解,可以看我画的图。假设对于某个GT Box,其实只要GT Box满足%5Ctimes%200.25%5Ctimes%204.0乘以某个Anchor Template的宽和高,就匹配成功。

YOLOv5网络详解
剩下的步骤和YOLOv4中一致:

  • 将 GT 投影到对应预测特征层上,根据 GT 的中心点定位到对应 Cell ,注意图中有三个对应的 Cell 。因为网络预测中心点的偏移范围已经调整到了%28-0.5%2C%201.5%29,所以按理说只要 Grid Cell 左上角点距离 GT 中心点在%28%E2%88%920.5%2C1.5%29范围内它们对应的 Anchor 都能回归到 GT 的位置处。这样会让正样本的数量得到大量的扩充。
  • 则这三个 Cell 对应的 AT2 和 AT3 都为正样本。

YOLOv5网络详解
还需要注意的是,YOLOv5源码中扩展Cell时只会往上、下、左、右四个方向扩展,不会往左上、右上、左下、右下方向扩展。下面又给出了一些根据GT_x%5E%7Bcenter%7D%2C%20GT_y%5E%7Bcenter%7D的位置扩展的一些Cell案例,其中%1表示取余并保留小数部分。

YOLOv5网络详解
到此,YOLOv5相关的内容基本上都分析完了。当然由于个人原因,肯定还有一些细节被我忽略掉了,也建议大家自己看看源码,收获肯定会更多。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
乘风的头像乘风管理团队
上一篇 2022年3月21日 下午4:41
下一篇 2022年3月21日 下午5:29

相关推荐