YOLOv5改进实战(导读篇) | YOLOv5 7.0 网络结构超详细解读


在这里插入图片描述


🚀 前言
YOLOv5 🚀 是一系列在 COCO 数据集上预训练的对象检测架构和模型,结合了在数千小时的研究和开发中获得的经验教训和最佳实践。本章节主要以yolov5s为例介绍YOLOv5-v7.0版本的网络结构及初始化超参数。

一、YOLOv5s网络结构图

网络结构主要分为以下方面:

  1. 输入端:自适应锚框计算、自适应图片缩放、Mosaic数据增强
  2. Backbone:CBS模块,C3模块,SPPF模块
  3. Neck:FPN+PAN结构
  4. Head: CIOU Loss
    在这里插入图片描述

特别注意:这是YOLOv5s的网络结构(因是自己画的图,难免会有疏漏,如有错误,望各位大佬在评论区指正!),想必很多小伙伴会有这样的疑惑,为什么我看到别人7.0版本的网络结构最后进入Head的是 80 ∗ 80 ∗ 256 80*80*256 8080256 40 ∗ 40 ∗ 512 40*40*512 4040512 20 ∗ 20 ∗ 1024 20*20*1024 20201024,跟上面写的 80 ∗ 80 ∗ 128 80*80*128 8080128 40 ∗ 40 ∗ 256 40*40*256 4040256 20 ∗ 20 ∗ 512 20*20*512 2020512不一致,这个问题主要跟yolov5s.yaml中的width_multiple参数有关,本章后续会详细讲解。

二、输入端

(1) 自适应锚框计算

  • 在YOLOv5中,每次训练开始之前,它都会根据你的数据集来自适应计算anchor锚框
  • 若觉得计算的锚框效果不佳,可以将--noautoanchor参数设置True default值即可关闭
    parser.add_argument('--noautoanchor', default=True, action='store_true', help=' 不自动调整anchor,默认为False')
    

1.1 计算过程

  1. 读取训练集中所有图片的w、h以及检测框的w、h
  2. 将读取的坐标修正为绝对坐标
  3. 使用Kmeans算法对训练集中所有的检测框进行聚类,得到k个anchors
  4. 通过遗传算法对得到的anchors进行变异,如果变异后效果好将其保留,否则跳过
  5. 将最终得到的最优anchors按照面积返回

1.2 默认锚框

