YOLOv5/v7 引入 RepVGG 重参数化模块

news2024/9/22 6:11:53

本篇博文代码出自YOLOv5-liteYOLOv5-lite的作者在CSDN的账号是 pogg_ ,大家可以关注一下,这也是一位在开源项目上做了很多工作的博主。

RepVGG的原理和融合推导过程可以看我的这篇博文:RepVGG:让VGG风格的ConvNets再次伟大
RepVGG思想融入到YOLOv5中的思想可以参考 pogg 的博文:Repvgg重参化对YOLO工业落地的实验和思考


重参数化算法原理


论文地址:https://arxiv.org/abs/2101.03697

我们提出了一种简单但功能强大的卷积神经网络结构,该模型在推理时类似于VGG,只有3×3的卷积和ReLU堆叠而成,而训练时间模型具有多分支拓扑结构。训练时间和推理时间结构的这种解耦是通过结构重新参数化技术实现的,因此该模型被命名为RepVGG。在ImageNet上,RepVGG达到了超过80%的TOP-1准确率,据我们所知,这是第一次使用普通模型。在NVIDIA 1080Ti GPU上,RepVGG型号的运行速度比ResNet-50快83%,比ResNet-101快101%,精度更高,并且与EfficientNet和RegNet等最先进的型号相比,显示出良好的精度和速度折衷。代码和经过训练的模型可在以下位置获得 https://github.com/megvii-model/RepVGG.


经典的卷积神经网络(ConvNet) VGG通过一个由convReLUpooling组成的简单体系结构在图像识别方面取得了巨大成功。随着Inception、ResNet和DenseNet的出现,大量的研究兴趣转移到了精心设计的架构上,使得模型越来越复杂 ,一些最近的架构是基于自动或手动架构搜索,或者搜索基于基本架构的混合尺寸策略得到的强大架构。

虽然许多复杂的卷积网络比简单的卷积网络具有更高的精度,但其缺点是明显的。

  1. 复杂的多分支设计(如ResNet中的残差相加和Inception中的分支连接)使模型难以实现和自定义,降低了推理速度和降低了内存利用率。
  2. 一些组件(例如Xception和MobileNets中的depth conv和ShuffleNets中的channel shuffle)增加了内存访问成本,缺乏各种设备的支持。

由于影响推理速度的因素太多,浮点运算(FLOPs)的数量并不能精确地反映实际速度。尽管一些新模型的FLOP低于老式模型,如VGG和ResNet-18/34/50,他们可能不会跑得更快,因此,VGG和ResNets的原始版本仍然大量用于学术界和工业界。
在本文中,我们提出了RepVGG,这是一种VGG风格的架构,其性能优于许多复杂的模型(图1)。RepVGG具有以下优点。

  • 该模型具有类似VGG的无分支(即前馈)拓扑,这意味着每一层都将其唯一前一层的输出作为输入,并将输出馈送到其唯一后一层。
  • 该模型的主体仅使用3×3 convReLU
  • 具体的架构(包括特定的深度和层宽度)实例化时不需要自动搜索、手动细化、复合缩放,也不需要其他繁重的设计。

对于一个普通模型来说,要达到与多分支体系结构相当的性能水平是很有挑战性的。一种解释是,多分支拓扑,如ResNet,使模型成为众多浅层模型的隐式集成,从而训练多分支模型避免了梯度消失问题。

由于多分支结构的优点都是训练的,而缺点是不利于推理的,我们提出了通过结构重新参数化将训练时多分支结构和推理时平面结构解耦,即通过变换结构参数将结构从一个结构转换到另一个结构。具体地说,网络结构与一组参数相耦合,例如,

卷积层由四阶核张量表示。如果某一结构的参数可以转换为另一结构耦合的另一组参数,我们可以等效地用后者替代前者,从而改变整个网络架构。

