YOLO11改进 - C3k2融合 | FourierSR傅里叶Token插件模块,扩展全局感受野、低成本提升重建细节,助力高效图像超分辨率稳定涨点 | TIP 2026

📅 发布时间:2026/8/17 19:06:32
YOLO11改进 - C3k2融合 | FourierSR傅里叶Token插件模块,扩展全局感受野、低成本提升重建细节,助力高效图像超分辨率稳定涨点 | TIP 2026
前言本文介绍了一种基于傅里叶频域 token 混合的轻量插件 FourierSR用于在低参数量和低计算开销下扩展模型全局感受野。该方法利用卷积定理将空间域特征转换到频域通过复数权重、实虚部分支调制与 Softshrink 稀疏约束完成高效全局特征交互再经逆傅里叶变换恢复空间表征从而增强纹理、边缘和重复结构建模能力。我们将 FourierSR 成功集成进 YOLO11 的 C3k2 模块替代原有局部卷积特征提取单元实现更高效的全局上下文建模与检测特征增强。文章目录 YOLO11改进大全卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总专栏链接: YOLO11改进专栏介绍图像超分辨率SR旨在将低分辨率图像恢复为高分辨率图像其中如何提升 SR 的效率一直是重要挑战。然而SR 中常用的卷积和窗口 Transformer 等单元往往受限于感受野难以在极低计算开销下进一步提升重建性能。为解决这一问题本文受到通过 token mix 建模卷积定理的启发提出一种基于 Fourier token 的插件 FourierSR用于统一提升现有高效 SR 方法并避免现有 token mix 技术作为插件时容易出现的不稳定或低效问题。相比卷积和窗口 TransformerFourierSR 仅使用傅里叶变换和乘法操作在显著降低复杂度的同时具备全局感受野。实验结果表明FourierSR 作为即插即用单元在 Manga109 ×4 测试中可为现有高效 SR 方法带来平均 0.34dB 的 PSNR 提升而参数量和 FLOPs 的平均增幅仅为原模型的 0.6% 和 1.5%。文章链接论文地址论文地址代码地址代码地址基本原理1. 解决的关键问题FourierSR 解决的是高效图像超分辨率中的一个典型矛盾轻量模型需要低参数量、低 FLOPs 和较快推理速度但 SR 又非常依赖较大的感受野来恢复纹理、边缘和重复结构。CNN 型高效 SR 方法通常依赖 3×3 卷积、特征蒸馏或宽激活来降低复杂度但局部卷积的感受野有限窗口 Transformer 能建模更长距离关系但窗口划分限制了跨窗口交互同时注意力计算仍会带来较高成本。已有 Fourier/token-mix 方法也并不天然适合作为 SR 插件。例如GFNet 类方法把与输入同尺寸的全局滤波器作为 token mix 核但 SR 训练和测试分辨率常常不一致容易造成尺寸不稳定AFNO、AFFNet 等方法涉及较多矩阵乘法或直接把 Fourier 特征作为全局核可能带来较高计算量、慢推理甚至训练中的梯度爆炸。FourierSR 的目标是在不重写整套 SR 网络的前提下提供一个小成本、可插入、能扩展全局感受野的模块。2. 整体架构FourierSR 被设计为 plug-and-play plugin可以随机插入到现有高效 SR 网络中。论文将其接入 CNN-based 方法如 EDSR、CARN、RFDN、ShuffleMixer、SMFANet也接入 Transformer-based 方法如 SwinIR、ESRT、SRFormer、HiT-SIR、CATANet以及 Mamba-based 方法如 MambaIR、MambaIRv2以验证通用性。从结构上看FourierSR 先对输入特征X ∈ R^{C×H×W}做 Fourier Transform将空间域特征转换到频域随后通过 reshape/embedding 得到 Fourier tokens并进行 Channel Tokens MixCTM让不同通道 token 之间交换信息。接着模块分别处理 Fourier 特征的实部和虚部用可学习的局部滤波器进行调制。由于广播机制局部滤波器在频域乘法中可以等效扩展成全局滤波器从而以很小参数量模拟全局动态卷积。最后经过逆 Fourier Transform 回到空间域并与原网络特征流结合。3. 技术原理FourierSR 的核心理论基础是卷积定理空间域中的卷积可以等价为频域中的逐元素乘法。也就是说如果能在 Fourier 域中用低成本乘法完成 token mix就可以间接获得类似全局卷积的建模效果。论文进一步利用 Fourier 特征实部和虚部的性质实部与输入及其翻转版本的对称结构相关虚部则与差异结构相关。分别处理实部和虚部可以让模型同时增强大尺度低频结构与高频边缘细节。模块中的 Filter ProductFP是关键设计。它使用形状较小的局部可学习滤波器通过广播机制扩展到与 Fourier token 相同的空间频率尺寸再与 token 做逐元素乘法。这样参数量仍接近局部滤波器但实际作用范围接近全局滤波器。相比直接定义全局滤波器这种方式更适合 SR 中训练/测试尺寸不一致的情况也避免了参数量膨胀。实验部分显示FourierSR 对 EDSR、CARN、LBNet 等方法在 Set5、Set14、BSDS100、Urban100、Manga109 上普遍带来 PSNR/SSIM 提升在 Manga109 ×4 上平均提升约 0.34dB同时参数和 FLOPs 增幅很小。消融实验也验证了 CTM、实部/虚部分支、上下分支和 FFT/IFFT 的必要性去掉 FFT/IFFT 后卷积定理不再成立PSNR 增益明显受限去掉实部会削弱大尺度低频结构去掉虚部会损失高频细节和边缘。整体来看FourierSR 的价值在于用频域 token mix 低成本扩展全局感受野让现有高效 SR 模型在几乎不增加复杂度的情况下获得更好的纹理恢复能力。核心代码classFrequency_Convolution(nn.Module): channels: channel dimension size num_blocks: how many blocks to use in the block diagonal weight matrices (higher less complexity but less parameters) sparsity_threshold: lambda for softshrink hard_thresholding_fraction: how many frequencies you want to completely mask out (lower hard_thresholding_fraction^2 less FLOPs) input shape [B N C] def__init__(self,channels,num_blocks8,sparsity_threshold0.01):super().__init__()assertchannels%num_blocks0,fchannels{channels}should be divisble by num_blocks{num_blocks}self.channelschannels self.sparsity_thresholdsparsity_threshold self.num_blocksnum_blocks self.block_sizechannels//self.num_blocks self.scale0.02self.wnn.Parameter(self.scale*torch.randn(self.num_blocks,self.block_size,self.block_size,2))self.w1nn.Parameter(self.scale*torch.randn(2,self.num_blocks,self.block_size,1,1))self.w2nn.Parameter(self.scale*torch.randn(2,self.num_blocks,self.block_size,1,1))self.bnn.Parameter(self.scale*torch.randn(2,self.num_blocks,self.block_size))defforward(self,x):biasx dtypex.dtype xx.float()B,C,H,Wx.shape xtorch.fft.rfft2(x,dim(2,3),normortho)xx.reshape(B,self.num_blocks,self.block_size,x.shape[2],x.shape[3])weighttorch.view_as_complex(self.w.contiguous())xtorch.einsum(bkihw,kio-bkohw,x,weight)o1_realF.relu(torch.mul(x.real,self.w1[0].unsqueeze(dim0))-\ torch.mul(x.imag,self.w1[1].unsqueeze(dim0))\ self.b[0,:,:,None,None])# [16, 8, 8, 48, 25] x.imag[16, 8, 8, 48, 25]o1_imagF.relu(torch.mul(x.imag,self.w2[0].unsqueeze(dim0))\ torch.mul(x.real,self.w2[1].unsqueeze(dim0))\ self.b[1,:,:,None,None])# [16, 8, 8, 48, 25] x.real[16, 8, 8, 48, 25]xtorch.stack([o1_real,o1_imag],dim-1)# [16, 8, 8, 48, 25, 2]xF.softshrink(x,lambdself.sparsity_threshold)xtorch.view_as_complex(x)# [16, 8, 8, 48, 25]xx.reshape(B,C,x.shape[3],x.shape[4])xtorch.fft.irfft2(x,s(H,W),dim(2,3),normortho)xx.type(dtype)returnxbiasYOLO11引入代码在根目录下的ultralytics/nn/目录新建一个C3k2目录然后新建一个以C3k2_FourierSR为文件名的py文件 把代码拷贝进去。importtorchimporttorch.nnasnnimporttorch.nn.functionalasFfromultralytics.nn.modules.convimportRepConv,Convfromultralytics.nn.modules.blockimportC3k,C3k2classFourierSR(nn.Module): channels: channel dimension size num_blocks: how many blocks to use in the block diagonal weight matrices (higher less complexity but less parameters) sparsity_threshold: lambda for softshrink hard_thresholding_fraction: how many frequencies you want to completely mask out (lower hard_thresholding_fraction^2 less FLOPs) input shape [B N C] def__init__(self,in_channels,out_channels,num_blocks8,sparsity_threshold0.01):super().__init__()assertin_channels%num_blocks0,fin_channels{in_channels}should be divisble by num_blocks{num_blocks}self.in_channelsin_channels self.out_channelsout_channels self.sparsity_thresholdsparsity_threshold self.num_blocksnum_blocks self.block_sizein_channels//self.num_blocks self.scale0.02self.wnn.Parameter(self.scale*torch.randn(self.num_blocks,self.block_size,self.block_size,2))self.w1nn.Parameter(self.scale*torch.randn(2,self.num_blocks,self.block_size,1,1))self.w2nn.Parameter(self.scale*torch.randn(2,self.num_blocks,self.block_size,1,1))self.bnn.Parameter(self.scale*torch.randn(2,self.num_blocks,self.block_size))self.conv_1x1nn.Conv2d(in_channels,out_channels,1)defforward(self,x):biasx dtypex.dtype xx.float()B,C,H,Wx.shape xtorch.fft.rfft2(x,dim(2,3),normortho)xx.reshape(B,self.num_blocks,self.block_size,x.shape[2],x.shape[3])weighttorch.view_as_complex(self.w.float().contiguous())xtorch.einsum(bkihw,kio-bkohw,x,weight)w1,w2,bself.w1.float(),self.w2.float(),self.b.float()o1_realF.relu(torch.mul(x.real,w1[0].unsqueeze(dim0))-\ torch.mul(x.imag,w1[1].unsqueeze(dim0))\ b[0,:,:,None,None])# [16, 8, 8, 48, 25] x.imag[16, 8, 8, 48, 25]o1_imagF.relu(torch.mul(x.imag,w2[0].unsqueeze(dim0))\ torch.mul(x.real,w2[1].unsqueeze(dim0))\ b[1,:,:,None,None])# [16, 8, 8, 48, 25] x.real[16, 8, 8, 48, 25]xtorch.stack([o1_real,o1_imag],dim-1)# [16, 8, 8, 48, 25, 2]xF.softshrink(x,lambdself.sparsity_threshold)xtorch.view_as_complex(x)# [16, 8, 8, 48, 25]xx.reshape(B,C,x.shape[3],x.shape[4])xtorch.fft.irfft2(x,s(H,W),dim(2,3),normortho)xx.type(dtype)returnself.conv_1x1(xbias)classC3k_FourierSR(C3k):def__init__(self,c1,c2,n1,shortcutFalse,g1,e0.5,k3):super().__init__(c1,c2,n,shortcut,g,e,k)c_int(c2*e)# hidden channelsself.mnn.Sequential(*(FourierSR(c_,c_)for_inrange(n)))classC3k2_FourierSR(C3k2):def__init__(self,c1,c2,n1,c3kFalse,e0.5,g1,shortcutTrue):super().__init__(c1,c2,n,c3k,e,g,shortcut)self.mnn.ModuleList(C3k_FourierSR(self.c,self.c,2,shortcut,g)ifc3kelseFourierSR(self.c,self.c)for_inrange(n))注册在ultralytics/nn/tasks.py中进行如下操作步骤1:fromultralytics.nn.C3k2.C3k2_FourierSRimportC3k2_FourierSR步骤2修改def parse_model(d, ch, verboseTrue):C3k2_FourierSR配置yolo11-C3k2_FourierSR.yaml# Ultralytics YOLO , AGPL-3.0 license# YOLO11 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect# Parametersnc:80# number of classesscales:# model compound scaling constants, i.e. modelyolo11n.yaml will call yolo11.yaml with scale n# [depth, width, max_channels]n:[0.50,0.25,1024]# summary: 319 layers, 2624080 parameters, 2624064 gradients, 6.6 GFLOPss:[0.50,0.50,1024]# summary: 319 layers, 9458752 parameters, 9458736 gradients, 21.7 GFLOPsm:[0.50,1.00,512]# summary: 409 layers, 20114688 parameters, 20114672 gradients, 68.5 GFLOPsl:[1.00,1.00,512]# summary: 631 layers, 25372160 parameters, 25372144 gradients, 87.6 GFLOPsx:[1.00,1.50,512]# summary: 631 layers, 56966176 parameters, 56966160 gradients, 196.0 GFLOPs# YOLO11n 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_FourierSR,[256,False,0.25]]-[-1,1,Conv,[256,3,2]]# 3-P3/8-[-1,2,C3k2_FourierSR,[512,False,0.25]]-[-1,1,Conv,[512,3,2]]# 5-P4/16-[-1,2,C3k2_FourierSR,[512,True]]-[-1,1,Conv,[1024,3,2]]# 7-P5/32-[-1,2,C3k2_FourierSR,[1024,True]]-[-1,1,SPPF,[1024,5]]# 9-[-1,2,C2PSA,[1024]]# 10# YOLO11n headhead:-[-1,1,nn.Upsample,[None,2,nearest]]-[[-1,6],1,Concat,[1]]# cat backbone P4-[-1,2,C3k2_FourierSR,[512,False]]# 13-[-1,1,nn.Upsample,[None,2,nearest]]-[[-1,4],1,Concat,[1]]# cat backbone P3-[-1,2,C3k2_FourierSR,[256,False]]# 16 (P3/8-small)-[-1,1,Conv,[256,3,2]]-[[-1,13],1,Concat,[1]]# cat head P4-[-1,2,C3k2_FourierSR,[512,False]]# 19 (P4/16-medium)-[-1,1,Conv,[512,3,2]]-[[-1,10],1,Concat,[1]]# cat head P5-[-1,2,C3k2_FourierSR,[1024,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/11/yolo11-C3k2_FourierSR.yaml)# 修改为自己的数据集地址model.train(data./ultralytics/cfg/datasets/coco8.yaml,cacheFalse,imgsz640,epochs10,single_clsFalse,# 是否是单类别检测batch8,close_mosaic10,workers0,optimizerSGD,ampTrue,projectruns/train,nameC3k2_FourierSR,)结果