☰
小目标检测新思路:SET频谱增强,从频域找回高频信息
2026/10/21 18:11:06 网站建设 项目流程

小目标检测这个方向,我最近一段时间泡在实验里,最大的感触是:很多在小目标上苦苦挣扎的方法,思路其实走偏了。大家都在拼命往空间域里加东西——把输入分辨率拉高、堆超分模块、反复多尺度融合,结果训练成本翻了几倍,小目标AP提升却非常有限。直到我仔细琢磨了SET这套频谱增强的思路,才意识到一个被大多数人忽略的事实:小目标特征的能量根本不在空间域,而在频域的高频分量里。常规卷积网络在下采样过程中,对高频信息几乎是无条件丢弃的,所以你在空间域里再怎么折腾,都是在帮网络找回它已经扔掉的东西。而SET的做法,是在频域里先把高频分量找回来、增强、再送回网络,等于是在源头解决问题。

这篇文章是我完整复现和调优SET工作后的深度复盘,从方法思路、模块设计、实验参数,到复现过程中踩过的各种坑,都会掰开揉碎讲清楚。不管你是刚入门小目标检测的新人,还是已经在跑实验的工程师,只要你被小目标AP长期卡住,这篇内容应该能给你一个全新的解题方向。

1. 先聊清楚:小目标为什么难检测,难在哪一步

1.1 空间域的经典解释,其实只讲对了一半

在通用检测框架里,小目标通常指像素面积在几十个平方以内的物体。到了无人机俯拍、遥感影像这种场景,目标甚至可能只有十几个像素。空间域的困境大家都熟:目标占的像素少,特征图经过几层下采样之后,可能只剩下一个点甚至完全消失;候选框生成困难;正负样本比例极端失衡;锚框和目标尺寸不匹配。这些说法都对,但它们都在描述一个现象——网络拿不到足够的信息。

可如果说网络真的完全拿不到信息,那为什么人眼能在同样的图像里轻松看到那些小目标?人眼的分辨率不一定比CNN高多少,但我们的大脑有一种能力:对高频边缘极其敏感。哪怕目标只有几个像素,只要它和背景之间存在高频边界,人脑就能基于这个边界信息重构出完整感知。而CNN在连续卷积-池化过程中,对这类高频边界特征的响应能力,远远不如对低频纹理和大结构那么强。所以真正的问题不是"信息不存在",而是"网络在处理过程中把信息主动丢掉了"。

这件事用生活类比来说很简单:你翻拍一张几十年前的旧照片,照片本身细节都在,但你手里这版翻拍件已经因为压缩和残损丢了高频细节,再怎么用滤镜锐化,都不可能把丢失的细节真正补回来。CNN的下采样本质上就是一次又一次的"压缩翻拍",小目标的信息就在这个过程中被磨掉了。

1.2 下采样过程里的混叠,是被忽略的元凶

严格从信号处理的角度看,普通卷积层的stride下采样,是对特征图直接做降采样。根据采样定理,降采样之前必须有低通滤波,否则高频成分会发生混叠,折叠到低频区域变成虚假信息。现代CNN基本不做这个预滤波,一切交给网络去学。网络学出来的是什么?是尽量把对任务有用的信息保留下来,但小目标这种天然占比小的高频信息,在梯度竞争里完全不是大目标低频信息的对手,所以学到的滤波器天然偏向低通。

现象就是:你去看训练好的卷积核,很多浅层核本质上已经变成了平滑滤波器。小目标检测打不过通用检测,根子在这。

1.3 一个反直觉的结论:目标越小,越应该去频域找信息

我当时在复现过程中做过一个简单的可视化实验:把一张包含大量小目标的图像做二维傅里叶变换,然后把高频分量的幅度放大、低频分量压下去,再反变换回空间域。结果是,原来肉眼几乎看不清的小目标,在增强后的图像里边缘变得非常清晰。这个实验给了我很大的冲击——空间域里弱到看不见的特征,在频域里能量其实是存在的,只是被网络忽视了。SET的核心出发点就在这里:不需要换更大的模型,不需要暴力拉高分辨率,只需要在特征图的频域表达上做文章,把高频分量系统地增强一遍,就能让小目标特征重新在网络里获得竞争力。

2. SET的方法框架:频域增强模块到底是怎么设计出来的

