【论文#目标检测】YOLOv7: Trainable bag-of-freebies sets new state-of-the-art for real-time object detectors

Author: Chien-Yao Wang, Alexey Bochkovskiy, Hong-Yuan Mark Liao
Subjects: Computer Vision and Pattern Recognition (cs.CV)
Cite as: arXiv:2207.02696 [cs.CV]
(or arXiv:2207.02696v1 [cs.CV] for this version)

https://doi.org/10.48550/arXiv.2207.02696

摘要

YOLOv7在5 FPS到160 FPS的范围内,速度和精度均超越所有已知的目标检测器,并且在GPU V100上,其精度达到56.8% AP,是所有已知实时目标检测器中精度最高的,这些检测器的帧率均在30 FPS或更高。YOLOv7-E6目标检测器(在V100上达到56 FPS,精度为55.9% AP)在速度上比基于Transformer的检测器SWINL Cascade-Mask R-CNN(在A100上达到9.2 FPS,精度为53.9% AP)快509%,精度高2%;比基于卷积的检测器ConvNeXt-XL Cascade-Mask R-CNN(在A100上达到8.6 FPS,精度为55.2% AP)快551%,精度高0.7% AP。此外,YOLOv7在速度和精度方面还超越了YOLOR、YOLOX、Scaled-YOLOv4、YOLOv5、DETR、Deformable DETR、DINO-5scale-R50、ViT-Adapter-B以及其他许多目标检测器。此外,我们仅使用MS COCO数据集从头开始训练YOLOv7,没有使用其他数据集或预训练权重。源代码已发布在https://github.com/WongKinYiu/yolov7

1.引言

实时目标检测是计算机视觉中一个非常重要的课题,因为它通常是计算机视觉系统中的必要组成部分。例如,多目标跟踪[94, 93]、自动驾驶[40, 18]、机器人[35, 58]、医学图像分析[34, 46]等。执行实时目标检测的计算设备通常是移动CPU或GPU,以及各大制造商开发的各种神经处理单元(NPU)。例如,苹果神经引擎(Apple)、英特尔神经计算棒(Intel)、英伟达Jetson AI边缘设备(Nvidia)、谷歌边缘TPU(Google)、高通神经处理引擎(Qualcomm)、联发科AI处理单元(MediaTek)和Kneron AI SoCs等都是NPU。上述提到的一些边缘设备专注于加速不同的操作,如普通卷积、深度卷积或MLP操作。在本文中,我们提出的实时目标检测器主要希望它能支持从边缘到云端的移动GPU和GPU设备。

近年来,实时目标检测器仍在为不同的边缘设备开发。例如,MCUNet[49, 48]和NanoDet[54]的开发专注于生产低功耗单芯片并提高边缘CPU上的推理速度。至于YOLOX[21]和YOLOR[81]等方法,它们专注于提高各种GPU上的推理速度。最近,实时目标检测器的开发专注于高效架构的设计。至于可以在CPU上使用的实时目标检测器[54, 88, 84, 83],它们的设计大多基于MobileNet[28, 66, 27]、ShuffleNet[92, 55]或GhostNet[25]。另一种主流的实时目标检测器是为GPU开发的[81, 21, 97],它们大多使用ResNet[26]、DarkNet[63]或DLA[87],然后使用CSPNet[80]策略来优化架构。本文中提出的方法的发展方向与当前主流的实时目标检测器不同。除了架构优化外,我们提出的方法将专注于训练过程的优化。我们将关注一些可能加强训练成本以提高目标检测精度的优化模块和优化方法,但不会增加推理成本。我们将这些提出的模块和优化方法称为可训练的免费包。

最近,模型重参数化[13, 12, 29]和动态标签分配[20, 17, 42]已经成为网络训练和目标检测中的重要课题。在这些新概念被提出之后,目标检测器的训练出现了许多新的问题。在本文中,我们将介绍我们发现的一些新问题,并设计有效的方法来解决它们。对于模型重参数化,我们以梯度传播路径的概念为基础,分析了适用于不同网络层的模型重参数化策略,并提出了有计划的重参数化模型。此外,当我们发现使用动态标签分配技术时,具有多个输出层的模型的训练会产生新的问题。即:“如何为不同分支的输出分配动态目标?”针对这一问题,我们提出了一种新的标签分配方法,称为从粗到细的引导式标签分配。

本文的贡献总结如下:(1)我们设计了几种可训练的免费包方法,使实时目标检测能够在不增加推理成本的情况下大幅提高检测精度;(2)对于目标检测方法的演变,我们发现了两个新问题,即重参数化模块如何取代原始模块,以及动态标签分配策略如何处理对不同输出层的分配。此外,我们还提出了应对这些问题所带来的困难的方法;(3)我们为实时目标检测器提出了“扩展”和“复合缩放”方法,这些方法可以有效地利用参数和计算资源;(4)我们提出的方法可以有效地减少约40%的参数和50%的计算量,与最先进的实时目标检测器相比,具有更快的推理速度和更高的检测精度。
在这里插入图片描述

