news 2026/11/2 19:27:06

小目标检测工程实践:从分辨率、切片到标签分配调优指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
小目标检测工程实践:从分辨率、切片到标签分配调优指南

小目标检测这四个字写在论文标题里挺学术,落到工程现场基本等于一句大白话:人眼能看见,模型就是检不出来。我在几个不同类型的项目里被这个问题反复折磨过——航拍画面里几十米外的车辆、监控里走廊尽头的人头、工业图像上宽度只有几个像素的划痕、遥感图上的小型设施。这些目标在原图里往往只有十几像素见方,主干网络下采样几次之后,特征图上剩下的可能就是一个点,甚至不到一个点,物理意义上"消失"了。这篇综述式的整理,是我把散落在各种论文、配置文件、失败实验记录里的东西重新捋一遍的结果,7.25 这个时间戳意味着它还会继续往下更,我打算一边做实验一边往里补。内容适合两类人看:一类是刚进这个方向、想知道该从哪几篇入手的研究生;另一类是被 AP_S 卡住、调了两周没动静的工程同学。我会尽量把"为什么这么选"讲清楚,也会把踩过的坑和不那么好看的数据一并摊开。

1. 小目标检测到底卡在哪:先把问题边界划清楚

很多人一上来就改网络结构,其实连自己的目标算不算"小"都没弄清楚。这一节我想先把判定口径、失效机理和几个真实痛点摆出来,后面所有方法的取舍都建立在这个前提上。

1.1 多大算"小":三种判定口径与实际影响

行业里最常引用的是通用检测数据集的那套划分:目标框面积小于 32×32 像素记为 small,32×32 到 96×96 之间记为 medium,超过 96×96 记为 large。这个口径的好处是简单,坏处是它只跟绝对像素面积挂钩,跟图像分辨率无关。同一辆车在 4K 图里是 medium,把同一张图缩到 640 宽它就变成 small 了,模型表现却完全不是一回事。

第二种是相对口径,用目标框面积的平方根除以图像面积的平方根,结果小于 0.1 就算小目标。这个口径更贴近"目标在画面里占多大比例"的直觉,做切片推理、算感受野需求的时候更好用。

第三种是特定领域的自定义口径。航拍和遥感类数据集里,平均目标尺寸常常只有十几个像素,如果还按 32×32 划,那 95% 以上的目标都算小目标,指标就失去区分度了。这类数据集一般会重新分层,比如按 8 像素、16 像素再切几档,单独统计极小目标的召回。

我的建议是:做实验前先在自己的数据上把 GT 框的宽高分布画出来,看 P5、P25、P50 分位数落在哪,再决定用哪套口径。我见过不止一个人拿 32×32 的标准去衡量一个平均目标尺寸 12 像素的数据集,然后困惑为什么 AP 一直在 0.1 上下浮动——不是模型不行,是指标设计本身就没提供信息量。

判定口径计算方式适用场景主要缺陷
绝对面积框面积 < 32×32 像素通用基准横向对比忽略输入分辨率
相对比例sqrt(面积比) < 0.1切片、感受野估算阈值缺乏统一共识
领域分层按 8/16/32 像素分档航拍、遥感、质检跨数据集不可比

1.2 五个真实痛点:从像素消失到标签摇摆

第一个痛点是特征在深层彻底消失。一个 16×16 的目标,输入 640×640,经过 stride 8 的特征图上是 2×2,到 stride 16 是 1×1,到 stride 32 只剩 0.5×0.5,小于一个像素。卷积核在这个位置做运算,相当于对目标做了一个极重的低通滤波,剩下的是背景和目标的混合响应,位置信息基本丢失。这就是为什么所有讲小目标的文章都绕不开"用更浅层的特征"。

第二个痛点是有效特征维度太低。32×32 的目标有 1024 个像素,能提供纹理和轮廓;8×8 的目标只有 64 个像素,纹理信息几乎为零,模型只能靠颜色对比和上下文猜测。这也是为什么小目标检测里"上下文"这个词出现的频率远高于通用检测。

第三个痛点是正样本太少且极不均衡。一张 2000×1500 的航拍图可能有 300 个目标,但 90% 集中在画面下方三分之一,其余大片区域是空的。采样器如果按均匀网格分配,大部分正样本都来自同一片区域,模型对稀疏区域的小目标几乎没有学习信号。

