
038、GFNet-Filter频域滤波注意力在YOLOv12中的复现——全局频域建模与目标检测增强兄弟们今天聊个有意思的东西。前阵子在调YOLOv12的时候遇到个特别头疼的问题——小目标检测精度死活上不去尤其是那种密集排列的遥感场景模型对全局上下文的理解明显不够。我试过加SE、加CBAM、加SimAM效果都一般感觉就像是在局部特征上打补丁治标不治本。后来翻到GFNet那篇论文突然意识到一个问题我们一直在空间域做注意力但有没有想过在频域里做全局建模这思路一下子就把我点醒了。GFNet的核心思想其实特别朴素——把图像特征变换到频域在频域里做可学习的滤波操作再变换回空间域。这玩意儿的好处在于频域里的一个点对应的是全局的空间频率信息天然就具备全局感受野不像空间注意力那样要堆好几层卷积才能看到全图。我当时就在想要是把这个思路塞进YOLOv12的骨干网络里是不是能解决那个全局上下文缺失的问题先说说GFNet的论文核心。它提出用2D FFT把特征图从空间域变换到频域然后在频域里用一个可学习的滤波器其实就是个复数权重矩阵去调制频谱最后用逆FFT变换回空间域。这个操作可以插在Transformer的每个block里替代原来的自注意力机制。关键点在于频域滤波的计算复杂度是O(HW log HW)比自注意力的O(H²W²)低太多了而且还能保持全局交互。那放到YOLOv12里插入位置就很有讲究了。我试了好几个地方最后发现最有效的是插在骨干网络的C3k2模块之后、SPPF之前。为什么选这儿因为这时候特征图分辨率还比较大一般是80×80或40×40频域变换能保留更多的空间细节信息而且这个位置正好是网络准备做多尺度融合的节点全局上下文信息在这里注入能更好地指导后续的检测头。你要是插在太浅的位置频域滤波会干扰底层纹理特征插在太深的位置特征图太小频域变换的优势就体现不出来了。代码实现上我直接贴核心部分。这里踩过一个坑——PyTorch的FFT默认是沿着最后一维做的但图像特征图是BCHW格式你得先permute成BHWC再变换不然维度对不上。别这样写# 错误示范别这样写x_ffttorch.fft.fft2(x,dim(-2,-1))正确做法是classGFNetFilter(nn.Module):def__init__(self,dim,h14,w8):super().__init__()# 这里h和w是特征图尺寸的一半因为FFT后是共轭对称的# 我们只需要保留一半的频谱分量self.complex_weightnn.Parameter(torch.randn(h,w,dim,2,dtypetorch.float32)*0.02)# 注意这里用randn初始化别用ones不然训练初期梯度爆炸defforward(self,x):B,C,H,Wx.shape# 先permute成BHWC方便FFTxx.permute(0,2,3,1).contiguous()# 做2D FFT只保留前一半频率x_ffttorch.fft.rfft2(x,dim(1,2))# 取实部和虚部拼成复数weighttorch.view_as_complex(self.complex_weight)# 频域滤波这里用复数乘法x_fftx_fft*weight# 逆变换回空间域xtorch.fft.irfft2(x_fft,s(H,W),dim(1,2))# 再permute回BCHWxx.permute(0,3,1,2).contiguous()returnx这里有个细节要注意——rfft2返回的频谱尺寸是(H, W//21)所以complex_weight的尺寸要对应好。我上面写的h和w就是H和W//21。你要是直接照搬论文里的参数很容易维度对不上报错。插入到YOLOv12里我是在C3k2后面加了个GFNetFilter然后接一个残差连接。这样设计的好处是如果频域滤波学不到有用信息残差连接能保证梯度直接回传不会影响原有特征。实际训练的时候我建议把学习率调低一点因为频域参数对学习率比较敏感我一开始用默认学习率loss直接飞了。实验对比这块我在VisDrone数据集上跑了50个epochbaseline是原版YOLOv12n。加了GFNet之后mAP50从34.2%涨到了36.8%mAP50:95从18.7%涨到了20.3%。最明显的是小目标那一栏AP_s从12.1%涨到了14.5%提升幅度接近20%。参数量增加了大概0.3M推理速度从62FPS降到58FPS这个代价完全可以接受。消融实验我做了三组第一组只加GFNet不加残差mAP50是35.1%说明残差连接确实有用第二组把GFNet插在SPPF后面mAP50只有33.9%比baseline还低说明位置选错了反而有害第三组把滤波器换成固定的高斯滤波不学习mAP50是34.5%说明可学习的滤波器才是关键。可视化分析的时候我做了个有趣的实验——把训练好的滤波器权重可视化出来。发现网络学到的滤波器在低频区域有较大的幅值在高频区域幅值较小但相位变化剧烈。这说明网络自动学会了保留低频的全局结构信息同时在高频区域做选择性增强这正好对应了目标检测里既要看全局又要抓细节的需求。最后给点个人经验。第一GFNet不是万能的它更适合那些背景复杂、目标尺度变化大的场景你要是做简单的工业检测可能收益不大。第二插入位置一定要多试我试了五个位置才找到最优的别偷懒直接照搬论文里的位置。第三训练的时候建议用warmup让频域参数先稳定下来不然前期震荡很厉害。第四如果你显存够大可以试试把GFNet和CBAM串起来用我试过效果还能再涨0.5个点但推理速度会再慢一些。这篇就先写到这儿代码我放在GitHub上了需要的兄弟自己去拿。有问题评论区聊我看到都会回。