2.相关工作

2.1 实时目标检测

目前,最先进的实时目标检测器主要基于YOLO [61, 62, 63]和FCOS [76, 77],这些检测器包括[3, 79, 81, 21, 54, 85, 23]。要成为最先进的实时目标检测器,通常需要具备以下特征:(1)更快且更强的网络架构;(2)更有效的特征融合方法[22, 97, 37, 74, 59, 30, 9, 45];(3)更精确的检测方法[76, 77, 69];(4)更稳健的损失函数[96, 64, 6, 56, 95, 57];(5)更高效的标签分配方法[99, 20, 17, 82, 42];以及(6)更高效的训练方法。在本文中,我们并不打算探索需要额外数据或大型模型的自监督学习或知识蒸馏方法。相反,我们将针对上述(4)、(5)和(6)相关问题,设计新的可训练的免费包方法。

2.2 模型重参数化

模型重参数化技术[71, 31, 75, 19, 33, 11, 4, 24, 13, 12, 10, 29, 14, 78]在推理阶段将多个计算模块合并为一个。模型重参数化技术可以被视为一种集成技术,我们可以将其分为两类,即模块级集成和模型级集成。有两种常见的模型级重参数化方法用于获得最终的推理模型。一种是使用不同的训练数据训练多个相同的模型,然后对多个训练模型的权重进行平均。另一种是对不同迭代次数的模型权重进行加权平均。模块级重参数化是最近更受欢迎的研究问题。这种类型的方法在训练期间将一个模块拆分为多个相同或不同的模块分支,并在推理期间将多个分支模块整合为一个完全等效的模块。然而,并非所有提出的重参数化模块都能完美应用于不同的架构。鉴于此,我们开发了新的重参数化模块,并为各种架构设计了相关应用策略。

2.3 模型缩放

模型缩放[72, 60, 74, 73, 15, 16, 2, 51]是一种对已经设计好的模型进行放大或缩小,使其能够适应不同计算设备的方法。模型缩放方法通常使用不同的缩放因子,如分辨率(输入图像的大小)、深度(层数)、宽度(通道数)和阶段(特征金字塔的数量),从而在网络参数数量、计算量、推理速度和精度之间实现良好的权衡。网络架构搜索(NAS)是常用的一种模型缩放方法。NAS可以在不定义过于复杂规则的情况下,自动从搜索空间中搜索适合的缩放因子。NAS的缺点是,它需要非常昂贵的计算来完成模型缩放因子的搜索。在[15]中,研究人员分析了缩放因子与参数数量和操作数量之间的关系,试图直接估计一些规则,从而获得模型缩放所需的缩放因子。查阅文献,我们发现几乎所有的模型缩放方法都是独立地分析单个缩放因子,即使是复合缩放类别的方法也独立地优化缩放因子。这是因为大多数流行的NAS架构处理的缩放因子之间的相关性不大。我们观察到,所有基于连接的模型,如DenseNet[32]或VoVNet[39],在对这些模型的深度进行缩放时,都会改变某些层的输入宽度。由于我们提出的架构是基于连接的,因此我们必须为这种模型设计一种新的复合缩放方法。

3.架构

3.1 扩展的高效层聚合网络

在大多数关于设计高效架构的文献中,主要考虑的因素无外乎参数数量、计算量和计算密度。Ma等人[55]从内存访问成本的特性出发,还分析了输入/输出通道比、架构的分支数量以及逐元素操作对网络推理速度的影响。Dollar等人[15]在进行模型缩放时还考虑了激活函数,即更多地考虑卷积层输出张量中的元素数量。图2(b)中的CSPVoVNet[79]是VoVNet[39]的一个变体。除了考虑上述基本的设计关注点外,CSPVoVNet[79]的架构还分析了梯度路径,以便使不同层的权重能够学习更多样的特征。上述梯度分析方法使推理更快、更准确。图2(c)中的ELAN[1]考虑了以下设计策略——“如何设计一个高效的网络?”。他们得出了一个结论:通过控制最短最长梯度路径,一个更深的网络可以有效地学习和收敛。在本文中,我们提出了基于ELAN的扩展ELAN(E-ELAN),其主要架构如图2(d)所示。
在这里插入图片描述
无论梯度路径的长度和大规模ELAN中计算块的堆叠数量如何,它都已达到一个稳定状态。如果无限制地堆叠更多的计算块,这种稳定状态可能会被破坏,参数利用率将降低。我们提出的E-ELAN使用扩展、洗牌、合并基数来实现持续增强网络学习能力,而不会破坏原始梯度路径。在架构方面,E-ELAN仅在计算块中更改架构,而过渡层的架构则完全保持不变。我们的策略是使用分组卷积来扩展计算块的通道和基数。我们将相同的分组参数和通道乘数应用于计算层的所有计算块。然后,每个计算块计算出的特征图将根据设置的分组参数g被洗牌分成g组,然后将它们连接在一起。此时,每组特征图中的通道数将与原始架构中的通道数相同。最后,我们将g组特征图相加以执行合并基数。除了保持原始ELAN设计架构外,E-ELAN还可以引导不同组的计算块学习更多样的特征。

