
本文基于YOLOv13 官方仓库iMoonLab/yolov13ultralytics 8.3.63 fork实测整理Windows/CPU 全程可跑。Neck 篇收官之作是最大刀阔斧的一篇AFPNAsymptotic Feature Pyramid NetworkYang et al., 2023官方源码 github.com/gyyang23/AFPN主张放弃自顶向下自底向上的固定路径让相邻尺度先融合、逐级渐进。本文用官方 AFPN 整体替换 v13 的 HyperACE/FullPAD Head——实测参数减少 825,746、GFLOPs 7.1。前言Neck 篇收官。前四篇都是节点级改动换一个汇合点、一处上采样、一处卷积这篇是结构级改动把 v13 的整个 headHyperACE 超图 7 个 FullPAD 门控 PANet 路径共 24 层替换为一个 AFPN 渐进式融合模块。AFPN 出自论文《AFPN: Asymptotic Feature Pyramid Network for Object Detection》2023核心思想是渐近asymptotic直接融合语义差距大的相邻尺度会有信息冲突先把最相邻的层级融合出中间结果再逐级向更远尺度扩散。替换后整网只有14 层。专栏目录YOLOv13改进目录一览专栏地址YOLOv13改进专栏——持续更新各方向即插即用改进一、替换点分析整个 Headbackbone: - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2 - [-1, 1, Conv, [128, 3, 2, 1, 2]] # 1-P2/4 - [-1, 2, DSC3k2, [256, False, 0.25]]# 2 ← AFPN 的第 4 路输入P2 尺度64 通道 - [-1, 1, Conv, [256, 3, 2, 1, 4]] # 3-P3/8 - [-1, 2, DSC3k2, [512, False, 0.25]]# 4 ← 第 1 路P3128 通道 - [-1, 1, DSConv, [512, 3, 2]] # 5-P4/16 - [-1, 4, A2C2f, [512, True, 4]] # 6 ← 第 2 路P4128 通道 - [-1, 1, DSConv, [1024, 3, 2]] # 7-P5/32 - [-1, 4, A2C2f, [1024, True, 1]] # 8 ← 第 3 路P5256 通道 head: - [[2, 4, 6, 8], 1, AFPN, [128]] # 9 ★AFPN整体替换4 尺度渐进融合 - [9, 1, GoI, [1]] # 10 取 P3 - [9, 1, GoI, [2]] # 11 取 P4 - [9, 1, GoI, [3]] # 12 取 P5 - [[10, 11, 12], 1, Detect, [nc]] # 13 Detect(P3, P4, P5)两个关键点AFPN 官方是 4 输入结构而 v13 backbone 恰好有 4 个天然尺度——层 2P2/464 通道、层 4P3/8128 通道、层 6P4/16128 通道、层 8P5/32256 通道与官方 [C2, C3, C4, C5] 的尺度约定完全对应多输出的桥梁AFPN 输出 4 路特征而 Detect 需要按层取用——本文引入一个 7 参数的辅助模块GoIGo-Index取第 idx 路搭桥。yaml 从 33 层瘦身到 14 层。二、AFPN 原理传统 FPN/PAN 的路径是写死的先自顶向下、再自底向上所有相邻尺度无差别融合。AFPN 问语义差距悬殊的尺度P5 与 P3直接融合冲突谁来解决渐近融合只让相邻尺度先融合P2P3 → P3P3P4 → P4……语义差距逐级消化。每一级融合用ASFF 节点自适应空间融合逐像素 softmax 权重完成渐进的两轮第一轮完成基础融合第二轮在融合结果上再做一次带残差的渐进融合最后各尺度过 BasicBlock 精炼输出尺度对齐跨尺度融合前用 1×1 卷积Downsample_x2/x4/x8 与 Upsample对齐空间尺寸与通道。一句话把金字塔的形状也交给网络按内容组织而不是设计者画死。⚠️实现说明官方 AFPN 为 mmdetection 的 4 级实现本文逐类内嵌BasicConv/BasicBlock/Upsample/Downsample/ASFF_2I/ASFF_3/ASFF_4/BlockBody仅将官方类名ASFF_2改为ASFF_2I避免与 ultralytics parse_model 的属性名冲突见下方新坑。内部节点与官方逐位一致。三、实现代码模块较长BasicConv / BasicBlock / Upsample / Downsample_x2/x4/x8 / ASFF_2I / ASFF_3 / ASFF_4 / BlockBody / AFPN / GoI 共约 230 行完整代码可在专栏配套代码包中获取核心骨架如下import torch import torch.nn as nn import torch.nn.functional as F class BasicConv(nn.Module): 官方 AFPN 的 BasicConvConv BN SiLUk1 时自动补 padding。 def __init__(self, in_planes, out_planes, kernel_size, stride1, padNone, reluTrue, bnTrue, biasFalse): super().__init__() if not pad: pad (kernel_size - 1) // 2 if kernel_size else 0 self.conv nn.Conv2d(in_planes, out_planes, kernel_size, stridestride, paddingpad, groupsgroups if False else 1, biasbias) self.bn nn.BatchNorm2d(out_planes, eps1e-5, momentum0.01, affineTrue) if bn else None self.relu nn.ReLU() if relu else None def forward(self, x): x self.conv(x) if self.bn is not None: x self.bn(x) if self.relu is not None: x self.relu(x) return x class ASFF_2(nn.Module): 两输入 ASFF 融合节点softmax 逐像素加权。 def __init__(self, inter_dim): super().__init__() compress_c 8 self.weight_level_1 BasicConv(inter_dim, compress_c, 1, 1) self.weight_level_2 BasicConv(inter_dim, compress_c, 1, 1) self.weight_levels nn.Conv2d(compress_c * 2, 2, 1, 1, 0) self.conv BasicConv(inter_dim, inter_dim, 3, 1) def forward(self, input1, input2): w1 self.weight_level_1(input1) w2 self.weight_level_2(input2) wei self.weight_levels(torch.cat((w1, w2), 1)) wei F.softmax(wei, dim1) return input1 * wei[:, 0:1] input2 * wei[:, 1:2] # Downsample_x2/x4/x8、Upsample、ASFF_3三输入、ASFF_4四输入、 # BlockBody两轮渐进融合与官方实现一致篇幅所限从略 # 完整代码与专栏配套包一致。 class AFPN(nn.Module): AFPN 渐进式特征金字塔输入 4 个尺度输出 4 路融合特征。 def __init__(self, in_channels, out_channels128): super().__init__() self.conv0 BasicConv(in_channels[0], in_channels[0] // 8, 1) self.conv1 BasicConv(in_channels[1], in_channels[1] // 8, 1) self.conv2 BasicConv(in_channels[2], in_channels[2] // 8, 1) self.conv3 BasicConv(in_channels[3], in_channels[3] // 8, 1) self.body BlockBody([in_channels[0] // 8, in_channels[1] // 8, in_channels[2] // 8, in_channels[3] // 8]) self.conv00 BasicConv(in_channels[0] // 8, out_channels, 1) self.conv11 BasicConv(in_channels[1] // 8, out_channels, 1) self.conv22 BasicConv(in_channels[2] // 8, out_channels, 1) self.conv33 BasicConv(in_channels[3] // 8, out_channels, 1) def forward(self, x): x0, x1, x2, x3 x x0 self.conv0(x0) x1 self.conv1(x1) x2 self.conv2(x2) x3 self.conv3(x3) out0, out1, out2, out3 self.body([x0, x1, x2, x3]) return self.conv00(out0), self.conv11(out1), self.conv22(out2), self.conv33(out3) class GoI(nn.Module): GoIndex从多输出模块取第 idx 路特征。 def __init__(self, c1, idx0): super().__init__() self.idx idx def forward(self, x): t x[0] if isinstance(x, (list, tuple)) and len(x) 1 else x return t[self.idx]⚠️v13 新坑属性名冲突ultralytics 的parse_model会给每个层实例写入m_.ffrom 索引等属性官方类里若有同名子模块如self.f会直接 TypeError。AFPN 的融合节点ASFF_2/ASFF_4无此问题但为区分对外注册的列表输入版与BlockBody 内部的双参数版本文将内部节点命名为ASFF_2I。注册到 ultralytics 的官方模块成员命名务必避开 f/i/type/np/params。四、添加步骤v13 版1. 修改ultralytics/nn/modules/conv.pyBasicConv若已有则跳过、BasicBlock、Upsample、Downsample_x2/x4/x8、ASFF_2I、ASFF_3、ASFF_4、BlockBody、AFPN、GoI依次粘贴到 conv.py 末尾。2. 修改ultralytics/nn/modules/__init__.pyfrom .conv import (...)里追加AFPN,与GoI,__all__里给最后一项DSConv补逗号后追加AFPN、GoI。3. 修改ultralytics/nn/tasks.py顶部导入块加AFPN、GoIparse_model()里新增两个分支AFPN 多输入取通道列表GoI 取路elif m is AFPN: # AFPN多输入整体 Neck输出通道取声明的统一值 args.insert(0, [ch[x] for x in f]) c2 args[-1] elif m is GoI: # GoI从多输出模块取第 idx 路 c2 ch[f] args [c2, *args]插入位置elif m is FullPAD_Tunnel:之后、else:之前。五、yaml 模型文件整体替换 Head全网仅14 层# Ultralytics YOLOv13n AFPN 渐进式特征金字塔整体替换 Head nc: 80 scales: n: [0.50, 0.25, 1024] s: [0.50, 0.50, 1024] l: [1.00, 1.00, 512] x: [1.00, 1.50, 512] backbone: # [from, repeats, module, args] - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2 - [-1, 1, Conv, [128, 3, 2, 1, 2]] # 1-P2/4 - [-1, 2, DSC3k2, [256, False, 0.25]]# 2P2 尺度AFPN 第 4 路输入 - [-1, 1, Conv, [256, 3, 2, 1, 4]] # 3-P3/8 - [-1, 2, DSC3k2, [512, False, 0.25]]# 4P3AFPN 第 1 路输入 - [-1, 1, DSConv, [512, 3, 2]] # 5-P4/16 - [-1, 4, A2C2f, [512, True, 4]] # 6P4第 2 路输入 - [-1, 1, DSConv, [1024, 3, 2]] # 7-P5/32 - [-1, 4, A2C2f, [1024, True, 1]] # 8P5第 3 路输入 head: - [[2, 4, 6, 8], 1, AFPN, [128]] # 9 ★AFPN整体替换 Head - [9, 1, GoI, [1]] # 10 取 P3 - [9, 1, GoI, [2]] # 11 取 P4 - [9, 1, GoI, [3]] # 12 取 P5 - [[10, 11, 12], 1, Detect, [nc]] # 13 Detect(P3, P4, P5)六、成功运行结果yolo detect train modelyolov13n-AFPN.yaml datamydata.yaml epochs100 imgsz640 batch16Python 方式from ultralytics import YOLO model YOLO(yolov13n-AFPN.yaml).load(yolov13n.pt) model.train(datamydata.yaml, epochs100, imgsz640, batch16)本文实测YOLOv13 官方仓库Windows CPUv13n 结构整体替换 Head基线 yolov13n: 2,494,151 parameters, 6.5 GFLOPs AFPN: 1,668,405 parameters, 7.1 GFLOPs Head→AFPN全网14层 3 epochs completed. Results saved to runs\detect\v13_afpn_smoke✅ 断言全部通过① 内嵌的融合节点与官方 AFPN 实现同权重输出逐位一致ASFF_2 max diff 0② 第 9 层为AFPN总参数量比基线减少 825,746HyperACE/FullPAD 整套结构退场③ 14 层 yaml 的 4 路输入与 GoI 取路全部正确整网 640 前向正常3 轮冒烟训练正常收敛。模型ParamsGFLOPs说明YOLOv13n 基线2,494,1516.5HyperACE 7×FullPAD PANetAFPN1,668,4057.1渐进式融合全网 14 层提示AFPN 是结构级替换与节点级改进不是同一量级的实验——它改变的是融合的路径组织本身。GFLOPs 略升P2 高分辨率路参与融合与参数大降并存属于用算力换结构简化的典型。若你的任务对平移稳定性和多尺度一致性敏感这套渐进式结构值得完整消融。七、总结AFPN 的四点记忆锚渐近融合只融合相邻尺度语义冲突逐级消化、ASFF 节点是融合的原子逐像素 softmax 权重、4 尺度输入恰好对上 v13 的层 2/4/6/8P2 高分辨率路变废为宝、GoI 桥梁多输出模块与 ultralytics 单张量书写的通用解法。它也是 Neck 篇的方法论收官节点级BiFPN/GSConv/CARAFE/ASFF_2解决某个点怎么做结构级AFPN解决整条路怎么组织——两层思路配合使用才能把 Neck 改透。Neck 篇五篇到此完整。下一篇进入【损失函数篇】——WIoU v3 动态非单调聚焦损失专治低质量标注。觉得有帮助的话点赞收藏关注三连支持一下评论区欢迎交流你的实验结果~专栏目录YOLOv13改进目录一览 专栏地址YOLOv13改进专栏——持续更新各方向即插即用改进