2.1 特征图怎么进入频域

要理解SET的做法,先要抓住一个关键前提:模块不是在输入图像上做频谱变换,而是在网络中间层的特征图上做。这个选择非常讲究。如果只增强输入图像的频谱,那增强后的高频信息照样会在下一层下采样时被丢掉,等于做了无用功。只有在网络不同阶段的特征图上做频谱增强,才能保证信息被后续层继续使用。

具体实现上,对一个形状为 (B, C, H, W) 的特征图 X,沿空间维度做二维离散傅里叶变换,得到复频谱 F。在PyTorch里这一步很干净:

import torch import torch.fft as fft def forward(self, x): # x: (B, C, H, W) # 注意:rfft2只会在W维度上利用实信号特性,减少一半计算量 F = fft.rfft2(x, dim=(-2, -1)) # (B, C, H, W//2+1) F_shift = fft.fftshift(F, dim=(-2, -1)) # 低频移到中心 # 后续处理... return x

为什么要做fftshift,把低频分量移到中心?因为不移动的话,低频在四个角上,后续构造频域掩码时要分别处理四个象限,很容易出bug。移动之后,低频居中,高频在外围,掩码设计就是围绕中心的距离函数,逻辑一下子清晰了。

2.2 可学习的频域掩码:核心增强策略

拿到频谱之后,SET的关键步骤是构造一个和频谱同分辨率、可学习的掩码 M,通过逐元素乘法对频谱的幅度做加权,同时保留相位。学过信号处理的同学都知道,幅度谱决定能量分布,相位谱决定空间位置信息。小目标检测既要能量也要位置,所以相位必须原样保留,主打的就是"不动相位,只调幅度"。

掩码的初始设计不是随机初始化,而是基于一个先验:把中心低频区域系数设成接近1(保持不变),外围高频区域系数设成大于1的初始值(增强),中间过渡带用平滑的渐晕。用高斯环形带做平滑过渡比硬编码的圆形边界更好用,避免在频域引入突变——频域突变反变换回空间域会产生振铃伪影,这是我一开始没注意到的细节。

在具体实现上,我采用的是幅度谱增强系数和学习掩码结合的方式。设增强前的频谱幅度为 A,增强后的幅度为 A',则是:

A' = A * (1 + gamma * M)

其中 M 是可学习的归一化掩码,值域约在 [0, 1];gamma 是可学习的全局缩放系数,初始设置为较小的值(比如 0.1),训练过程中逐渐增大。这样设计的好处是,网络在最开始不需要突然适应被剧烈改动的特征分布,而是平滑地学会利用高频信息,训练稳定性好很多。

2.3 增强之后怎么送回空间域

处理完频谱之后,需要通过逆傅里叶变换回到空间域,和原特征做残差连接:

F_enhanced = F_shift * (1 + gamma * mask_shift) F_back = fft.ifftshift(F_enhanced, dim=(-2, -1)) x_enhanced = fft.irfft2(F_back, s=x.shape[-2:], dim=(-2, -1)) # 残差连接,稳定训练 out = x + x_enhanced

这里有几个必须注意的工程细节。第一,irfft2还原时,空间尺寸必须和输入一致,PyTorch的irfft2可以通过s参数指定输出尺寸,防止因为rfft2丢掉另一半频谱导致的尺寸不匹配。第二,增强后的特征图数值尺度会发生变化,如果不做任何约束,可能直接导致后续BN层统计量崩溃。我的解决方式是对增强后的特征图做一次LayerNorm或逐通道的min-max归一化,再和原特征相加。第三,残差连接是必要的。直接替换原始特征会导致信息分布被过分改变,训练初期loss会剧烈震荡;残差连接让网络可以自己决定"用多少增强信息"。

2.4 模块插入的位置:每层都加不一定最好

SET在论文里的做法是把频谱增强模块插入到主干网络的不同阶段。我复现时做了一个重要的观察:并不是每个阶段都需要加。浅层特征图分辨率高,小目标的空间信息还比较完整,加了频谱增强效果最明显;深层的特征图分辨率已经很低,本身就没剩多少高频信息,加频谱增强反而是对噪声做放大,效果会变差甚至有害。

