前言本文介绍了渐进特征金字塔网络AFPN及其在YOLO26中的结合应用。AFPN用于目标检测任务中的多尺度特征提取解决了传统特征金字塔网络信息丢失和融合效果不佳的问题。其创新点在于跨非相邻层的直接特征融合、自适应空间融合操作具备高效计算性能与广泛适用性。AFPN通过渐进式融合策略和自适应操作避免语义差距和信息冲突。我们将AFPN集成进YOLO26替换部分模块。本文完成 AFPN 在 YOLO26 中的工程接入具体检测精度与速度需通过目标数据集上的完整训练和消融实验评估。文章目录 YOLO26改进大全卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总专栏链接: 最新YOLO26改进专栏文章目录前言介绍摘要创新点文章链接基本原理核心代码YOLO26引入代码tasks注册步骤1导包步骤2解析多路输入与通道配置yolo26-AFPN.yaml实验脚本结果介绍摘要在目标检测任务当中多尺度特征对于对具有尺度变化的对象进行编码起着至关重要的作用。采用经典的自顶向下与自底向上特征金字塔网络是提取多尺度特征的常用策略。然而这些方法存在特征信息丢失或降级的问题这对非相邻层次之间的融合效果产生了不利影响。本文提出了一种渐进特征金字塔网络AFPN旨在支持非相邻层次之间的直接交互。AFPN从融合两个相邻的低级特征开始启动并逐步地将更高级别的特征纳入到融合过程之中。通过这种方式能够避免非相邻层次之间存在的较大语义差距。考虑到在每个空间位置的特征融合过程中存在多对象信息冲突的可能性进一步运用自适应空间融合操作来缓解这些不一致情况。我们将所提出的AFPN融入两阶段和单阶段目标检测框架里并利用MS - COCO 2017验证和测试数据集进行评估。实验评估结果显示相较于其他最先进的特征金字塔网络我们的方法取得了更具竞争力的成果。创新点AFPN渐进特征金字塔网络的创新点主要体现在以下几个方面跨非相邻层的直接特征融合AFPN通过渐进式的特征融合策略允许非相邻层次之间的直接交互这有助于防止在特征传递和交互过程中的信息丢失或退化。这种方法与传统的特征金字塔网络FPN不同后者通常仅在相邻层次之间进行特征融合。自适应空间融合操作为了处理不同层次特征之间可能出现的信息矛盾AFPN引入了自适应空间融合操作。这种方法能够根据不同空间位置的特征动态调整融合权重从而有效地解决了多目标信息冲突问题。高效的计算性能与竞争力的检测结果在MS COCO 2017验证和测试数据集上的大量实验表明AFPN不仅在计算效率上优于其他先进的特征金字塔网络而且还获得了更具竞争力的检测结果。这表明AFPN能够在保持高效计算的同时提高目标检测任务的准确性。广泛的应用适用性AFPN被成功集成到两阶段和单阶段目标检测框架中并在不同的检测模型如Faster R-CNN和YOLOv5上验证了其有效性和提升性能证明了其广泛的适用性和灵活性。启发自HRNet的设计AFPN的设计受到了高分辨率网络HRNet的启发它在整个特征提取过程中保持了低级别特征的丰富性并通过重复融合低级别和高级别特征来生成更丰富的特征表示。这种设计思想在人体姿态估计领域已显示出其优势AFPN将其成功应用于目标检测任务。通过这些创新点AFPN解决了传统特征金字塔网络在处理尺度变化大的对象时的限制提高了目标检测的性能和效率。文章链接论文地址论文地址代码地址代码地址核心代码代码地址基本原理AFPN渐进特征金字塔网络的核心设计理念是在Backbone网络的自下而上特征提取过程中通过渐进的方式集成不同层级的特征。这个过程从低级别特征的融合开始逐步融合更高级别的特征最终融合顶级最高级别的特征。这种方法旨在缓解直接融合非相邻层次特征时由于语义差距较大而导致的融合效果不佳的问题。通过逐渐融合特征AFPN能够使不同层级的特征在语义上更加接近有效减少了不同层级特征间的语义差距特别是在最低级别和最高级别的特征之间。这种渐进的融合策略不仅优化了特征的融合效率而且提高了特征表示的丰富性和有效性。在AFPN的设计中自适应空间融合操作进一步增强了不同层次特征融合的灵活性和效果通过调整融合策略来应对特征间的潜在矛盾从而为目标检测任务提供了更加精确和鲁棒的特征表示。核心代码class ASFF2(nn.Module): ASFF2 module for YOLO AFPN head https://arxiv.org/abs/2306.15988 def __init__(self, c1, c2, level0): super().__init__() if len(c1) ! 2 or level not in (0, 1): raise ValueError(ASFF2 requires two input channel counts and level 0 or 1) c1_l, c1_h c1 self.level level self.dim c1_l, c1_h self.inter_dim self.dim[self.level] compress_c 8 if level 0: self.stride_level_1 Upsample(c1_h, self.inter_dim) if level 1: self.stride_level_0 Conv(c1_l, self.inter_dim, 2, 2, 0) # downsample 2x self.weight_level_0 Conv(self.inter_dim, compress_c, 1, 1) self.weight_level_1 Conv(self.inter_dim, compress_c, 1, 1) self.weights_levels nn.Conv2d(compress_c * 2, 2, kernel_size1, stride1, padding0) self.conv Conv(self.inter_dim, c2, 3, 1) def forward(self, x): x_level_0, x_level_1 x[0], x[1] if self.level 0: level_0_resized x_level_0 level_1_resized self.stride_level_1(x_level_1) elif self.level 1: level_0_resized self.stride_level_0(x_level_0) level_1_resized x_level_1 level_0_weight_v self.weight_level_0(level_0_resized) level_1_weight_v self.weight_level_1(level_1_resized) levels_weight_v torch.cat((level_0_weight_v, level_1_weight_v), 1) levels_weight self.weights_levels(levels_weight_v) levels_weight F.softmax(levels_weight, dim1) fused_out_reduced level_0_resized * levels_weight[:, 0:1] level_1_resized * levels_weight[:, 1:2] return self.conv(fused_out_reduced) class ASFF3(nn.Module): def __init__(self, c1, c2, level0): super().__init__() if len(c1) ! 3 or level not in (0, 1, 2): raise ValueError(ASFF3 requires three input channel counts and level 0, 1 or 2) c1_l, c1_m, c1_h c1 self.level level self.dim c1_l, c1_m, c1_h self.inter_dim self.dim[self.level] compress_c 8 if level 0: self.stride_level_1 Upsample(c1_m, self.inter_dim) self.stride_level_2 Upsample(c1_h, self.inter_dim, scale_factor4) if level 1: self.stride_level_0 Conv(c1_l, self.inter_dim, 2, 2, 0) self.stride_level_2 Upsample(c1_h, self.inter_dim) if level 2: self.stride_level_0 Conv(c1_l, self.inter_dim, 4, 4, 0) self.stride_level_1 Conv(c1_m, self.inter_dim, 2, 2, 0) self.weight_level_0 Conv(self.inter_dim, compress_c, 1, 1) self.weight_level_1 Conv(self.inter_dim, compress_c, 1, 1) self.weight_level_2 Conv(self.inter_dim, compress_c, 1, 1) self.weights_levels nn.Conv2d(compress_c * 3, 3, kernel_size1, stride1, padding0) self.conv Conv(self.inter_dim, c2, 3, 1) def forward(self, x): x_level_0, x_level_1, x_level_2 x[0], x[1], x[2] if self.level 0: level_0_resized x_level_0 level_1_resized self.stride_level_1(x_level_1) level_2_resized self.stride_level_2(x_level_2) elif self.level 1: level_0_resized self.stride_level_0(x_level_0) level_1_resized x_level_1 level_2_resized self.stride_level_2(x_level_2) elif self.level 2: level_0_resized self.stride_level_0(x_level_0) level_1_resized self.stride_level_1(x_level_1) level_2_resized x_level_2 level_0_weight_v self.weight_level_0(level_0_resized) level_1_weight_v self.weight_level_1(level_1_resized) level_2_weight_v self.weight_level_2(level_2_resized) levels_weight_v torch.cat((level_0_weight_v, level_1_weight_v, level_2_weight_v), 1) w self.weights_levels(levels_weight_v) w F.softmax(w, dim1) fused_out_reduced level_0_resized * w[:, :1] level_1_resized * w[:, 1:2] level_2_resized * w[:, 2:] return self.conv(fused_out_reduced)YOLO26引入代码在根目录下的ultralytics/nn/目录新建一个featureFusion目录然后新建一个以AFPN为文件名的py文件 把代码拷贝进去。importtorchimporttorch.nnasnnfromtorch.nnimportfunctionalasFfromultralytics.nn.modules.convimportConvclassUpsample(nn.Module):Applies convolution followed by upsampling.def__init__(self,c1,c2,scale_factor2):super().__init__()ifscale_factor2:self.cv1nn.ConvTranspose2d(c1,c2,2,2,0,biasTrue)# nn.Upsample(scale_factor2, modenearest)elifscale_factor4:self.cv1nn.ConvTranspose2d(c1,c2,4,4,0,biasTrue)else:raiseValueError(AFPN Upsample supports scale_factor 2 or 4)defforward(self,x):returnself.cv1(x)classASFF2(nn.Module):ASFF2 module for YOLO AFPN head https://arxiv.org/abs/2306.15988def__init__(self,c1,c2,level0):super().__init__()iflen(c1)!2orlevelnotin(0,1):raiseValueError(ASFF2 requires two input channel counts and level 0 or 1)c1_l,c1_hc1 self.levellevel self.dimc1_l,c1_h self.inter_dimself.dim[self.level]compress_c8iflevel0:self.stride_level_1Upsample(c1_h,self.inter_dim)iflevel1:self.stride_level_0Conv(c1_l,self.inter_dim,2,2,0)# downsample 2xself.weight_level_0Conv(self.inter_dim,compress_c,1,1)self.weight_level_1Conv(self.inter_dim,compress_c,1,1)self.weights_levelsnn.Conv2d(compress_c*2,2,kernel_size1,stride1,padding0)self.convConv(self.inter_dim,c2,3,1)defforward(self,x):x_level_0,x_level_1x[0],x[1]ifself.level0:level_0_resizedx_level_0 level_1_resizedself.stride_level_1(x_level_1)elifself.level1:level_0_resizedself.stride_level_0(x_level_0)level_1_resizedx_level_1 level_0_weight_vself.weight_level_0(level_0_resized)level_1_weight_vself.weight_level_1(level_1_resized)levels_weight_vtorch.cat((level_0_weight_v,level_1_weight_v),1)levels_weightself.weights_levels(levels_weight_v)levels_weightF.softmax(levels_weight,dim1)fused_out_reducedlevel_0_resized*levels_weight[:,0:1]level_1_resized*levels_weight[:,1:2]returnself.conv(fused_out_reduced)classASFF3(nn.Module):def__init__(self,c1,c2,level0):super().__init__()iflen(c1)!3orlevelnotin(0,1,2):raiseValueError(ASFF3 requires three input channel counts and level 0, 1 or 2)c1_l,c1_m,c1_hc1 self.levellevel self.dimc1_l,c1_m,c1_h self.inter_dimself.dim[self.level]compress_c8iflevel0:self.stride_level_1Upsample(c1_m,self.inter_dim)self.stride_level_2Upsample(c1_h,self.inter_dim,scale_factor4)iflevel1:self.stride_level_0Conv(c1_l,self.inter_dim,2,2,0)self.stride_level_2Upsample(c1_h,self.inter_dim)iflevel2:self.stride_level_0Conv(c1_l,self.inter_dim,4,4,0)self.stride_level_1Conv(c1_m,self.inter_dim,2,2,0)self.weight_level_0Conv(self.inter_dim,compress_c,1,1)self.weight_level_1Conv(self.inter_dim,compress_c,1,1)self.weight_level_2Conv(self.inter_dim,compress_c,1,1)self.weights_levelsnn.Conv2d(compress_c*3,3,kernel_size1,stride1,padding0)self.convConv(self.inter_dim,c2,3,1)defforward(self,x):x_level_0,x_level_1,x_level_2x[0],x[1],x[2]ifself.level0:level_0_resizedx_level_0 level_1_resizedself.stride_level_1(x_level_1)level_2_resizedself.stride_level_2(x_level_2)elifself.level1:level_0_resizedself.stride_level_0(x_level_0)level_1_resizedx_level_1 level_2_resizedself.stride_level_2(x_level_2)elifself.level2:level_0_resizedself.stride_level_0(x_level_0)level_1_resizedself.stride_level_1(x_level_1)level_2_resizedx_level_2 level_0_weight_vself.weight_level_0(level_0_resized)level_1_weight_vself.weight_level_1(level_1_resized)level_2_weight_vself.weight_level_2(level_2_resized)levels_weight_vtorch.cat((level_0_weight_v,level_1_weight_v,level_2_weight_v),1)wself.weights_levels(levels_weight_v)wF.softmax(w,dim1)fused_out_reducedlevel_0_resized*w[:,:1]level_1_resized*w[:,1:2]level_2_resized*w[:,2:]returnself.conv(fused_out_reduced)tasks注册在ultralytics/nn/tasks.py中进行如下操作步骤1导包fromultralytics.nn.featureFusion.AFPNimportASFF2,ASFF3步骤2解析多路输入与通道在parse_model(d, ch, verboseTrue)的模块判断链中增加以下专用分支。elifmin{ASFF2,ASFF3}:expected_inputs2ifmisASFF2else3ifnotisinstance(f,list)orlen(f)!expected_inputs:raiseValueError(f{m.__name__}requires{expected_inputs}input layers)c2make_divisible(min(args[0],max_channels)*width,8)args[[ch[x]forxinf],c2,*args[1:]]配置yolo26-AFPN.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]s:[0.50,0.50,1024]m:[0.50,1.00,512]l:[1.00,1.00,512]x:[1.00,1.50,512]# YOLO26n backbonebackbone:# [from, repeats, module, args]-[-1,1,Conv,[64,3,2]]# 0-P1/2-[-1,1,Conv,[128,3,2]]# 1-P2/4-[-1,2,C3k2,[256,False,0.25]]-[-1,1,Conv,[256,3,2]]# 3-P3/8-[-1,2,C3k2,[512,False,0.25]]-[-1,1,Conv,[512,3,2]]# 5-P4/16-[-1,2,C3k2,[512,True]]-[-1,1,Conv,[1024,3,2]]# 7-P5/32-[-1,2,C3k2,[1024,True]]-[-1,1,SPPF,[1024,5,3,True]]# 9-[-1,2,C2PSA,[1024]]# 10# YOLO26n head: progressive two-scale and three-scale adaptive fusionhead:-[4,1,Conv,[128,1,1]]# 11 project backbone P3/8-[6,1,Conv,[256,1,1]]# 12 project backbone P4/16-[[11,12],1,ASFF2,[128,0]]# 13 fuse at P3-[[11,12],1,ASFF2,[256,1]]# 14 fuse at P4-[13,2,C3k2,[128,True]]# 15 refine P3-[14,2,C3k2,[256,True]]# 16 refine P4-[10,1,Conv,[512,1,1]]# 17 project backbone P5/32-[[15,16,17],1,ASFF3,[128,0]]# 18 fuse at P3-[[15,16,17],1,ASFF3,[256,1]]# 19 fuse at P4-[[15,16,17],1,ASFF3,[512,2]]# 20 fuse at P5-[18,2,C3k2,[128,True]]# 21 P3/8-small-[19,2,C3k2,[256,True]]# 22 P4/16-medium-[20,1,C3k2,[512,True,0.5,True]]# 23 P5/32-large-[[21,22,23],1,Detect,[nc]]# Detect(P3, P4, P5)实验脚本importwarnings warnings.filterwarnings(ignore)fromultralyticsimportYOLOif__name____main__:# 修改为自己的配置文件地址modelYOLO(./ultralytics/cfg/models/26/yolo26-AFPN.yaml)# 修改为自己的数据集地址model.train(data./ultralytics/cfg/datasets/coco8.yaml,cacheFalse,imgsz640,epochs10,single_clsFalse,# 是否是单类别检测batch8,close_mosaic10,workers0,optimizerMuSGD,# optimizerSGD,ampFalse,projectruns/train,nameAFPN,)结果