前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >YOLO系列介绍(三)

YOLO系列介绍(三)

作者头像
算法之名
发布2022-11-16 19:51:25
2.4K0
发布2022-11-16 19:51:25
举报
文章被收录于专栏:算法之名算法之名

YOLO 系列介绍 (二)

YOLOV7

YOLOV7 是 YOLOV4 的原班人马于 2022 年提出的最新的 YOLO 版本。 YOLOv7 的在速度和精度上的表现也优于 YOLOR、YOLOX、Scaled-YOLOv4、YOLOv5、DETR 等多种目标检测器。

YOLOV7 整体网络结构

YOLOV7 跟 V4、V5 的结构差不多,依然是 Backbone+Neck+Head

在 Backbone 中,输入的图像会先经过 4 个普通卷积 CBS (Conv2D+BatchNorm+SiLu),这 4 个 CBS 都是 3*3 的卷积核,它们的不同在于橙色的 CBS 步长为 1,不会改变特征图的尺寸;而褐色的 CBS 步长为 2,会进行下采样;YOLOV5 只有两个普通卷积 CBS。

class Conv(nn.Module):
    # Standard convolution
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
        super(Conv, self).__init__()
        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
        self.bn = nn.BatchNorm2d(c2)
        self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())

    def forward(self, x):
        return self.act(self.bn(self.conv(x)))

    def fuseforward(self, x):
        return self.act(self.conv(x))

配置文件如下

# 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, 1, Conv, [128, 3, 2]],  # 3-P2/4  

YOLOV5 在进一步的 Backbone 中使用的是 C3 结构,V7 中改成了 ELAN 结构 + MP 结构。

  • ELAN 结构

通过控制最短最长的梯度路径,更深的网络可以有效地学习和收敛。ELAN 由多个 CBS 构成,其输入输出特征大小保持不变,通道数在开始的两个 CBS 会有变化, 后面的几个输入通道都是和输出通道保持一致的,经过最后一个 CBS 输出为需要的通道。上图中粉色的 CBS 是 1*1 的卷积核,橙色的 CBS 是 3*3 的卷积核。这里需要注意的是,特征图的 concat 合并是 4 层合并而不是 2 层合并 (第二张图片更加清楚)。所以经过 concat 输出的是 2c 而不是 c。

class Concat(nn.Module):
    def __init__(self, dimension=1):
        super(Concat, self).__init__()
        self.d = dimension

    def forward(self, x):
        return torch.cat(x, self.d)

配置文件如下