具体来说,我们使用identity1×1分支构造了训练时的RepVGG,这是受ResNet的启发,但采用了不同的方式,可以通过结构重新参数化来删除分支(图2、4)。经过训练后,我们用简单代数进行变换,将一个identity分支看作是一个降级的1×1 conv,后者可以进一步看作是一个降级的3×3 conv,这样我们就可以用原3×3 kernelidentity1×1分支以及批归一化(BN)层的训练参数构造一个3×3 kernel。因此,转换后的模型有一堆3×3conv层,保存用于测试和部署。
在这里插入图片描述

在这里插入图片描述


参数量与计算量

模型layersparametersgradientsGFLOPs
yolov5_RepVGG融合前3755574845557484516.2
yolov5_RepVGG融合后2805390365118016015.7

代码添加方式

第一步;将如下代码添加到common.py中:

# build repvgg block
# -----------------------------
def conv_bn(in_channels, out_channels, kernel_size, stride, padding, groups=1):
    result = nn.Sequential()
    result.add_module('conv', nn.Conv2d(in_channels=in_channels, out_channels=out_channels,
                                        kernel_size=kernel_size, stride=stride, padding=padding, groups=groups,
                                        bias=False))
    result.add_module('bn', nn.BatchNorm2d(num_features=out_channels))

    return result


class SEBlock(nn.Module):

    def __init__(self, input_channels, internal_neurons):
        super(SEBlock, self).__init__()
        self.down = nn.Conv2d(in_channels=input_channels, out_channels=internal_neurons, kernel_size=1, stride=1,
                              bias=True)
        self.up = nn.Conv2d(in_channels=internal_neurons, out_channels=input_channels, kernel_size=1, stride=1,
                            bias=True)
        self.input_channels = input_channels

    def forward(self, inputs):
        x = F.avg_pool2d(inputs, kernel_size=inputs.size(3))
        x = self.down(x)
        x = F.relu(x)
        x = self.up(x)
        x = torch.sigmoid(x)
        x = x.view(-1, self.input_channels, 1, 1)
        return inputs * x


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

    def fusevggforward(self, x):
        return self.nonlinearity(self.rbr_dense(x))
# repvgg block end
# -----------------------------

第二步;在yolo.py中找到 def fuse(self): ,用如下代码替换:

# --------------------------repvgg refuse---------------------------------
    def fuse(self):  # fuse model Conv2d() + BatchNorm2d() layers
        print('Fusing layers... ')
        for m in self.model.modules():
            if type(m) is RepVGGBlock:
                if hasattr(m, 'rbr_1x1'):
                    kernel, bias = m.get_equivalent_kernel_bias()
                    rbr_reparam = nn.Conv2d(in_channels=m.rbr_dense.conv.in_channels,
                                                out_channels=m.rbr_dense.conv.out_channels,
                                                kernel_size=m.rbr_dense.conv.kernel_size,
                                                stride=m.rbr_dense.conv.stride,
                                                padding=m.rbr_dense.conv.padding, dilation=m.rbr_dense.conv.dilation,
                                                groups=m.rbr_dense.conv.groups, bias=True)
                    rbr_reparam.weight.data = kernel
                    rbr_reparam.bias.data = bias
                    for para in self.parameters():
                        para.detach_()
                    m.rbr_dense = rbr_reparam
                    m.__delattr__('rbr_1x1')
                    if hasattr(m, 'rbr_identity'):
                        m.__delattr__('rbr_identity')
                    if hasattr(m, 'id_tensor'):
                        m.__delattr__('id_tensor')
                    m.deploy = True
                    delattr(m, 'se')
                    m.forward = m.fusevggforward  # update forward
            if isinstance(m, (Conv, DWConv)) and hasattr(m, 'bn'):
                m.conv = fuse_conv_and_bn(m.conv, m.bn)  # update conv
                delattr(m, 'bn')  # remove batchnorm
                m.forward = m.forward_fuse  # update forward
        self.info()
        return self
    # --------------------------end repvgg & shuffle refuse--------------------------------

第三步;在yolo.py中将RepVGGBlock添加到如下位置:

