• YOLOv5全面解析教程①:网络结构逐行代码解读


    e22910166c884ab92afe47ab1cb3656a.jpeg

    撰文 | Fengwen, BBuf

    本教程涉及的代码在:

    https://github.com/Oneflow-Inc/one-yolov5

    教程也同样适用于 Ultralytics/YOLOv5,因为 One-YOLOv5 仅仅是换了一个运行时后端而已,计算逻辑和代码相比 Ultralytics/YOLOv5 没有做任何改变,欢迎 star 。详细信息请看:一个更快的YOLOv5问世,附送全面中文解析教程

     1

    引言

    YOLOv5针对不同大小(n, s, m, l, x)的网络整体架构都是一样的,只不过会在每个子模块中采用不同的深度和宽度,分别应对yaml文件中的depth_multiple和width_multiple参数。

    还需要注意一点,官方除了n, s, m, l, x版本外还有n6, s6, m6, l6, x6,区别在于后者是针对更大分辨率的图片比如1280x1280, 当然结构上也有些差异,前者只会下采样到32倍且采用3个预测特征层 , 而后者会下采样64倍,采用4个预测特征层。

    本章将以YOLOv5s为例,

    从配置文件models/yolov5s.yaml

    (https://github.com/Oneflow-Inc/one-yolov5/blob/main/models/yolov5s.yaml)到models/yolo.py (https://github.com/Oneflow-Inc/one-yolov5/blob/main/models/yolo.py)

    源码进行解读。

     2

    yolov5s.yaml文件内容

    1. nc: 80  # number of classes 数据集中的类别数
    2. depth_multiple: 0.33  # model depth multiple  模型层数因子(用来调整网络的深度)
    3. width_multiple: 0.50  # layer channel multiple 模型通道数因子(用来调整网络的宽度)
    4. # 如何理解这个depth_multiple和width_multiple呢?它决定的是整个模型中的深度(层数)和宽度(通道数),具体怎么调整的结合后面的backbone代码解释。
    5. anchors: # 表示作用于当前特征图的Anchor大小为 xxx
    6. # 9个anchor,其中P表示特征图的层级,P3/8该层特征图缩放为1/8,是第3层特征
    7.   - [10,1316,3033,23]  # P3/8, 表示[10,13],[16,30], [33,23]3个anchor
    8.   - [30,6162,4559,119]  # P4/16
    9.   - [116,90156,198373,326]  # P5/32
    10. # YOLOv5s v6.0 backbone
    11. backbone:
    12.   # [from, number, module, args]
    13.   [[-11, Conv, [64622]],  # 0-P1/2
    14.    [-11, Conv, [12832]],  # 1-P2/4
    15.    [-13, C3, [128]],
    16.    [-11, Conv, [25632]],  # 3-P3/8
    17.    [-16, C3, [256]],
    18.    [-11, Conv, [51232]],  # 5-P4/16
    19.    [-19, C3, [512]],
    20.    [-11, Conv, [102432]],  # 7-P5/32
    21.    [-13, C3, [1024]],
    22.    [-11, SPPF, [10245]],  # 9
    23.   ]
    24. # YOLOv5s v6.0 head
    25. head:
    26.   [[-11, Conv, [51211]],
    27.    [-11, nn.Upsample, [None, 2'nearest']],
    28.    [[-16], 1, Concat, [1]],  # cat backbone P4
    29.    [-13, C3, [512, False]],  # 13
    30.    [-11, Conv, [25611]],
    31.    [-11, nn.Upsample, [None, 2'nearest']],
    32.    [[-14], 1, Concat, [1]],  # cat backbone P3
    33.    [-13, C3, [256, False]],  # 17 (P3/8-small)
    34.    [-11, Conv, [25632]],
    35.    [[-114], 1, Concat, [1]],  # cat head P4
    36.    [-13, C3, [512, False]],  # 20 (P4/16-medium)
    37.    [-11, Conv, [51232]],
    38.    [[-110], 1, Concat, [1]],  # cat head P5
    39.    [-13, C3, [1024, False]],  # 23 (P5/32-large)
    40.    [[172023], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
    41.   ]

    3

    anchors 解读

    YOLOv5 初始化了 9 个 anchors,分别在三个特征图 (feature map)中使用,每个 feature map 的每个 grid cell 都有三个 anchor 进行预测。分配规则:

    • 尺度越大的 feature map 越靠前,相对原图的下采样率越小,感受野越小, 所以相对可以预测一些尺度比较小的物体(小目标),分配到的 anchors 越小。

    • 尺度越小的 feature map 越靠后,相对原图的下采样率越大,感受野越大, 所以可以预测一些尺度比较大的物体(大目标),所以分配到的 anchors 越大。

    • 即在小特征图(feature map)上检测大目标,中等大小的特征图上检测中等目标, 在大特征图上检测小目标。

    4

    backbone & head 解读

    [from, number, module, args] 参数

    四个参数的意义分别是:

    1. 第一个参数 from :从哪一层获得输入,-1表示从上一层获得,[-1, 6]表示从上层和第6层两层获得。

    2. 第二个参数 number:表示有几个相同的模块,如果为9则表示有9个相同的模块。

    3. 第三个参数 module:模块的名称,这些模块写在common.py中。

    4. 第四个参数 args:类的初始化参数,用于解析作为 moudle 的传入参数。

    下面以第一个模块Conv 为例介绍下common.py中的模块

    Conv 模块定义如下:

    1. class Conv(nn.Module):
    2.     # Standard convolution
    3.     def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
    4.         """
    5.         @Pargm c1: 输入通道数
    6.         @Pargm c2: 输出通道数
    7.         @Pargm k : 卷积核大小(kernel_size)
    8.         @Pargm s : 卷积步长 (stride)
    9.         @Pargm p : 特征图填充宽度 (padding)
    10.         @Pargm g : 控制分组,必须整除输入的通道数(保证输入的通道能被正确分组)
    11.         """
    12.         super().__init__()
    13.         # https://oneflow.readthedocs.io/en/master/generated/oneflow.nn.Conv2d.html?highlight=Conv
    14.         self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
    15.         self.bn = nn.BatchNorm2d(c2)
    16.         self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Moduleelse nn.Identity())
    17.     def forward(self, x):
    18.         return self.act(self.bn(self.conv(x)))
    19.     def forward_fuse(self, x):
    20.         return self.act(self.conv(x))

    比如上面把width_multiple设置为了0.5,那么第一个 [64, 6, 2, 2] 就会被解析为 [3,64*0.5=32,6,2,2],其中第一个 3 为输入channel(因为输入),32 为输出channel。

    关于调整网络大小的详解说明

    在yolo.py (https://github.com/Oneflow-Inc/one-yolov5/blob/main/models/yolo.py)的256行 有对yaml 文件的nc,depth_multiple等参数读取,具体代码如下:

    anchors, nc, gd, gw = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple']

    "width_multiple"参数的作用前面介绍args参数中已经介绍过了,那么"depth_multiple"又是什么作用呢?

    在yolo.py (https://github.com/Oneflow-Inc/one-yolov5/blob/main/models/yolo.py) 的257行有对参数的具体定义:

    n = n_ = max(round(n * gd), 1) if n > 1 else n  # depth gain 暂且将这段代码当作公式(1)

    其中 gd 就是depth_multiple的值,n的值就是backbone中列表的第二个参数:

    根据公式(1)很容易看出 gd 影响 n 的大小,从而影响网络的结构大小。

    后面各层之间的模块数量、卷积核大小和数量等也都产生了变化,YOLOv5l 与 YOLOv5s 相比较起来训练参数的大小成倍数增长,

    其模型的深度和宽度也会大很多,这就使得 YOLOv5l 的精度值要比 YOLOv5s 好很多,因此在最终推理时的检测精度高,但是模型的推理速度更慢。

  • 相关阅读:
    Golang反射学习
    微信小程序项目搭建医生挂号健康管理网站+后台管理系统|前后分离VUE.js
    大数据认知
    Visual Studio 2022安装教程及创建窗体应用程序介绍
    Windows cmd窗口常用命令
    备份系统规划不得不考虑的几个关键性问题,究竟该怎么解决?
    【Rust】使用HashMap解决官方文档中的闭包限制
    Leecode刷题 412. Fizz Buzz——二级指针、字符串数组、malloc
    实时人脸五观检测:基于libfacedetection(CNN模型)
    一文讲透 Redis 事务 (事务模式 VS Lua 脚本)
  • 原文地址:https://blog.csdn.net/OneFlow_Official/article/details/128310247