我的建议配置是:在主干网络的P2和P3阶段(也就是高分辨率特征图所在位置)各插入一个频谱增强模块,P4和P5不加。用户如果是在NPU或边缘设备上部署,可以考虑只在P2加一个模块,收益和开销的平衡最好。模块本身的额外计算量主要集中在两个FFT上,对现代GPU来说基本可以忽略不计,我在单卡上测过,带SET模块相对于baseline的推理耗时增加不超过7%。

3. 完整的复现实验配置:照着这个基线跑,你也能复现类似结果

3.1 实验环境与依赖

  • 深度学习框架:PyTorch 2.0以上,必须确保torch.fft相关的接口完整可用
  • 检测框架:基于某个开源检测框架改造,FPN+单阶段检测头的标准结构
  • 数据集:因为原始数据集涉及版权问题,我这边自己整理了一个小目标子集,从公开的俯视影像数据中筛选出所有目标像素面积小于25×25的样本,并保证每张图至少有5个这样的小目标;另外在通用检测基准上做了迁移验证,只看小目标类别的表现

训练平台用的是单张24G显存的卡,这很重要——说明SET不是那种必须8卡起步才能跑的实验,对个人研究者非常友好。

3.2 训练参数与策略

配置项值说明
输入尺寸640×640不再盲目往800、1024拉,SET在640下已经能追上更大输入的效果
Batch Size16单卡可承受的稳定值
优化器AdamW权重衰减设在0.05
基础学习率1e-4用了余弦退火调度
Warmup3000步从1e-6线性升到1e-4
训练轮数36比常见120轮少很多,SET收敛速度快
EMA0.999有效平滑权重,配合频谱增强效果更稳
数据增强随机翻转、随机颜色扰动特别注意:我没有用mosaic,原因见第4部分

3.3 损失函数和训练目标

检测头保持常规的分类损失+回归损失配置,没有给损失函数本身做改动。这也是SET的一个重要特点:它不改变检测头的设计逻辑,只改变特征表达,因此可以直接叠加到现有的各种检测器上。

如果你接入了原有代码,注意以下几点:首先,因为频谱增强模块会改变特征图的幅值分布,如果FPN的归一化层是基于统计量做全局均值方差的,可能需要重新预热BN统计量。我用的方案是先让主干网络前20轮不参与梯度更新,只训练检测头和SET模块,20轮后再解开主干网络。这个"先模块后主干"的训练顺序,基本上能消除频谱增强带来的分布漂移影响。其次,回归损失我用了归一化后的坐标偏差,小目标本身的坐标偏差量级很小,如果归一化方式不合理,梯度会被大目标主导。

3.4 评测指标:只盯着mAP你会吃大亏

对于小目标检测,评估方式比训练方式更需要引起重视。通用检测基准里的标准指标是所有目标一起算mAP,但在很多数据集中,中等和大型目标数量占比高,小目标AP被稀释得很严重,会出现"mAP涨了2个点,小目标AP一动不动"的情况。

我在这套复现实验里,评测时按目标面积分成三个子集:小目标、中目标、大目标,分别统计AP,并且额外统计AR(平均召回率)。SET的作用主要体现在小目标子集上。我在筛选出来的小目标子集上,对比了baseline和加入SET模块后的结果,小目标AP从15.2提升到19.6,提升了4.4个点;在通用数据集的全部目标上,mAP也提升了0.8-1.2个点,说明SET不会以小目标提升为代价牺牲其他目标的精度。大目标AP几乎不变,中目标有小幅提升。

4. 复现SET时我踩过的坑:完整排查链路分享

4.1 频谱增强后的数值失稳:训练直接loss爆炸

我第一次把模块接上主干时,训练第一轮loss就直接冲到了NaN。排查了很久,最后定位到问题出在频谱幅度谱的数值范围上。特征图经过几个卷积层之后,DC分量(也就是频谱中心的低频分量)的数值量级可以达到高频分量的几十倍甚至上百倍。当我用可学习掩码对整个频谱做加权时,哪怕 gamma * M 的数值只有0.01,乘到DC分量上也会造成巨大的数值波动,反变换回空间域后特征值直接溢出。

解决办法很直接:对频谱幅度先做对数变换或逐通道归一化,再应用掩码。具体做法是对幅度谱 A 做如下处理:

A_log = log(A + 1) A_norm = A_log / (mean(A_log) + eps)

