一、本文介绍
本文记录的是基于Shuffle Attention注意力模块的YOLOv9目标检测改进方法研究。Shuffle Attention模块
通过独特的设计原理,在保持轻量级的同时实现了高效的特征注意力机制,增强了网络的表示能力。本文对YOLOv9
的RepNCSPELAN4模块
进行二次创新,以增强模型性能。
文章目录
- 一、本文介绍
- 二、Shuffle Attention注意力原理
- 2.1、设计原理
- 2.2、优势
- 三、Shuffle Attention的实现代码
- 四、添加步骤
- 4.1 修改common.py
- 4.1.1 基础模块1
- 4.1.2 创新模块2⭐
- 4.2 修改yolo.py
- 五、yaml模型文件
- 5.1 模型改进版本一
- 5.2 模型改进版本二⭐
- 六、成功运行结果
二、Shuffle Attention注意力原理
深度卷积神经网络的Shuffle Attention
Shuffle Attention(SA)模块
是一种用于深度卷积神经网络的高效注意力模块,其设计原理和优势如下:
2.1、设计原理
- 特征分组(Feature Grouping):对于给定的特征图 X ∈ R C × H × W X \in R^{C \times H \times W} X∈RC×H×W(其中 C C C、 H H H、 W W W分别表示通道数、空间高度和宽度),
SA
首先沿着通道维度将 X X X分为 G G G组,即 X = [ X 1 , ⋯ , X G ] X = [X_1, \cdots, X_G] X=[X1,⋯,XG], X k ∈ R C G × H × W X_k \in R^{\frac{C}{G} \times H \times W} Xk∈RGC×H×W。在每个注意力单元开始时, X k X_k Xk的输入沿着通道维度被拆分为两个分支 X k 1 X_{k1} Xk1和 X k 2 X_{k2} Xk2( X k 1 , X k 2 ∈ R C 2 G × H × W X_{k1}, X_{k2} \in R^{\frac{C}{2G} \times H \times W} Xk1,Xk2∈R2GC×H×W)。一个分支用于通过利用通道间的相互关系来生成通道注意力图,另一个分支用于通过利用特征的空间间关系来生成空间注意力图,从而使模型能够关注“什么”和“哪里”是有意义的。 - 通道注意力(Channel Attention):为了充分捕获通道间的依赖关系,
SA
使用全局平均池化(GAP)
来生成通道级别的统计信息 s ∈ R C 2 G × 1 × 1 s \in R^{\frac{C}{2G} \times 1 \times 1} s∈R2GC×1×1,即 s = F g p ( X k 1 ) = 1 H × W ∑ i = 1 H ∑ j = 1 W X k 1 ( i , j ) s = \mathcal{F}_{gp}(X_{k1}) = \frac{1}{H \times W} \sum_{i = 1}^{H} \sum_{j = 1}^{W} X_{k1}(i, j) s=Fgp(Xk1)=H×W1∑i=1H∑j=1WXk1(i,j)。然后,通过一个简单的带有sigmoid激活的门控机制创建一个紧凑的特征,以实现精确和自适应的选择指导。通道注意力的最终输出通过 X k 1 ′ = σ ( F c ( s ) ) ⋅ X k 1 = σ ( W 1 s + b 1 ) ⋅ X k 1 X_{k1}' = \sigma(\mathcal{F}_{c}(s)) \cdot X_{k1} = \sigma(W_1 s + b_1) \cdot X_{k1} Xk1′=σ(Fc(s))⋅Xk1=σ(W1s+b1)⋅Xk1获得,其中 W 1 ∈ R C 2 G × 1 × 1 W_1 \in R^{\frac{C}{2G} \times 1 \times 1} W1∈R2GC×1×1和 b 1 ∈ R C 2 G × 1 × 1 b_1 \in R^{\frac{C}{2G} \times 1 \times 1} b1∈R2GC×1×1是用于缩放和移动 s s s的参数。 - 空间注意力(Spatial Attention):与通道注意力不同,空间注意力关注“哪里”是信息丰富的部分,这与通道注意力是互补的。首先,使用
组归一化(GN)
对 X k 2 X_{k2} Xk2进行处理以获得空间级别的统计信息,然后采用 F c ( ⋅ ) Fc(\cdot) Fc(⋅)来增强 X ^ k 2 \hat{X}_{k2} X^k2的表示。空间注意力的最终输出通过 X k 2 ′ = σ ( W 2 ⋅ G N ( X k 2 ) + b 2 ) ⋅ X k 2 X_{k2}' = \sigma(W_2 \cdot GN(X_{k2}) + b_2) \cdot X_{k2} Xk2′=σ(W2⋅GN(Xk2)+b2)⋅Xk2获得,其中 W 2 W_2 W2和 b 2 b_2 b2是形状为 R C 2 G × 1 × 1 R^{\frac{C}{2G} \times 1 \times 1} R2GC×1×1的参数。 - 聚合(Aggregation):之后,所有子特征被聚合。最后,类似于
ShuffleNet v2
,采用“通道洗牌”(channel shuffle)操作来实现跨组信息在通道维度上的流动。
2.2、优势
- 轻量级且高效:
SA模块
通过将通道维度分组为子特征,并利用Shuffle Unit
为每个子特征集成互补的通道和空间注意力模块,参数和计算量相对较少。例如,在ResNet50中,SA的参数为300,计算量为2.76e - 3 GFLOPs,而ResNet50的参数为25.56M,计算量为4.12 GFLOPs,但SA在Top - 1准确率上的提升超过了1.34%。 - 增强语义表示:通过特征分组和通道洗牌,
SA
能够显著增强特征图的语义表示。实验表明,在使用SA模块后,Top - 1准确率统计上有所提高,并且“通道洗牌”使得每个组的平均得分增加(约0.4%)。 - 验证有效性:通过对不同深度的平均激活分布的观察以及使用GradCAM进行可视化,验证了
SA模块
能够使分类模型更关注相关区域,从而有效提高分类准确率。 - 在各种任务中表现出色:在ImageNet - 1k分类、MS COCO对象检测和实例分割等任务的实验中,
SA
相比于当前的SOTA方法,在实现更高准确率的同时具有更低的模型复杂度,验证了其在各种计算机视觉任务中具有良好的泛化能力。
论文:https://arxiv.org/pdf/2102.00240
源码:https://github.com/wofmanaf/SA-Net
三、Shuffle Attention的实现代码
Shuffle Attention模块
的实现代码如下:
from torch.nn.parameter import Parameter
from torch.nn import initclass ShuffleAttention(nn.Module):def __init__(self, channel=512, reduction=16, G=8):super().__init__()self.G = Gself.channel = channelself.avg_pool = nn.AdaptiveAvgPool2d(1)self.gn = nn.GroupNorm(channel // (2 * G), channel // (2 * G))self.cweight = Parameter(torch.zeros(1, channel // (2 * G), 1, 1))self.cbias = Parameter(torch.ones(1, channel // (2 * G), 1, 1))self.sweight = Parameter(torch.zeros(1, channel // (2 * G), 1, 1))self.sbias = Parameter(torch.ones(1, channel // (2 * G), 1, 1))self.sigmoid = nn.Sigmoid()def init_weights(self):for m in self.modules():if isinstance(m, nn.Conv2d):init.kaiming_normal_(m.weight, mode='fan_out')if m.bias is not None:init.constant_(m.bias, 0)elif isinstance(m, nn.BatchNorm2d):init.constant_(m.weight, 1)init.constant_(m.bias, 0)elif isinstance(m, nn.Linear):init.normal_(m.weight, std=0.001)if m.bias is not None:init.constant_(m.bias, 0)@staticmethoddef channel_shuffle(x, groups):b, c, h, w = x.shapex = x.reshape(b, groups, -1, h, w)x = x.permute(0, 2, 1, 3, 4)# flattenx = x.reshape(b, -1, h, w)return xdef forward(self, x):b, c, h, w = x.size()# group into subfeaturesx = x.view(b * self.G, -1, h, w) # bs*G,c//G,h,w# channel_splitx_0, x_1 = x.chunk(2, dim=1) # bs*G,c//(2*G),h,w# channel attentionx_channel = self.avg_pool(x_0) # bs*G,c//(2*G),1,1x_channel = self.cweight * x_channel + self.cbias # bs*G,c//(2*G),1,1x_channel = x_0 * self.sigmoid(x_channel)# spatial attentionx_spatial = self.gn(x_1) # bs*G,c//(2*G),h,wx_spatial = self.sweight * x_spatial + self.sbias # bs*G,c//(2*G),h,wx_spatial = x_1 * self.sigmoid(x_spatial) # bs*G,c//(2*G),h,w# concatenate along channel axisout = torch.cat([x_channel, x_spatial], dim=1) # bs*G,c//G,h,wout = out.contiguous().view(b, -1, h, w)# channel shuffleout = self.channel_shuffle(out, 2)return out
四、添加步骤
4.1 修改common.py
此处需要修改的文件是models/common.py
common.py中定义了网络结构的通用模块
,我们想要加入新的模块就只需要将模块代码放到这个文件内即可。
4.1.1 基础模块1
模块改进方法1️⃣:直接加入ShuffleAttention模块
。
ShuffleAttention模块
添加后如下:
注意❗:在4.2小节
中的yolo.py
文件中需要声明的模块名称为:ShuffleAttention
。
4.1.2 创新模块2⭐
模块改进方法2️⃣:基于ShuffleAttention模块
的RepNCSPELAN4
。
相较方法一中的直接插入注意力模块,利用注意力模块对卷积等其他模块进行改进,其新颖程度会更高一些,训练精度可能会表现的更高。
第二种改进方法是对YOLOv9
中的RepNCSPELAN4模块
进行改进,将ShuffleAttention注意力模块
替换RepNCSPELAN4
中的卷积模块。Shuffle Attention模块
能够关注特征的空间和通道维度的依赖关系,在与RepNCSPELAN4模块
结合可以更全面地提取和强调重要特征,从而增强特征提取的效果。
改进代码如下:
class SARepNCSPELAN4(nn.Module):# csp-elandef __init__(self, c1, c2, c3, c4, c5=1): # ch_in, ch_out, number, shortcut, groups, expansionsuper().__init__()self.c = c3//2self.cv1 = Conv(c1, c3, 1, 1)self.cv2 = nn.Sequential(RepNCSP(c3//2, c4, c5), ShuffleAttention(c4, c4))self.cv3 = nn.Sequential(RepNCSP(c4, c4, c5), ShuffleAttention(c4, c4))self.cv4 = Conv(c3+(2*c4), c2, 1, 1)def forward(self, x):y = list(self.cv1(x).chunk(2, 1))y.extend((m(y[-1])) for m in [self.cv2, self.cv3])return self.cv4(torch.cat(y, 1))def forward_split(self, x):y = list(self.cv1(x).split((self.c, self.c), 1))y.extend(m(y[-1]) for m in [self.cv2, self.cv3])return self.cv4(torch.cat(y, 1))
注意❗:在4.2小节
中的yolo.py
文件中需要声明的模块名称为:SARepNCSPELAN4
。
4.2 修改yolo.py
此处需要修改的文件是models/yolo.py
yolo.py用于函数调用
,我们只需要将common.py
中定义的新的模块名添加到parse_model函数
下即可。
ShuffleAttention模块
以及SARepNCSPELAN4模块
添加后如下:
五、yaml模型文件
5.1 模型改进版本一
在代码配置完成后,配置模型的YAML文件。
此处以models/detect/yolov9-c.yaml
为例,在同目录下创建一个用于自己数据集训练的模型文件yolov9-c-sa.yaml
。
将yolov9-c.yaml
中的内容复制到yolov9-c-sa.yaml
文件下,修改nc
数量等于自己数据中目标的数量。
在骨干网络的最后一层添加ShuffleAttention模块
,即下方代码中的第45行,只需要填入一个参数,通道数,和前一层通道数一致。
📌 Shuffle Attention模块
能够有效地捕捉特征的空间和通道维度的依赖关系,从而使模型更加关注输入中相关的元素。在骨干网络的最后一层添加该模块,可以对高层特征进行重新校准,突出重要特征,抑制不重要的特征,提高特征的表达能力。
# YOLOv9# parameters
nc: 1 # number of classes
depth_multiple: 1.0 # model depth multiple
width_multiple: 1.0 # layer channel multiple
#activation: nn.LeakyReLU(0.1)
#activation: nn.ReLU()# anchors
anchors: 3# YOLOv9 backbone
backbone:[[-1, 1, Silence, []], # conv down[-1, 1, Conv, [64, 3, 2]], # 1-P1/2# conv down[-1, 1, Conv, [128, 3, 2]], # 2-P2/4# elan-1 block[-1, 1, RepNCSPELAN4, [256, 128, 64, 1]], # 3# avg-conv down[-1, 1, ADown, [256]], # 4-P3/8# elan-2 block[-1, 1, RepNCSPELAN4, [512, 256, 128, 1]], # 5# avg-conv down[-1, 1, ADown, [512]], # 6-P4/16# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]], # 7# avg-conv down[-1, 1, ADown, [512]], # 8-P5/32# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]], # 9[-1, 1, ShuffleAttention, [512]], # 10 # 注意力添加在此处]# YOLOv9 head
head:[# elan-spp block[-1, 1, SPPELAN, [512, 256]], # 10# up-concat merge[-1, 1, nn.Upsample, [None, 2, 'nearest']],[[-1, 7], 1, Concat, [1]], # cat backbone P4# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]], # 13# up-concat merge[-1, 1, nn.Upsample, [None, 2, 'nearest']],[[-1, 5], 1, Concat, [1]], # cat backbone P3# elan-2 block[-1, 1, RepNCSPELAN4, [256, 256, 128, 1]], # 16 (P3/8-small)# avg-conv-down merge[-1, 1, ADown, [256]],[[-1, 14], 1, Concat, [1]], # cat head P4# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]], # 19 (P4/16-medium)# avg-conv-down merge[-1, 1, ADown, [512]],[[-1, 11], 1, Concat, [1]], # cat head P5# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]], # 22 (P5/32-large)# multi-level reversible auxiliary branch# routing[5, 1, CBLinear, [[256]]], # 23[7, 1, CBLinear, [[256, 512]]], # 24[9, 1, CBLinear, [[256, 512, 512]]], # 25# conv down[0, 1, Conv, [64, 3, 2]], # 26-P1/2# conv down[-1, 1, Conv, [128, 3, 2]], # 27-P2/4# elan-1 block[-1, 1, RepNCSPELAN4, [256, 128, 64, 1]], # 28# avg-conv down fuse[-1, 1, ADown, [256]], # 29-P3/8[[24, 25, 26, -1], 1, CBFuse, [[0, 0, 0]]], # 30 # elan-2 block[-1, 1, RepNCSPELAN4, [512, 256, 128, 1]], # 31# avg-conv down fuse[-1, 1, ADown, [512]], # 32-P4/16[[25, 26, -1], 1, CBFuse, [[1, 1]]], # 33 # elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]], # 34# avg-conv down fuse[-1, 1, ADown, [512]], # 35-P5/32[[26, -1], 1, CBFuse, [[2]]], # 36# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]], # 37# detection head# detect[[32, 35, 38, 17, 20, 23], 1, DualDDetect, [nc]], # DualDDetect(A3, A4, A5, P3, P4, P5)]
5.2 模型改进版本二⭐
此处同样以models/detect/yolov9-c.yaml
为例,在同目录下创建一个用于自己数据集训练的模型文件yolov9-c-SARepNCSPELAN4.yaml
。
将yolov9-c.yaml
中的内容复制到yolov9-c-SARepNCSPELAN4.yaml
文件下,修改nc
数量等于自己数据中目标的数量。
📌 模型的修改方法是将骨干网络中的所有RepNCSPELAN4模块
替换成SARepNCSPELAN4模块
。
# YOLOv9# parameters
nc: 1 # number of classes
depth_multiple: 1.0 # model depth multiple
width_multiple: 1.0 # layer channel multiple
#activation: nn.LeakyReLU(0.1)
#activation: nn.ReLU()# anchors
anchors: 3# YOLOv9 backbone
backbone:[[-1, 1, Silence, []], # conv down[-1, 1, Conv, [64, 3, 2]], # 1-P1/2# conv down[-1, 1, Conv, [128, 3, 2]], # 2-P2/4# elan-1 block[-1, 1, SARepNCSPELAN4, [256, 128, 64, 1]], # 3# avg-conv down[-1, 1, ADown, [256]], # 4-P3/8# elan-2 block[-1, 1, SARepNCSPELAN4, [512, 256, 128, 1]], # 5# avg-conv down[-1, 1, ADown, [512]], # 6-P4/16# elan-2 block[-1, 1, SARepNCSPELAN4, [512, 512, 256, 1]], # 7(可替换)# avg-conv down[-1, 1, ADown, [512]], # 8-P5/32# elan-2 block[-1, 1, SARepNCSPELAN4, [512, 512, 256, 1]], # 9(可替换)]# YOLOv9 head
head:[# elan-spp block[-1, 1, SPPELAN, [512, 256]], # 10# up-concat merge[-1, 1, nn.Upsample, [None, 2, 'nearest']],[[-1, 7], 1, Concat, [1]], # cat backbone P4# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]], # 13# up-concat merge[-1, 1, nn.Upsample, [None, 2, 'nearest']],[[-1, 5], 1, Concat, [1]], # cat backbone P3# elan-2 block[-1, 1, RepNCSPELAN4, [256, 256, 128, 1]], # 16 (P3/8-small)# avg-conv-down merge[-1, 1, ADown, [256]],[[-1, 13], 1, Concat, [1]], # cat head P4# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]], # 19 (P4/16-medium)# avg-conv-down merge[-1, 1, ADown, [512]],[[-1, 10], 1, Concat, [1]], # cat head P5# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]], # 22 (P5/32-large)# multi-level reversible auxiliary branch# routing[5, 1, CBLinear, [[256]]], # 23[7, 1, CBLinear, [[256, 512]]], # 24[9, 1, CBLinear, [[256, 512, 512]]], # 25# conv down[0, 1, Conv, [64, 3, 2]], # 26-P1/2# conv down[-1, 1, Conv, [128, 3, 2]], # 27-P2/4# elan-1 block[-1, 1, RepNCSPELAN4, [256, 128, 64, 1]], # 28# avg-conv down fuse[-1, 1, ADown, [256]], # 29-P3/8[[23, 24, 25, -1], 1, CBFuse, [[0, 0, 0]]], # 30 # elan-2 block[-1, 1, RepNCSPELAN4, [512, 256, 128, 1]], # 31# avg-conv down fuse[-1, 1, ADown, [512]], # 32-P4/16[[24, 25, -1], 1, CBFuse, [[1, 1]]], # 33 # elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]], # 34# avg-conv down fuse[-1, 1, ADown, [512]], # 35-P5/32[[25, -1], 1, CBFuse, [[2]]], # 36# elan-2 block[-1, 1, RepNCSPELAN4, [512, 512, 256, 1]], # 37# detection head# detect[[31, 34, 37, 16, 19, 22], 1, DualDDetect, [nc]], # DualDDetect(A3, A4, A5, P3, P4, P5)]
六、成功运行结果
分别打印网络模型可以看到ShuffleAttention模块
和SARepNCSPELAN4
已经加入到模型中,并可以进行训练了。
yolov9-c-sa:
from n params module arguments 0 -1 1 0 models.common.Silence [] 1 -1 1 1856 models.common.Conv [3, 64, 3, 2] 2 -1 1 73984 models.common.Conv [64, 128, 3, 2] 3 -1 1 212864 models.common.RepNCSPELAN4 [128, 256, 128, 64, 1] 4 -1 1 164352 models.common.ADown [256, 256] 5 -1 1 847616 models.common.RepNCSPELAN4 [256, 512, 256, 128, 1] 6 -1 1 656384 models.common.ADown [512, 512] 7 -1 1 2857472 models.common.RepNCSPELAN4 [512, 512, 512, 256, 1] 8 -1 1 656384 models.common.ADown [512, 512] 9 -1 1 2857472 models.common.RepNCSPELAN4 [512, 512, 512, 256, 1] 10 -1 1 192 models.common.ShuffleAttention [512, 512] 11 -1 1 656896 models.common.SPPELAN [512, 512, 256] 12 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] 13 [-1, 7] 1 0 models.common.Concat [1] 14 -1 1 3119616 models.common.RepNCSPELAN4 [1024, 512, 512, 256, 1] 15 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] 16 [-1, 5] 1 0 models.common.Concat [1] 17 -1 1 912640 models.common.RepNCSPELAN4 [1024, 256, 256, 128, 1] 18 -1 1 164352 models.common.ADown [256, 256] 19 [-1, 14] 1 0 models.common.Concat [1] 20 -1 1 2988544 models.common.RepNCSPELAN4 [768, 512, 512, 256, 1] 21 -1 1 656384 models.common.ADown [512, 512] 22 [-1, 11] 1 0 models.common.Concat [1] 23 -1 1 3119616 models.common.RepNCSPELAN4 [1024, 512, 512, 256, 1] 24 5 1 131328 models.common.CBLinear [512, [256]] 25 7 1 393984 models.common.CBLinear [512, [256, 512]] 26 9 1 656640 models.common.CBLinear [512, [256, 512, 512]] 27 0 1 1856 models.common.Conv [3, 64, 3, 2] 28 -1 1 73984 models.common.Conv [64, 128, 3, 2] 29 -1 1 212864 models.common.RepNCSPELAN4 [128, 256, 128, 64, 1] 30 -1 1 164352 models.common.ADown [256, 256] 31 [24, 25, 26, -1] 1 0 models.common.CBFuse [[0, 0, 0]] 32 -1 1 847616 models.common.RepNCSPELAN4 [256, 512, 256, 128, 1] 33 -1 1 656384 models.common.ADown [512, 512] 34 [25, 26, -1] 1 0 models.common.CBFuse [[1, 1]] 35 -1 1 2857472 models.common.RepNCSPELAN4 [512, 512, 512, 256, 1] 36 -1 1 656384 models.common.ADown [512, 512] 37 [26, -1] 1 0 models.common.CBFuse [[2]] 38 -1 1 2857472 models.common.RepNCSPELAN4 [512, 512, 512, 256, 1] 39[32, 35, 38, 17, 20, 23] 1 21542822 DualDDetect [1, [512, 512, 512, 256, 512, 512]]
yolov9-c-sa summary: 608 layers, 50698470 parameters, 48373702 gradients, 236.6 GFLOPs
yolov9-c-SARepNCSPELAN4:
from n params module arguments 0 -1 1 0 models.common.Silence [] 1 -1 1 1856 models.common.Conv [3, 64, 3, 2] 2 -1 1 73984 models.common.Conv [64, 128, 3, 2] 3 -1 1 138928 models.common.SARepNCSPELAN4 [128, 256, 128, 64, 1] 4 -1 1 164352 models.common.ADown [256, 256] 5 -1 1 552288 models.common.SARepNCSPELAN4 [256, 512, 256, 128, 1] 6 -1 1 656384 models.common.ADown [512, 512] 7 -1 1 1676992 models.common.SARepNCSPELAN4 [512, 512, 512, 256, 1] 8 -1 1 656384 models.common.ADown [512, 512] 9 -1 1 1676992 models.common.SARepNCSPELAN4 [512, 512, 512, 256, 1] 10 -1 1 656896 models.common.SPPELAN [512, 512, 256] 11 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] 12 [-1, 7] 1 0 models.common.Concat [1] 13 -1 1 3119616 models.common.RepNCSPELAN4 [1024, 512, 512, 256, 1] 14 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] 15 [-1, 5] 1 0 models.common.Concat [1] 16 -1 1 912640 models.common.RepNCSPELAN4 [1024, 256, 256, 128, 1] 17 -1 1 164352 models.common.ADown [256, 256] 18 [-1, 13] 1 0 models.common.Concat [1] 19 -1 1 2988544 models.common.RepNCSPELAN4 [768, 512, 512, 256, 1] 20 -1 1 656384 models.common.ADown [512, 512] 21 [-1, 10] 1 0 models.common.Concat [1] 22 -1 1 3119616 models.common.RepNCSPELAN4 [1024, 512, 512, 256, 1] 23 5 1 131328 models.common.CBLinear [512, [256]] 24 7 1 393984 models.common.CBLinear [512, [256, 512]] 25 9 1 656640 models.common.CBLinear [512, [256, 512, 512]] 26 0 1 1856 models.common.Conv [3, 64, 3, 2] 27 -1 1 73984 models.common.Conv [64, 128, 3, 2] 28 -1 1 212864 models.common.RepNCSPELAN4 [128, 256, 128, 64, 1] 29 -1 1 164352 models.common.ADown [256, 256] 30 [23, 24, 25, -1] 1 0 models.common.CBFuse [[0, 0, 0]] 31 -1 1 847616 models.common.RepNCSPELAN4 [256, 512, 256, 128, 1] 32 -1 1 656384 models.common.ADown [512, 512] 33 [24, 25, -1] 1 0 models.common.CBFuse [[1, 1]] 34 -1 1 2857472 models.common.RepNCSPELAN4 [512, 512, 512, 256, 1] 35 -1 1 656384 models.common.ADown [512, 512] 36 [25, -1] 1 0 models.common.CBFuse [[2]] 37 -1 1 2857472 models.common.RepNCSPELAN4 [512, 512, 512, 256, 1] 38[31, 34, 37, 16, 19, 22] 1 21542822 DualDDetect [1, [512, 512, 512, 256, 512, 512]]yolov9-c-SARepNCSPELAN4 summary: 620 layers, 47969462 parameters, 45644694 gradients, 224.4 GFLOPs