在这里插入图片描述
第四步;修改配置文件,yolov5_RepVGG配置文件如下:

# create by pogg
# parameters
nc: 80  # number of classes
depth_multiple: 1  # model depth multiple
width_multiple: 1  # layer channel multiple

# anchors
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

# YOLOv5-repvgg backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Focus, [32, 3]],  # 0-P1/2
   [-1, 1, RepVGGBlock, [64, 3, 2]], # 1-P2/4
   [-1, 1, C3, [64]],
   [-1, 1, RepVGGBlock, [128, 3, 2]], # 3-P3/8
   [-1, 3, C3, [128]],
   [-1, 1, RepVGGBlock, [256, 3, 2]], # 5-P4/16
   [-1, 3, C3, [256]],
   [-1, 1, RepVGGBlock, [512, 3, 2]], # 7-P4/16
   [-1, 1, SPP, [512, [5, 9, 13]]],
   [-1, 1, C3, [512, False]],  # 9
  ]

# YOLOv5 head
head:
  [[-1, 1, Conv, [128, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 6], 1, Concat, [1]],  # cat backbone P4
   [-1, 3, C3, [128, False]],  # 13

   [-1, 1, Conv, [128, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P3
   [-1, 3, C3, [128, False]],  # 17 (P3/8-small)

   [-1, 1, Conv, [128, 3, 2]],
   [[-1, 14], 1, Concat, [1]],  # cat head P4
   [-1, 3, C3, [128, False]],  # 20 (P4/16-medium)

   [-1, 1, Conv, [128, 3, 2]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [128, False]],  # 23 (P5/32-large)

   [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

这个结构的用法有很多,大家可以自己探索~


本人更多YOLOv5实战内容导航🍀🌟🚀

  1. 手把手带你调参Yolo v5 (v6.2)(推理)🌟强烈推荐

  2. 手把手带你调参Yolo v5 (v6.2)(训练)🚀

  3. 手把手带你调参Yolo v5 (v6.2)(验证)

  4. 如何快速使用自己的数据集训练Yolov5模型

  5. 手把手带你Yolov5 (v6.2)添加注意力机制(一)(并附上30多种顶会Attention原理图)🌟强烈推荐🍀新增8种

  6. 手把手带你Yolov5 (v6.2)添加注意力机制(二)(在C3模块中加入注意力机制)

  7. Yolov5如何更换激活函数?

  8. Yolov5如何更换BiFPN?

  9. Yolov5 (v6.2)数据增强方式解析

  10. Yolov5更换上采样方式( 最近邻 / 双线性 / 双立方 / 三线性 / 转置卷积)

  11. Yolov5如何更换EIOU / alpha IOU / SIoU?

  12. Yolov5更换主干网络之《旷视轻量化卷积神经网络ShuffleNetv2》

  13. YOLOv5应用轻量级通用上采样算子CARAFE

  14. 空间金字塔池化改进 SPP / SPPF / SimSPPF / ASPP / RFB / SPPCSPC / SPPFCSPC🚀

  15. 用于低分辨率图像和小物体的模块SPD-Conv

  16. GSConv+Slim-neck 减轻模型的复杂度同时提升精度🍀

  17. 头部解耦 | 将YOLOX解耦头添加到YOLOv5 | 涨点杀器🍀

  18. Stand-Alone Self-Attention | 搭建纯注意力FPN+PAN结构🍀

  19. YOLOv5模型剪枝实战🚀

  20. YOLOv5知识蒸馏实战🚀

  21. YOLOv7知识蒸馏实战🚀

  22. 改进YOLOv5 | 引入密集连接卷积网络DenseNet思想 | 搭建密集连接模块🍀

  23. YOLOv5更换骨干网络之 PP-LCNet🍀

  24. YOLOv5更换骨干网络之 EfficientNet-B0🍀

  25. YOLOv5更换骨干网络之 MobileNet V3🍀

  26. YOLOv5更换骨干网络之 GhostNet🍀

  27. YOLOv5 引入 最新 BiFusion Neck🍀


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/177088.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机制设计原理与应用(三)Screening

文章目录3 Screening3.1 为单个不可分割的项目定价3.1.1 对θ\thetaθ的假设3.1.2 问题描述3.1.3 特性3.2 为无限可分的项目定价3.2.1 对θ\thetaθ的假设3.2.3 特性3.2.4 收益最大化3.2.5 最优解决方案3 Screening Screening theory:机制设计理论可以被看作是其多…

Cadence PCB仿真使用Allegro PCB SI生成振铃ringing仿真报告及报告导读图文教程

🏡《Cadence 开发合集目录》   🏡《Cadence PCB 仿真宝典目录》 目录 1,概述2,生成报告3,报告导读4,总结1,概述 本文简单介绍使用Allegro PCB SI生成网络的振铃性能评估的报告的方法,及振铃ringing报告要点导读。 2,生成报告 第1步,选择需要生成报告的网络,然后…

第二章 ArcGIS数据和地理数据库

文章目录第一节 ArcGIS和4D数据基本知识1 4D数据介绍1.1 DLG1.2 DEM1.3 DOM1.4 DRG1.5 4D表现2 ArcGIS的数据和4D数据对应3 栅格数据3.1 查看帮助3.2 空间分辨率3.3 分辨率与比例尺换算3.4 栅格数据介绍——cellsize3.5 栅格数据波段3.6 栅格格式4 栅格数据改变分辨率5 转换栅格…

【 uniapp - 黑马优购 | 登录与支付(2)】如何实现三秒后跳转和微信支付

个人名片: 🐼作者简介:一名大二在校生,讨厌编程🎋 🐻‍❄️个人主页🥇:小新爱学习. 🐼个人WeChat:见文末 🕊️系列专栏:🖼…

Ubuntu20.04+MAVROS+PX4+Gazebo安装教程

Ubuntu20.04MAVROSPX4Gazebo安装PX4步骤安装MAVROS安装QGCPX4仿真安装PX4步骤 从github上clone源码 git clone https://github.com/PX4/PX4-Autopilot.git --recursive进入PX4-Autopilot文件夹,继续下载未下载完的组件 cd PX4-Autopilot/ git submodule update -…

flowable使用 act_hi_xxx

HistoryService 流程历史信息 act_hi_procinst : 历史流程信息&#xff0c;&#xff0c;如果流程执行完了&#xff0c;end_time_ 和 duration不为null // 没有执行完的List<HistoricProcessInstance> list historyService.createHistoricProcessInstanceQuery().unfi…

uniapp封装并全局挂载request请求

前言 日常开发中,前端项目中需要调用服务端api完成页面渲染,uniapp提供的请求api:uni.request相对繁琐;另外服务端提供的不同api仅子路径不同,api域名以及根路径都是相同的,一旦接口api变更,需要更改地方就会很多.鉴于以上可以将uni.request进行封装,简化开发. 目前uniapp项…

MySQL(四):B+树索引、聚簇索引、二级索引、联合索引

目录一、B树索引1.1 在没有索引时进行查找记录1.2 索引方案1.3 InnoDB中的索引方案二、聚簇索引三、二级索引四、联合索引五、InnoDB中B树索引的注意事项5.1 根页面的位置不会改变5.2 内节点中目录项记录的唯一性5.3 一个页面至少容纳两条记录一、B树索引 数据库中的用来存储数…

MySQL进阶篇之索引1

02、索引 2.1、索引概述 1、介绍 索引&#xff08;index&#xff09;是帮助MySQL高效获取数据的数据结构&#xff08;有序&#xff09;。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08;指向&#…

Cepstral Analysis 倒谱分析

源过滤器分离 倒谱分析是另一种将声道滤波器响应与激励分开的方法&#xff08;如线性预测&#xff09; 它基于以下观察&#xff1a;语音信号的频谱是激励频谱和声道频率响应的乘积 可以使用log将乘法转换为加法&#xff0c;因此&#xff0c;“对数频谱”可以看作是对数激励频…

十七、Gtk4-Menu and action

Menu 用户经常使用菜单向计算机发出命令。它是这样的: 现在让我们分析一下上面的菜单。对象有两种类型。 “File”, “Edit”, “View”, “Cut”, “Copy”, “Paste” and “Select All”. 它们被称为“菜单项&#xff08;menu item&#xff09;”或简单地称为“item”。当…

字节青训前端笔记 | 前端调试

在程序员的世界中&#xff0c;BUG 一词相信同学们再熟悉不过了&#xff0c;本节课将围绕前端开发中所遇见的 BUG 出发&#xff0c;讲解作为一名合格的前端开发人员&#xff0c;你应该掌握哪些开发调试知识 Chorme DevTools Chorme DevTools 是 chorme内核为大家提供的高效的前…

gdb使用

gdb是一款UNIX及UNIX-like下的调试工具 gdb可用于调试用gcc编译的可执行文件&#xff0c;用gdb调试时gcc编译需要使用参数-g 本文是对于gdb在Linux下使用的基本命令的总结gdb调试视频演示&#xff0c;gdb调试基础指令&#xff0c;gdb调试其他命令&#xff0c;gdb常见错误说明 目…

23种设计模式(二十一)——命令模式【行为变化】

文章目录 意图什么时候使用命令真实世界类比命令模式的实现命令模式的优缺点亦称:动作、事务、Action、Transaction、Command 意图 将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递…

Centos7 Docker安装APISIX

Centos7 Docker安装APISIX1 基础介绍1.1 概念1.2 特性1.3 架构图2 快速安装2.1 前提条件2.2 安装步骤2.2.1 git命令克隆apisix-docker仓库2.2.2 docker-compose启动apisix2.2.3 访问apisix dashboard3 简单使用3.1 准备接口3.2 创建服务3.3 创建路由3.4 测试请求1 基础介绍 1.…

Kubernetes:分享一个很简洁的 k8s 管理工具 Skooner

写在前面 博文内容为 Skooner 简单介绍包括下载安装导入集群基本功能使用Skooner 的 sa 使用的当前命名空间默认的 sa不会显示创建 sa ,当然可以单独创建理解不足小伙伴帮忙指正 我所渴求的&#xff0c;無非是將心中脫穎語出的本性付諸生活&#xff0c;為何竟如此艱難呢 -----…

Spring_FrameWork_10(MyBatisPlus)

lombok mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplTestvoid selectById(){IPage page new Page(2,2);bookDao.selectPage(page,null);System.out.println("当前页码值&#xff1a;"page.getCurrent());System.out.println…

Java---微服务---Nacos集群搭建

Nacos集群搭建1.集群结构图2.搭建集群2.1.初始化数据库2.2.下载nacos2.3.配置Nacos2.4.启动2.5.nginx反向代理2.6.优化1.集群结构图 官方给出的Nacos集群图&#xff1a; 其中包含3个nacos节点&#xff0c;然后一个负载均衡器代理3个Nacos。这里负载均衡器可以使用nginx。 我们…

解锁Windows所有电源选项

20230123 By wdhuag 目录 参考&#xff1a; 查看当前选项&#xff1a; 警告&#xff01;修改前&#xff0c;先使用Registry Workshop备份注册表&#xff0c;导出PowerSettings&#xff1a; 在PowerSettings里增加Attributes参数&#xff08;十六进制、数值2&#xff09;&a…

Shell脚本从入门到实战

Shell 文章目录Shellshell概述Shell脚本入门1. 脚本格式2. 第一个Shell脚本:helloworld3. 第二个Shell脚本&#xff1a;多命令处理Shell中的变量系统变量1. 常用系统变量2. 案例实操自定义变量1. 基本语法2. 变量定义规则特殊变量&#xff1a;$n特殊变量&#xff1a;$#特殊变量…