第四个痛点是标签分配对定位误差极度敏感。这是最容易被忽略的一条。算一下就很清楚:一个 8×8 的框,中心偏移 1 个像素,交集变成 7×8=56,并集是 64+64-56=72,IoU 掉到 0.78;偏移 2 个像素,交集 48,并集 80,IoU 只剩 0.6。同样偏移 2 个像素,一个 32×32 的框 IoU 还有 0.88。也就是说,标注时同样的手抖,对小目标来说 IoU 的直接损失是大目标的三倍以上,基于 IoU 阈值的正负样本划分会剧烈摇摆,同一个目标在不同 epoch 可能一会儿是正样本一会儿是负样本,训练极不稳定。

第五个痛点是评测指标本身的方差很大。小目标样本数少,一个类别可能只有几十个实例,随机种子换一下 AP_S 波动 2 到 3 个点很常见。这一点后面第 5 节会展开,先记住结论:单次实验的 AP_S 涨点,可信度远低于 AP 整体涨点,必须做多次重复。

提示:如果你在自己的数据上发现"训练 loss 正常下降但验证集小目标召回一直不动",先别急着改网络,先把 GT 框尺寸分布和每张图的目标数量分布打出来,八成问题出在数据分布而不是模型容量。

2. 数据侧的第一刀:分辨率和切片,投入产出比最高

我个人的经验排序是:数据与推理策略带来的收益,通常大于换主干网络。改结构要重训、要调参、要写新算子,切片推理只要改十几行调用代码。这一节把这条路上几个关键决策的账算清楚。

2.1 输入分辨率的账要这么算

最直接的办法是提高输入分辨率。640 提到 1024,线性尺度放大 1.6 倍,一个原本 10×10 的目标变成 16×16,在 stride 8 上的响应从 1.25×1.25 变成 2×2,特征可用性明显改善。

但代价不是线性的。计算量大致按面积增长,1024² 是 640² 的 2.56 倍,显存占用也接近这个比例。同时 batch size 被迫下降,BN 统计量变得不稳,学习率要重调。我在一个 24GB 显存的卡上做过对比:640 输入 batch 16 能跑满,1024 输入 batch 只能压到 4,训练总时长从 8 小时涨到 30 小时以上,而 AP_S 涨了大约 4 个点。值不值,取决于你的场景是离线批处理还是端上实时。

这里有个折中技巧值得说:非正方形输入。航拍图往往是 2000×1500 这种宽幅比例,强行 resize 到 1024×1024 会让目标在垂直方向被压缩,同时浪费大量算力在无意义的 padding 上。改成 1280×960 这类保持原比例的尺寸,同样的算力预算下,目标在特征图上的有效像素更多。

还有一点很多人没注意:训练分辨率和推理分辨率最好一致,但允许训练略低。用小分辨率训练、大分辨率推理,在小目标上通常能白捡 1 到 2 个点,原因是模型见过更多"上下文范围",推理时目标变大反而更容易。但反过来做(大分辨率训练、小分辨率推理)掉点会很惨,因为模型依赖的细节在小图上没了。我一般会跑一组 768 训练 / 1024 推理的对比,作为低成本提点的备选方案。

2.2 切片推理的参数怎么定

切片推理的思路很朴素:把大图切成有重叠的小块,每块单独送进模型,最后把结果映射回原图坐标做 NMS 合并。这样每个小块等效于被放大,小目标在块内变成了正常尺寸的目标。

关键在于参数怎么定。以一张 4000×3000 的图、切片 512、重叠 0.2 为例:步长是 512×(1-0.2)=409.6,横向切 4000/409.6 向上取整约 10 块,纵向 3000/409.6 约 8 块,总共 80 块,加上全图推理 1 次,推理次数变成 81 次。这就是切片推理最大的代价——时间线性膨胀。如果你的业务要求 30 帧每秒,切片推理基本可以直接排除,它更适合离线质检、卫星影像批处理这类不在乎延迟的场景。

切片尺寸的选取有个实用准则:让目标在切片中的像素尺寸落入模型的"舒适区"。假定你的模型在目标 32 到 200 像素时表现最好,而原始数据里目标平均 16 像素,那么切片尺寸取原图尺寸的 1/2 到 1/3 就够了,再小就是浪费算力。重叠率我一般设 0.2,目标密集且互相遮挡明显的场景可以提到 0.3,但要注意重叠越大块数增长越快。

