改进YOLOv5系列:29.YOLOv5 结合 极简又强大的RepVGG 重参数化模型结构
最新创新点改进推荐
-💡统一使用 YOLO 代码框架,结合不同模块来构建不同的YOLO目标检测模型。
🔥 《芒果书》系列改进专栏内的改进文章,均包含多种模型改进方式,均适用于YOLOv3
、YOLOv4
、 YOLOR
、 YOLOX
、YOLOv5
、 YOLOv7
、 YOLOv8
改进(重点)!!!
🔥 专栏创新点教程 均有不少同学反应和我说已经在自己的数据集上有效涨点啦!! 包括COCO数据集也能涨点
所有文章博客均包含 改进源代码部分,一键训练即可
🔥 对应专栏订阅的越早,就可以越早使用原创创新点
去改进模型,抢先一步
以下《芒果书》改进YOLO专栏内容 适用于以下主流模型的改进
YOLOv3
、YOLOv4
、 YOLOR
、 YOLOX
YOLOv5
、 YOLOv7
、 YOLOv8
… 以上模型改进均适用《芒果书》🥭专栏系列
芒果书 点击以下链接 查看文章目录详情🔗
-
💡🎈☁️:一、CSDN原创《芒果改进YOLO高阶指南》强烈改进涨点推荐!📚推荐指数:🌟🌟🌟🌟🌟
-
💡🎈☁️:二、CSDN原创YOLO进阶 | 《芒果改进YOLO进阶指南》改进涨点推荐!📚推荐指数:🌟🌟🌟🌟🌟
-
💡🎈☁️:三、CSDN独家全网首发专栏 | 《目标检测YOLO改进指南》改进涨点推荐!推荐指数:🌟🌟🌟🌟🌟
本篇是《RepVGG结构🚀》的修改 演示
使用YOLOv5网络🚀作为示范,可以无缝加入到 YOLOv7、YOLOX、YOLOR、YOLOv4、Scaled_YOLOv4、YOLOv3等一系列YOLO算法模块
文章目录
1.RepVGG模型理论部分
论文参考:最新RepVGG结构: Paper
模型定义
我们所说的“VGG式”指的是:
-
没有任何分支结构。即通常所说的plain或feed-forward架构。
-
仅使用3x3卷积。
-
仅使用ReLU作为激活函数。
结构重参数化让VGG再次伟大
相比于各种多分支架构(如ResNet,Inception,DenseNet,各种NAS架构),近年来VGG式模型鲜有关注,主要自然是因为性能差。例如,有研究[1]认为,ResNet性能好的一种解释是ResNet的分支结构(shortcut)产生了一个大量子模型的隐式ensemble(因为每遇到一次分支,总的路径就变成两倍),单路架构显然不具备这种特点。
2.在YOLOv5中加入RepVGG模块🚀
使用YOLOv5算法🚀作为演示,模块可以无缝插入到YOLOv7、YOLOv5、YOLOv4、Scaled_YOLOv4、YOLOv3、YOLOR等一系列YOLO算法中
新增YOLOv5的yaml配置文件
首先增加以下yolov5_RepVGG.yaml文件,作为改进演示
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
# Parameters
nc: 80 # number of classes
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.50 # layer channel multiple
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 v6.0 backbone by yoloair
backbone:
# [from, number, module, args]
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
[-1, 1, RepVGGBlock, [128]], # 5-P4/16
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
[-1, 6, RepVGGBlock, [256]],
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
[-1, 9, C3, [512]],
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
[-1, 3, C3, [1024]],
[-1, 1, SPPF, [1024, 5]], # 9
]
# YOLOv5 v6.0 head
head:
[[-1, 1, Conv, [512, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 6], 1, Concat, [1]], # cat backbone P4
[-1, 3, C3, [512, False]], # 13
[-1, 1, Conv, [256, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 4], 1, Concat, [1]], # cat backbone P3
[-1, 3, C3, [256, False]], # 17 (P3/8-small)
[-1, 1, Conv, [256, 3, 2]],
[[-1, 14], 1, Concat, [1]], # cat head P4
[-1, 3, C3, [512, False]], # 20 (P4/16-medium)
[-1, 1, Conv, [512, 3, 2]],
[[-1, 10], 1, Concat, [1]], # cat head P5
[-1, 3, C3, [1024, False]], # 23 (P5/32-large)
[[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5)
]
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))
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:]]
训练yolov5_RepVGGBlock模型
python train.py --cfg yolov5_RepVGGBlock.yaml
推理过程效果
以下使用单独测试的RepVGG模块作为参考:
训练的时候代码
Model Summary: 375 layers, 5574845 parameters, 5574845 gradients, 16.2 GFLOPs
推理时候的代码
Model Summary: 284 layers, 5390365 parameters, 1567680 gradients, 15.7 GFLOPs
推理模型的数据相比于训练模型的数据
参数量、计算量、推理时间均有所减少
参考文献: 理论部分来自RepVGG作者的知乎文章:https://zhuanlan.zhihu.com/p/344324470
相关文章
- 微服务系列之授权认证(三) JWT
- Python入门系列(一)安装环境
- 低代码系列之代码生成器模型配置--filter
- PyTorch学习系列教程:三大神经网络在股票数据集上的实战
- PGL图学习之图神经网络ERNIESage、UniMP进阶模型[系列八]
- 【愚公系列】2022年09月 微信小程序-three.js加载3D模型
- 语音识别系列︱paddlespeech的开源语音识别模型测试(三)
- Python学习系列之lambda表达式
- Java系列 | 属性依赖注入被认为是有害的
- rabbitMQ系列高级整合应用rabbitTemplate
- 广告行业中那些趣事系列56:超实用的多模态学习模型VILT源码实践
- Paddle Graph Learning (PGL)图学习之图游走类deepwalk、node2vec模型[系列四]
- 客快物流大数据项目(九十二):ClickHouse的MergeTree系列引擎介绍和MergeTree深入了解
- 系列文章| R语言分布滞后线性和非线性模型DLM和DLNM建模应用|附代码数据
- Struct2系列漏洞
- odoo 开发入门教程系列-模型和基本字段
- odoo 开发入门教程系列-模型之间的关系(Relations Between Models)
- Javascript拖拽系列文章1之offsetParent属性