本篇博文代码出自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层,保存用于测试和部署。


参数量与计算量

模型 layers parameters gradients GFLOPs
yolov5_RepVGG融合前 375 5574845 5574845 16.2
yolov5_RepVGG融合后 280 5390365 1180160 15.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 resultclass 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_channelsdef 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 * xclass 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 = deployself.groups = groupsself.in_channels = in_channelspadding_11 = padding - kernel_size // 2self.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 Noneself.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 + biasiddef _pad_1x1_to_3x3_tensor(self, kernel1x1):if kernel1x1 is None:return 0else:return torch.nn.functional.pad(kernel1x1, [1, 1, 1, 1])def _fuse_bn_tensor(self, branch):if branch is None:return 0, 0if isinstance(branch, nn.Sequential):kernel = branch.conv.weightrunning_mean = branch.bn.running_meanrunning_var = branch.bn.running_vargamma = branch.bn.weightbeta = branch.bn.biaseps = branch.bn.epselse:assert isinstance(branch, nn.BatchNorm2d)if not hasattr(self, 'id_tensor'):input_dim = self.in_channels // self.groupskernel_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] = 1self.id_tensor = torch.from_numpy(kernel_value).to(branch.weight.device)kernel = self.id_tensorrunning_mean = branch.running_meanrunning_var = branch.running_vargamma = branch.weightbeta = branch.biaseps = branch.epsstd = (running_var + eps).sqrt()t = (gamma / std).reshape(-1, 1, 1, 1)return kernel * t, beta - running_mean * gamma / stddef 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 = 0else: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() layersprint('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 = kernelrbr_reparam.bias.data = biasfor para in self.parameters():para.detach_()m.rbr_dense = rbr_reparamm.__delattr__('rbr_1x1')if hasattr(m, 'rbr_identity'):m.__delattr__('rbr_identity')if hasattr(m, 'id_tensor'):m.__delattr__('id_tensor')m.deploy = Truedelattr(m, 'se')m.forward = m.fusevggforward  # update forwardif isinstance(m, (Conv, DWConv)) and hasattr(m, 'bn'):m.conv = fuse_conv_and_bn(m.conv, m.bn)  # update convdelattr(m, 'bn')  # remove batchnormm.forward = m.forward_fuse  # update forwardself.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实战内容导航

YOLOv5/v7 引入 RepVGG 重参数化模块相关推荐

  1. 追求极致:Repvgg重参数化对YOLO工业落地的实验和思考

    本文作者主要借鉴repvgg重参化的思想,将原有的3×3conv替换成Repvgg Block,为原有的YOLO模型涨点. 前言: 之前做了一次shufflenetv2与yolov5的组合,目的是为了 ...

  2. 改进YOLOv5、YOLOv8系列:29.YOLOv5 结合 极简又强大的RepVGG 重参数化模型结构

    最新创新点改进推荐 -

  3. 轻量级CNN模块!RepGhost:重参数化实现硬件高效的Ghost模块

    点击下方卡片,关注"CVer"公众号 AI/CV重磅干货,第一时间送达 点击进入-> CV 微信技术交流群 转载自:极市平台  作者丨科技猛兽 导读 本文作者希望通过结构重新 ...

  4. PFLD+GhostNet+MobileOne=PFLD_GhostOne,重参数化让PFLD重生,精度提升超过4%,速度提升超过55%,代码已开源

      在两年前,我曾经分享过利用GhostNet对PFLD进行优化的文章--<人脸关键点检测算法PFLD的优化实战记录>,那里面介绍了经过各种奇技淫巧,GhostNet确实能够提升PFLD的 ...

  5. CVPR 2022 | 超越RepVGG!浙大阿里提出OREPA:在线卷积重参数化

    点击下方卡片,关注"CVer"公众号 AI/CV重磅干货,第一时间送达 转载自:集智书童 OREPA: Online Convolutional Re-parameterizati ...

  6. 清华丁霄汉:从RepVGG系列谈起,结构重参数化如何暴力提升性能

    [专栏:前沿进展]"结构重参数化"是清华大学丁霄汉博士近年来提出的一种通用深度学习模型设计方法论.在青源 Live 第 34 期中,丁霄汉博士分享了题为「结构重参数化与通用视觉模型 ...

  7. 【论文学习】RepVGG: Making VGG-style ConvNets Great Again及网络重参数化的用途及效果分析

      本文学习了RepVGG以及同作者在网络重参数化领域的几篇文章,总结其主要原理,试验重参数化方法的效果并分析其价值意义.   RepVGG是CVPR2021收录的一篇论文,作者是清华大学的丁霄汉博士 ...

  8. 46FPS+1080Px2超分+手机NPU,arm提出一种基于重参数化思想的超高效图像超分方案

    编辑:Happy 首发:AIWalker 本文是ARM的研究员在大分辨率图像超分方面的一次探索,它将重参数化思想嵌入到图像超分结构中,并结合手机NPU硬件性能对现有超分的性能进行的对比分析.在Arm ...

  9. 结构重参数化(Structural Re-Parameters)PipLine

    文章目录 BASICS strcutural Inception 算法思想 算法核心 算法架构 Re-Parameter四部曲:ACNet ACNet原理 ACNet分析 涨点原因 推理阶段融合机制 ...

最新文章

  1. iOS:消除项目中警告
  2. html得到画布的颜色的值,从画布上获取像素颜色
  3. ML之RF:利用Pipeline(客户年龄/职业/婚姻/教育/违约/余额/住房等)预测客户是否购买该银行的产品二分类(预测、推理)
  4. 静态html搜索,如何为网站增加相关性较强的静态搜索页?
  5. 给Java程序员的Golang教程
  6. 创建一个dynamics CRM workflow (二) - Build in Workflows
  7. 关于Integer大小比较的问题
  8. vue中设置height:100%无效的问题及解决方法
  9. java21天打卡 day10-字符串2
  10. python如何下载os库_简谈下载安装Python第三方库的三种方法
  11. 虚假信息成物联网“毒瘤”
  12. 【Turtle合集】提前祝大家圣诞快乐,我为大家献歌一首,叮叮当,叮叮当,穷的响叮当——快开门,我送礼物来了哟~(圣诞树代码)
  13. Prettier formatter for vscode 配置单引号问题
  14. docker opengrok
  15. HTML学习笔记9——CSS3制作网页动画
  16. 关于内存、外存、磁盘、硬盘、软盘、光盘的区别
  17. Oracle数据库 —— DDL
  18. 那年杏花微雨,你说你是DOM
  19. “十进制网络”遭质疑 数字域名被当作笑话
  20. Y - 献给杭电五十周年校庆的礼物

热门文章

  1. 香港中文大学推荐的书单~
  2. Linux 命令量测试
  3. 关于微信引流的几种方法
  4. WordPress 使用 CDN 后获取访客真实 IP
  5. C++ opengl 漫反射和镜面反射参数
  6. ElasticSearch用法和IK分词器
  7. 三菱FX系列PLC以太网连接kepwareopc软件
  8. JPEG算法解密 JPEG原理详解 (转载 by jinchao)
  9. php数字对应的大字母,PHP判断数字,字母,特殊符号,中文 - 米扑博客
  10. 苹果公司的企业文化_企业文化到底有什么用?