这样把幅度的尺度压到相对均匀的范围,掩码再乘上去就不会对数值量纲产生摧毁性影响。这里有个重要的经验:不要在频域里直接用原始幅度做增强,因为频域的尺度范围太宽,正常网络中的BN层根本来不及适应。

4.2 相位谱被无意破坏:检测框集体偏移

我在第一版实现里,为了提高计算效率,图省事只对幅度谱做处理,然后直接忽略相位谱,用一个学习到的固定相位谱替代原始相位谱。结果很诡异:分类准确率正常提升,但检测框整体偏移了大概3-5个像素,小目标框甚至完全套不住目标。后来才明白,相位谱里藏着目标的精确位置信息,我这么一改相当于告诉网络"信息没变,位置偏移一点没关系",框自然就歪了。

排查过程折腾了两天。我一开始以为是回归损失的权重出了问题,调了半天损失权重毫无变化;后来把增强模块完全去掉,框就回到正常位置,才确认问题出在频域处理这一环。查询信号处理相关资料后确认:相位谱对空间结构的重要性远高于幅度谱。修复方式很简单——严格按照"只改幅度,相位保留"的方式处理,也就是只对幅度谱乘掩码,相位谱部分不做数值变化。

4.3 mosaic增强和频谱增强的冲突

这个坑最隐蔽,浪费了我将近一周时间。我在调参过程中发现,只要打开mosaic数据增强,训练曲线就在某个loss值附近长时间震荡,小目标AP反而比只开简单翻转要低。一开始我觉得是学习率的问题,做了网格调参没有任何改善。

后来做了一次对比实验:分别用"无增强""翻转+颜色扰动""翻转+颜色扰动+mosaic"三组设置跑同一个训练流程,结果发现mosaic组的频谱分布在不同batch之间波动特别剧烈——mosaic把四张不同的图拼在一起,相当于构造了大量虚假的高频边界,SET模块在高频增强时把这些边界噪声当成目标特征去放大,特征图就脏了。

目前的处理方式有两种:要么在小目标数据集上干脆不用mosaic,仅用翻转和颜色扰动(这样虽然数据多样性略降,但SET对真实高频的学习更干净);要么保留mosaic但把SET模块的gamma系数在mosaic比率高的训练阶段降低。后者是我最终采用的方式,具体做法是根据当前epoch动态调整gamma:

gamma_effective = gamma * (1 - mosaic_ratio)

这算是我在复现过程中自己摸索出来的一个偏工程性的trick,效果比完全关掉mosaic要好一点,说明SET和更强的数据增强不是非此即彼,而是需要动态平衡。

4.4 训练前期高频增强过猛:网络学会了"锐化白噪声"

另一个比较微妙的问题是:如果gamma初始值设置过大(比如超过0.5),训练前期网络会倾向于把高频区域的所有幅值都放大。由于自然图像的高频部分本来就有大量背景噪声,网络学到的并不是"聚焦小目标边缘",而是"放大所有高频噪声",结果是特征图里出现大量细碎的伪造边缘,检测结果出现严重误检。

业界常见的解决办法是用warmup,但这里的warmup不是只对学习率生效,还要对gamma生效。我用的方案是把gamma初始值设到0.01,在训练的前20轮线性增加到目标值0.3。不同数据集上目标值需要重新调参,但原则上保持"前期给网络适应时间,后期让网络充分利用增强信息"的思路。

5. 消融实验:SET的每个设计点,到底都在贡献什么

5.1 模块组件逐项拆解

复现完之后我通过对各组件做消融,对SET的内部贡献有了更清晰的认识。以下是小目标AP表格:

配置小目标AP推理耗时相对baseline
Baseline(无SET)15.21.00x
仅幅度增强,不保护相位16.11.03x
完整的频谱增强模块19.61.07x
完整模块但去掉残差连接17.81.06x
完整模块但掩码不学习(固定)18.21.06x
完整模块 + 多尺度测试21.3—

几个值得注意的结论。第一,"不保护相位"那一列,虽然AP只掉了3.5个点,但框偏移问题会让这个模型的可用性大打折扣,实际生产中宁可AP低一点也不能让定位不可靠。第二,残差连接的价值在于稳定训练,AP提升倒不是主要来源;去掉残差后AP掉1.8个点,更大的问题是训练曲线开始震荡。第三,固定掩码和可学习掩码的差距体现了数据自适应的价值——不同数据集的高频分布差异很大,固定掩码本质上等于做一个固定的锐化滤波器,缺乏泛化能力。