[-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
  • MP-1 结构

这里就是使用了最大池化 (上层) 和步长为 2 的 CBS (下层) 同时进行降采样,通道数通过 concat 合并后 MP1 前后保持不变。

class MP(nn.Module):
    def __init__(self, k=2):
        super(MP, self).__init__()
        self.m = nn.MaxPool2d(kernel_size=k, stride=k)

    def forward(self, x):
        return self.m(x)

配置文件如下

[-1, 1, MP, []],
[-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  

YOLOV7 的 Backbone 最深层的网络输出的特征图会经过一个叫 SPPCSPC 的结构再输入到 Neck 中,YOLOV5 中则是 SPPF 的结构

  • SPPCSPC 结构

上图中首先会通过两个 1*1 的 CBS 分成两个通路,上面的通路会再接一个 3*3 的 CBS,再 1*1 的 CBS。YOLOV5 的 SPPF 中只有一个 CBS 就接最大池化了。YOLOV7 的最大池化改回了 YOLOV4 的结构,使用的是最大池化核数 5、9、13 并行的结构,而 YOLOV5 的 SPPF 使用的是 5*5 的最大池化串行的结构。

class SPPCSPC(nn.Module):
    # CSP https://github.com/WongKinYiu/CrossStagePartialNetworks
    def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5, k=(5, 9, 13)):
        super(SPPCSPC, self).__init__()
        c_ = int(2 * c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(c_, c_, 3, 1)
        self.cv4 = Conv(c_, c_, 1, 1)
        self.m = nn.ModuleList([nn.MaxPool2d(kernel_size=x, stride=1, padding=x // 2) for x in k])
        self.cv5 = Conv(4 * c_, c_, 1, 1)
        self.cv6 = Conv(c_, c_, 3, 1)
        self.cv7 = Conv(2 * c_, c2, 1, 1)

    def forward(self, x):
        x1 = self.cv4(self.cv3(self.cv1(x)))
        y1 = self.cv6(self.cv5(torch.cat([x1] + [m(x1) for m in self.m], 1)))
        y2 = self.cv2(x)
        return self.cv7(torch.cat((y1, y2), dim=1))

配置文件

# yolov7 head
head:
  [[-1, 1, SPPCSPC, [512]], # 51

Neck 部分

YOLOV7 依然是一个分层输出的特征金字塔结构。Backbone 在第 2、3、4 个 ELAN 后进行特征分层输出。Neck 部分依然采用的是 PAN 的双向特征融合,从深层到浅层进行上采样,再从浅层到深层进行下采样。PAN 的上采样部分主要有 CBS 普通卷积,UpSample 反卷积和 ELAN-W 的结构。

  • ELAN-W 结构

ELAN-W 跟 ELAN 基本上是一样的,但是它的 concat 合并是 6 层合并,ELAN 是 4 层合并。配置文件如下

[-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

PAN 的下采样部分包括 ELAN-W 和 MP-2 两种结构

  • MP-2 结构

MP-2 结构和 MP-1 结构是一样的,它们的区别在于输出通道数不同,MP-2 的输出通道数是 MP-1 的 2 倍。配置文件如下

[-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]],

而 YOLOV5 的 PAN 部分除了 UpSample 外只有 C3 结构。

Head 部分

YOLOV7 的 Head 部分也是三个检测头,对不同尺度的特征图进行坐标预测和分类。

相比于 YOLOV5 的 PAN 每一层的直接输出,YOLOV7 用到了现今比较流行的重参的 REP 结构。

  • REP 结构

REP 在部署和训练的时候结构不同。在训练的时候,如果输入输出通道数相同,则包含了第三层 BN 的直连通道,否则只有第一层的 1*1 卷积 + BN 以及第二层的 3*3 卷积 + BN,上面虽然画的是 CBS,但其实这里是没有激活函数 SiLu 的,只有三层相加才会被最后激活。在部署时,为了方便部署,会将分支的参数重参数化到主分支上,取 3*3 的主分支卷积输出,但这里是没有 BN 的,只有激活。所以上面这个图是存在歧义的,需要指出。

class RepConv(nn.Module):
    # Represented convolution
    # https://arxiv.org/abs/2101.03697

    def __init__(self, c1, c2, k=3, s=1, p=None, g=1, act=True, deploy=False):
        super(RepConv, self).__init__()

        self.deploy = deploy
        self.groups = g
        self.in_channels = c1
        self.out_channels = c2

        assert k == 3
        assert autopad(k, p) == 1

        padding_11 = autopad(k, p) - k // 2

        self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())

        if deploy:
            # 如果是部署,则直接使用3*3的卷积
            self.rbr_reparam = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=True)

        else:  # 如果是训练
            # 如果输入输出通道数相同,则进行BatchNorm的直连
            self.rbr_identity = (nn.BatchNorm2d(num_features=c1) if c2 == c1 and s == 1 else None)
            # 3*3卷积+BatchNorm
            self.rbr_dense = nn.Sequential(
                nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False),
                nn.BatchNorm2d(num_features=c2),
            )
            # 1*1卷积+BatchNorm
            self.rbr_1x1 = nn.Sequential(
                nn.Conv2d( c1, c2, 1, s, padding_11, groups=g, bias=False),
                nn.BatchNorm2d(num_features=c2),
            )

    def forward(self, inputs):
        if hasattr(self, "rbr_reparam"):
            return self.act(self.rbr_reparam(inputs))

        if self.rbr_identity is None:
            id_out = 0
        else:
            id_out = self.rbr_identity(inputs)

        return self.act(self.rbr_dense(inputs) + self.rbr_1x1(inputs) + id_out)

配置文件

[75, 1, RepConv, [256, 3, 1]],
[88, 1, RepConv, [512, 3, 1]],
[101, 1, RepConv, [1024, 3, 1]],
  • 模型的重参化

RepConv 我们需要看成一个整体,重参化的意思就是它相比于一个普通的卷积,因为有三层结构,它的参数量更多了,也更适合于反向传播。它在 VGG 上取得了优异的性能,但将它直接用于 ResNet 和 DenseNet 时,它的精度反而会显著下降。作者使用梯度传播路径来分析不同的重参化模块应该和哪些网络搭配使用。通过分析 RepConv 与不同架构的组合以及产生的性能,发现 RepConv 中的 identity 破坏了 ResNet 中的残差结构和 DenseNet 中的跨层连接,这为不同的特征图提供了梯度的多样性。

上图中的 a 是两个普通的卷积串联,b 是两个 RepConv 串联,c 是 ResNet,d 是将 ResNet 中的两个普通卷积替换成了 RepConv,作者发现这种情况由于 RepConv 内部存在 identity 的直连会对 ResNet 造成破坏。e 是 ResNet 中的第一个普通卷积替换成 RepConv,而第二个不替换是可以的,反之在 f 中是不行的。在 g 和 h 中,RepConvN 放在上下都是可以的。这些都是通过实验得来的结果。

  • 辅助训练模块

深度监督是一种常用于训练深度网络的技术,其主要概念是在网络的中间层增加额外的辅助头,以及在辅助损失为指导的浅层网络权重。即使对于像 ResNet 和 DenseNet 这样收敛效果好的网络结构,深度监督仍然可以显著提高模型在许多任务上的性能。

深度监督 (Deep Supervision) 又称为中继监督 (intermediate supervision),就是在深度神经网络的某些中间隐藏层加了一个辅助的分类器作为一种网络分支来对主干网络进行监督的技巧,其实就是在网络的中间部分添加了额外的 loss,跟多任务是有区别的,多任务有不同的 ground truth 计算不同的 loss,而深度监督的 ground truth 都是同一个 ground truth,不同位置的 loss 按系数求和。深度监督的目的是为了浅层能够得到更加充分的训练,解决深度神经网络训练梯度消失和收敛速度过慢等问题。

上图中 a 是常见的目标检测网络结构,不包含深度监督。b 是包含了深度监督的目标检测器架构,这里最终输出的头称为引导头,用于辅助训练的头称为辅助头。在标签分配的问题中,过去在深度网络的训练中,标签分配通常直接指的是 ground truth,并根据指定的规则生成 hard label。近年来,研究者经常利用网络预测的质量分布来结合 ground truth,使用一些计算和优化方法来生成可靠的软标签 (soft label)。在本文中,作者将网络预测结果与 ground truth 一起考虑后再分配软标签的机制称为 "标签分配器"。无论辅助头或引导头,都需要对目标进行深度监督。进行软标签分配的方法如上图中 c 所示,将辅助头和引导头分离,然后利用它们各自的预测结果和 ground truth 执行标签分配。通过引导头的预测来引导辅助头以及自身。换句话说,首先使用引导头的 prediction 作为指导,生成从粗到细的层次标签,分别作用于辅助头和引导头的学习。

Coarse-to-fine lead guided label assigner (粗到细引导标签分配器):Coarse-to-fine 引导头使用到了自身的 prediction 和 ground truth 来生成软标签,引导标签进行分配。然而在这个过程中,作者生成了两组不同的软标签,即粗标签和细标签。其中细标签与引导头在标签分配器上生成的软标签相同,粗标签是通过降低正样本分配的约束,允许更多的网络作为正目标。原因是一个辅助头的学习能力并不需要强大的引导头,为了避免丢失信息,作者将专注于优化样本召回的辅助头。对于引导头的输出,可以从查准率中过滤出高精度值的结果作为最终输出。然而,值得注意的是,如果粗标签的附加权值接近细标签的附加权值,则可能会在最终预测时产生错误的先验结果。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-11-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • YOLOV7
相关产品与服务
云硬盘
云硬盘(Cloud Block Storage,CBS)为您提供用于 CVM 的持久性数据块级存储服务。云硬盘中的数据自动地在可用区内以多副本冗余方式存储,避免数据的单点故障风险,提供高达99.9999999%的数据可靠性。同时提供多种类型及规格,满足稳定低延迟的存储性能要求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档