在 YOLOv5 的配置文件model/*.yaml 中已经预设了一些针对 COCO数据集在 640 × 640 640×640 640×640图像大小下锚定框的尺寸:

anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32
  • anchors参数共有三行,每行6个数值;且每一行代表应用不同的特征图;
    1. 第一行是在最大的特征图上的锚框, 80 x 80 80x80 80x80代表浅层的特征图(P3),包含较多的低层级信息,适合用于检测小目标,所以这一特征图所用的anchor尺度较小;
    2. 第二行是在中间的特征图上的锚框, 40 x 40 40x40 40x40特征图上就用介于这两个尺度之间的anchor用来检测中等大小的目标;
    3. 第三行是在最小的特征图上的锚框, 20 x 20 20x20 20x20代表深层的特征图,包含更多高层级的信息,如轮廓、结构等信息,适合用于大目标的检测,所以这一特征图所用的anchor尺度较大。

1.3 自定义锚框

锚框核查函数 /utils/autoanchor.py 文件中:

@TryExcept(f'{PREFIX}ERROR')
def check_anchors(dataset, model, thr=4.0, imgsz=640):
    # Check anchor fit to data, recompute if necessary
    m = model.module.model[-1] if hasattr(model, 'module') else model.model[-1]  # Detect()
    shapes = imgsz * dataset.shapes / dataset.shapes.max(1, keepdims=True)
    scale = np.random.uniform(0.9, 1.1, size=(shapes.shape[0], 1))  # augment scale
    wh = torch.tensor(np.concatenate([l[:, 3:5] * s for s, l in zip(shapes * scale, dataset.labels)])).float()  # wh

    def metric(k):  # compute metric
        r = wh[:, None] / k[None]
        x = torch.min(r, 1 / r).min(2)[0]  # ratio metric
        best = x.max(1)[0]  # best_x
        aat = (x > 1 / thr).float().sum(1).mean()  # anchors above threshold
        bpr = (best > 1 / thr).float().mean()  # best possible recall
        return bpr, aat

    stride = m.stride.to(m.anchors.device).view(-1, 1, 1)  # model strides
    anchors = m.anchors.clone() * stride  # current anchors
    bpr, aat = metric(anchors.cpu().view(-1, 2))
    s = f'\n{PREFIX}{aat:.2f} anchors/target, {bpr:.3f} Best Possible Recall (BPR). '
    if bpr > 0.98:  # threshold to recompute
        LOGGER.info(f'{s}Current anchors are a good fit to dataset ✅')
    else:
        LOGGER.info(f'{s}Anchors are a poor fit to dataset ⚠️, attempting to improve...')
        na = m.anchors.numel() // 2  # number of anchors
        anchors = kmean_anchors(dataset, n=na, img_size=imgsz, thr=thr, gen=1000, verbose=False)
        new_bpr = metric(anchors)[0]
        if new_bpr > bpr:  # replace anchors
            anchors = torch.tensor(anchors, device=m.anchors.device).type_as(m.anchors)
            m.anchors[:] = anchors.clone().view_as(m.anchors)
            check_anchor_order(m)  # must be in pixel-space (not grid-space)
            m.anchors /= stride
            s = f'{PREFIX}Done ✅ (optional: update model *.yaml to use these anchors in the future)'
        else:
            s = f'{PREFIX}Done ⚠️ (original anchors better than new anchors, proceeding with original anchors)'
        LOGGER.info(s)
        

YOLOv5 在开始训练前会计算数据集标注信息针对默认锚定框的最佳召回率,如果最佳召回率大于或等于0.98,则不需要重新计算锚定框,使用默认锚框;如果最佳召回率小于0.98,则需要重新计算符合此数据集的锚框。

def metric(k):  # compute metric
    r = wh[:, None] / k[None]
    x = torch.min(r, 1 / r).min(2)[0]  # ratio metric
    best = x.max(1)[0]  # best_x
    aat = (x > 1 / thr).float().sum(1).mean()  # anchors above threshold
    bpr = (best > 1 / thr).float().mean()  # best possible recall
    return bpr, aat

stride = m.stride.to(m.anchors.device).view(-1, 1, 1)  # model strides
anchors = m.anchors.clone() * stride  # current anchors
bpr, aat = metric(anchors.cpu().view(-1, 2))

其中,bpr(best possible recall)参数就是判断是否需要重新计算锚定框的依据(是否小于 0.98)

重新计算符合此数据集标注框的锚定框,是利用 k均值聚类算法(k-means clustering)和遗传算法(genetic algorithm)实现的

1.4 手动计算锚框

import utils.autoanchor as autoAC
 
# 对数据集重新计算 anchors
new_anchors = autoAC.kmean_anchors('./data/mydata.yaml', 9, 640, 5.0, 1000, True)
print(new_anchors)

(2) Mosaic数据增强

最早出现于YOLOv4,YOLOv5也是延用了Mosaic数据增强。Mosaic数据增强的主要思想是将多张图片按一定比例组合成一张图片,实则是参考了CutMix数据增强方式,CutMix数据增强是将两张图片进行拼接,Mosaic数据增强则利用了四张图片,对四张图片进行拼接,每一张图片都有其对应的目标框,将四张图片拼接之后就获得一张新的图片,同时也获得这张图片对应的目标框,然后我们将这样一张新的图片传入到神经网络当中去学习,相当于一下子传入四张图片进行学习。
在这里插入图片描述
Mosaic数据增强的主要步骤:

  1. 随机选择四张不同的图像作为输入
  2. 分别对四张图片进行翻转(对原始图片进行左右的翻转)、缩放(对原始图片进行大小的缩放)、色域变化(对原始图片的明亮度、饱和度、色调进行改变)等操作。
  3. 操作完成之后然后再将原始图片按照 第一张图片摆放在左上,第二张图片摆放在左下,第三张图片摆放在右下,第四张图片摆放在右上四个方向位置摆好。
  4. 根据每张图片的尺寸变换方式,将映射关系对应到图片标签上。
  5. 依据指定的横纵坐标,对大图进行拼接。处理超过边界的检测框坐标。

Mosaic数据增强的优点:

  • 增加数据多样性,随机选取四张图像进行组合,组合得到图像个数比原图个数要多。
  • 增强模型鲁棒性,混合四张具有不同语义信息的图片,可以让模型检测超出常规语境的目标。
  • 加强批归一化层(Batch Normalization)的效果。当模型设置 BN 操作后,训练时会尽可能增大批样本总量(BatchSize),因为 BN 原理为计算每一个特征层的均值和方差,如果批样本总量越大,那么 BN 计算的均值和方差就越接近于整个数据集的均值和方差,效果越好。
  • Mosaic 数据增强算法有利于提升小目标检测性能。Mosaic 数据增强图像由四张原始图像拼接而成,这样每张图像会有更大概率包含小目标。

YOLOv5训练默认是开启了Mosaic数据增强的,可参考本章第六节超参数详解-(1)hpy超参数所以各位小伙伴可以放心训练!⭐

三、Backbone

Backbone骨干网络的主要作用就是提取特征,并不断缩小特征图Backbone中的主要结构有Conv模块、C3模块、SPPF模块。

(1)CBS模块

由一个Conv2d、一个BatchNorm2dSiLU激活函数构成。如下图所示:
在这里插入图片描述

  • Conv2dpadding自动计算的,通过修改stride来决定特征图缩小的倍数。
    	def autopad(k, p=None, d=1):  # kernel, padding, dilation
    	    # Pad to 'same' shape outputs
    	    if d > 1:
    	        k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k]  # actual kernel-size
    	    if p is None:
    	        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # auto-pad
    	    return p
    
  • BackboneCBS模块的stride均为2kernel均为3。因此CBS模块每次会将特征图的宽高减半下采样特征图,同时提取到目标特征。
  • BatchNorm2d为批归一化层,对每批的数据做归一化,其详细作用不在这里赘述。
  • SiLU激活函数,SiLU是Sigmoid和ReLU的改进版。SiLU具备无上界有下界、平滑、非单调的特性。SiLU在深层模型上的效果优于 ReLU。具有平滑性和非线性特性,有助于网络在训练过程中更快地收敛。
    σ ( x ) = x ∗ s i g m o i d ( x ) = x 1 + e − x \sigma \left ( x \right ) = x*sigmoid\left ( x \right ) =\frac{x}{1+e^{-x} } σ(x)=xsigmoid(x)=1+exx
    SiLU

(2)C3模块

C3由三个CBS模块和一个BottleNeck模块组成,得名C3。在Backbone中,C3是更为重要的提取特征的模块。其结构图如下:
在这里插入图片描述

  • 进入C3后,将会分为两路,左路经过CBS和一个Bottleneck,右路只经过一个CBS,最后将两路Concat,再经过一个CBSC3中的3个CBS模块均为 1 ∗ 1 1*1 11卷积,起到降维或升维的作用,对于提取特征意义不大。
  • BottleneckBackbone中使用的是残差连接,Bottleneck中有两个CBS,第一个CBS 1 ∗ 1 1*1 11卷积,将通道缩减为原来的一半,第二个为 3 ∗ 3 3*3 33卷积,将通道数翻倍先降维有利于卷积核更好的理解特征信息,升维将有利于提取到更多更详细的特征
    在这里插入图片描述
  • 在残差结构中,主分支和残差分支的特征图尺寸和维度是相同的。add操作是将主分支和残差分支的特征图进行直接相加,不会改变特征图的尺寸和维度,只是将对应位置的特征值进行相加。下述C3中带有False参数则表示不使用残差结构。通过残差结构,可以实现在深层网络中传递梯度和信息的快速传递,并有助于解决深层网络训练中的梯度消失问题。

(3)SPPF模块

SPP是空间金字塔池化,采用 1 × 1 1×1 1×1 5 × 5 5×5 5×5 9 × 9 9×9 9×9 13 × 13 13×13 13×13的最大池化的方式,进行多尺度融合YOLOv5 6.0版本开始使用了在SPP基础上改进的SPPF
在这里插入图片描述

  • SPP是将三个并行的MaxPool2d和输入Concat到一起,第一个MaxPool2d的kernel为 5 ∗ 5 5*5 55,第二个为 9 ∗ 9 9*9 99,第三个为 13 ∗ 13 13*13 1313。用三个不同大小的kernel,代表三个尺度。 5 ∗ 5 5*5 55的kernel可以理解为比较大的尺度,而 13 ∗ 13 13*13 1313就是比较小的尺度。这样就在图片的不同尺度下取到了最大的代表特征值,并Concat融合。
  • SPPF是将三个kernel为 5 ∗ 5 5*5 55MaxPool2d串行计算。第一个MaxPool2d表示较大的尺度,第二个MaxPool在第一个MaxPool2d的基础上进一步做池化,那么产生的尺度将会进一步缩小,第三个同理。

注意:图像的尺度并非指图像的大小,而是指图像的模糊程度(σ),例如,人近距离看一个物体和远距离看一个物体模糊程度是不一样的,从近距离到远距离图像越来越模糊的过程,也是图像的尺度越来越大的过程。

(4)Foucs结构(6.0版本开始已弃用)

在这里插入图片描述
Focus模块是对图片进行切片操作,具体操作是在一张图片中每隔一个像素拿到一个值,这样获得了四个独立的特征层,然后将四个独立的特征层进行堆叠,此时宽高信息就集中到了通道信息,输入通道扩充了四倍。拼接起来的特征层相对于原先的三通道变成了十二个通道,最后将得到的新图片再经过卷积操作,最终得到了没有信息丢失情况下的二倍下采样特征图。
在这里插入图片描述
YOLOv5 6.0开始将Focus模块替换成了一个 6 ∗ 6 6*6 66的卷积层。两者的计算量是等价的,但使用 6 ∗ 6 6*6 66的卷积会更加高效。

四、Neck

Neck的作用就是从Backbone中获取相对于较浅的特征,再与深层的语义特征Concat到一起。

(1)特征金字塔FPN+PAN

FPN 结构通过自顶向下进行上采样,使得底层特征图包含更强的图像强语义信息

  • 传入FPN结构中,通过Upsample上采样的方式,向特征图中插值,使特征图的尺寸变大,以便于融合来自Backbone的特征图,做特征的向上融合,特征图不断变大;
  • PAN 结构自底向上进行下采样,使顶层特征包含图像位置信息,两个特征最后进行融合,使不同尺寸的特征图都包含图像语义信息和图像特征信息,保证了对不同尺寸的图片的准确预测。

总结:FPN层自顶向下可以捕获强语义特征,而PAN则通过自底向上传达强定位特征

五、Head

Head层为Detect模块,Detect模块的网络结构很简单,仅由三个 1 ∗ 1 1*1 11卷积构成,对应三个检测特征层。
在这里插入图片描述

  • 上述经过FPN特征金字塔,我们可以获得 20 ∗ 20 ∗ 512 20*20*512 2020512 40 ∗ 40 ∗ 256 40*40*256 4040256 80 ∗ 80 ∗ 128 80*80*128 8080128三个加强特征,然后我们利用这三个shape的特征层传入Yolo Head获得预测结果。
  • 对于每一个特征层,我们可以获得利用一个 1 ∗ 1 1*1 11卷积调整通道数,最终的通道数和需要区分的种类个数相关,每一个特征层上每一个特征点存在3个先验框。
  • 如果使用的是COCO训练集,类则为 80 80 80种,最后的维度应该为 255 = 3 ∗ 85 255 = 3*85 255=385,三个特征层的shape为 20 ∗ 20 ∗ 255 20*20*255 2020255 40 ∗ 40 ∗ 255 40*40*255 4040255 80 ∗ 80 ∗ 255 80*80*255 8080255
    最后的255可以拆分成 3 3 3 85 85 85,对应 3 3 3个先验框的 85 85 85个参数,85可以拆分成 4 + 1 + 80 4+1+80 4+1+80
    这里的3是指每个位置先验框(锚框)的数量;
    前4个参数用于判断每一个特征点的回归参数,回归参数调整后可以获得预测框
    第5个参数用于判断每一个特征点是否包含物体
    最后80个参数用于判断每一个特征点所包含的物体种类

六、超参数详解

(1)hpy超参数

  • 文件位于data/hyps文件夹下
    # Hyperparameters for VOC finetuning  
    # ython train.py --batch 64 --weights yolov5m.pt --data voc.yaml --img 512 --epochs 50  
    lr0: 0.01  # 学习率, SGD=1E-2, Adam=1E-3
    lrf: 0.01    # 余弦退火超参数 
    momentum: 0.937  # 学习率动量
    weight_decay: 0.0005  # 权重衰减系数
    warmup_epochs: 3.0  # 预热学习epoch
    warmup_momentum: 0.8 # 预热学习率动量
    warmup_bias_lr: 0.1 # 预热学习率
    box: 0.05  # Bounding Box Regeression 损失的系数
    cls: 0.5  # 分类损失的系数
    cls_pw: 1.0 # 分类BCELoss中正样本的权重 
    obj: 1.0  # 有无物体损失的系数
    obj_pw: 1.0  # 有无物体BCELoss中正样本的权重
    iou_t: 0.20  # 标签与anchors的iou阈值 iou training threshold
    anchor_t: 4  # 标签的长h宽w/anchor的长h_a宽w_a阈值, 即h/h_a, w/w_a都要在(1/4, 4)之间anchor-multiple threshold
    # anchors: 3.63  
    # 下面是一些数据增强的系数, 包括颜色空间和图片空间  
    fl_gamma: 0.0  
    hsv_h: 0.015  # 色调  
    hsv_s: 0.7   # 饱和度  
    hsv_v: 0.4   # 明度  
    degrees: 0.0 #旋转角度  
    translate: 0.1  # 水平和垂直平移  
    scale: 0.5   # 缩放  
    shear: 0.0  # 剪切  
    perspective: 0.0  # 透视变换参数  
    flipud: 0.0  # 上下翻转  
    fliplr: 0.5   # 左右翻转
    mosaic: 1.0   #进行mosaic的概率
    mixup: 0.0  #进行mixup的概率, 在mosaic启用时才可启用
    copy_paste: 0.0  # segment copy-paste (probability), 在mosaic启用时, 才可以启用 
    

(3)Acnhor

  • YOLOv5在yaml文件中预设好了输入图像为 640 ∗ 640 640*640 640640分辨率对应的anchor尺寸,YOLOv5的anchor也是在大特征图上检测小目标,在小特征图上检测大目标。三个特征图,每个特征图上的格子有三种尺寸的anchor
    # anchors  
    anchors:  
      - [10,13, 16,30, 33,23]  # P3/8  检测小目标  10,13是一组尺寸,一共三组  
      - [30,61, 62,45, 59,119]  # P4/16     
      - [116,90, 156,198, 373,326]  # P5/32  检测大目标  
    

(4)Backbone

  • YOLOv5 Backbone
    # YOLOv5s v6.0 backbone  
    backbone:  
    # YOLOv5 v6.0 backbone
    backbone:
      # from   第一列 输入来自哪一层  -1代表上一层, 4代表第4层     
      # number 第二列 卷积核的数量    最终数量需要乘上width  
      # module 第三列 模块名称 包括:Conv Focus BottleneckCSP  SPP  
      # args   第四列 模块的参数   
      # [from, number, module, args]
      [[-1, 1, Conv, [64, 6, 2, 2]],  # 0-P1/2
       [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4 卷积核的数量 = 128 * wedith = 128*0.5=64    
       [-1, 3, C3, [128]],  # 模块数量 = 3 * depth =3*0.33=1  
       [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
       [-1, 6, C3, [256]],  # 模块数量 = 6 * depth =6*0.33=2
       [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
       [-1, 9, C3, [512]],  # # 模块数量 = 9 * depth =9*0.33=3
       [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
       [-1, 3, C3, [1024]],
       [-1, 1, SPPF, [1024, 5]],  # 9
      ] 
    

(5)网络结构参数

  • 根据训练过程验证网络结构,结合训练时输出的网络结构信息

    **backbone的前3个C3数量对应yolov5s.yaml的配置369分别除了3,变为1/3后的123,和模型深度参数有关depth_multiple: 0.33**                  
    层数,第几层      from  n    params  module                                  arguments
                   ch[-1]  数量  参数量 模块名称(m)                             网络结构参数:输入维度,输出维度,卷积核大小,卷积步长
      0                -1  1      3520  models.common.Conv                      [3, 32, 6, 2, 2]
      1                -1  1     18560  models.common.Conv                      [32, 64, 3, 2]
      2                -1  1     18816  models.common.C3                        [64, 64, 1]
      3                -1  1     73984  models.common.Conv                      [64, 128, 3, 2]
      4                -1  2    115712  models.common.C3                        [128, 128, 2]
      5                -1  1    295424  models.common.Conv                      [128, 256, 3, 2]
      6                -1  3    625152  models.common.C3                        [256, 256, 3]
      7                -1  1   1180672  models.common.Conv                      [256, 512, 3, 2]
      8                -1  1   1182720  models.common.C3                        [512, 512, 1]
      9                -1  1    656896  models.common.SPPF                      [512, 512, 5]
     10                -1  1    131584  models.common.Conv                      [512, 256, 1, 1]
     12           [-1, 6]  1         0  models.common.Concat                    [1]
     13                -1  1    361984  models.common.C3                        [512, 256, 1, False]
     14                -1  1     33024  models.common.Conv                      [256, 128, 1, 1]
     15                -1  1         0  torch.nn.modules.upsampling.Upsample    [None, 2, 'nearest']
     16           [-1, 4]  1         0  models.common.Concat                    [1]
     17                -1  1     90880  models.common.C3                        [256, 128, 1, False]
     18                -1  1    147712  models.common.Conv                      [128, 128, 3, 2]
     19          [-1, 14]  1         0  models.common.Concat                    [1]
     20                -1  1    296448  models.common.C3                        [256, 256, 1, False]
     21                -1  1    590336  models.common.Conv                      [256, 256, 3, 2]
     22          [-1, 10]  1         0  models.common.Concat                    [1]
     23                -1  1   1182720  models.common.C3                        [512, 512, 1, False]
     24      [17, 20, 23]  1    229245  Detect                                  [80, [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]], [128, 256, 512]]
    
  • 38
    点赞
  • 257
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w94ghz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值