合并阶段有几个坑:一是跨块目标被切断,一个目标正好落在切片边界,两半各自检测出半个框,NMS 因为 IoU 低不会合并,最后出现两个残框。解决办法是提高重叠率,或者在合并阶段做一次基于中心点距离的聚合。二是类别置信度分布不一致,边界区域的目标只被部分切片看到,置信度普遍偏低,如果统一用 0.25 的阈值,边界会漏检。我通常会在合并后对跨越切片缝合线的检测框做一次置信度补偿。

from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model = AutoDetectionModel.from_pretrained( model_type="yolov8", model_path="weights/best.pt", confidence_threshold=0.25, device="cuda:0", ) result = get_sliced_prediction( "demo.jpg", detection_model, slice_height=512, slice_width=512, overlap_height_ratio=0.2, overlap_width_ratio=0.2, postprocess_type="NMS", postprocess_match_threshold=0.5, ) result.export_visuals(export_dir="vis/")

提示:切片推理一定要先在小样本上跑通坐标映射和 NMS,确认没有系统性偏移再用到全量数据。我见过坐标映射的缩放系数写反了半个多月没人发现的案例,因为框看上去"大概在那个位置",肉眼看不出 3 像素的偏移。

2.3 复制粘贴与过采样的分寸

数据增强里对小目标最有效的是两类:一类是把小目标"搬"到更多位置,典型是复制粘贴增强——把标注好的小目标裁剪出来,随机贴到图像的其他区域,同时生成对应的标注;另一类是拼图式增强,把四张图拼成一张,等效于缩小了单张图的内容尺度,间接让目标变大。

拼图增强的好处是顺带解决了小目标样本的数量问题,一张图里有四张图的目标,正样本密度翻倍。但它的副作用是破坏了场景的上下文一致性,比如把地面目标和天空目标拼在一起,模型可能学到错误的上下文关联,在真实场景反而掉点。我在航拍数据上用过一段时间,AP_S 涨了 3 个点但误检率也涨了,最后是在训练后期关掉这个增强才平衡回来。

复制粘贴增强要注意两点。第一,粘贴位置要避开已标注目标,否则会产生重叠框,标签本身变脏。第二,尺度和光照要匹配,把一个高对比度的目标贴到低对比度背景上,模型会学到"亮的东西就是目标"这种捷径特征,泛化能力断崖式下跌。我的做法是按背景亮度分桶,同桶内做粘贴,虽然麻烦一点,但脏数据的比例能压下来。

还有一个容易被忽视的点:小目标不要做过度的颜色抖动和模糊。通用检测里那套随机亮度、对比度、高斯模糊的组合拳,对大目标是增加鲁棒性,对小目标可能是直接抹掉信息。一个 10×10 的目标经过半径 2 的高斯核,等于什么都没剩。我的经验是把模糊类增强的概率调低到 0.1 以内,缩放类增强反而可以加大。

3. 结构侧:把浅层特征留住,把感受野撑开

数据侧做到位之后,剩下的才是结构。结构改进的核心矛盾只有一个:浅层特征分辨率高但语义弱,深层特征语义强但分辨率低,怎么把两者结合好,同时不让计算量爆炸。这一节按融合路径、检测头、主干、上下文四条线展开。

3.1 特征金字塔的融合路径选择

从最早的横向连接加自顶向下,到后来的加一条自底向上路径,再到加权双向融合,这条演进线背后的逻辑其实很清晰:自顶向下传递的是语义,自底向上传递的是位置精度。

单纯自顶向下的结构有个天然缺陷:高层语义经过多次上采样之后,位置信息已经被稀释,P3 层虽然有 80×80 的分辨率,但它的语义特征是从 P5 一路插值上来的,对小目标的定位不够准。加一条自底向上路径,把 P3 的原始位置信息再往上传一次,可以让 P4、P5 也获得一些精确的位置线索,代价是额外的卷积计算。

加权融合则是把"每一层贡献多少"变成可学习参数。这在理论上很吸引人,实测下来收益有限——通常 0.5 到 1 个点,而且会引入额外的归一化层,在混合精度训练下容易数值不稳。我的看法是:如果你的基线已经很成熟,加权融合属于边际改进;如果是从零搭结构,这一层的设计自由度可以留到后面再优化。

