资讯详情 小目标检测工程实践:从分辨率、切片到标签分配调优指南
📅 2026/11/2 19:27:52
小目标检测这四个字写在论文标题里挺学术落到工程现场基本等于一句大白话人眼能看见模型就是检不出来。我在几个不同类型的项目里被这个问题反复折磨过——航拍画面里几十米外的车辆、监控里走廊尽头的人头、工业图像上宽度只有几个像素的划痕、遥感图上的小型设施。这些目标在原图里往往只有十几像素见方主干网络下采样几次之后特征图上剩下的可能就是一个点甚至不到一个点物理意义上消失了。这篇综述式的整理是我把散落在各种论文、配置文件、失败实验记录里的东西重新捋一遍的结果7.25 这个时间戳意味着它还会继续往下更我打算一边做实验一边往里补。内容适合两类人看一类是刚进这个方向、想知道该从哪几篇入手的研究生另一类是被 AP_S 卡住、调了两周没动静的工程同学。我会尽量把为什么这么选讲清楚也会把踩过的坑和不那么好看的数据一并摊开。1. 小目标检测到底卡在哪先把问题边界划清楚很多人一上来就改网络结构其实连自己的目标算不算小都没弄清楚。这一节我想先把判定口径、失效机理和几个真实痛点摆出来后面所有方法的取舍都建立在这个前提上。1.1 多大算小三种判定口径与实际影响行业里最常引用的是通用检测数据集的那套划分目标框面积小于 32×32 像素记为 small32×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×856并集是 6464-5672IoU 掉到 0.78偏移 2 个像素交集 48并集 80IoU 只剩 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_typeyolov8, model_pathweights/best.pt, confidence_threshold0.25, devicecuda:0, ) result get_sliced_prediction( demo.jpg, detection_model, slice_height512, slice_width512, overlap_height_ratio0.2, overlap_width_ratio0.2, postprocess_typeNMS, postprocess_match_threshold0.5, ) result.export_visuals(export_dirvis/)提示切片推理一定要先在小样本上跑通坐标映射和 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×80P2 是 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), replaceFalse)] k 9 kmeans KMeans(n_clustersk, random_state42, n_init10).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 系配置为例关键片段大致是这样不同版本字段名有差异需要对照你手上的实现# 骨干多保留一级 stride4 的输出记为 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 # 检测头注册 stride4 的一层 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 像素。先把这条账算清楚比什么都强。