3.2 基于连接的模型的模型缩放

模型缩放的主要目的是调整模型的一些属性,生成不同规模的模型,以满足不同推理速度的需求。例如,EfficientNet[72]的缩放模型考虑了宽度、深度和分辨率。至于Scaled-YOLOv4[79],其缩放模型是调整阶段的数量。在[15]中,Dollar等人分析了在进行宽度和深度缩放时,普通卷积和分组卷积对参数数量和计算量的影响,并据此设计了相应的模型缩放方法。上述方法主要应用于PlainNet或ResNet等架构。当这些架构执行放大或缩小时,每一层的输入度和输出度都不会改变,因此我们可以独立分析每个缩放因子对参数数量和计算量的影响。然而,如果将这些方法应用于基于连接的架构,我们会发现,在对深度进行放大或缩小时,紧随基于连接的计算块之后的转换层的输入度会减少或增加,如图3(a)和(b)所示。从上述现象可以推断,对于基于连接的模型,我们不能单独分析不同的缩放因子,而必须综合考虑。以放大深度为例,这种操作会导致转换层的输入通道和输出通道之间的比例发生变化,这可能会导致模型的硬件利用率降低。因此,我们必须为基于连接的模型提出相应的复合模型缩放方法。当我们对计算块的深度因子进行缩放时,我们还必须计算该块输出通道的变化。然后,我们将在转换层上以相同的变化量执行宽度因子缩放,结果如图3(c)所示。我们提出的复合缩放方法可以保持模型在最初设计时所具有的属性,并保持最优结构。
在这里插入图片描述

注解
(1)参考yolov7.yaml结构,可以看到yolov7的网络架构设计分为backbone和head两部分
预定义参数

# parameters
nc: 80  # number of classes,分类的数量
depth_multiple: 1.0  # model depth multiple,模型深度,用于计算某一层会重复几次
width_multiple: 1.0  # layer channel multiple

# anchors
anchors:
  - [12,16, 19,36, 40,28]  # P3/8
  - [36,75, 76,55, 72,146]  # P4/16
  - [142,110, 192,243, 459,401]  # P5/32

backbone结构中,以# 3-P2/4为例,第1层是[-1, 1, Conv, [128, 3, 2]],-1表示将当前层的前一层的输出作为输入,1表示当前这一层重复几次(计算公式是1 * depth_multiple = 1),Conv表示当前层为卷积层,[128, 3, 2]表示卷积层的输出维度为128,kernelSize为3,stride为2