还有一个常被忽略的细节是上采样的方式。最近邻插值快但会产生棋盘格效应,转置卷积能学但容易在目标边缘产生伪影,双线性插值加一个 3×3 卷积是相对稳妥的折中。我在一个 4 像素宽的目标上对比过,最近邻上采样产生的棋盘格恰好落在目标位置,导致该位置的响应出现周期性波动,换成双线性后 AP 稳定了接近 1.5 个点。这种细节不写在论文里,但它真实存在。

3.2 加 P2 检测头的代价核算

P2 指的是 stride 4 的特征层。以 640 输入为例,P3 是 80×80,P2 是 160×160,面积上 P2 是 P3 的 4 倍。加一个 P2 检测头,意味着:

  • 该层的特征图元素数量是 P3 的 4 倍,检测头的卷积计算量同比增加;
  • 该层的锚框或先验点数量是 P3 的 4 倍,正样本候选数量大增,标签分配和损失计算的开销上升;
  • 浅层特征的语义信息弱,误检率会明显上升,需要重新调整置信度阈值。

我在一个航拍数据集上做过完整对比:加 P2 之后 AP_S 从 0.183 涨到 0.221,涨了将近 4 个点,但整体推理时间增加约 35%,误检数量翻了一倍多。对于一个目标是"宁可多报不可漏报"的巡检场景,这个交换是划算的;对于需要严格控制误报的场景,就得配合更强的后处理。

加 P2 不是万能药。如果你的目标最小尺寸在 20 像素以上,加 P2 的收益会小很多,因为 P3 已经够用。判断方法是算一下目标在 P3 特征图上的尺寸:20 像素的目标在 stride 8 上是 2.5×2.5,勉强够;12 像素的目标在 P3 上只有 1.5×1.5,就必须要 P2 了。

3.3 高分辨率主干与空洞卷积

另一条思路是全程保持高分辨率,而不是靠金字塔把低分辨率特征拉上来。典型做法是并行多个分辨率的分支,让高分辨率分支从头到尾不被降采样,只在分支之间反复做信息交换。这样做的好处是浅层的位置精度没有被破坏,对小目标的位置回归特别友好;代价是显存占用显著增加,因为高分辨率特征图要一直驻留在显存里。

空洞卷积是另一条路:不降低分辨率,靠增大膨胀率来扩大感受野。比如连续堆叠膨胀率为 1、2、4 的 3×3 卷积,感受野能覆盖到 15×15 以上,同时特征图尺寸保持不变。这条路的优势是计算量比堆叠大卷积核小得多,劣势是膨胀卷积会产生网格状的空洞效应,相邻像素之间缺乏连续性,小目标的精细结构可能被割裂。

我在实际选型时的判断逻辑是:显存够、延迟不敏感,优先考虑高分辨率主干;显存紧、需要实时,用空洞卷积加轻量级特征融合。两者不是互斥的,很多结构是在高分辨率分支上再用空洞卷积扩感受野,前提是你愿意付显存。

3.4 上下文与注意力的真实收益

小目标的纹理信息少,模型必须依赖上下文。这句话在论文里被引用了无数遍,但真正实现好的并不多。有几条经验值得记:

上下文范围要匹配目标尺度。一个 10 像素的目标,它的"有效上下文"可能只是周围 50 像素的一块区域;如果你用一个全局注意力去建模,远处无关区域的响应会稀释掉有用信号。所以小目标的上下文建模更适合用局部窗口注意力,或者多尺度的空洞卷积并行。

注意力模块放在哪一层比用什么注意力更重要。我试过在 P5 层加注意力,收益微乎其微,因为 P5 上小目标早就消失了;加在 P3 甚至 P2 上,收益才明显。这个道理很简单,但配置的时候很容易随手写在主干末端。

通道注意力和空间注意力的组合收益不是线性的。很多结构是把两者串联,实际测下来串联和并联的差距在 0.3 个点以内,真正起作用的往往是通道维度的重标定,空间注意力在小目标上容易误伤——因为目标本身就几个像素,空间注意力学出来的权重图分辨率不够,反而把目标位置压掉了。

提示:加任何注意力模块之前,先做一个"删除实验"——把模块去掉重训一次,看指标掉多少。如果掉了不到 0.5 个点,那这个模块在你的数据上就是冗余的,留着只是增加推理延迟。

4. 检测头与标签分配:小目标回归是精细活

