
本文基于YOLOv13 官方仓库iMoonLab/yolov13ultralytics 8.3.63 fork实测整理Windows/CPU 全程可跑。标准卷积有个隐含假设3×3 感受野里的 9 个位置同等重要。RFAConvReceptive-Field Attention Convolution2023推翻了这个假设——先给感受野里每个位置打分加权之后再卷积。本文把它替换进 v13n 的第 1 层下采样卷积实测4,000 参数。前言卷积层改进的替换式玩法本文的候选是 RFAConv出自论文《RFAConv: Innovating Spatital Attention and Standard Convolution》2023官方源码 github.com/Liuchen1997/RFAConv。它是空间注意力 标准卷积的融合体注意力不再作用在特征图上而是作用在卷积核的感受野内部。替换点选在 v13n backbone 的层 1输入 16 通道 320×320 → 输出 32 通道 160×160与普通 stride-2 卷积的输入输出完全对齐。专栏目录YOLOv13改进目录一览专栏地址YOLOv13改进专栏——持续更新各方向即插即用改进一、替换点分析backbone: - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2 - [-1, 1, Conv, [128, 3, 2, 1, 2]] # 1-P2/4 ← 本文替换这一层 ...层数不变无顺移。层 1 的输入 16 通道、输出 32 通道、stride 2——RFAConv 官方实现支持 stride 参数k3, s2 时输出恰为输入的一半本文已数值验证可直接对齐。二、RFAConv 原理动机空间注意力CBAM 的空间分支等回答特征图上哪个位置重要但卷积核滑窗内部——即感受野的 9 个位置——始终被同等对待。感受野内部的差异信息被池化/下采样给抹掉了RFAConv 的三步打分AvgPool 采样 分组 1×1 卷积为每个感受野生成 k² 个权重softmax 归一化——这 9 个位置谁重要提特征分组 3×3 卷积把每个感受野展开成 k² 个特征片乘上权重重排 卷积把加权后的特征片按空间顺序重排回大图等价于官方的rearrange(b c (n1 n2) h w - b c (h n1) (w n2))再做一次标准卷积输出效果感受野内部的位置信息被显式保留和加权而标准卷积把 9 个位置压成一组共享权重。三、实现代码由官方 Liuchen1997/RFAConv 等价适配官方用einops.rearrange做空间重排本文以 view/permute 等价替换已做同权重输出逐位比对320×320 输入下 max diff 0import torch import torch.nn as nn class RFAConv(nn.Module): RFAConv 感受野注意力卷积 (Liu et al., 2023)官方等价适配版。 输出空间尺寸 输入 / stridek3, s2 时与普通 stride-2 卷积对齐 可直接替换下采样卷积。 def __init__(self, c1, c2, kernel_size3, stride1): super().__init__() self.kernel_size kernel_size self.get_weight nn.Sequential( nn.AvgPool2d(kernel_sizekernel_size, paddingkernel_size // 2, stridestride), nn.Conv2d(c1, c1 * (kernel_size ** 2), kernel_size1, groupsc1, biasFalse)) self.generate_feature nn.Sequential( nn.Conv2d(c1, c1 * (kernel_size ** 2), kernel_sizekernel_size, paddingkernel_size // 2, stridestride, groupsc1, biasFalse), nn.BatchNorm2d(c1 * (kernel_size ** 2)), nn.ReLU()) self.conv nn.Sequential( nn.Conv2d(c1, c2, kernel_sizekernel_size, stridekernel_size)) def forward(self, x): b, c x.shape[0:2] weight self.get_weight(x) h, w weight.shape[2:] weighted weight.view(b, c, self.kernel_size ** 2, h, w).softmax(2) feature self.generate_feature(x).view(b, c, self.kernel_size ** 2, h, w) weighted_data feature * weighted # 等价于官方 rearrange(b c (n1 n2) h w - b c (h n1) (w n2)) conv_data weighted_data.view(b, c, self.kernel_size, self.kernel_size, h, w) conv_data conv_data.permute(0, 1, 4, 2, 5, 3).reshape( b, c, h * self.kernel_size, w * self.kernel_size) return self.conv(conv_data)注意签名与Conv对齐c1 自动传入c2/k/s 来自 yaml注册用卷积类分支c2 宽度缩放。四、添加步骤v13 版1. 修改ultralytics/nn/modules/conv.pyRFAConv类粘贴到 conv.py 末尾。2. 修改ultralytics/nn/modules/__init__.pyfrom .conv import (...)里追加RFAConv,__all__里给最后一项DSConv补逗号后追加RFAConv。3. 修改ultralytics/nn/tasks.py顶部导入块加RFAConvparse_model()里新增卷积类分支c2 宽度缩放elif m is RFAConv: # 卷积类c1 自动传入c2 按宽度系数缩放 c1 ch[f] c2 args[0] if c2 ! nc: c2 make_divisible(min(c2, max_channels) * width, 8) args [c1, c2, *args[1:]]插入位置elif m is FullPAD_Tunnel:之后、else:之前。五、yaml 模型文件只改第 1 层其余与官方 yolov13.yaml 逐行一致# Ultralytics YOLOv13n RFAConv 感受野注意力卷积 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, RFAConv, [128, 3, 2]] # 1-P2/4 ★RFAConv替换stride-2卷积层数不变 - [-1, 2, DSC3k2, [256, False, 0.25]]# 2 - [-1, 1, Conv, [256, 3, 2, 1, 4]] # 3-P3/8 - [-1, 2, DSC3k2, [512, False, 0.25]]# 4 - [-1, 1, DSConv, [512, 3, 2]] # 5-P4/16 - [-1, 4, A2C2f, [512, True, 4]] # 6 - [-1, 1, DSConv, [1024, 3, 2]] # 7-P5/32 - [-1, 4, A2C2f, [1024, True, 1]] # 8 head: - [[4, 6, 8], 2, HyperACE, [512, 8, True, True, 0.5, 1, both]] # 9 - [-1, 1, nn.Upsample, [None, 2, nearest]] # 10 - [ 9, 1, DownsampleConv, []] # 11 - [[6, 9], 1, FullPAD_Tunnel, []] # 12 - [[4, 10], 1, FullPAD_Tunnel, []] # 13 - [[8, 11], 1, FullPAD_Tunnel, []] # 14 - [-1, 1, nn.Upsample, [None, 2, nearest]] # 15 - [[-1, 12], 1, Concat, [1]] # 16 cat backbone P4 - [-1, 2, DSC3k2, [512, True]] # 17 - [[-1, 9], 1, FullPAD_Tunnel, []] # 18 - [17, 1, nn.Upsample, [None, 2, nearest]] # 19 - [[-1, 13], 1, Concat, [1]] # 20 cat backbone P3 - [-1, 2, DSC3k2, [256, True]] # 21 - [10, 1, Conv, [256, 1, 1]] # 22 - [[21, 22], 1, FullPAD_Tunnel, []] # 23 - [-1, 1, Conv, [256, 3, 2]] # 24 - [[-1, 18], 1, Concat, [1]] # 25 cat head P4 - [-1, 2, DSC3k2, [512, True]] # 26 - [[-1, 9], 1, FullPAD_Tunnel, []] # 27 - [26, 1, Conv, [512, 3, 2]] # 28 - [[-1, 14], 1, Concat, [1]] # 29 cat head P5 - [-1, 2, DSC3k2, [1024,True]] # 30 - [[-1, 11], 1, FullPAD_Tunnel, []] # 31 - [[23, 27, 31], 1, Detect, [nc]] # 32 Detect(P3, P4, P5)六、成功运行结果yolo detect train modelyolov13n-RFAConv.yaml datamydata.yaml epochs100 imgsz640 batch16Python 方式from ultralytics import YOLO model YOLO(yolov13n-RFAConv.yaml).load(yolov13n.pt) model.train(datamydata.yaml, epochs100, imgsz640, batch16)本文实测YOLOv13 官方仓库Windows CPUv13n 结构替换层 1基线 yolov13n: 2,494,151 parameters, 6.5 GFLOPs层1为 2,368 参数 RFAConv: 2,498,151 parameters, 6.7 GFLOPs层1为 6,368 参数 3 epochs completed. Results saved to runs\detect\v13_rfaconv_smoke✅ 断言全部通过① 适配版与官方 RFAConv 同权重输出逐位一致max diff 0einops 重排已等价替换② 第 1 层为RFAConv总参数量恰为基线 4,000③ 层数不变整网 640 前向正常3 轮冒烟训练正常收敛。参数量分解c116, c232, k3, s2部件参数量说明get_weight 分组 1×1144感受野打分16 组每组 9 分generate_feature 分组 3×3 BN1,584感受野特征展开输出卷积 3×34,640重排后的标准卷积合计6,3684,000模型ParamsGFLOPs层1参数变化说明YOLOv13n 基线2,494,1516.52,368官方 stride-2 分组卷积RFAConv2,498,1516.76,3684,000感受野内逐位置加权七、总结RFAConv 的三点记忆锚注意力作用在感受野内部k² 个位置打分而非整张特征图、空间重排是关键一步把加权后的特征片拼回大图再卷积、stride 由输出卷积的 stridekernel_size 实现k3, s2 恰好对齐下采样。它与 CoordConv 同位替换层 1两者互为对照CoordConv 补绝对位置感RFAConv 补感受野内相对重要性。觉得有帮助的话点赞收藏关注三连支持一下评论区欢迎交流你的实验结果~专栏目录YOLOv13改进目录一览 专栏地址YOLOv13改进专栏——持续更新各方向即插即用改进