前言本文介绍了一种基于分割的卷积操作SPConv及其在YOLO26中的结合。传统卷积方法忽视了特征图中的模式冗余SPConv将输入特征图分割为代表性部分和不确定冗余部分分别采用k × k k \times kk×k卷积和1 × 1 1 \times 11×1卷积处理并引入组卷积减少代表性部分冗余还设计了无参数特征融合模块。SPConv可直接替代原始卷积。我们将其集成进YOLO26实验表明配备SPConv的网络在准确性、推理时间上优于基准模型同时减少了浮点运算和参数量。文章目录 YOLO26改进大全卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总专栏链接: YOLO26改进专栏文章目录前言介绍摘要文章链接基本原理代表性部分的进一步减少3.2 无参数特征融合模块核心代码YOLO26引入代码注册步骤1:步骤2配置yolo26-SPConv.yaml实验脚本结果介绍摘要许多有效的解决方案已被提出以减少推理加速中模型的冗余。然而常见的方法大多集中在消除不重要的滤波器或构建高效的操作上而忽视了特征图中的模式冗余。我们揭示了在一个层内许多特征图分享相似但不完全相同的模式。然而确定具有类似模式的特征是否冗余或包含重要细节是困难的。因此我们提出了一种基于分割的卷积操作即SPConv来容忍具有相似模式但需要较少计算的特征。具体来说我们将输入的特征图分割成代表性部分和不确定冗余部分从代表性部分中通过相对复杂的计算提取内在信息而在不确定冗余部分中处理微小的隐藏细节则采用轻量级操作。为了重新校准和融合这两组处理过的特征我们提出了一个无需参数的特征融合模块。此外我们的SPConv设计成可以直接替代原始卷积在使用中非常便捷。实验结果表明基准测试中配备SPConv的网络在GPU上在准确性和推理时间上始终优于最先进的基准模型同时大幅减少了浮点运算和参数量。文章链接论文地址论文地址代码地址代码地址基本原理在现有的滤波器中如常规卷积、GhostConv、OctConv和HetConv通常在所有输入通道上执行 ( k \times k ) 卷积操作。然而这些方法未能解决同一层特征中存在的冗余问题即使不存在完全相同的两个通道特征也难以直接剔除冗余。受此现象启发研究者提出了一种新的方法将所有输入特征按比例分为两部分代表性部分执行k × k k \times kk×k卷积以提取重要信息不确定部分执行1 × 1 1 \times 11×1卷积以补充隐含细节信息。这种过程可以用以下公式描述SPConv Representative部分 ⊕ Uncertain部分 \text{SPConv} \text{Representative部分} \oplus \text{Uncertain部分}SPConvRepresentative部分⊕Uncertain部分代表性部分的进一步减少在将所有输入通道分成两个主要部分后代表性部分之间可能存在冗余。为了进一步减少这种冗余我们引入了组卷积的概念。组卷积可以视作具有稀疏块对角卷积核的普通卷积其中每个块对应一个通道并且分组之间没有连接。这一步骤旨在消除代表性部分内部的冗余尽管可能会牺牲一些跨通道连接的有用信息。为了弥补信息损失我们在所有代表性通道上应用逐点卷积将组卷积和逐点卷积的结果通过直接求和融合从而获得更丰富的特征表示。3.2 无参数特征融合模块为了克服分组信息损失带来的影响我们设计了一个无参数特征融合模块用于控制不同输入通道特征的有效融合。这一模块与传统的直接求和融合不同不需要额外的参数却能提升模型的性能表现。这种设计使得我们的SPConv在性能和效率上都有显著的提升。通过这些优化SPConv不仅解决了传统方法中存在的特征冗余和信息损失问题还有效地提升了模型的整体性能和学习能力。这些创新的特征提取和融合方法使得SPConv在图像处理和计算机视觉任务中具有广泛的应用前景。核心代码classSPConv_3x3(nn.Module):def__init__(self,inplanes,outplanes,stride1,ratio0.5):super(SPConv_3x3,self).__init__()# 计算3x3卷积和1x1卷积的输入输出通道数self.inplanes_3x3int(inplanes*ratio)self.inplanes_1x1inplanes-self.inplanes_3x3 self.outplanes_3x3int(outplanes*ratio)self.outplanes_1x1outplanes-self.outplanes_3x3 self.outplanesoutplanes self.stridestride# 定义3x3组卷积和1x1卷积层self.gwcnn.Conv2d(self.inplanes_3x3,self.outplanes,kernel_size3,strideself.stride,padding1,groups2,biasFalse)self.pwcnn.Conv2d(self.inplanes_3x3,self.outplanes,kernel_size1,biasFalse)# 定义1x1卷积层self.conv1x1nn.Conv2d(self.inplanes_1x1,self.outplanes,kernel_size1)# 定义平均池化层self.avgpool_s2_1nn.AvgPool2d(kernel_size2,stride2)self.avgpool_s2_3nn.AvgPool2d(kernel_size2,stride2)self.avgpool_add_1nn.AdaptiveAvgPool2d(1)self.avgpool_add_3nn.AdaptiveAvgPool2d(1)# 定义批归一化层self.bn1nn.BatchNorm2d(self.outplanes)self.bn2nn.BatchNorm2d(self.outplanes)self.ratioratio self.groupsint(1/self.ratio)defforward(self,x):b,c,_,_x.size()# 分割输入特征为3x3卷积部分和1x1卷积部分x_3x3x[:,:int(c*self.ratio),:,:]x_1x1x[:,int(c*self.ratio):,:,:]# 3x3卷积部分的前向传播out_3x3_gwcself.gwc(x_3x3)ifself.stride2:x_3x3self.avgpool_s2_3(x_3x3)out_3x3_pwcself.pwc(x_3x3)out_3x3out_3x3_gwcout_3x3_pwc out_3x3self.bn1(out_3x3)out_3x3_ratioself.avgpool_add_3(out_3x3).squeeze(dim3).squeeze(dim2)# 1x1卷积部分的前向传播ifself.stride2:x_1x1self.avgpool_s2_1(x_1x1)out_1x1self.conv1x1(x_1x1)out_1x1self.bn2(out_1x1)out_1x1_ratioself.avgpool_add_1(out_1x1).squeeze(dim3).squeeze(dim2)# 将3x3和1x1卷积部分的输出按通道进行堆叠并应用Softmax进行权重融合out_31_ratiotorch.stack((out_3x3_ratio,out_1x1_ratio),2)out_31_rationn.Softmax(dim2)(out_31_ratio)# 按融合的权重计算最终的输出outout_1x1*(out_31_ratio[:,:,1].view(b,self.outplanes,1,1).expand_as(out_1x1))\out_3x3*(out_31_ratio[:,:,0].view(b,self.outplanes,1,1).expand_as(out_3x3))returnoutYOLO26引入代码在根目录下的ultralytics/nn/目录新建一个conv目录然后新建一个以SPConv为文件名的py文件 把代码拷贝进去。importtorchimporttorch.nnasnnclassSPConv(nn.Module):def__init__(self,inplanes,outplanes,stride1,ratio0.5):super(SPConv,self).__init__()self.inplanes_3x3int(inplanes*ratio)self.inplanes_1x1inplanes-self.inplanes_3x3 self.outplanes_3x3int(outplanes*ratio)self.outplanes_1x1outplanes-self.outplanes_3x3 self.outplanesoutplanes self.stridestride self.gwcnn.Conv2d(self.inplanes_3x3,self.outplanes,kernel_size3,strideself.stride,padding1,groups2,biasFalse)self.pwcnn.Conv2d(self.inplanes_3x3,self.outplanes,kernel_size1,biasFalse)self.conv1x1nn.Conv2d(self.inplanes_1x1,self.outplanes,kernel_size1)self.avgpool_s2_1nn.AvgPool2d(kernel_size2,stride2)self.avgpool_s2_3nn.AvgPool2d(kernel_size2,stride2)self.avgpool_add_1nn.AdaptiveAvgPool2d(1)self.avgpool_add_3nn.AdaptiveAvgPool2d(1)self.bn1nn.BatchNorm2d(self.outplanes)self.bn2nn.BatchNorm2d(self.outplanes)self.ratioratio self.groupsint(1/self.ratio)defforward(self,x):b,c,_,_x.size()x_3x3x[:,:int(c*self.ratio),:,:]x_1x1x[:,int(c*self.ratio):,:,:]out_3x3_gwcself.gwc(x_3x3)ifself.stride2:x_3x3self.avgpool_s2_3(x_3x3)out_3x3_pwcself.pwc(x_3x3)out_3x3out_3x3_gwcout_3x3_pwc out_3x3self.bn1(out_3x3)out_3x3_ratioself.avgpool_add_3(out_3x3).squeeze(dim3).squeeze(dim2)# use avgpool first to reduce information lostifself.stride2:x_1x1self.avgpool_s2_1(x_1x1)out_1x1self.conv1x1(x_1x1)out_1x1self.bn2(out_1x1)out_1x1_ratioself.avgpool_add_1(out_1x1).squeeze(dim3).squeeze(dim2)out_31_ratiotorch.stack((out_3x3_ratio,out_1x1_ratio),2)out_31_rationn.Softmax(dim2)(out_31_ratio)outout_1x1*(out_31_ratio[:,:,1].view(b,self.outplanes,1,1).expand_as(out_1x1))\out_3x3*(out_31_ratio[:,:,0].view(b,self.outplanes,1,1).expand_as(out_3x3))returnout注册在ultralytics/nn/tasks.py中进行如下操作步骤1:fromultralytics.nn.conv.SPConvimportSPConv步骤2修改def parse_model(d, ch, verboseTrue):SPConv配置yolo26-SPConv.yamlultralytics/cfg/models/26/yolo26-SPConv.yaml# Ultralytics AGPL-3.0 License - https://ultralytics.com/license# Ultralytics YOLO26 object detection model with P3/8 - P5/32 outputs# Model docs: https://docs.ultralytics.com/models/yolo26# Task docs: https://docs.ultralytics.com/tasks/detect# Parametersnc:80# number of classesend2end:True# whether to use end-to-end modereg_max:1# DFL binsscales:# model compound scaling constants, i.e. modelyolo26n.yaml will call yolo26.yaml with scale n# [depth, width, max_channels]n:[0.50,0.25,1024]# summary: 260 layers, 2,572,280 parameters, 2,572,280 gradients, 6.1 GFLOPss:[0.50,0.50,1024]# summary: 260 layers, 10,009,784 parameters, 10,009,784 gradients, 22.8 GFLOPsm:[0.50,1.00,512]# summary: 280 layers, 21,896,248 parameters, 21,896,248 gradients, 75.4 GFLOPsl:[1.00,1.00,512]# summary: 392 layers, 26,299,704 parameters, 26,299,704 gradients, 93.8 GFLOPsx:[1.00,1.50,512]# summary: 392 layers, 58,993,368 parameters, 58,993,368 gradients, 209.5 GFLOPs# YOLO26n backbonebackbone:# [from, repeats, module, args]-[-1,1,Conv,[64,3,2]]# 0-P1/2-[-1,1,SPConv,[128,2]]# 1-P2/4-[-1,2,C3k2,[256,False,0.25]]-[-1,1,SPConv,[256,2]]# 3-P3/8-[-1,2,C3k2,[512,False,0.25]]-[-1,1,SPConv,[512,2]]# 5-P4/16-[-1,2,C3k2,[512,True]]-[-1,1,SPConv,[1024,2]]# 7-P5/32-[-1,2,C3k2,[1024,True]]-[-1,1,SPPF,[1024,5,3,True]]# 9-[-1,2,C2PSA,[1024]]# 10# YOLO26n headhead:-[-1,1,nn.Upsample,[None,2,nearest]]-[[-1,6],1,Concat,[1]]# cat backbone P4-[-1,2,C3k2,[512,True]]# 13-[-1,1,nn.Upsample,[None,2,nearest]]-[[-1,4],1,Concat,[1]]# cat backbone P3-[-1,2,C3k2,[256,True]]# 16 (P3/8-small)-[-1,1,Conv,[256,3,2]]-[[-1,13],1,Concat,[1]]# cat head P4-[-1,2,C3k2,[512,True]]# 19 (P4/16-medium)-[-1,1,Conv,[512,3,2]]-[[-1,10],1,Concat,[1]]# cat head P5-[-1,1,C3k2,[1024,True,0.5,True]]# 22 (P5/32-large)-[[16,19,22],1,Detect,[nc]]# Detect(P3, P4, P5)实验脚本importwarnings warnings.filterwarnings(ignore)fromultralyticsimportYOLOif__name____main__:# 修改为自己的配置文件地址modelYOLO(./ultralytics/cfg/models/26/yolo26-SPConv.yaml)# 修改为自己的数据集地址model.train(data./ultralytics/cfg/datasets/coco8.yaml,cacheFalse,imgsz640,epochs10,single_clsFalse,# 是否是单类别检测batch8,close_mosaic10,workers0,optimizerMuSGD,# optimizerSGD,ampFalse,projectruns/train,nameyolo26-SPConv,)结果