前面两节解决的是"特征能不能被看见",这一节解决的是"看见了能不能被正确分配和回归"。我的判断是,在小目标场景下,标签分配策略的影响往往比检测头本身更大,因为小目标的正样本极易被 IoU 阈值筛掉。

4.1 锚框重聚类与自适应匹配

用通用数据集上聚类出来的锚框去训小目标数据集,是新手最常见的失误。那些默认锚框的最小尺寸往往在 16 像素以上,而你的目标可能只有 8 像素,导致没有任何锚框能与目标产生足够的 IoU,目标直接变成"无主"的负样本,训练信号为零。

正确做法是在自己的训练集上重新做 k-means 聚类。距离度量用 1 减 IoU 而不是欧氏距离,因为宽高的绝对差值对大小目标不公平。聚类数量一般是每个检测层 3 个,总共 9 个。如果目标尺寸分布特别集中(比如全是 8 到 20 像素),可以减少到 6 个,把容量留给更关键的层。

import numpy as np from sklearn.cluster import KMeans # wh: 归一化到 [0,1] 的 (w, h) 数组,形状 (N, 2) wh = np.load("gt_wh.npy").astype(np.float32) wh = wh[np.random.choice(len(wh), min(len(wh), 20000), replace=False)] k = 9 kmeans = KMeans(n_clusters=k, random_state=42, n_init=10).fit(wh) anchors = kmeans.cluster_centers_ anchors = anchors[np.argsort(anchors[:, 0] * anchors[:, 1])] print((anchors * 640).round(1)) # 换算回 640 输入下的像素尺寸

不过更彻底的解法是干脆换成无锚框或者自适应分配。基于统计量的自适应分配会先算出每个目标与所有候选点的 IoU 均值与标准差,用均值加标准差作为该目标的阈值,这样大目标阈值高、小目标阈值低,小目标也能获得足够的正样本。动态分配则更进一步,在训练过程中根据当前的预测质量动态决定每个目标分配给哪些位置。

动态分配也有坑:训练早期预测质量很差,动态分配选出来的正样本可能是错的,越训越偏。解决办法是前期用静态分配预热若干 epoch,再切换到动态分配。这个 trick 在好几个实现里都有,但文档里往往不写。

4.2 从 IoU 到 NWD:定位损失的替换逻辑

第 1 节算过,小目标对偏移极其敏感。这带来一个连锁反应:基于 IoU 的损失函数在小目标上梯度信号很弱。一个 8×8 的目标,预测框和 GT 的 IoU 从 0.3 优化到 0.6,看起来提升很大,但反映到坐标上可能只是中心移动了 1 个像素。损失值的变化幅度很小,梯度自然小,收敛慢。

改进方向有两条。一条是把框建模成二维高斯分布,用分布之间的距离来衡量相似度,这种度量对目标的绝对尺度不敏感,8 像素的框和 80 像素的框在同样的相对偏移下得到相似的损失值,小目标因此获得了与大目标相当的梯度。另一条是直接对坐标做归一化,把宽高从绝对值转成相对值,再算 IoU 类的损失。

我在一个极小目标占比很高的数据集上做过对比:把 GIoU 换成基于分布距离的损失,AP_S 涨了约 2.5 个点,AP_M 和 AP_L 基本不变。这个结果符合预期——改进只作用在小目标上。但也有副作用,训练的早期收敛速度变慢了,因为分布距离的梯度在框完全错开时反而不如 IoU 类损失稳定。所以我一般会用带权重的混合损失,前若干 epoch 以 IoU 类为主,后期逐渐增大分布距离的权重。

损失类型对小目标的友好度主要问题适用阶段
普通 IoU低小框偏移导致梯度骤降不推荐单独使用
GIoU中依赖最小闭包框,尺度敏感通用基线
DIoU / CIoU中高引入中心点距离,改善有限通用基线
分布距离高早期收敛慢中后期主损失

4.3 Transformer 检测器在小目标上的表现

基于 Transformer 的检测器走了另一条路:不用锚框,用一组查询向量去和图像特征做交互,直接输出检测结果。理论上它没有锚框尺寸的先验限制,对小目标应该更友好,但实际用下来有几个具体的坎。