# yolov7 backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Conv, [32, 3, 1]],  # 0
  
   [-1, 1, Conv, [64, 3, 2]],  # 1-P1/2      
   [-1, 1, Conv, [64, 3, 1]],
   # 第1个E-ELAN模块
   [-1, 1, Conv, [128, 3, 2]],  # 3-P2/4  
   [-1, 1, Conv, [64, 1, 1]],
   [-2, 1, Conv, [64, 1, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [[-1, -3, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [256, 1, 1]],  # 11
   # 第2个E-ELAN模块
   [-1, 1, MP, []], # MP = MaxPool
   [-1, 1, Conv, [128, 1, 1]],
   [-3, 1, Conv, [128, 1, 1]],
   [-1, 1, Conv, [128, 3, 2]],
   [[-1, -3], 1, Concat, [1]],  # 16-P3/8  
   [-1, 1, Conv, [128, 1, 1]],
   [-2, 1, Conv, [128, 1, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [[-1, -3, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [512, 1, 1]],  # 24
   # 第3个E-ELAN模块
   [-1, 1, MP, []],
   [-1, 1, Conv, [256, 1, 1]],
   [-3, 1, Conv, [256, 1, 1]],
   [-1, 1, Conv, [256, 3, 2]],
   [[-1, -3], 1, Concat, [1]],  # 29-P4/16  
   [-1, 1, Conv, [256, 1, 1]],
   [-2, 1, Conv, [256, 1, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [[-1, -3, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [1024, 1, 1]],  # 37
   # 第4个E-ELAN模块
   [-1, 1, MP, []],
   [-1, 1, Conv, [512, 1, 1]],
   [-3, 1, Conv, [512, 1, 1]],
   [-1, 1, Conv, [512, 3, 2]],
   [[-1, -3], 1, Concat, [1]],  # 42-P5/32  
   [-1, 1, Conv, [256, 1, 1]],
   [-2, 1, Conv, [256, 1, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [[-1, -3, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [1024, 1, 1]],  # 50
  ]

以第1个E-ELAN模块为例,模块中的数据流向是
在这里插入图片描述

(2)head部分主要涉及的模块是SPPCSPC、自顶向下和自底向上的金字塔融合、RepConv重参数化、IDetect自适应头

# yolov7 head
head:
   # 替换传统 SPP,通过 ​​跨阶段部分连接(CSP)​​ 减少计算量,同时融合多尺度特征
  [[-1, 1, SPPCSPC, [512]], # 51
   # 51-75层为自顶向下的特征金字塔融合
   [-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']], # 上采样P5的分辨率到P4
   [37, 1, Conv, [256, 1, 1]], # route backbone P4,接收第88层的输出作为输入,也就是P4,并调整输出通道数
   [[-1, -2], 1, Concat, [1]], # 合并特征
   
   [-1, 1, Conv, [256, 1, 1]],
   [-2, 1, Conv, [256, 1, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [[-1, -2, -3, -4, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [256, 1, 1]], # 63
   
   [-1, 1, Conv, [128, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [24, 1, Conv, [128, 1, 1]], # route backbone P3,接收第90层的输出作为输入
   [[-1, -2], 1, Concat, [1]],
   
   [-1, 1, Conv, [128, 1, 1]],
   [-2, 1, Conv, [128, 1, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [-1, 1, Conv, [64, 3, 1]],
   [[-1, -2, -3, -4, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [128, 1, 1]], # 75
   # 75-101层为自底向上增强
   # 先将浅层特征P3传递到P4、P5,补充细节信息
   [-1, 1, MP, []],
   [-1, 1, Conv, [128, 1, 1]],
   [-3, 1, Conv, [128, 1, 1]],
   [-1, 1, Conv, [128, 3, 2]],
   [[-1, -3, 63], 1, Concat, [1]],
   
   [-1, 1, Conv, [256, 1, 1]],
   [-2, 1, Conv, [256, 1, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [-1, 1, Conv, [128, 3, 1]],
   [[-1, -2, -3, -4, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [256, 1, 1]], # 88
      
   [-1, 1, MP, []],
   [-1, 1, Conv, [256, 1, 1]],
   [-3, 1, Conv, [256, 1, 1]],
   [-1, 1, Conv, [256, 3, 2]],
   [[-1, -3, 51], 1, Concat, [1]],
   
   [-1, 1, Conv, [512, 1, 1]],
   [-2, 1, Conv, [512, 1, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [-1, 1, Conv, [256, 3, 1]],
   [[-1, -2, -3, -4, -5, -6], 1, Concat, [1]],
   [-1, 1, Conv, [512, 1, 1]], # 101
   # 102-104层是重参数化模块,能够提升推理速度
   [75, 1, RepConv, [256, 3, 1]],
   [88, 1, RepConv, [512, 3, 1]],
   [101, 1, RepConv, [1024, 3, 1]],
   # 自适应检测头
   [[102,103,104], 1, IDetect, [nc, anchors]],   # Detect(P3, P4, P5)
  ]

SPPCSPC的结构相对比与SPP结构而言,在maxpool层前后多加了几层的卷积,增强不同尺度的特征表达能力。
在这里插入图片描述
IDetect的作用是检测头部,与之前的头部检测不同的地方,加入了ImplicitA和ImplicitM这两项,从定义上看,ImplicitA相当于在原有的x基础上增加了一个可学习的偏置项(A应该是Add的意思),ImplicitM相当于在原有的x基础上增加了一个乘法项(M应该是Multiple的意思),两者同时对输入输出的特征进行了微调。IDetect大致的结构图

输入特征图 (P3, P4, P5)
     │
     ├─ 分支1 (P3) ── ImplicitA → Conv2d → ImplicitM → 解码坐标/尺寸 → 输出
     │
     ├─ 分支2 (P4) ── ImplicitA → Conv2d → ImplicitM → 解码坐标/尺寸 → 输出
     │
     └─ 分支3 (P5) ── ImplicitA → Conv2d → ImplicitM → 解码坐标/尺寸 → 输出

IDetect在训练模式下的前向函数,特征图先经过self.ia[]和self.m[](卷积层),之后再经过self.im[],如果是推理模式的话,会去解析坐标框的xywh

def forward(self, x):
        # x = x.copy()  # for profiling
        z = []  # inference output
        self.training |= self.export
        for i in range(self.nl):
            x[i] = self.m[i](self.ia[i](x[i]))  # conv
            x[i] = self.im[i](x[i])
            bs, _, ny, nx = x[i].shape  # x(bs,255,20,20) to x(bs,3,20,20,85)
            x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()

            if not self.training:  # inference
                if self.grid[i].shape[2:4] != x[i].shape[2:4]:
                    self.grid[i] = self._make_grid(nx, ny).to(x[i].device)

                y = x[i].sigmoid()
                y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i]  # xy
                y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i]  # wh
                z.append(y.view(bs, -1, self.no))

        return x if self.training else (torch.cat(z, 1), x)

ImplicitA和ImplicitM定义在yolov7/models/common.py中

class ImplicitA(nn.Module):
    def __init__(self, channel, mean=0., std=.02):
        super(ImplicitA, self).__init__()
        self.channel = channel
        self.mean = mean
        self.std = std
        self.implicit = nn.Parameter(torch.zeros(1, channel, 1, 1))
        nn.init.normal_(self.implicit, mean=self.mean, std=self.std)

    def forward(self, x):
        return self.implicit + x # 增加了一个self.implicit的偏置项
    

class ImplicitM(nn.Module):
    def __init__(self, channel, mean=1., std=.02):
        super(ImplicitM, self).__init__()
        self.channel = channel
        self.mean = mean
        self.std = std
        self.implicit = nn.Parameter(torch.ones(1, channel, 1, 1))
        nn.init.normal_(self.implicit, mean=self.mean, std=self.std)

    def forward(self, x):
        return self.implicit * x # 乘以一个self.implicit的偏置项

在推理模式下,IDetect使用fuseforward()前向推理函数,此时会将前面训练阶段的模块进行参数融合,fuse()函数

def fuse(self):
    # 融合 ImplicitA 到卷积偏置
    self.conv.bias += (self.conv.weight @ self.ia.implicit.flatten()).squeeze()
    
    # 融合 ImplicitM 到卷积权重和偏置
    self.conv.weight *= self.im.implicit.reshape(1, -1, 1, 1)
    self.conv.bias *= self.im.implicit.flatten()

fuseforward()函数

def fuseforward(self, x):
        # x = x.copy()  # for profiling
        z = []  # inference output
        self.training |= self.export
        for i in range(self.nl):
            x[i] = self.m[i](x[i])  # conv
            bs, _, ny, nx = x[i].shape  # x(bs,255,20,20) to x(bs,3,20,20,85)
            x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()

            if not self.training:  # inference
                if self.grid[i].shape[2:4] != x[i].shape[2:4]:
                    self.grid[i] = self._make_grid(nx, ny).to(x[i].device)

                y = x[i].sigmoid()
                if not torch.onnx.is_in_onnx_export():
                    y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i]  # xy
                    y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i]  # wh
                else:
                    xy, wh, conf = y.split((2, 2, self.nc + 1), 4)  # y.tensor_split((2, 4, 5), 4)  # torch 1.8.0
                    xy = xy * (2. * self.stride[i]) + (self.stride[i] * (self.grid[i] - 0.5))  # new xy
                    wh = wh ** 2 * (4 * self.anchor_grid[i].data)  # new wh
                    y = torch.cat((xy, wh, conf), 4)
                z.append(y.view(bs, -1, self.no))

        if self.training:
            out = x
        elif self.end2end:
            out = torch.cat(z, 1)
        elif self.include_nms:
            z = self.convert(z)
            out = (z, )
        elif self.concat:
            out = torch.cat(z, 1)            
        else:
            out = (torch.cat(z, 1), x)

        return out

4. 可训练的免费包

4.1. 有计划的重参数化卷积

尽管RepConv [13]在VGG [68]上取得了出色的性能,但当我们直接将其应用于ResNet [26]、DenseNet [32]等其他架构时,其精度会显著下降。我们使用梯度流传播路径来分析重参数化卷积应该如何与不同的网络结合,并相应地设计了有计划的重参数化卷积。

RepConv实际上在一个卷积层中结合了3×3卷积、1×1卷积和恒等连接。在分析了RepConv与不同架构的组合及其相应性能之后,我们发现RepConv中的恒等连接破坏了ResNet中的残差和DenseNet中的连接,而后者为不同的特征图提供了更多样化的梯度。基于上述原因,我们使用没有恒等连接的RepConv(RepConvN)来设计有计划的重参数化卷积的架构。在我们的思考中,当一个具有残差或连接的卷积层被重参数化卷积替换时,应该没有恒等连接。图4展示了我们在PlainNet和ResNet中使用的“有计划的重参数化卷积”的一个例子。至于在基于残差的模型和基于连接的模型中进行的完整的有计划的重参数化卷积实验,将在消融研究部分进行展示。
在这里插入图片描述

4.2. 粗略用于辅助,精细用于引导损失

深度监督[38]是一种常用于训练深度网络的技术。其主要概念是在网络的中间层添加额外的辅助头(auxiliary head),并以辅助损失(assistant loss)为指导来训练浅层网络权重。即使对于通常收敛良好的架构,如ResNet[26]和DenseNet[32],深度监督[70, 98, 67, 47, 82, 65, 86, 50]仍然可以在许多任务上显著提升模型的性能。图5(a)和(b)分别展示了“没有”和“有”深度监督的目标检测架构。在本文中,我们将负责最终输出的头称为引导头(lead head),而用于辅助训练的头称为辅助头(auxiliary head)。
在这里插入图片描述

接下来,我们想要讨论标签分配的问题。在过去,深度网络的训练中,标签分配通常直接指向真实标签(ground truth),并根据给定的规则生成硬标签(hard label)。然而,近年来,以目标检测为例,研究人员经常使用网络预测输出的质量和分布,然后结合真实标签,通过一些计算和优化方法来生成可靠的软标签(soft label)[61, 8, 36, 99, 91, 44, 43, 90, 20, 17, 42]。例如,YOLO[61]使用边界框回归预测与真实标签的IoU作为目标性的软标签。在本文中,我们将考虑网络预测结果与真实标签相结合,然后分配软标签的机制称为“标签分配器”(label assigner)。

深度监督需要针对目标进行训练,无论辅助头或引导头的情况如何。在开发与软标签分配器相关的技术过程中,我们偶然发现了一个新的衍生问题,即“如何为辅助头和引导头分配软标签?”。据我们所知,相关文献尚未探讨过这一问题。目前最流行的方法的结果如图5(c)所示,即将辅助头和引导头分开,然后使用它们自己的预测结果和真实标签来执行标签分配。本文提出的方法是一种新的标签分配方法,通过引导头预测来指导辅助头和引导头。换句话说,我们使用引导头预测作为指导,生成从粗到细的层次化标签,分别用于辅助头和引导头的学习。两种提出的深度监督标签分配策略分别如图5(d)和(e)所示。

引导头引导的标签分配器主要基于引导头的预测结果和真实标签进行计算,并通过优化过程生成软标签。这组软标签将被用作辅助头和引导头的目标训练模型。这样做的原因是引导头具有相对较强的学习能力,因此从它生成的软标签应该更能代表源数据和目标之间的分布和相关性。此外,我们可以将这种学习视为一种广义的残差学习。通过让较浅的辅助头直接学习引导头已经学到的信息,引导头将更能专注于学习尚未被学习的残差信息。

从粗到细的引导头引导的标签分配器也使用引导头的预测结果和真实标签来生成软标签。然而,在这个过程中,我们生成了两组不同的软标签,即粗标签和细标签。细标签与引导头引导的标签分配器生成的软标签相同,而粗标签则是通过放宽正样本分配过程的约束,允许更多网格被视为正目标来生成的。这样做的原因是辅助头的学习能力不如引导头强,为了避免丢失需要学习的信息,我们将专注于优化辅助头在目标检测任务中的召回率。至于引导头的输出,我们可以从高召回率的结果中筛选出高精度的结果作为最终输出。然而,我们必须注意,如果粗标签的额外权重接近细标签的权重,它可能会在最终预测中产生不良的先验。因此,为了使那些额外的粗正网格的影响更小,我们在解码器中设置了限制,使得这些额外的粗正网格不能完美地产生软标签。上述机制允许在学习过程中动态调整细标签和粗标签的重要性,并使细标签的可优化上限始终高于粗标签。

4.3 其他可训练的免费包

在本节中,我们将列出一些可训练的免费包。这些免费包是我们训练中使用的一些技巧,但原始概念并非由我们提出。这些免费包的训练细节将在附录中详细阐述,包括:(1)在卷积-批量归一化-激活拓扑结构中的批量归一化:这一部分主要将批量归一化层直接连接到卷积层。这样做的目的是在推理阶段将批量归一化的均值和方差整合到卷积层的偏置和权重中。(2)YOLOR [81] 中的隐式知识与卷积特征图以加法和乘法方式结合:YOLOR中的隐式知识可以通过预计算在推理阶段简化为一个向量。这个向量可以与前一层或后一层卷积层的偏置和权重相结合。(3)EMA模型:EMA是在平均教师[75]中使用的一种技术,在我们的系统中,我们纯粹使用EMA模型作为最终的推理模型。

5.实验

5.1 实验设置

我们使用Microsoft COCO数据集进行实验,以验证我们的目标检测方法。我们的所有实验均未使用预训练模型,即所有模型都是从头开始训练的。在开发过程中,我们使用train 2017数据集进行训练,然后使用val 2017数据集进行验证和选择超参数。最后,我们在test 2017数据集上展示目标检测的性能,并将其与最先进的目标检测算法进行比较。详细的训练参数设置在附录中有描述。

我们为边缘GPU、普通GPU和云端GPU设计了基础模型,分别命名为YOLOv7-tiny、YOLOv7和YOLOv7-W6。同时,我们还使用基础模型进行模型缩放,以满足不同的服务需求,从而获得不同类型的模型。对于YOLOv7,我们在颈部进行堆叠缩放,并使用提出的复合缩放方法对整个模型的深度和宽度进行放大,从而获得YOLOv7-X。至于YOLOv7-W6,我们使用新提出的复合缩放方法获得YOLOv7-E6和YOLOv7-D6。此外,我们使用提出的E-ELAN对YOLOv7-E6进行改进,从而完成YOLOv7-E6E。由于YOLOv7-tiny是一种面向边缘GPU的架构,它将使用Leaky ReLU作为激活函数。对于其他模型,我们使用SiLU作为激活函数。我们将在附录中详细描述每个模型的缩放因子。

5.2 Baseline

我们选择YOLO的先前版本[3, 79]和最先进的目标检测器YOLOR[81]作为我们的基线。表1展示了我们提出的YOLOv7模型与那些在相同设置下训练的基线之间的比较。
在这里插入图片描述
从结果来看,与YOLOv4相比,YOLOv7的参数减少了75%,计算量减少了36%,并且平均精度(AP)提高了1.5%。与最先进的YOLOR-CSP相比,YOLOv7的参数减少了43%,计算量减少了15%,并且AP提高了0.4%。在小型模型的性能方面,与YOLOv4-tiny-31相比,YOLOv7-tiny将参数数量减少了39%,计算量减少了49%,但保持了相同的AP。在云端GPU模型方面,我们的模型仍然能够在减少19%的参数和33%的计算量的同时,保持更高的AP。

5.3 与SOTA的对比

我们将所提出的方法与针对通用GPU和移动GPU的最先进的目标检测器进行了比较,结果如表2所示。从表2的结果来看,所提出的方法在速度和精度的综合权衡方面表现最佳。如果我们比较YOLOv7-tiny-SiLU与YOLOv5-N(r6.1),我们的方法在AP精度上高出10.7%,并且速度快了127fps。此外,YOLOv7在161fps的帧率下达到了51.4%的AP,而具有相同AP的PPYOLOE-L仅有78fps的帧率。在参数使用方面,YOLOv7比PPYOLOE-L少了41%。如果我们比较具有114fps推理速度的YOLOv7-X与具有99fps推理速度的YOLOv5-L(r6.1),YOLOv7-X可以将AP提高3.9%。如果将YOLOv7-X与类似规模的YOLOv5-X(r6.1)进行比较,YOLOv7-X的推理速度快了31fps。此外,在参数数量和计算量方面,与YOLOv5-X(r6.1)相比,YOLOv7-X减少了22%的参数和8%的计算量,但将AP提高了2.2%。
在这里插入图片描述
如果我们使用1280的输入分辨率来比较YOLOv7与YOLOR,YOLOv7-W6的推理速度比YOLOR-P6快8fps,同时检测率也提高了1% AP。至于YOLOv7-E6与YOLOv5-X6(r6.1)的比较,前者比后者在AP上提高了0.9%,参数减少了45%,计算量减少了63%,推理速度提高了47%。YOLOv7-D6的推理速度与YOLOR-E6相近,但AP提高了0.8%。YOLOv7-E6E的推理速度与YOLOR-D6相近,但AP提高了0.3%。

5.4 消融研究

5.4.1 提出的复合缩放方法

表3展示了使用不同模型缩放策略进行放大时所获得的结果。其中,我们提出的复合缩放方法是将计算块的深度放大1.5倍,将转换块的宽度放大1.25倍。如果将我们的方法与仅放大宽度的方法进行比较,我们的方法可以在使用更少的参数和计算量的情况下将AP提高0.5%。如果将我们的方法与仅放大深度的方法进行比较,我们的方法只需要将参数数量增加2.9%,计算量增加1.2%,就可以将AP提高0.2%。从表3的结果可以看出,我们提出的复合缩放策略能够更高效地利用参数和计算资源。
在这里插入图片描述

5.4.2 提出的有计划的重参数化模型

为了验证我们提出的有计划的重参数化模型的普适性,我们分别在基于连接的模型和基于残差的模型上进行了验证。我们选择的用于验证的基于连接的模型和基于残差的模型分别是3层堆叠的ELAN和CSPDarknet。

在基于连接的模型的实验中,我们将3层堆叠的ELAN中不同位置的3×3卷积层替换为RepConv,具体的配置如图6所示。从表4中展示的结果来看,我们提出的有计划的重参数化模型在所有情况下都获得了更高的平均精度(AP)值。
在这里插入图片描述
在这里插入图片描述
在处理基于残差的模型的实验中,由于原始的Dark块并不包含符合我们设计策略的3×3卷积块,我们为此实验额外设计了一个反向Dark块,其架构如图7所示。由于带有Dark块和反向Dark块的CSPDarknet在参数数量和操作数量上完全相同,因此进行比较是公平的。表5中展示的实验结果充分证实了我们提出的有计划的重参数化模型在基于残差的模型上同样有效。我们发现RepCSPResNet[85]的设计也符合我们的设计模式。
在这里插入图片描述
在这里插入图片描述

5.4.3 提出的辅助头的辅助损失

在辅助头的辅助损失实验中,我们比较了引导头和辅助头的一般独立标签分配方法,并且也比较了两种提出的引导头引导的标签分配方法。我们在表6中展示了所有的比较结果。从表6中列出的结果来看,很明显,任何增加辅助损失的模型都能显著提高整体性能。此外,我们提出的引导头引导的标签分配策略在AP、AP50和AP75方面比一般独立标签分配策略表现更好。至于我们提出的“辅助头使用粗标签,引导头使用细标签”的分配策略,在所有情况下都取得了最佳结果。在图8中,我们展示了不同方法在辅助头和引导头处预测的目标性地图。从图8中我们发现,如果辅助头学习引导头引导的软标签,它确实可以帮助引导头从一致的目标中提取残差信息。
在这里插入图片描述
在这里插入图片描述
在表7中,我们进一步分析了提出的从粗到细的引导头引导的标签分配方法对辅助头解码器的影响。也就是说,我们比较了引入上限约束和不引入上限约束的结果。从表中的数字来看,通过限制目标中心距离来约束目标性上限的方法可以实现更好的性能。
在这里插入图片描述
由于提出的YOLOv7使用多个金字塔共同预测目标检测结果,我们可以直接将辅助头连接到中间层的金字塔进行训练。这种训练方式可以弥补在下一级金字塔预测中可能丢失的信息。基于上述原因,我们在提出的E-ELAN架构中设计了部分辅助头。我们的方法是在合并基数之前的一组特征图之后连接辅助头,这种连接可以使新生成的一组特征图的权重不直接由辅助损失更新。我们的设计允许引导头的每个金字塔仍然从不同大小的目标中获取信息。表8展示了使用两种不同方法(即从粗到细的引导头引导和部分从粗到细的引导头引导方法)所获得的结果。显然,部分从粗到细的引导头引导方法具有更好的辅助效果。
在这里插入图片描述

6.结论

在本文中,我们提出了一个新的实时目标检测器架构以及相应的模型缩放方法。此外,我们发现目标检测方法的演变过程产生了新的研究课题。在研究过程中,我们发现了重参数化模块的替换问题以及动态标签分配的分配问题。为了解决这些问题,我们提出了可训练的免费包方法来提高目标检测的精度。基于以上研究,我们开发了YOLOv7系列目标检测系统,并取得了最先进的结果。

7. 致谢

作者们感谢国家高性能计算中心(NCHC)提供计算和存储资源。

8.更多对比

YOLOv7在5 FPS到160 FPS的范围内,速度和精度均超越所有已知的目标检测器,并且在GPU V100上,其精度达到56.8% AP test-dev / 56.8% AP min-val,是所有已知实时目标检测器中精度最高的,这些检测器的帧率均在30 FPS或更高。YOLOv7-E6目标检测器(在V100上达到56 FPS,精度为55.9% AP)在速度上比基于Transformer的检测器SWIN-L Cascade-Mask R-CNN(在A100上达到9.2 FPS,精度为53.9% AP)快509%,精度高2%;比基于卷积的检测器ConvNeXt-XL Cascade-Mask R-CNN(在A100上达到8.6 FPS,精度为55.2% AP)快551%,精度高0.7% AP。此外,YOLOv7在速度和精度方面还超越了YOLOR、YOLOX、Scaled-YOLOv4、YOLOv5、DETR、Deformable DETR、DINO-5scale-R50、ViT-Adapter-B以及其他许多目标检测器。此外,我们仅使用MS COCO数据集从头开始训练YOLOv7,没有使用其他数据集或预训练权重。

YOLOv7-E6E(56.8% AP)实时模型的最大精度比当前最准确的meituan/YOLOv6-s模型(43.1% AP)在COCO数据集上高出+13.7% AP。我们的YOLOv7-tiny(35.2% AP,0.4 ms)模型在COCO数据集和V100 GPU上(batch=32)与meituan/YOLOv6-n(35.0% AP,0.5 ms)相比,速度快了+25%,精度高了+0.2% AP。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值