一、本文介绍
本文给大家带来的最新改进机制是 融合改进 ,最近有好几个读者和我反应单独的机制都能够涨点,一融合起来就掉点,这是大家不了解其中的原理(这也是为什么我每一个机制都给大家讲解一下原理, 大家要明白其中的每个单独的机制涨点原理然后才能够更好的融合,有一些结构是有冲突的 ),不知道哪些模块和那些模块融合起来才能够涨点。所以本文给大家带来的改进机制是融合 ASFYOLO + RepViT 的融合改进机制, 同时本文的RepViT可以替换专栏内的任何一个主干 。
| 版本1训练信息:YOLO11-ASFYOLO-RepViT summary: 585 layers, 2,148,326 parameters, 2,148,310 gradients, 5.8 GFLOPs |
| 版本2训练信息:YOLO11-ASFYOLO-RepViT-2 summary: 597 layers, 1,672,093 parameters, 1,672,077 gradients, 4.4 GFLOPs |
| 基础版本:YOLO11 summary: 319 layers, 2,591,010 parameters, 2,590,994 gradients, 6.4 GFLOPs |
二、RepViT基本原理
官方论文地址: 官方论文地址点击即可跳转
官方代码地址: 官方代码地址点击即可跳转
RepViT: Revisiting Mobile CNN From ViT Perspective 这篇论文探讨了如何改进轻量级 卷积神经网络 (CNN)以提高其在移动设备上的性能和效率。作者们发现,虽然轻量级视觉变换器( ViT )因其能够学习全局表示而表现出色,但轻量级CNN和轻量级ViT之间的架构差异尚未得到充分研究。因此,他们通过整合轻量级ViT的高效架构设计,逐步改进标准轻量级CNN(特别是MobileNetV3),从而创造了一系列全新的纯CNN模型,称为RepViT。这些模型在各种视觉任务上表现出色,比现有的轻量级ViT更高效。
其主要的改进机制包括:
-
结构性重组 :通过结构性重组(Structural Re-parameterization, SR),引入多分支拓扑结构,以提高训练时的性能。
-
扩展比率调整 :调整卷积层中的扩展比率,以减少参数冗余和延迟,同时提高网络宽度以增强模型性能。
-
宏观设计优化 :对网络的宏观架构进行优化,包括早期卷积层的设计、更深的下采样层、简化的分类器,以及整体阶段比例的调整。
-
微观设计调整 :在微观架构层面进行优化,包括卷积核大小的选择和压缩激励(SE)层的最佳放置。
这些创新机制共同推动了轻量级CNN的性能和效率,使其更适合在移动设备上使用,下面的是官方论文中的结构图,我们对其进行简单的分析。
这张图片是论文中的图3,展示了RepViT架构的总览。RepViT有四个阶段,输入图像的分辨率依次为
每个阶段的通道维度用 Ci 表示,批处理大小用 B 表示。
- Stem :用于预处理输入图像的模块。
- Stage1-4 :每个阶段由多个RepViTBlock组成,以及一个可选的RepViTSEBlock,包含深度可分离卷积(3x3DW),1x1卷积,压缩激励模块(SE)和前馈网络(FFN)。每个阶段通过下采样减少空间维度。
- Pooling :全局平均池化层,用于减少特征图的空间维度。
- FC :全连接层,用于最终的类别预测。
总结: 大家可以将RepViT看成是MobileNet系列的改进版本
三、 BiFPN 原理
官方论文地址:
官方论文地址
官方代码地址: 官方代码地址
ASF-YOLO 是一个基于YOLO的新型模型,专为细胞实例分割设计。它将注意力尺度序列融合(ASF)集成到YOLO框架中,提高了对细胞图像的检测和分割性能。ASF-YOLO包括尺度序列 特征融合 (SSFF)模块、三重特征编码器(TFE)模块和通道及位置注意力机制(CPAM),这些特性共同提升了模型的准确性。在细胞数据集上的评估表明,ASF-YOLO在分割精度和速度方面表现出色。论文详细介绍了模型架构、方法论和在细胞图像数据集上的评估,证明了其相较于现有方法的有效性。
ASF-YOLO的主要创新点包括:
1. 尺度序列特征融合(SSFF)模块:
用于增强网络在多尺度信息提取上的能力。
2. 三重特征编码器(TFE)模块:
融合不同尺度的特征图以增加详细信息。
3. 通道和位置注意力机制(CPAM):
集成SSFF和TFE模块,专注于信息通道和与空间位置相关的小对象,从而提高检测和分割性能。
4. 优异的细胞实例分割性能:
在细胞数据集上显示出高度的分割准确性和速度。
这个图片来源于论文,文章中这个图片就特别模糊,不知道为什么,怎么发出来的文章。
这张图展示了ASF-YOLO模型的概览。模型主要包括尺度序列特征融合(SSFF)模块、三重特征编码(TFE)模块,以及基于CSPDarkNet主干网络和YOLO头部的通道和位置注意力模型(CPAM)。CSP和Concat模块来自于YOLOv5。在这个框架中,输入图像通过主干网络的不同层(P1到P5)进行特征提取,然后通过SSFF和CPAM模块进行特征融合和注意力加权,最终实现分割掩模的生成和不同尺度的边界框(P3,P4,P5 Box)的预测。
2.1 尺度序列特征融合(SSFF)模块
尺度序列特征融合(SSFF)模块是设计用于处理多尺度信息的关键 组件 。它的主要功能是增强 神经网络 在提取不同尺度特征时的能力。通过聚合来自网络不同层的特征,SSFF模块能够提供更为丰富和细致的特征表示,有助于改善模型在处理不同大小对象时的表现。这种融合策略特别适用于那些需要精确定位和识别图像中多尺度对象的任务,如细胞分割或面部识别等。通过SSFF,模型可以更有效地识别和理解图像中的细节,从而提高整体的检测和识别性能。
2.2 三重特征编码器(TFE)模块
在ASF-YOLO中的三重特征编码器(TFE)模块是一个创新的特征融合机制,它设计来处理不同尺寸的输入特征图。这个模块通过特定的操作,如卷积、批量标准化和激活 函数 ,对来自网络不同层的特征图进行处理。然后,它使用上采样和下采样技术来对特征图进行空间尺寸的调整,以确保特征图的尺寸一致。最后,通过一个融合操作(通常是拼接),将这些不同尺度的特征图结合在一起,形成一个综合特征表示,以捕获更多的上下文信息和细节。这样的设计使得网络能够更有效地处理多尺度信息,对于诸如细胞分割这样的细粒度任务尤为重要。
图4展示了三重特征编码器(TFE)模块的结构。C代表通道数,S代表特征图大小。每个三重特征编码器模块使用三种不同大小的特征图作为输入。该模块通过卷积、批归一化和SiLU激活函数对特征进行处理,并使用最大池化和平均池化进行下采样,以及最近邻上采样方法进行上采样,最后通过Concat操作合并特征,生成综合的特征表示。
2.3 通道和位置注意力机制(CPAM)
通道和位置注意力机制(CPAM)旨在通过集成尺度序列特征融合(SSFF)和三重特征编码器(TFE)模块,提高对细节丰富的小对象的检测和分割能力。CPAM通过专注于信息丰富的通道以及与空间位置相关的小对象特征,能够为模型提供一种有效的注意力指导。这样的机制允许模型更加精准地识别和定 位图 像中的细小目标,从而提高检测和分割任务的性能
这张图展示了通道和位置注意力机制(CPAM)模块的结构。该模块包括通道注意力网络和位置注意力网络两部分。通道注意力部分通过平均池化和1D卷积来生成每个通道的权重,然后使用Sigmoid函数进行归一化。位置注意力网络分别对宽度和高度进行平均池化,再通过一个卷积层和Sigmoid函数生成空间权重。最后,这两个权重通过哈达玛积(元素间相乘)合并,以产生最终的加权输出,增强模型对特定通道和位置的聚焦能力。
四、手把手教你修改ASFYOLO
4.1 修改一
第一还是建立文件,我们找到如下ultralytics/nn文件夹下建立一个目录名字呢就是'Addmodules'文件夹( 用群内的文件的话已经有了无需新建) !然后在其内部建立一个新的py文件将核心代码复制粘贴进去即可。
4.2 修改二
第二步我们在该目录下创建一个新的py文件名字为'__init__.py'( 用群内的文件的话已经有了无需新建) ,然后在其内部导入我们的检测头如下图所示。
4.3 修改三
第三步我门中到如下文件'ultralytics/nn/tasks.py'进行导入和注册我们的模块( 用群内的文件的话已经有了无需重新导入直接开始第四步即可) !
4.4 修改四
按照我的添加在parse_model里添加即可。
- # ------------------------------ASF-YOLO--------------------------------
- elif m is Zoom_cat:
- c2 = sum(ch[x] for x in f)
- elif m is Add:
- c2 = ch[f[-1]]
- elif m is ScalSeq:
- c1 = [ch[x] for x in f]
- c2 = make_divisible(args[0] * width, 8)
- args = [c1, c2]
- elif m is attention_model:
- args = [ch[f[-1]]]
- # ------------------------------ASF-YOLO--------------------------------
五、手把手教你添加RepViT
5.1 RepViT核心代码
- from symbol import factor
- import torch.nn as nn
- from timm.models.layers import SqueezeExcite
- import torch
- __all__ = ['repvit_m0_6','repvit_m0_9', 'repvit_m1_0', 'repvit_m1_1', 'repvit_m1_5', 'repvit_m2_3']
- def _make_divisible(v, divisor, min_value=None):
- """
- This function is taken from the original tf repo.
- It ensures that all layers have a channel number that is divisible by 8
- It can be seen here:
- https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py
- :param v:
- :param divisor:
- :param min_value:
- :return:
- """
- if min_value is None:
- min_value = divisor
- new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
- # Make sure that round down does not go down by more than 10%.
- if new_v < 0.9 * v:
- new_v += divisor
- return new_v
- class Conv2d_BN(torch.nn.Sequential):
- def __init__(self, a, b, ks=1, stride=1, pad=0, dilation=1,
- groups=1, bn_weight_init=1, resolution=-10000):
- super().__init__()
- self.add_module('c', torch.nn.Conv2d(
- a, b, ks, stride, pad, dilation, groups, bias=False))
- self.add_module('bn', torch.nn.BatchNorm2d(b))
- torch.nn.init.constant_(self.bn.weight, bn_weight_init)
- torch.nn.init.constant_(self.bn.bias, 0)
- @torch.no_grad()
- def fuse_self(self):
- c, bn = self._modules.values()
- w = bn.weight / (bn.running_var + bn.eps) ** 0.5
- w = c.weight * w[:, None, None, None]
- b = bn.bias - bn.running_mean * bn.weight / \
- (bn.running_var + bn.eps) ** 0.5
- m = torch.nn.Conv2d(w.size(1) * self.c.groups, w.size(
- 0), w.shape[2:], stride=self.c.stride, padding=self.c.padding, dilation=self.c.dilation,
- groups=self.c.groups,
- device=c.weight.device)
- m.weight.data.copy_(w)
- m.bias.data.copy_(b)
- return m
- class Residual(torch.nn.Module):
- def __init__(self, m, drop=0.):
- super().__init__()
- self.m = m
- self.drop = drop
- def forward(self, x):
- if self.training and self.drop > 0:
- return x + self.m(x) * torch.rand(x.size(0), 1, 1, 1,
- device=x.device).ge_(self.drop).div(1 - self.drop).detach()
- else:
- return x + self.m(x)
- @torch.no_grad()
- def fuse_self(self):
- if isinstance(self.m, Conv2d_BN):
- m = self.m.fuse_self()
- assert (m.groups == m.in_channels)
- identity = torch.ones(m.weight.shape[0], m.weight.shape[1], 1, 1)
- identity = torch.nn.functional.pad(identity, [1, 1, 1, 1])
- m.weight += identity.to(m.weight.device)
- return m
- elif isinstance(self.m, torch.nn.Conv2d):
- m = self.m
- assert (m.groups != m.in_channels)
- identity = torch.ones(m.weight.shape[0], m.weight.shape[1], 1, 1)
- identity = torch.nn.functional.pad(identity, [1, 1, 1, 1])
- m.weight += identity.to(m.weight.device)
- return m
- else:
- return self
- class RepVGGDW(torch.nn.Module):
- def __init__(self, ed) -> None:
- super().__init__()
- self.conv = Conv2d_BN(ed, ed, 3, 1, 1, groups=ed)
- self.conv1 = torch.nn.Conv2d(ed, ed, 1, 1, 0, groups=ed)
- self.dim = ed
- self.bn = torch.nn.BatchNorm2d(ed)
- def forward(self, x):
- return self.bn((self.conv(x) + self.conv1(x)) + x)
- @torch.no_grad()
- def fuse_self(self):
- conv = self.conv.fuse_self()
- conv1 = self.conv1
- conv_w = conv.weight
- conv_b = conv.bias
- conv1_w = conv1.weight
- conv1_b = conv1.bias
- conv1_w = torch.nn.functional.pad(conv1_w, [1, 1, 1, 1])
- identity = torch.nn.functional.pad(torch.ones(conv1_w.shape[0], conv1_w.shape[1], 1, 1, device=conv1_w.device),
- [1, 1, 1, 1])
- final_conv_w = conv_w + conv1_w + identity
- final_conv_b = conv_b + conv1_b
- conv.weight.data.copy_(final_conv_w)
- conv.bias.data.copy_(final_conv_b)
- bn = self.bn
- w = bn.weight / (bn.running_var + bn.eps) ** 0.5
- w = conv.weight * w[:, None, None, None]
- b = bn.bias + (conv.bias - bn.running_mean) * bn.weight / \
- (bn.running_var + bn.eps) ** 0.5
- conv.weight.data.copy_(w)
- conv.bias.data.copy_(b)
- return conv
- class RepViTBlock(nn.Module):
- def __init__(self, inp, hidden_dim, oup, kernel_size, stride, use_se, use_hs):
- super(RepViTBlock, self).__init__()
- assert stride in [1, 2]
- self.identity = stride == 1 and inp == oup
- assert (hidden_dim == 2 * inp)
- if stride == 2:
- self.token_mixer = nn.Sequential(
- Conv2d_BN(inp, inp, kernel_size, stride, (kernel_size - 1) // 2, groups=inp),
- SqueezeExcite(inp, 0.25) if use_se else nn.Identity(),
- Conv2d_BN(inp, oup, ks=1, stride=1, pad=0)
- )
- self.channel_mixer = Residual(nn.Sequential(
- # pw
- Conv2d_BN(oup, 2 * oup, 1, 1, 0),
- nn.GELU() if use_hs else nn.GELU(),
- # pw-linear
- Conv2d_BN(2 * oup, oup, 1, 1, 0, bn_weight_init=0),
- ))
- else:
- self.token_mixer = nn.Sequential(
- RepVGGDW(inp),
- SqueezeExcite(inp, 0.25) if use_se else nn.Identity(),
- )
- self.channel_mixer = Residual(nn.Sequential(
- # pw
- Conv2d_BN(inp, hidden_dim, 1, 1, 0),
- nn.GELU() if use_hs else nn.GELU(),
- # pw-linear
- Conv2d_BN(hidden_dim, oup, 1, 1, 0, bn_weight_init=0),
- ))
- def forward(self, x):
- return self.channel_mixer(self.token_mixer(x))
- class RepViT(nn.Module):
- def __init__(self, cfgs, factor):
- super(RepViT, self).__init__()
- # setting of inverted residual blocks
- cfgs = [sublist[:2] + [_make_divisible(int(sublist[2] * factor) , 8)] + sublist[3:] for sublist in cfgs]
- self.cfgs = cfgs
- # building first layer
- input_channel = self.cfgs[0][2]
- patch_embed = torch.nn.Sequential(Conv2d_BN(3, input_channel // 2, 3, 2, 1), torch.nn.GELU(),
- Conv2d_BN(input_channel // 2, input_channel, 3, 2, 1))
- layers = [patch_embed]
- # building inverted residual blocks
- block = RepViTBlock
- for k, t, c, use_se, use_hs, s in self.cfgs:
- output_channel = _make_divisible(c , 8)
- exp_size = _make_divisible(input_channel * t, 8)
- layers.append(block(input_channel, exp_size, output_channel, k, s, use_se, use_hs))
- input_channel = output_channel
- self.features = nn.ModuleList(layers)
- self.width_list = [i.size(1) for i in self.forward(torch.randn(1, 3, 640, 640))]
- def forward(self, x):
- # x = self.features(x
- results = [None, None, None, None]
- temp = None
- i = None
- for index, f in enumerate(self.features):
- x = f(x)
- if index == 0:
- temp = x.size(1)
- i = 0
- elif x.size(1) == temp:
- results[i] = x
- else:
- temp = x.size(1)
- i = i + 1
- return results
- def repvit_m0_6(factor):
- """
- Constructs a MobileNetV3-Large model
- """
- cfgs = [
- [3, 2, 40, 1, 0, 1],
- [3, 2, 40, 0, 0, 1],
- [3, 2, 80, 0, 0, 2],
- [3, 2, 80, 1, 0, 1],
- [3, 2, 80, 0, 0, 1],
- [3, 2, 160, 0, 1, 2],
- [3, 2, 160, 1, 1, 1],
- [3, 2, 160, 0, 1, 1],
- [3, 2, 160, 1, 1, 1],
- [3, 2, 160, 0, 1, 1],
- [3, 2, 160, 1, 1, 1],
- [3, 2, 160, 0, 1, 1],
- [3, 2, 160, 1, 1, 1],
- [3, 2, 160, 0, 1, 1],
- [3, 2, 160, 0, 1, 1],
- [3, 2, 320, 0, 1, 2],
- [3, 2, 320, 1, 1, 1],
- ]
- model = RepViT(cfgs, factor)
- return model
- def repvit_m0_9(factor):
- """
- Constructs a MobileNetV3-Large model
- """
- cfgs = [
- # k, t, c, SE, HS, s
- [3, 2, 48, 1, 0, 1],
- [3, 2, 48, 0, 0, 1],
- [3, 2, 48, 0, 0, 1],
- [3, 2, 96, 0, 0, 2],
- [3, 2, 96, 1, 0, 1],
- [3, 2, 96, 0, 0, 1],
- [3, 2, 96, 0, 0, 1],
- [3, 2, 192, 0, 1, 2],
- [3, 2, 192, 1, 1, 1],
- [3, 2, 192, 0, 1, 1],
- [3, 2, 192, 1, 1, 1],
- [3, 2, 192, 0, 1, 1],
- [3, 2, 192, 1, 1, 1],
- [3, 2, 192, 0, 1, 1],
- [3, 2, 192, 1, 1, 1],
- [3, 2, 192, 0, 1, 1],
- [3, 2, 192, 1, 1, 1],
- [3, 2, 192, 0, 1, 1],
- [3, 2, 192, 1, 1, 1],
- [3, 2, 192, 0, 1, 1],
- [3, 2, 192, 1, 1, 1],
- [3, 2, 192, 0, 1, 1],
- [3, 2, 192, 0, 1, 1],
- [3, 2, 384, 0, 1, 2],
- [3, 2, 384, 1, 1, 1],
- [3, 2, 384, 0, 1, 1]
- ]
- model = RepViT(cfgs, factor)
- return model
- def repvit_m1_0(factor):
- """
- Constructs a MobileNetV3-Large model
- """
- cfgs = [
- # k, t, c, SE, HS, s
- [3, 2, 56, 1, 0, 1],
- [3, 2, 56, 0, 0, 1],
- [3, 2, 56, 0, 0, 1],
- [3, 2, 112, 0, 0, 2],
- [3, 2, 112, 1, 0, 1],
- [3, 2, 112, 0, 0, 1],
- [3, 2, 112, 0, 0, 1],
- [3, 2, 224, 0, 1, 2],
- [3, 2, 224, 1, 1, 1],
- [3, 2, 224, 0, 1, 1],
- [3, 2, 224, 1, 1, 1],
- [3, 2, 224, 0, 1, 1],
- [3, 2, 224, 1, 1, 1],
- [3, 2, 224, 0, 1, 1],
- [3, 2, 224, 1, 1, 1],
- [3, 2, 224, 0, 1, 1],
- [3, 2, 224, 1, 1, 1],
- [3, 2, 224, 0, 1, 1],
- [3, 2, 224, 1, 1, 1],
- [3, 2, 224, 0, 1, 1],
- [3, 2, 224, 1, 1, 1],
- [3, 2, 224, 0, 1, 1],
- [3, 2, 224, 0, 1, 1],
- [3, 2, 448, 0, 1, 2],
- [3, 2, 448, 1, 1, 1],
- [3, 2, 448, 0, 1, 1]
- ]
- model = RepViT(cfgs,factor=factor)
- return model
- def repvit_m1_1(factor):
- """
- Constructs a MobileNetV3-Large model
- """
- cfgs = [
- # k, t, c, SE, HS, s
- [3, 2, 64, 1, 0, 1],
- [3, 2, 64, 0, 0, 1],
- [3, 2, 64, 0, 0, 1],
- [3, 2, 128, 0, 0, 2],
- [3, 2, 128, 1, 0, 1],
- [3, 2, 128, 0, 0, 1],
- [3, 2, 128, 0, 0, 1],
- [3, 2, 256, 0, 1, 2],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 512, 0, 1, 2],
- [3, 2, 512, 1, 1, 1],
- [3, 2, 512, 0, 1, 1]
- ]
- model = RepViT(cfgs,factor=factor)
- return model
- def repvit_m1_5(factor):
- """
- Constructs a MobileNetV3-Large model
- """
- cfgs = [
- # k, t, c, SE, HS, s
- [3, 2, 64, 1, 0, 1],
- [3, 2, 64, 0, 0, 1],
- [3, 2, 64, 1, 0, 1],
- [3, 2, 64, 0, 0, 1],
- [3, 2, 64, 0, 0, 1],
- [3, 2, 128, 0, 0, 2],
- [3, 2, 128, 1, 0, 1],
- [3, 2, 128, 0, 0, 1],
- [3, 2, 128, 1, 0, 1],
- [3, 2, 128, 0, 0, 1],
- [3, 2, 128, 0, 0, 1],
- [3, 2, 256, 0, 1, 2],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 1, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 256, 0, 1, 1],
- [3, 2, 512, 0, 1, 2],
- [3, 2, 512, 1, 1, 1],
- [3, 2, 512, 0, 1, 1],
- [3, 2, 512, 1, 1, 1],
- [3, 2, 512, 0, 1, 1]
- ]
- model = RepViT(cfgs,factor=factor)
- return model
- def repvit_m2_3(factor):
- """
- Constructs a MobileNetV3-Large model
- """
- cfgs = [
- # k, t, c, SE, HS, s
- [3, 2, 80, 1, 0, 1],
- [3, 2, 80, 0, 0, 1],
- [3, 2, 80, 1, 0, 1],
- [3, 2, 80, 0, 0, 1],
- [3, 2, 80, 1, 0, 1],
- [3, 2, 80, 0, 0, 1],
- [3, 2, 80, 0, 0, 1],
- [3, 2, 160, 0, 0, 2],
- [3, 2, 160, 1, 0, 1],
- [3, 2, 160, 0, 0, 1],
- [3, 2, 160, 1, 0, 1],
- [3, 2, 160, 0, 0, 1],
- [3, 2, 160, 1, 0, 1],
- [3, 2, 160, 0, 0, 1],
- [3, 2, 160, 0, 0, 1],
- [3, 2, 320, 0, 1, 2],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 1, 1, 1],
- [3, 2, 320, 0, 1, 1],
- # [3, 2, 320, 1, 1, 1],
- # [3, 2, 320, 0, 1, 1],
- [3, 2, 320, 0, 1, 1],
- [3, 2, 640, 0, 1, 2],
- [3, 2, 640, 1, 1, 1],
- [3, 2, 640, 0, 1, 1],
- # [3, 2, 640, 1, 1, 1],
- # [3, 2, 640, 0, 1, 1]
- ]
- model = RepViT(cfgs,factor=factor)
- return model
- if __name__ == '__main__':
- model = repvit_m0_6(factor=0.25)
- inputs = torch.randn((1, 3, 640, 640))
- for i in model(inputs):
- print(i.size())
5.2 修改一
第一步还是建立文件,我们找到如下ultralytics/nn文件夹下建立一个目录名字呢就是'Addmodules'文件夹( 用群内的文件的话已经有了无需新建) !然后在其内部建立一个新的py文件将核心代码复制粘贴进去即可
5.3 修改二
第二步我们在该目录下创建一个新的py文件名字为'__init__.py'( 用群内的文件的话已经有了无需新建) ,然后在其内部导入我们的检测头如下图所示。
5.4 修改三
第三步我门中到如下文件'ultralytics/nn/tasks.py'进行导入和注册我们的模块( 用群内的文件的话已经有了无需重新导入直接开始第四步即可) !
从今天开始以后的教程就都统一成这个样子了,因为我默认大家用了我群内的文件来进行修改!!
5.5 修改四
添加如下两行代码!!!
5.6 修改五
找到七百多行大概把具体看图片,按照图片来修改就行,添加红框内的部分,注意没有()只是函数名。
- elif m in {自行添加对应的模型即可,下面都是一样的}:
- m = m(*args)
- c2 = m.width_list # 返回通道列表
- backbone = True
5.7 修改六
下面的两个红框内都是需要改动的。
- if isinstance(c2, list):
- m_ = m
- m_.backbone = True
- else:
- m_ = nn.Sequential(*(m(*args) for _ in range(n))) if n > 1 else m(*args) # module
- t = str(m)[8:-2].replace('__main__.', '') # module type
- m.np = sum(x.numel() for x in m_.parameters()) # number params
- m_.i, m_.f, m_.type = i + 4 if backbone else i, f, t # attach index, 'from' index, type
5.8 修改七
如下的也需要修改,全部按照我的来。
代码如下把原先的代码替换了即可。
- if verbose:
- LOGGER.info(f'{i:>3}{str(f):>20}{n_:>3}{m.np:10.0f} {t:<45}{str(args):<30}') # print
- save.extend(x % (i + 4 if backbone else i) for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist
- layers.append(m_)
- if i == 0:
- ch = []
- if isinstance(c2, list):
- ch.extend(c2)
- if len(c2) != 5:
- ch.insert(0, 0)
- else:
- ch.append(c2)
5.9 修改八
修改八和前面的都不太一样,需要修改前向传播中的一个部分, 已经离开了parse_model方法了。
可以在图片中开代码行数,没有离开task.py文件都是同一个文件。 同时这个部分有好几个前向传播都很相似,大家不要看错了, 是70多行左右的!!!,同时我后面提供了代码,大家直接复制粘贴即可,有时间我针对这里会出一个视频。
代码如下->
- def _predict_once(self, x, profile=False, visualize=False, embed=None):
- """
- Perform a forward pass through the network.
- Args:
- x (torch.Tensor): The input tensor to the model.
- profile (bool): Print the computation time of each layer if True, defaults to False.
- visualize (bool): Save the feature maps of the model if True, defaults to False.
- embed (list, optional): A list of feature vectors/embeddings to return.
- Returns:
- (torch.Tensor): The last output of the model.
- """
- y, dt, embeddings = [], [], [] # outputs
- for m in self.model:
- if m.f != -1: # if not from previous layer
- x = y[m.f] if isinstance(m.f, int) else [x if j == -1 else y[j] for j in m.f] # from earlier layers
- if profile:
- self._profile_one_layer(m, x, dt)
- if hasattr(m, 'backbone'):
- x = m(x)
- if len(x) != 5: # 0 - 5
- x.insert(0, None)
- for index, i in enumerate(x):
- if index in self.save:
- y.append(i)
- else:
- y.append(None)
- x = x[-1] # 最后一个输出传给下一层
- else:
- x = m(x) # run
- y.append(x if m.i in self.save else None) # save output
- if visualize:
- feature_visualization(x, m.type, m.i, save_dir=visualize)
- if embed and m.i in embed:
- embeddings.append(nn.functional.adaptive_avg_pool2d(x, (1, 1)).squeeze(-1).squeeze(-1)) # flatten
- if m.i == max(embed):
- return torch.unbind(torch.cat(embeddings, 1), dim=0)
- return x
到这里就完成了修改部分,但是这里面细节很多,大家千万要注意不要替换多余的代码,导致报错,也不要拉下任何一部,都会导致运行失败,而且报错很难排查!!!很难排查!!!
注意!!! 额外的修改!
关注我的其实都知道,我大部分的修改都是一样的,这个网络需要额外的修改一步,就是s一个参数,将下面的s改为640!!!即可完美运行!!
打印计算量问题解决方案
我们找到如下文件'ultralytics/utils/torch_utils.py'按照如下的图片进行修改,否则容易打印不出来计算量。
注意事项!!!
如果大家在验证的时候报错形状不匹配的错误可以固定验证集的图片尺寸,方法如下 ->
找到下面这个文件ultralytics/ models /yolo/detect/train.py然后其中有一个类是DetectionTrainer class中的build_dataset函数中的一个参数rect=mode == 'val'改为rect=False
六、融合改进yaml文件
6.1 ASFYOLO-RepViT-1的yaml文件
训练信息:YOLO11-ASFYOLO-RepViT summary: 585 layers, 2,148,326 parameters, 2,148,310 gradients, 5.8 GFLOPs
- # Ultralytics YOLO 🚀, AGPL-3.0 license
- # YOLO11 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
- # Parameters
- nc: 80 # number of classes
- scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n'
- # [depth, width, max_channels]
- n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
- s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
- m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
- l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
- x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
- # 下面 [-1, 1, repvit_m0_6, [0.25]] 参数位置的0.25是通道放缩的系数, YOLOv11N是0.25 YOLOv11S是0.5 YOLOv11M是1. YOLOv11l是1 YOLOv11是1.5大家根据自己训练的YOLO版本设定即可.
- # 本文支持版本有 __all__ = ['repvit_m0_6','repvit_m0_9', 'repvit_m1_0', 'repvit_m1_1', 'repvit_m1_5', 'repvit_m2_3']
- # YOLO11n backbone
- backbone:
- # [from, repeats, module, args]
- - [-1, 1, repvit_m0_6, [0.25]] # 0-4 P1/2 这里是四层大家不要被yaml文件限制住了思维,不会画图进群看视频.
- - [-1, 1, SPPF, [1024, 5]] # 5
- - [-1, 2, C2PSA, [1024]] # 6
- # YOLO11n head
- head:
- - [-1, 1, Conv, [512, 1, 1]] # 7
- - [2, 1, Conv, [512, 1, 1]] # 8
- - [[-1, 3, -2], 1, Zoom_cat, []] # 9 cat backbone P4
- - [-1, 2, C3k2, [512, False]] # 10
- - [-1, 1, Conv, [256, 1, 1]] # 11
- - [1, 1, Conv, [256, 1, 1]] # 12
- - [[-1, 2, -2], 1, Zoom_cat, []] # 13 cat backbone P3
- - [-1, 2, C3k2, [256, False]] # 14 (P3/8-small)
- - [-1, 1, Conv, [256, 3, 2]] # 15
- - [[-1, 11], 1, Concat, [1]] # 16 cat head P4
- - [-1, 2, C3k2, [512, False]] # 17(P4/16-medium)
- - [-1, 1, Conv, [512, 3, 2]] # 18
- - [[-1, 7], 1, Concat, [1]] # 19 cat head P5
- - [-1, 2, C3k2, [1024, True]] # 20 (P5/32-large)
- - [[2, 3, 4], 1, ScalSeq, [256]] # 21 args[inchane]
- - [[14, -1], 1, Add, [64]] # 22
- - [[22, 17, 20], 1, Detect, [nc]] # RTDETRDecoder(P3, P4, P5)
6.2 ASFYOLO-RepViT-2的yaml文件
训练信息:YOLO11-ASFYOLO-RepViT-2 summary: 597 layers, 1,672,093 parameters, 1,672,077 gradients, 4.4 GFLOPs
- # Ultralytics YOLO 🚀, AGPL-3.0 license
- # YOLO11 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
- # Parameters
- nc: 80 # number of classes
- scales: # model compound scaling constants, i.e. 'model=yolo11n.yaml' will call yolo11.yaml with scale 'n'
- # [depth, width, max_channels]
- n: [0.50, 0.25, 1024] # summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPs
- s: [0.50, 0.50, 1024] # summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPs
- m: [0.50, 1.00, 512] # summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPs
- l: [1.00, 1.00, 512] # summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPs
- x: [1.00, 1.50, 512] # summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs
- # 下面 [-1, 1, repvit_m0_6, [0.25]] 参数位置的0.25是通道放缩的系数, YOLOv11N是0.25 YOLOv11S是0.5 YOLOv11M是1. YOLOv11l是1 YOLOv11是1.5大家根据自己训练的YOLO版本设定即可.
- # 本文支持版本有 __all__ = ['repvit_m0_6','repvit_m0_9', 'repvit_m1_0', 'repvit_m1_1', 'repvit_m1_5', 'repvit_m2_3']
- # YOLO11n backbone
- backbone:
- # [from, repeats, module, args]
- - [-1, 1, repvit_m0_6, [0.25]] # 0-4 P1/2 这里是四层大家不要被yaml文件限制住了思维,不会画图进群看视频.
- - [-1, 1, SPPF, [1024, 5]] # 5
- - [-1, 2, C2PSA, [1024]] # 6
- # YOLO11n head
- head:
- - [-1, 1, Conv, [512, 1, 1]] # 7
- - [2, 1, Conv, [512, 1, 1]] # 8
- - [[-1, 3, -2], 1, Zoom_cat, []] # 9 cat backbone P4
- - [-1, 2, C3k2, [512, False]] # 10
- - [-1, 1, Conv, [256, 1, 1]] # 11
- - [1, 1, Conv, [256, 1, 1]] # 12
- - [[-1, 2, -2], 1, Zoom_cat, []] # 13 cat backbone P3
- - [-1, 2, C3k2, [256, False]] # 14 (P3/8-small)
- - [-1, 1, Conv, [256, 3, 2]] # 15
- - [[-1, 11], 1, Concat, [1]] # 16 cat head P4
- - [-1, 2, C3k2, [512, False]] # 17(P4/16-medium)
- - [-1, 1, Conv, [512, 3, 2]] # 18
- - [[-1, 7], 1, Concat, [1]] # 19 cat head P5
- - [-1, 2, C3k2, [1024, True]] # 20 (P5/32-large)
- - [[2, 3, 4], 1, ScalSeq, [256]] # 21 args[inchane]
- - [[14, -1], 1, attention_model, [256]] # 22
- - [[22, 17, 20], 1, Detect, [nc]] # RTDETRDecoder(P3, P4, P5)
七、本文总结
到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv11改进有效涨点专栏,本专栏目前为新开的平均质量分98分,后期我会根据各种最新的前沿顶会进行论文复现,也会对一些老的改进机制进行补充,目前本专栏免费阅读(暂时,大家尽早关注不迷路~),如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~