第一是特征分辨率的坎。原始版本的注意力是在较低分辨率的特征图上做的,一个 8 像素的目标在那个尺度上根本不存在。后来引入多尺度可变形注意力,把浅层高分辨率特征也纳入进来,情况才好转。所以如果你要用这类方法做小目标,一定要确认实现里包含了 stride 4 甚至 stride 2 的尺度,很多默认配置只到 stride 8。

第二是匹配的坎。这类方法靠二分图匹配来决定哪个查询对应哪个目标,匹配代价通常包含分类置信度和框的 L1 距离。小目标的框坐标数值小,L1 距离的绝对差异也小,不同目标之间的区分度下降,匹配容易出错,表现为训练后期 loss 震荡。常见的缓解办法是在匹配代价里加入基于尺度的归一化项。

第三是收敛速度的坎。这类方法普遍需要更长的训练周期,在数据量不大的小目标数据集上,很容易欠拟合。我在一个小规模数据集上试过,训练 100 个 epoch 时的指标还不如卷积检测器训 30 个 epoch。后来加了更强的数据增强和更长的预热,才追平。

结论是:Transformer 类检测器在小目标上不是不能用,但它要求你在数据量、训练预算、实现细节上都给足,否则不如老老实实用卷积加 P2。实时版本的 Transformer 检测器在速度上已经有竞争力了,但在极小目标上,卷积结构加高分辨率特征目前还是更稳的选择。

5. 数据集与评测:别被一个涨点骗了

这一节是我最想强调的部分,因为我在复现别人工作和被别人复现的过程中,见过太多因为评测设计不严谨导致的错误结论。

5.1 常用基准与指标读法

通用检测基准的价值在于可比性,它的 AP_S 分档是行业共识,写论文跑这个指标几乎是必须的。但它的局限也很明显:小目标样本占比不高,且这些"小目标"大多是正常场景里被缩小的物体,纹理信息相对完整,与航拍、遥感那种天生就小、纹理稀薄的目标差别很大。

航拍类基准的目标密度极高,一张图几百个目标,且大量集中在 10 到 20 像素,是检验小目标能力的更好选择。遥感类基准通常用旋转框标注,涉及角度回归,跟水平框的方法不完全可比,引用时要注意说明。还有专门为极小目标设计的基准,把目标按尺寸细分了多个档位,指标上能看到模型在 8 像素以下目标上的真实表现,这个细分对定位问题特别有用。

读指标时有个细节很多人忽略:AP_S 的计算里包含了所有类别的小目标,如果某个类别本身就没有小目标实例,它对这个指标的贡献是零但会计入类别数。所以在类别不均衡的数据集上,AP_S 的绝对值意义不大,看相对变化更靠谱。更好的做法是分门别类地看每个类别的小目标 AP,找出到底是哪一类拖了后腿。

指标含义使用时注意
AP全尺寸平均精度会被大目标主导
AP_S小目标平均精度样本少、方差大
AP_S 的 std多次实验标准差建议至少 3 次
AR_S小目标召回率比 AP 更能反映漏检

5.2 消融实验的最小可信配置

我的最低要求是三条。

第一,固定随机种子并重复至少三次。小目标样本少,单次实验的 AP_S 波动 2 个点是常态。如果你的改进只涨了 1.5 个点,那很可能在噪声范围内。三次取均值和标准差,如果改进量小于标准差,这个改进就不成立。

第二,消融要控制变量到位。加了 P2 检测头同时改了学习率,涨点了算谁的?这种低级错误在赶进度的时候特别容易犯。我的习惯是每次只动一个变量,配置文件用版本管理,实验记录里写清楚改了什么、为什么改、预期是什么。

第三,要看混淆矩阵和误检分布,不能只看总指标。小目标的改进往往伴随着误检上升,如果只看 AP 是看不出来的。我会额外统计每张图的平均误检数,以及误检目标在原图中的尺寸分布。有一次我的改进让 AP_S 涨了 3 个点,但误检数从每张 4 个涨到每张 15 个,业务上完全不可接受,最后推倒重来。

还有一条经验:验证集和测试集的目标尺寸分布要一致。我遇到过验证集里恰好有一批尺寸偏大的小目标,模型在上面表现很好,换到真实数据就崩了。做法很简单,画两张尺寸分布直方图叠在一起对比,五秒钟的事。

6. 一套可复现的调优路线

前面讲的都是分模块的取舍,这一节把它们串成一条实际可执行的路线,从拿到数据到最终上线,按优先级排序。

