• 改进YOLOv7系列:25.YOLOv7 加入RepVGG模型结构,重参数化 极简架构


    最新创新点改进推荐

    -💡统一使用 YOLO 代码框架,结合不同模块来构建不同的YOLO目标检测模型。

    🔥 《芒果书》系列改进专栏内的改进文章,均包含多种模型改进方式,均适用于YOLOv3YOLOv4YOLORYOLOXYOLOv5YOLOv7YOLOv8 改进(重点)!!!

    🔥 专栏创新点教程 均有不少同学反应和我说已经在自己的数据集上有效涨点啦!! 包括COCO数据集也能涨点

    所有文章博客均包含 改进源代码部分,一键训练即可

    🔥 对应专栏订阅的越早,就可以越早使用原创创新点去改进模型,抢先一步

    点击查看详情:YOLOv5改进、YOLOv7改进|YOLO改进超过50种注意力机制,全篇共计30万字(内附改进源代码),原创改进50种Attention注意力机制和Transformer自注意力机制

    芒果书 点击以下链接 查看文章目录详情🔗


    本篇是《RepVGG结构🚀》的修改 演示

    使用YOLOv7网络🚀作为示范,可以无缝加入到 YOLOv7、YOLOX、YOLOR、YOLOv4、Scaled_YOLOv4、YOLOv3等一系列YOLO算法模块

    1.RepVGG模型理论部分

    论文参考:最新RepVGG结构: Paper

    在这里插入图片描述

    模型定义

    我们所说的“VGG式”指的是:

    1. 没有任何分支结构。即通常所说的plain或feed-forward架构。

    2. 仅使用3x3卷积。

    3. 仅使用ReLU作为激活函数。

    在这里插入图片描述

    结构重参数化让VGG再次伟大

    相比于各种多分支架构(如ResNet,Inception,DenseNet,各种NAS架构),近年来VGG式模型鲜有关注,主要自然是因为性能差。例如,有研究[1]认为,ResNet性能好的一种解释是ResNet的分支结构(shortcut)产生了一个大量子模型的隐式ensemble(因为每遇到一次分支,总的路径就变成两倍),单路架构显然不具备这种特点。

    在这里插入图片描述

    2.在YOLOv7中加入RepVGG模块🚀

    使用YOLOv7算法🚀作为演示,模块可以无缝插入到YOLOv7、YOLOv5、YOLOv4、Scaled_YOLOv4、YOLOv3、YOLOR等一系列YOLO算法中

    新增YOLOv7的yaml配置文件

    首先增加以下yolov7_RepVGG.yaml文件,作为改进演示

    代码
    # YOLOv7 🚀, GPL-3.0 license
    # parameters
    nc: 80  # number of classes
    depth_multiple: 0.33  # 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
    
    # yolov7 backbone by yoloair
    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 
       [-1, 1, RepVGGBlock, [128, 3, 2]], # 5-P4/16
       [-1, 1, Conv, [256, 3, 2]], 
       [-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
       [-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]],
       [-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]],
       [-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]],          
       [-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]],
       [-1, 1, C3C2, [1024]],
       [-1, 1, Conv, [256, 3, 1]],
      ]
    
    # yolov7 head by yoloair
    head:
      [[-1, 1, SPPCSPC, [512]],
       [-1, 1, Conv, [256, 1, 1]],
       [-1, 1, nn.Upsample, [None, 2, 'nearest']],
       [31, 1, Conv, [256, 1, 1]],
       [[-1, -2], 1, Concat, [1]],
       [-1, 1, C3C2, [128]],
       [-1, 1, Conv, [128, 1, 1]],
       [-1, 1, nn.Upsample, [None, 2, 'nearest']],
       [18, 1, Conv, [128, 1, 1]],
       [[-1, -2], 1, Concat, [1]],
       [-1, 1, C3C2, [128]],
       [-1, 1, MP, []],
       [-1, 1, Conv, [128, 1, 1]],
       [-3, 1, Conv, [128, 1, 1]],
       [-1, 1, Conv, [128, 3, 2]],
       [[-1, -3, 44], 1, Concat, [1]],
       [-1, 1, C3C2, [256]], 
       [-1, 1, MP, []],
       [-1, 1, Conv, [256, 1, 1]],
       [-3, 1, Conv, [256, 1, 1]],
       [-1, 1, Conv, [256, 3, 2]], 
       [[-1, -3, 39], 1, Concat, [1]],
       [-1, 3, C3C2, [512]],
    
    # 检测头 -----------------------------
       [49, 1, RepConv, [256, 3, 1]],
       [55, 1, RepConv, [512, 3, 1]],
       [61, 1, RepConv, [1024, 3, 1]],
    
       [[62,63,64], 1, IDetect, [nc, anchors]],   # Detect(P3, P4, P5)
      ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    当需要修改yaml配置文件,将xx模块 加到你想加入的位置(层数);
    首先基于一个可以成功运行的.yaml模型配置文件,进行新增或者减少层数 之后,那么该层网络后续的层的编号都会发生改变,对应的一些层都需要针对性的修改,以匹配通道和层数的关系

    common.py配置

    在./models/common.py文件中增加以下模块,直接复制即可

    class RepVGGBlock(nn.Module):
        def __init__(self, in_channels, out_channels, kernel_size=3,
                     stride=1, padding=1, dilation=1, groups=1, padding_mode='zeros', deploy=False, use_se=False):
            super(RepVGGBlock, self).__init__()
            self.deploy = deploy
            self.groups = groups
            self.in_channels = in_channels
            padding_11 = padding - kernel_size // 2
            self.nonlinearity = nn.SiLU()
            # self.nonlinearity = nn.ReLU()
            if use_se:
                self.se = SEBlock(out_channels, internal_neurons=out_channels // 16)
            else:
                self.se = nn.Identity()
            if deploy:
                self.rbr_reparam = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size,
                                             stride=stride,
                                             padding=padding, dilation=dilation, groups=groups, bias=True,
                                             padding_mode=padding_mode)
    
            else:
                self.rbr_identity = nn.BatchNorm2d(
                    num_features=in_channels) if out_channels == in_channels and stride == 1 else None
                self.rbr_dense = conv_bn(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size,
                                         stride=stride, padding=padding, groups=groups)
                self.rbr_1x1 = conv_bn(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=stride,
                                       padding=padding_11, groups=groups)
                # print('RepVGG Block, identity = ', self.rbr_identity)
    def switch_to_deploy(self):
            if hasattr(self, 'rbr_1x1'):
                kernel, bias = self.get_equivalent_kernel_bias()
                self.rbr_reparam = nn.Conv2d(in_channels=self.rbr_dense.conv.in_channels, out_channels=self.rbr_dense.conv.out_channels,
                                        kernel_size=self.rbr_dense.conv.kernel_size, stride=self.rbr_dense.conv.stride,
                                        padding=self.rbr_dense.conv.padding, dilation=self.rbr_dense.conv.dilation, groups=self.rbr_dense.conv.groups, bias=True)
                self.rbr_reparam.weight.data = kernel
                self.rbr_reparam.bias.data = bias
                for para in self.parameters():
                    para.detach_()
                self.rbr_dense = self.rbr_reparam
                # self.__delattr__('rbr_dense')
                self.__delattr__('rbr_1x1')
                if hasattr(self, 'rbr_identity'):
                    self.__delattr__('rbr_identity')
                if hasattr(self, 'id_tensor'):
                    self.__delattr__('id_tensor')
                self.deploy = True
    
        def get_equivalent_kernel_bias(self):
            kernel3x3, bias3x3 = self._fuse_bn_tensor(self.rbr_dense)
            kernel1x1, bias1x1 = self._fuse_bn_tensor(self.rbr_1x1)
            kernelid, biasid = self._fuse_bn_tensor(self.rbr_identity)
            return kernel3x3 + self._pad_1x1_to_3x3_tensor(kernel1x1) + kernelid, bias3x3 + bias1x1 + biasid
    
        def _pad_1x1_to_3x3_tensor(self, kernel1x1):
            if kernel1x1 is None:
                return 0
            else:
                return torch.nn.functional.pad(kernel1x1, [1, 1, 1, 1])
    
        def _fuse_bn_tensor(self, branch):
            if branch is None:
                return 0, 0
            if isinstance(branch, nn.Sequential):
                kernel = branch.conv.weight
                running_mean = branch.bn.running_mean
                running_var = branch.bn.running_var
                gamma = branch.bn.weight
                beta = branch.bn.bias
                eps = branch.bn.eps
            else:
                assert isinstance(branch, nn.BatchNorm2d)
                if not hasattr(self, 'id_tensor'):
                    input_dim = self.in_channels // self.groups
                    kernel_value = np.zeros((self.in_channels, input_dim, 3, 3), dtype=np.float32)
                    for i in range(self.in_channels):
                        kernel_value[i, i % input_dim, 1, 1] = 1
                    self.id_tensor = torch.from_numpy(kernel_value).to(branch.weight.device)
                kernel = self.id_tensor
                running_mean = branch.running_mean
                running_var = branch.running_var
                gamma = branch.weight
                beta = branch.bias
                eps = branch.eps
            std = (running_var + eps).sqrt()
            t = (gamma / std).reshape(-1, 1, 1, 1)
            return kernel * t, beta - running_mean * gamma / std
    
        def forward(self, inputs):
            if self.deploy:
                return self.nonlinearity(self.rbr_dense(inputs))
            if hasattr(self, 'rbr_reparam'):
                return self.nonlinearity(self.se(self.rbr_reparam(inputs)))
    
            if self.rbr_identity is None:
                id_out = 0
            else:
                id_out = self.rbr_identity(inputs)
    
            return self.nonlinearity(self.se(self.rbr_dense(inputs) + self.rbr_1x1(inputs) + id_out))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    其中缺少的C3C2模块 需要补充,在Git中

    yolo.py配置

    然后找到./models/yolo.py文件下里的parse_model函数,将类名加入进去
    在 models/yolo.py文件夹下

    • parse_model函数中
    • for i, (f, n, m, args) in enumerate(d['backbone'] + d['head']):内部
    • 对应位置 下方只需要增加 RepVGGBlock模块

    参考代码

    elif m is RepVGGBlock:
                c1, c2 = ch[f], args[0]
                if c2 != no:  # if not output
                    c2 = make_divisible(c2 * gw, 8)
                args = [c1, c2, *args[1:]]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    训练yolov7_RepVGGBlock模型

    python train.py --cfg yolov7_RepVGGBlock.yaml
    
    • 1

    推理过程效果

    以下使用单独测试的RepVGG模块(基于v5)作为参考:

    训练的时候代码
    Model Summary: 375 layers, 5574845 parameters, 5574845 gradients, 16.2 GFLOPs
    
    推理时候的代码
    Model Summary: 284 layers, 5390365 parameters, 1567680 gradients, 15.7 GFLOPs
    
    • 1
    • 2
    • 3
    • 4
    • 5

    推理模型的数据相比于训练模型的数据

    参数量、计算量、推理时间均有所减少

    参考文献: 理论部分来自RepVGG作者的知乎文章:https://zhuanlan.zhihu.com/p/344324470

  • 相关阅读:
    【Myatis】mybatis的缓存机制
    shallow fusion--学习笔记
    11 医院挂号系统【平台前端搭建与首页】
    双十二买什么牌子电容笔?值得买的平价电容笔推荐
    解决ubuntu登录密码问题
    鲜花商城|基于Springboot实现鲜花商城系统
    SpringBoot整合liquibase
    PostgreSQL创建数据库、数据库管理员用户、该库的只读用户
    JVM_逃逸分析
    Spring Cloud 因为请求上游接口,没设置超时时间导致的服务雪崩
  • 原文地址:https://blog.csdn.net/qq_38668236/article/details/126715391