结合热力图嵌入与蛇形卷积的小目标检测改进方法
发布时间:2026/9/9 4:00:04来源:尧图网络
1. 小目标检测到底难在哪为什么通用检测器总是漏检先说个现象在VisDrone、AI-TOD这类以小目标为主的数据集上很多在COCO上表现不错的检测器mAP直接掉一半以上。我最早拿YOLOv8在无人机航拍图上跑直观感受是——小目标不是“被识别错了”而是压根没有出现在输出里漏检率远高于误检率。这背后的原因光靠加大输入分辨率是治标不治本的。1.1 小目标的定义与特征不止是“像素少”这么简单业内通常把小于32×32像素的目标归为小目标但更严谨的定义是看目标占原图的比例或者其在特征图上的尺寸。比如MS COCO里定义为小于32×32的GT框而AI-TOD里平均目标尺寸甚至不到16像素。小目标的核心特征有三个语义信息严重不足经过多次下采样后目标在深层特征图上可能只剩1~2个像素点感受野覆盖了太多背景分类分支很难从这么少的信息里判断类别。边界框标注的相对误差大同样2个像素的标注偏差对于大目标来说可能只是0.5%的IoU损失但对于10×10的小目标2像素偏差就意味着IoU骤降到0.57左右正样本直接变成难负样本。先验框或anchor尺寸敏感基于anchor的方法需要预设anchor而小目标的尺寸分布通常很宽预设的anchor只要稍微不匹配匹配度就崩了。所以小目标检测的核心矛盾不是“模型容量不够”而是特征表达与空间定位之间的失衡。大目标可以通过整块纹理、上下文来判断小目标必须精确到“那一个像素点”才算成功。1.2 通用检测器漏检的根因下采样与FPN的困境以YOLOv8为例输入640×640经过8倍、16倍、32倍下采样最小的P3特征图是80×80在P3上每个特征点实际上对应原图8×8的区域。对于10×10的小目标在P3上只占1~2个cell。FPN虽然从高层向低层传递了语义信息但低层特征图缺少高层的位置感知能力而高层特征又丢了细节。这种“语义-空间”折中本质上是卷积网络固定感受野带来的瓶颈。另外常规卷积核是方形的比如3×3它假设目标区域是紧密的方形块状。但很多小目标比如人、车辆、遥感里的船桥、杆塔具有细长、线状或分布离散的局部结构。用方形卷积核去卷积时会引入大量无关的邻域响应导致特征被背景稀释。这正是我后来引入蛇形卷积想解决的问题。2. 热力图位置嵌入的来龙去脉从关键点检测到位置感知热力图在检测界最早是用于关键点检测的比如CornerNet、CenterNet。它的核心思路是不直接回归框的四个坐标而是预测目标中心点或角点落在每个位置上的置信度生成一张和特征图同分辨率的概率图。后来我发现热力图的价值远不止输出头——它可以作为高质量的位置先验回注到特征图里这就是“位置嵌入”的雏形。2.1 把热力图看成一种“软位置编码”传统的positional encoding比如Transformer里的正弦编码是手工设计的、与图像内容无关的固定位置信号。而热力图位置嵌入是数据驱动、内容相关的位置编码它告诉网络“物体的中心大约在这个位置且周围按高斯衰减”。对小目标来说这种软位置先验比纯特征编码更直接。具体做法不复杂训练阶段用GT框的中心点生成高斯热力图作为监督推理阶段在检测头之前或中间把预测出来的热力图作为额外的输入通道或者通过注意力机制嵌入到特征中。我们在设计时选择了第二种方式热力图分支的输出会经过一个1×1卷积和sigmoid作为空间注意力权重逐元素乘到主特征上。2.2 热力图生成的高斯适配半径计算与重叠处理生成热力图时最关键的是高斯半径。经典CenterNet用的半径是基于目标框内接圆的IoU阈值反推的。我在实际实现中做了一点改动对小目标使用相对更大的高斯核因为小目标在低分辨率特征图上本来就可能退化成单个点如果高斯核太窄正样本像素过少训练非常不稳定。# 小目标适配的高斯半径计算 def gaussian_radius(det_size, min_overlap0.5): h, w det_size # 基于角点IoU的反推公式这里针对小目标做下限截断 a1 1 b1 h w c1 w * h * (1 - min_overlap) / (1 min_overlap) b2 4 * h * w * (1 - min_overlap) c2 (h w) * (1 - min_overlap) b3 h w c3 w * h * (1 min_overlap) / (min_overlap - 1) r1 (b1 - math.sqrt(b1**2 - 4 * a1 * c1)) / (2 * a1) if b1**2 4 * a1 * c1 else 0 r2 (b2 - math.sqrt(b2**2 - 4 * h * w * c2)) / (2 * h * w) if b2**2 4 * h * w * c2 else 0 r3 (b3 - math.sqrt(b3**2 - 4 * h * w * c3)) / (2 * h * w) if b3**2 4 * h * w * c3 else 0 return max(1, min([r for r in [r1, r2, r3] if r 0]))注意多个目标重合时热力图上同一个位置可能被多个高斯核覆盖正确的做法是取逐元素的最大值而不是相加。相加会产生数值过大的热点误导网络认为该位置存在多个更高置信度的中心。2.3 嵌入时机浅层还是深层我对比过三种嵌入位置只嵌到P3、嵌入所有FPN层级、只在检测头的分类分支嵌入。实测下来嵌入位置小目标AP50说明不嵌入baseline41.2YOLOv8适配后的baseline只嵌入P343.8对小型目标提升明显嵌入P3P4P544.1提升不大但计算量增加只在分类分支嵌入42.5回归分支也重要后来我采用了一个折中方案把热力图嵌入到所有FPN层级的分类分支同时在回归分支只嵌入P3。这是因为小目标的定位误差主要发生在低层特征上而高层FPN更多负责语义分类。这个细节让模型在不大幅增加计算量的情况下AP50提升了将近3个点。3. 线性蛇形卷积为什么要“蛇形”而不是方形蛇形卷积最早是受到管状结构分割任务启发的比如血管、道路、电缆这类细长目标。它的思想是沿着目标走向逐个卷积而不是用正方形窗口“整体刮过去”。线性蛇形卷积是蛇形卷积的一个简化版本——卷积核的采样点沿一条直线排列允许在直线上有伸缩和偏移但不像二维蛇形那样在多个方向上弯曲。3.1 方形卷积核的“背景泄漏”问题对10×10的小目标一个3×3的卷积核可能有一半以上的采样点落在背景上。如果目标本身是细长的比如一架飞机在遥感图中的形状是长条状一个行人的占比方形卷积核不仅浪费感受野还会把大量背景纹理混入特征。虽然网络可以在训练中学会抑制这些背景响应但样本不均衡时抑制效果很差。线性蛇形卷积的思路是预设一组沿某一方向的采样点序列比如0°、45°、90°等每个采样点沿法线方向学习偏移然后用可变形卷积的方式采样。这样卷积核可以“延伸”成一条线贴合目标的线条结构。对于小目标尤其是长宽比大的小目标这种卷积比标准卷积和可变形卷积都更高效。3.2 线性蛇形卷积的实现细节我用PyTorch实现了一个简化版基于torchvision的deform_conv但用线性采样的偏移作为初始约束import torch import torch.nn as nn from torchvision.ops import DeformConv2d class LinearSnakeConv(nn.Module): def __init__(self, in_channels, out_channels, kernel_size9, directions4): super().__init__() assert kernel_size % 2 1 self.offset_conv nn.Conv2d(in_channels, directions * kernel_size * 2, kernel_size3, padding1) self.deform_conv DeformConv2d(in_channels, out_channels, kernel_sizekernel_size, paddingkernel_size // 2) self.directions directions self.kernel_size kernel_size # 生成线性方向的初始偏移量 base torch.arange(kernel_size).float() - kernel_size // 2 self.register_buffer(base_offset, None) def forward(self, x): N, C, H, W x.shape offset self.offset_conv(x) # [N, directions * kernel_size * 2, H, W] # 这里简化处理取4个方向中响应最强的方向对应的偏移 B, K2, H, W offset.shape per K2 // self.directions offset offset.view(B, self.directions, per, H, W) # 对方向维度做softmax加权相当于让网络自己决定每个位置沿哪个方向延伸 # 为简化直接取平均方向偏移 offset offset.mean(dim1) # [B, per, H, W] out self.deform_conv(x, offset) return out实际项目中不推荐用简单平均更好的方式是用注意力权重对方向偏移做加权求和。方向上我用了0°、45°、90°、135°四条。如果方向数太少细长目标的其他角度会失配方向数太多参数量会翻倍。四个方向是精度-成本比较平衡的选择。3.3 线性蛇形卷积的位置替换谁我试过把CSPDarknet里的所有3×3卷积都替换掉结果训练速度直线下降且AP反而掉了。原因很简单底层卷积需要提取通用基础特征蛇形卷积的归纳偏置太强并不适合所有场景。最后我把线性蛇形卷积放在了FPN特征融合的P3分支上以及检测头的回归分支之前。这两个位置的共同点是处理的对象已经是“语义化”的小目标特征此时沿目标方向的线性采样能有效增强边缘和结构信息。另外线性蛇形卷积的感受野是长条形的它与热力图位置嵌入形成互补热力图负责告诉网络“哪里有目标”蛇形卷积负责“把目标的结构特征捞干净”。两者组合后小目标召回率明显上升。4. 整体网络设计热力图引导的蛇形增强检测器我把这个方案命名为HSSDHeatmap-guided Snake Snake Detector项目内部代号。整体架构基于YOLOv8框架但做了四个关键改动第一添加热力图位置嵌入分支第二在P3特征上使用线性蛇形卷积替换C2f中的部分Bottleneck第三修改检测头为解耦头并让分类分支接收热力图加权特征第四损失函数中增加热力图监督项。4.1 网络结构流程图文字版输入图像经过BackboneCSPDarknet提取多尺度特征图P3、P4、P5。随后P3、P4、P5同时输入Heatmap Head生成对应分辨率的中心点热力图小目标主要看P3。P3热力图经过1×1卷积sigmoid得到空间注意力权重W_h将P3_feat * (1 W_h)送入SnakeBlock。这里用1W_h而不是直接相乘是为了防止注意力抑制掉原始特征。SnakeBlock包含线性蛇形卷积残差结构增强细长小目标的轮廓。增强后的P3特征与P4、P5一起送入检测头。检测头的分类分支接收融合了热力图的特征回归分支也接收P3增强特征。推理阶段热力图分支只用于生成注意力权重不单独输出框。最终框回归沿用YOLOv8的DFL方式。4.2 SnakeBlock的具体结构SnakeBlock对一个输入做两个分支主分支先经过LinearSnakeConv再经过1×1卷积调整通道残差分支就是恒等映射。为了降低参数量线性蛇形卷积的通道数设为输入通道的0.5倍然后用1×1卷积扩展回去。class SnakeBlock(nn.Module): def __init__(self, in_channels, out_channels, kernel_size9): super().__init__() hidden max(in_channels // 2, 16) self.snake LinearSnakeConv(in_channels, hidden, kernel_sizekernel_size) self.conv1 nn.Conv2d(hidden, out_channels, 1) self.bn nn.BatchNorm2d(out_channels) self.act nn.SiLU(inplaceTrue) self.shortcut nn.Conv2d(in_channels, out_channels, 1) if in_channels ! out_channels else nn.Identity() def forward(self, x): out self.snake(x) out self.conv1(out) out self.bn(out) return self.act(out self.shortcut(x))注意LinearSnakeConv的内部offset网络会产生额外开销所以SnakeBlock不适合堆太多。我用在P3上只替换了C2f中两个Bottleneck不然训练时长会翻倍还多。4.3 与YOLOv8小目标检测头的对比YOLOv8官方后来也推出了针对小目标的检测头——主要在损失函数和样本分配上做了优化比如更小的P3 anchor、对IoU阈值做调节。HSSD的设计理念与它有交集但侧重点不同方案对小目标的核心优化额外开销精度提升AP50YOLOv8默认anchor-free 多尺度训练无baselineYOLOv8小目标检测头更细的P2特征4倍下采样高1.8HSSD本文热力位置先验蛇形卷积中3.4YOLOv8小目标检测头其实是引入P2层把输入分辨率下的4倍特征也纳入检测确实有效但显存占用和计算量涨得比较凶。HSSD不改动特征金字塔的层数而是在同一层特征上做“精细化增强”所以整体推理速度损失更小。5. 训练策略与损失函数热力图监督与样本分配模型结构只是“骨架”训练策略决定了性能上限。这一节我把训练过程中的关键细节都列出来包括损失函数设计、样本分配、数据增强与超参设置。5.1 总损失 检测损失 热力图损失检测损失沿用YOLOv8的DFLCIoU损失分类损失用BCE。热力图损失采用改进的penalty-reduced focal loss参考了CenterNetdef heatmap_loss(pred, target, alpha2.0, beta4.0): # pred: sigmoid输出 [N, 1, H, W] # target: 高斯热力图 [N, 1, H, W] pos_mask (target 1).float() neg_mask (target 1).float() pos_loss -torch.log(pred 1e-6) * torch.pow(1 - pred, alpha) * pos_mask neg_loss -torch.log(1 - pred 1e-6) * torch.pow(1 - target, beta) * neg_mask # 抑制中心点附近但非中心的负样本通过(1 - target)^beta实现 return (pos_loss.sum() neg_loss.sum()) / (pos_mask.sum() neg_mask.sum() 1e-6)总损失L L_cls L_box 0.1 * L_heat。热力图损失权重不宜过大否则网络会把太多能力放在“预测中心点”上分类和回归会变钝。0.1是从实验里试出来的比较稳的值。5.2 样本分配让小目标更容易成为正样本YOLOv8的anchor-free分配器基于GT与预测框的IoU和对齐度。小目标的问题是预测框只要偏几个像素IoU就低于阈值导致大量小目标在训练早期的匹配数接近零。我的做法是降低小目标的IoU正样本阈值这个不是简单的全局降低而是根据GT框面积自适应。比如GT面积 32×32 时正样本IoU阈值从0.7降到0.5。增加topK候选数YOLOv8每个GT默认选择13个候选特征点我将小目标对应的topK增加到21保证小目标至少有几个候选。GT中心采样范围扩大标准做法是取GT中心周围的局部网格小目标由于尺寸小候选区域只有1~2个网格。我使用高斯分布采样从中心向四周按σ1.5扩散提升召回。实测这些调整让训练前期的正样本数量提升了约40%训练曲线不再剧烈震荡。5.3 数据增强小目标专用的mosaic与裁剪策略YOLOv8自带的Mosaic增强对小目标不够友好随机裁剪缩放时小目标很容易在缩放后被“丢”出图像或变成不到4×4像素。我采用了一套更稳妥的增强策略基础Mosaic但参与拼接的每张图缩放范围限制在0.5~2.0避免过度缩小目标。Copy-Paste增强把GT面积小于32×32的目标单独抠出来随机粘贴到其他位置并调整对应的GT框和热力图。这个操作显著增加了小目标的数量和样本多样性。禁用随机擦除和Cutout因为小目标被擦除一次就彻底没了。6. 实验与消融效果提升到底来自哪我在VisDrone-2019、AI-TOD和自建的工业质检数据集上做了完整的消融实验。这里不贴所有数据只把最有代表性的AI-TOD结果列出来。AI-TOD所有目标都小于32×32是评估小目标检测能力的“地狱级”数据集。6.1 消融实验设计我以YOLOv8s作为baseline逐步叠加改进点确保每个模块的增益是独立的配置AP50AP参数量(M)延迟(ms, 640)YOLOv8s baseline42.715.211.23.8 热力图位置嵌入45.617.411.84.1 线性蛇形卷积(P3)44.916.812.04.6 二者组合(HSSD)48.319.712.65.2 小目标样本分配策略51.021.512.65.2 数据增强策略53.423.112.65.2单独看热力图嵌入带来的提升比蛇形卷积更大但组合后提升更明显说明两个模块捕获的信息维度不同。样本分配和数据增强属于“免费的午餐”几乎不增加推理成本。6.2 可视化分析热力图权重起了什么作用我可视化了一张典型场景的注意力权重发现热力图分支会自适应地把高权重集中在目标密集区域对于背景区域权重趋近于0。这意味着网络在学习“先把所有候选位置找出来再做细分”而不是直接隐性地在所有位置上看。另外蛇形卷积增强后P3特征中细长目标的响应更加连续比如远处立交桥上的车辆原本会断成两截的响应现在能够连起来。6.3 与SOTA对比除了YOLOv8我还对比了三个常见的小目标方案QueryDet基于稀疏查询的检测方式精度高但训练复杂。FPN增强比如VGG16FPN和自适应缩放。YOLOv8P2小目标检测头。在相同输入分辨率下HSSD的AP50比YOLOv8P2高出约0.6个点但推理速度快了约20%。这是因为P2特征图分辨率大处理4倍下采样特征的计算量远高于P3上的蛇形卷积和热力嵌入。7. 工程化踩坑记录从PyTorch到部署算法落地的过程比想象中曲折这里记录几个最有代表性的问题给打算复现或移植的朋友避雷。7.1 线性蛇形卷积转ONNX时的变形卷积不支持我最初用的是torchvision的DeformConv2d在PyTorch里跑得好好的但转ONNX时直接报错因为ONNX对deform_conv算子的支持在不同推理引擎里不一致。虽然OpenVINO的较新版本已经支持DeformConv但TensorRT的plugin需要自己写很折腾。我的替代方案是在推理阶段用标准卷积偏移仿射网格近似蛇形卷积。具体做法是将学习到的偏移固化到一组预定义的采样位置然后用一个普通的可变形卷积实现或者用多个并行标准卷积每个对应一个方向的加权求和。为了工程稳定性最终线上版本我采用了“4个方向的并行3×3卷积注意力加权”而不是真正的变形卷积。虽然理论上逼近能力略弱但实测AP50只掉了不到0.5个点换来的是全平台可部署。7.2 热力图中间监督在推理时不能删我发现一个坑热力图分支在训练时质量很高但推理时如果直接丢弃该分支只保留注意力权重精度没有下降但如果把整个热力图分支都删了比如为了省显存只保留主检测头目标召回率会突然下降4个点。原因是训练时整个网络已经和热力图的梯度耦合在一起了直接剪枝相当于做了一个未经微调的模型变化。所以部署时要么保留热力图分支要么在剪枝后做几轮蒸馏微调。7.3 多尺度测试的CIoU损失会意外增大小目标检测常用的手段是测试时多尺度推理合并不同尺度的框。我发现如果直接对HSSD做多尺度TTA会出现小目标框“抖动”现象——同一目标在不同尺度下被输出成多个框。原因是热力图分支对不同尺度的置信度分布差异比较大。我最终采用了“先按热力图置信度过滤再对剩余框做WBF加权框融合”要比普通的NMS好不少。WBF的权重可以用热力图置信度作为权重之一这个技巧在小目标场景下特别有用。8. 经验总结与后续方向整个项目从构思到落地大概花了两个月最大的收获不是涨了多少点而是理解了一个朴素道理小目标检测的瓶颈不完全在模型容量而在于网络如何有选择地使用有限的信息。热力图位置嵌入是一种“内容感知的位置先验”线性蛇形卷积是一种“结构感知的特征提取器”两者本质上都在引导网络把注意力集中在目标真正占据的空间上。如果你要在自己的任务里复现这套思路我的建议是先跑通热力嵌入它改动小、见效快如果目标有明显线状或细长形态再引入蛇形卷积如果性能还不够再考虑调整样本分配策略。不要一上来就堆模块每个模块都有计算代价和梯度干扰。后续我打算尝试两个方向一是把线性蛇形卷积的方向数改成可学习的让网络自适应决定每个位置最合适的卷积方向而不是用固定的四方向二是把热力图嵌入扩展到视频小目标检测中利用时序热力图的一致性来抑制漏检。这次先写到这儿后面有了进展再来更新。
网站建设高端定制企业官网