6.1 六步走清单

第一步,摸底。统计 GT 框的尺寸分布、每张图的目标数分布、类别分布,画图看。这一步不做,后面所有决策都是盲猜。耗时大概半天,收益巨大。

第二步,跑通基线。用现成的检测器在原始分辨率上跑一遍,不做任何小目标特化。这个数字是所有改进的参照系,必须要有一个干净的结果。

第三步,调输入分辨率和切片。这是投入产出比最高的一步。先试分辨率,448、640、768、1024 各跑一组,看 AP_S 和推理延迟的曲线,找到拐点。再试切片,只在离线场景做。

第四步,重聚类锚框或换自适应分配。这一步的收益取决于你的数据分布和默认锚框差多远。差得远的话,这一步就能带来 3 到 5 个点。

第五步,加浅层检测头。加 P2,配合重新调整的正样本采样比例,注意误检率的变化。

第六步,换损失函数和加数据增强。这两步属于收尾优化,收益通常在 1 到 3 个点之间,但要小心过拟合。

顺序不能乱。我见过有人第一步就上注意力模块,训了两周发现是锚框尺寸不对,白费功夫。

6.2 关键配置与代码片段

加 P2 检测头的核心改动是四件事:骨干网络多引出一层、颈部多一条融合路径、检测头多注册一层、锚框多一组。以常见的 YOLO 系配置为例,关键片段大致是这样(不同版本字段名有差异,需要对照你手上的实现):

# 骨干:多保留一级 stride=4 的输出,记为 P2 backbone: - [-1, 1, Conv, [64, 3, 2]] # P1/2 - [-1, 1, Conv, [128, 3, 2]] # P2/4 <-- 新增输出 - [-1, 3, C2f, [128, True]] - [-1, 1, Conv, [256, 3, 2]] # P3/8 # ... 后续保持不变 # 颈部:自顶向下路径延长到 P2 head: - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [[-1, 3], 1, Concat, [1]] # 与骨干 P2 拼接 - [-1, 3, C2f, [128, False]] # 输出 N2 # 检测头:注册 stride=4 的一层 detect: - [[17, 20, 23, 26], 1, Detect, [nc, anchors, (4, 8, 16, 32)]]

配套的锚框用量化结果替换,正样本采样比例也要跟着调,否则新增的 P2 层会被大量简单负样本淹没。

切片推理的调用在第 2.2 节的代码里已经给过,这里补充一个合并阶段的处理要点:跨切片的重复框,建议先用基于中心的距离做一次粗聚类,再做 NMS。只用 NMS 的话,因为两半框的重叠面积小,往往合不上。

6.3 常见问题速查表

现象最可能的原因处理方向
小目标召回极低,大目标正常锚框尺寸与小目标不匹配重聚类锚框,或降 IoU 阈值
训练 loss 正常但 AP_S 不涨正样本数量不足换自适应分配,加 P2
加了 P2 后误检暴增浅层语义弱,阈值未调提高置信度阈值,加困难负样本挖掘
切片推理后出现重复残框跨块目标未合并提高重叠率,加中心距离聚类
多次实验 AP_S 波动大样本少,随机性大固定种子,至少三次取均值
换更高分辨率后显存溢出batch 未同步下调降 batch,开梯度累积
验证集好,真实数据差尺寸分布不一致对齐分布,重划分验证集

6.4 几个不写在论文里的实操心得

关于学习率。加了 P2 检测头之后,新增的层是随机初始化的,跟预训练权重不同步。如果沿用原来的学习率,新增层要么学得太慢要么训崩。我的做法是给新增层单独设一个较小的初始学习率,用分组参数配置,训若干 epoch 后再统一。

关于困难负样本。小目标误检的主要来源是背景中的纹理相似区域,比如草地上的石块、水面上的波纹。与其在结构上想办法,不如在训练时把这类误检框收集起来,作为负样本再训一轮。这个方法简陋但有效,我在两个项目上都用过,误检率能压下来 30% 以上。

关于标注质量。小目标的标注一致性是最容易被低估的变量。两个人标同一批 8 像素的目标,框的中心可能差 2 个像素,按前面的计算,IoU 直接掉到 0.6 以下。如果你的数据是自己标的,建议先做一次标注一致性检查,随机抽 100 个目标重标一遍算 IoU,低于 0.7 就说明标注规范需要重新对齐。