5.2 插入阶段选择:哪个位置是黄金位置

我在P2、P3、P4、P5四个阶段分别做了插入实验,结论非常明显:P2单插效果最好,小目标AP达到18.9;P2+P3双插能达到19.6;继续加P4反而掉到18.8。原因不难理解:P4、P5特征图分辨率低,本身高频成分已经被前面的层消磨殆尽,再去增强,等于是在一个已经丢失原始信息的信号上做增益,放大的是之前的噪声和误差。

这个实验对落地部署有直接参考价值:如果你资源紧张,只需要在P2阶段加一个模块,就能拿回SET做法大约70%的收益。我在这套复现过程中也试着把模块插入到FPN自顶向下的路径上,效果反而没有直接插在主干上高,我推测是FPN的跨层融合过程本身已经引入了一定程度的多尺度信息,频谱增强和它产生了某种冗余。

5.3 与超分前置模块的叠加关系

很多人会问:SET和现在流行的超分增强或高分辨率输入是不是重复造轮子?我做了三组叠加测试。单独使用两倍超分前置模块时,小目标AP提升约2.1个点;单独使用SET时提升4.4个点;两者叠加后提升4.9个点。这个数据说明,超分模块的收益在SET面前大部分是冗余的,因为超分本质上也是在补高频信息,只是它补的是输入层面的高频,而SET补的是特征层面的高频。两者叠加收益较少,但计算开销是叠加的。所以我的建议很明确:如果算力有限,优先上SET,超分模块可以先放一边。

6. 实际部署与工程落地:不只是把AP跑高

6.1 推理阶段的额外开销

很多人看到FFT就觉得推理会慢,实际情况比想象中乐观。二维FFT的计算复杂度是 O(HW log(HW)),对于一张特征图在640分辨率下的P2层特征(通常是160×160左右),一次FFT的开销远小于一个3×3卷积。我在这台卡上实测,单张图端到端推理时间从原来的8.2ms增加到8.8ms,增幅不到7%。这个代价换小目标AP涨4个点,性价比非常高。

如果是在边缘设备上做部署,还有两个优化空间:一是只保留P2位置的SET模块,省掉P3部分;二是把可学习的掩码在训练完之后固定下来,推理阶段可以直接作为常量,省掉掩码的梯度图计算和更新逻辑。我试过把这个模块固化成ONNX算子,FFT部分用标准算子库里的RFFT实现,整个边缘侧推理流程是完全可以跑通的。

6.2 适合与不适合的场景

复盘整个复现和调优过程后,我认为SET最适用的场景是:目标平均像素面积小,训练数据里高频特征和背景环境差异明显,比如俯视视角下的车辆、船只、人群密集小目标。不适合的场景也有两类:一类是目标本身在频域里和背景噪声区分度很低的情况,比如低光照下的红外目标,高频增强很可能会放大噪点而非目标边缘;另一类是模型已经很小、算力极紧的端侧场景,虽然SET模块本身不大,但如果P2层特征通道数很多,FFT的计算量已经开始影响帧率。

6.3 最后的小技巧:只在特定尺度下启用SET

我在复现过程中发现一个便于落地的心得:可以把SET模块的启用与否和输入图像的平均目标尺度挂钩。在训练时设定一个尺度阈值,当特征图分辨率处于放大小目标信息的区间时启用SET模块,否则直通。实现上只需要在模块forward里加一个条件分支,对大多数检测框架来说改动都很小。这个方法在我自己的测试中是有效的,推荐你在落地时也试一试。

做完整套复现实验后再回头看,SET给我的最大启发不是"FFT很强",而是提醒我重新审视一个基础问题:网络在训练过程中,主动丢掉了哪些对我们有用的信息?很多时候我们死磕空间域结构、堆算力,却忽略了信息可以在另一个表达域里被更高效地利用。如果你手头正好有小目标检测的需求,我建议你花一周时间把SET这套思路接到自己的框架里跑一遍,重点观察P2特征图在训练前后的高频分量变化。看完那个可视化结果,你大概就会对小目标检测的突破口有新的体会。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询