关于推理后处理。小目标的置信度天然偏低,用一个全局阈值会系统性漏检。可以按目标尺寸分档设阈值,小目标的阈值适当降低。这个调整在离线场景几乎零成本,效果立竿见影。

7. 这条线上我还在观察的几件事

写到这里,其实还有很多没展开的东西。我最近在试的一个方向是把切片推理和模型结构联合设计,而不是把切片当成一个纯粹的后处理技巧——既然知道会切片,训练时就可以模拟切片边界的目标截断,让模型学会处理"半截目标"。初步结果还行,但还没跑完全部消融。

另一个我一直在纠结的问题是评测的一致性。同一个模型,用不同的 NMS 阈值、不同的置信度阈值、不同的切片参数,AP_S 能差出四五个点。这意味着跨论文的数字对比其实很不可靠,除非所有后处理参数都公开。我现在的做法是,引用别人的指标时一定注明后处理配置,自己的实验也保留完整的推理脚本。

最后一个体会是关于方向的取舍:小目标检测最常见的误区是把它当成一个纯结构问题。实际上,数据分布摸底、锚框重聚类、分辨率选择这三件事做完,往往能拿到七成以上的收益,而它们都不需要改一行网络代码。结构改进是锦上添花,不是雪中送炭。我见过太多人在结构上折腾了几个月,最后发现问题出在默认锚框的最小尺寸是 16 像素,而他的目标平均只有 9 像素。先把这条账算清楚,比什么都强。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/2 19:23:41

Codeforces竞赛机制与Div. 2参赛策略详解

1. Codeforces竞赛体系解析Codeforces是全球最具影响力的算法竞赛平台之一&#xff0c;其独特的竞赛机制和评级系统吸引了来自世界各地的编程爱好者。平台采用Elo评级系统&#xff0c;根据选手表现动态调整rating值&#xff0c;主要分为以下几个等级区间&#xff1a;灰名&#…

作者头像 李华
网站建设 2026/11/2 19:21:55

n8n实战:XML与JSON数据格式转换技巧

1. n8n数据转换核心技能&#xff1a;XML与JSON互转实战指南作为一款强大的工作流自动化工具&#xff0c;n8n在处理不同数据格式转换时展现出惊人的灵活性。XML和JSON作为两种最常用的数据交换格式&#xff0c;在实际业务场景中经常需要相互转换。本教程将深入解析n8n中XML节点的…

作者头像 李华
网站建设 2026/11/2 19:18:04

Node版本管理工具nvm的安装与使用指南

1. 为什么需要Node版本管理工具作为一名长期与Node.js打交道的开发者&#xff0c;我经历过无数次因版本问题导致的"灵异事件"。最典型的场景是&#xff1a;本地开发时一切正常&#xff0c;部署到服务器却突然报错&#xff1b;或者团队中不同成员因为Node版本差异&…

作者头像 李华
网站建设 2026/11/2 19:18:01

C#股票交易模拟系统:本地化订单撮合与T+1风控沙盒

简介&#xff1a;这是一套基于C#开发的股票交易模拟系统实战项目&#xff0c;面向C#初学者及.NET平台进阶学习者&#xff0c;帮助其综合掌握Windows桌面应用开发、数据库交互、实时数据处理与MVC架构设计等核心能力。资源包含63个文件&#xff0c;涵盖21个C#源码文件&#xff0…

作者头像 李华
网站建设 2026/11/2 19:15:13

Windows内网邮件服务器搭建:Foxmail Server v2.0避坑

简介&#xff1a;Foxmail Server For Windows v2.0 公测版面向需要在 Windows 环境下搭建内部邮件系统的个人学习者、中小企业运维人员与邮件服务器爱好者&#xff0c;提供一套可快速安装试用的一体化邮件服务方案。它支持 SMTP、POP3、LDAP 及 MIME 邮件扩充协议&#xff0c;既…

作者头像 李华
网站建设 2026/11/2 19:14:02

三层双链模型:智能体协作架构设计与实践

1. 组织智能体的三层双链模型概述在复杂系统管理和多智能体协作领域&#xff0c;组织智能体的架构设计一直是核心挑战。我通过多年项目实践发现&#xff0c;传统单层决策模型在面对动态环境时往往表现出响应迟滞和协调不足的问题。三层双链模型正是为解决这一痛点而提出的创新架…

作者头像 李华