FPN详解:特征金字塔网络如何解决多尺度目标检测与小目标漏检问题

📅 发布时间:2026/10/14 16:52:11
FPN详解:特征金字塔网络如何解决多尺度目标检测与小目标漏检问题
FPNFeature Pyramid Networks for Object Detection这篇论文算是多尺度目标检测绕不开的一块基石。我最早读它的时候手头正好在做一个小目标检测的项目当时的困惑很具体模型对大目标已经很准了可一到小目标上要么漏检要么框偏移把网络加深加宽也没太大改善。后来系统地把这篇论文扣了一遍才发现问题不在网络容量而在特征利用方式上——大多数检测框架只用最后一层高层特征做预测尺度信息被白白浪费了。这篇笔记我会从论文想解决的问题、网络结构设计、如何嵌入 RPN 和 Fast R-CNN、容易被忽略的实现细节以及我自己复现时的踩坑记录这几个角度展开。适合正在读这篇论文的初学者也适合想动手复现、或者打算把 FPN 用到自己数据集上的人。内容不追求面面俱到但凡是关键的地方我都会把为什么这么做讲清楚。1. 多尺度为什么困扰了目标检测这么久1.1 检测任务的尺度挑战和图像分类完全不同图像分类通常会把输入 resize 到固定尺寸目标在画面里一般占比较大的面积所以尺度问题不那么突出。检测任务则不一样一张自然图像里可能同时出现占据半个画面的大卡车以及只有几十个像素的小行人。模型既要把大目标完整框出来又要把小目标从背景里分辨出来这本质上是一个既要高分辨率、又要强语义的矛盾需求。卷积网络的前几层分辨率高保留了边缘、纹理、局部结构等空间信息但语义抽象程度低越往后走分辨率越低但对这是什么物体的判断能力越强。检测器如果在低层直接做预测空间定位精细却分不清类别如果在高层做预测语义清楚了小目标的位置和细节却已经被卷积和池化抹掉了。这就是检测里经典的语义-空间失衡问题。FPN 这篇论文的核心贡献是给出了一套把高层语义往低层空间回传的机制让每一层特征既保留对应的分辨率又具备足够强的语义信息从而在不同尺度上都能做出可靠的预测。1.2 在 FPN 之前业界至少有三种多尺度玩法传统方案大致分三类各有各的账要算。第一类是图像金字塔image pyramid。把输入图像缩放到多个分辨率比如从 0.5 倍到 2 倍每个尺度都独立跑一遍网络最后把不同尺度的检测结果合并。效果比较直接但代价是训练和推理时间成倍增加。四层金字塔就意味着四倍计算量这在实时场景里基本不可接受。第二类是多尺度滑窗和多尺度 anchor。这就是 Faster R-CNN 早期常见的方案在同一个特征图上放置不同大小和不同长宽比的 anchor试图用一个特征层覆盖所有尺度。这个做法实现简单但对尺度跨度太大的情况效果有限因为 anchor 变大了特征图本身的分辨率却没有变小目标对应的高频空间信息早就丢完了。第三类是利用卷积网络天然的多层特征。像 SSD 这样从不同层引出预测理论上也是特征金字塔。但作者在论文里明确指出了一个关键问题SSD 是从比较高的层开始做预测的通常是 conv4 之后没有把更低层、更高分辨率的特征利用起来而小目标恰恰需要这些低层的空间细节。所以 SSD 对小目标仍然算不上友好。1.3 一个反直觉的结论深层网络并不能自己学会尺度不变性很多人一开始会想卷积网络本身有平移不变性目标大了小了卷积核不都能捕捉到吗为什么还需要显式构造金字塔实际实验告诉我们纯靠加深网络解决不了尺度问题。原因是卷积和池化天然是局部操作kernel 大小固定比如 3x3在 32 倍下采样的特征图上它看到的是原图 96x96 的区域在 4 倍下采样的特征图上它看到的只有原图 12x12 的区域。高层神经元的有效感受野大负责大目标小目标的响应集中在低层而低层又缺少语义。让同一个分类器同时处理这两种情况本质上是在强迫它压缩过大的信息量。理解了这一点再看 FPN 的结构就不会觉得它复杂了。它做的事情不是发明新的卷积而是重新组织已有特征的流向。2. FPN 的核心结构自底向上、自顶向下加横向连接2.1 自底向上路径还是熟悉的骨干网络FPN 的自底向上路径直接复用 ResNet 这种标准骨干网络的前向过程。以 ResNet 为例输入图像经过 conv1 之后依次产生 conv2、conv3、conv4、conv5 四个层级的特征。这四个层级的下采样倍数分别是 4、8、16、32通道数则通常是 256、512、1024、2048。FPN 论文里把这四个输出记作 C2、C3、C4、C5。注意它没有使用 conv1 输出也就是 stride 为 2 的 C1。原因很实际C1 的特征图分辨率太高如果放进金字塔内存占用和计算量都扛不住而且 gain 相对有限。在自底向上的过程中每一层特征都会经过很多卷积层语义信息逐层增强分辨率逐层降低。这个过程不需要任何改造直接使用 ImageNet 预训练的 ResNet 权重即可。2.2 自顶向下路径把语义一层层回传自顶向下路径是整个 FPN 的精华。它从最高层 C5 开始逐层向低层回传具体做法分三步。第一步对 C5 先做一次 1x1 卷积把通道数压缩到固定值 d比如 256得到 P5 的初始特征。为什么不直接用 C5因为 C5 是 2048 通道后续融合时和低层特征通道数不一致直接相加在维度上过不去。用 1x1 卷积降维既统一了通道又对 C5 做了一次线性压缩可以理解成把冗余的高维特征浓缩到 256 维表示中。第二步把 P5 做 2 倍最近邻上采样使空间尺寸和 C4 对齐。然后取 C4 做 1x1 卷积得到 256 通道把两者逐元素相加得到初始 P4。第三步得到的 P4 还要过一层 3x3 卷积才作为最终的金字塔输出。这个 3x3 卷积非常重要论文里给出的解释是消除上采样带来的混叠效应。简单理解就是抹平最近邻上采样造成的块状痕迹让融合后的特征更平滑。以此类推一直做到 P2。最终的金字塔层级是 P2、P3、P4、P5对应 stride 为 4、8、16、32通道数都是 256。整个过程可以写成P5 1x1 conv(C5) P4 3x3 conv(1x1 conv(C4) upsample(P5)) P3 3x3 conv(1x1 conv(C3) upsample(P4)) P2 3x3 conv(1x1 conv(C2) upsample(P3))2.3 横向连接到底在做什么横向连接就是指从 C2 到 C5 各自引出 1x1 卷积和自顶向下路径的特征相加这个操作。有人会问为什么不直接对 P5 连续上采样生成 P4、P3、P2而要绕一圈先拉下来再做 1x1 卷积然后相加关键在于自顶向下路径携带的是高层语义但毕竟经过多次上采样空间细节已经模糊。而 C2 这类低层特征虽然语义弱却保留了原始图像里更精细的边缘、角点等信息。横向连接就是把这两个信息源合并起来高层语义是这是什么东西低层特征是这东西具体在哪、边界长什么样。两者相加既避免了低层特征说不上来是什么的问题又避免了高层特征知道是什么但找不到精确边界的问题。从梯度流的角度看横向连接还给了自底向上路径一个更短的梯度通路。低层特征能直接从最终的预测损失里获得梯度而不是绕一大圈穿过所有高层这在训练上也有正向作用。2.4 为什么自顶向下路径不能做得太深或太复杂读者很容易想到既然融合有效那多做几次融合是不是更好FPN 的答案是每一层只融合一次就够。原因有两个。第一论文用消融实验证明多次迭代融合的收益微乎其微但计算量线性上涨。特征金字塔的价值在于把不同尺度的特征组织成并列的层级而不是做无限度的特征反复混合。真正的收益来自互补性而不是叠加次数。第二FPN 是接到 RPN 和 Fast R-CNN 这种检测头里的需要保持结构简单和训练稳定。结构越复杂超参数越多第一次复现时就越难调。这也是 FPN 能快速普及的原因之一改动清晰行为可控拿过来就能用。3. 把 FPN 装进检测框架Anchor 和 ROI 都要跟着改FPN 本身不是端到端的检测器它需要配合 RPN 的候选框生成和 Fast R-CNN 的区域分类。论文的关键创新在于之前的检测器通常在单一特征图上处理所有尺度的候选而 FPN 让不同尺度的提议回归到不同的特征层去解决分工明确。3.1 RPN 在 FPN 上的改造每层只做一个尺度的 Anchor原始的 RPN 在一张特征图上使用多种尺度和多种长宽比的 anchor比如面积 32、64、128、256、512 各配三种长宽比。如果把这套逻辑原封不动套到 FPN 上不仅浪费计算也没有发挥金字塔的尺度分工优势。FPN 对 RPN 的改造思路很巧妙既然 P2、P3、P4、P5 已经分别对应 stride 4、8、16、32相当于天然把图像分成了四个尺度范围那就让每层只负责一个基础尺度。论文默认的设置是特征层strideanchor 基础面积P2432^2P3864^2P416128^2P532256^2P664512^2每层都使用三种长宽比{0.5, 1, 2}。这样设计的好处是没有哪一层需要同时承担兼顾大小目标的任务模型可以更专心地学习每一层的表达能力。需要注意一个很容易忽略的点RPN 用到的特征金字塔实际上是 P2 到 P6而 P6 是由 P5 通过 stride 2 的最大池化或步长为 2 的下采样得到的。为什么多一层 P6因为检测任务里确实可能存在比 256^2 更大的目标如果没有 P6这些大目标的 anchor 就没有合适的落脚点。FPN 在提到这个设置时特别说明P6 只用于 RPN 阶段不参与最终的 Fast R-CNN 分类回归。3.2 Fast R-CNN 的 ROI 分配按面积选择合适的层级RPN 负责生成提议框Fast R-CNN 负责对提议框分类和回归。在 FPN 版 Fast R-CNN 里每个提议框并不是同时在所有金字塔层上做 RoI 池化而是根据自身大小被分配到某一层。论文给出的分配公式很简洁k k0 log2( sqrt(w*h) / 224 )其中 w、h 是提议框的宽和高k0 默认取 4224 是 ImageNet 预训练时常用的输入尺寸。计算出的 k 会限制在 2 到 5 之间对应 P2 到 P5。这个公式的含义非常直观。sqrt(w*h) 可以理解成框的等效边长。如果一个框的边长正好是 224那么 log2(224/224)0k 就等于 4使用 P4 特征。如果框边长小于 224log2 项为负k 变小就映射到 P2、P3 这类高分辨率特征层如果框边长大于 224k 变大映射到 P4、P5 这类高层语义特征。用一句话记住就是小框去高分辨率层大框去低分辨率层。这和人类看图的直觉完全一致看小目标时需要把眼睛凑近一点看清细节看大目标时反而要退远一点把握整体轮廓。3.3 训练细节和论文汇报的提升FPN 版 RPN 和 Fast R-CNN 的训练逻辑大体沿用 Faster R-CNN 的设置RPN 阶段anchor 与真实框 IoU 大于 0.7 为正样本小于 0.3 为负样本Fast R-CNN 阶段RoI 与真实框 IoU 大于 0.5 为正样本。采样数量上每张图取约 256 个 RoI正负样本比例保持 1 比 1 左右。论文在 COCO 数据集上的实验显示把 FPN 接入 Faster R-CNN 之后检测精度相比只在最后一层特征做预测的 baseline 有明显提升尤其在小目标AP_S和大目标AP_L两个分项上的改善显著。原因也好理解小目标被分配到了高分辨率的 P2、P3空间细节保住了大目标在 P4、P5 上语义信息充足不会因为强行塞进更小的特征图而丢失上下文。从工程角度看FPN 带来的计算增量相对可控。多出来的主要是每条自顶向下路径上的 1x1 卷积、上采样和 3x3 卷积参数量比主干网络小得多。这也是 FPN 能在 GPU 资源有限的实验室环境里被广泛复现的客观条件之一。4. 论文里容易看漏、但实际很关键的几个细节FPN 论文篇幅不长正文读起来很顺但有几个细节初读时容易一带而过真正复现时却会直接影响效果。我单独把它们拎出来讲。4.1 为什么金字塔从 conv2 开始而不是 conv1论文解释了没有使用 conv1 的原因conv1 输出的 stride 为 2分辨率是输入的一半这个特征图过大内存和计算开销会明显增加而带来的精度收益有限。实际上后面很多工作也延续了这个取舍金字塔的起步层通常选 stride 为 4 的 conv2而不是直接贴着输入做。如果你做的是无人机视角、显微图像这类小物体特别密集的任务可能希望分辨率更高比如从 stride 2 的特征开始建金字塔。这个想法可以试验但要注意显存占用和训练速度的平衡。我试过在显存充足的情况下从 stride 2 开始小目标有一定改善但训练时间大概增加了一倍性价比需要仔细评估。4.2 上采样用最近邻而不是转置卷积FPN 的自顶向下路径用的是 2 倍最近邻上采样不是可学习的转置卷积。很多人第一次实现时会下意识选择转置卷积觉得可学习总比固定插值好。实际并非如此。最近邻上采样不引入任何参数计算简单输出像素值是周围像素的直接复制语义信息不会因为插值过程被稀释。转置卷积虽然有学习能力但在特征融合这种场景里容易产生棋盘伪影需要额外的技巧去抑制反而给训练增加负担。论文用最近邻是有意为之的不要随便改。4.3 横向连接用 1x1 卷积最后再用 3x3 卷积这里有一个容易混淆的地方C 层到融合阶段先用 1x1 卷积融合之后再接 3x3 卷积。两个卷积的职责不同。1x1 卷积负责把每个自底向上层级的通道数压缩到 256。如果不降维C5 的 2048 通道和 C4 的 1024 通道直接相加维度都对应不上而且参数量巨大。3x3 卷积则负责在融合之后做一次局部空间交互把上采样相加带来的锯齿感抹掉。论文原文用的词是消除上采样的混叠效应翻译成人话就是让特征看起来更自然不出现明显的格子状痕迹。复现时最容易翻车的地方就是把这个 3x3 卷积漏掉或者省掉下一步的输出。它虽然只有一层但对最终特征质量的影响不容忽视。4.4 P6 到底怎么来它对结果有什么影响前面提到 RPN 会用到 P6具体生成方式是从 P5 做步长为 2 的 3x3 卷积或最大池化。论文里提到 P6 的目的是让金字塔覆盖更大的尺度在 COCO 这种目标尺寸跨度大的数据集上多一层对 anchor 的攻击范围更完整。但 P6 只服务 RPN 阶段不进入 Fast R-CNN 的 RoI 分配范围因为 RoI 分配公式把 k 限幅在 2 到 5。换句话说检测头最多使用 P5不会到 P6。这个设计保证了最终分类回归仍然在语义足够强的范围内完成P6 只是一个放大 anchor 覆盖面的辅助角色。5. 复现 FPN 时我踩过的坑和调整思路论文读通和代码跑通之间还存在好几道坎。这里记录一些我在复现和迁移 FPN 时遇到的真实问题供大家参考。5.1 不要在主干部位乱加 BatchNormFPN 的横向连接和 3x3 输出层在论文的默认设计里是不额外加 BN 的。它的设计思路是复用预训练骨干网络的统计信息新增模块尽量轻量、不改变分布语义。我第一次实践时觉得加了 BN 能加速收敛就在 1x1 和 3x3 卷积后各接了一层 BN结果训练时 loss 反而掉得慢小目标检测也没有明显收益。原因可能是 FPN 本身只是一个特征组织层不是深度特征提取器过强的归一化反而干扰了预训练特征原有的统计特性。如果真要用 BN必须配合小学习率和大 batch 仔细调性价比不高。5.2 上采样和融合顺序不要随便改自顶向下路径的正确顺序是先上采样、后相加、再做 3x3 卷积。有人实现时习惯先 3x3 再做上采样结果在每一层都引入额外的空间模糊效果可以差到 2 到 3 个点。另外相加和拼接的选择也值得说道。FPN 选的是逐元素相加add不是通道拼接concat。相加的前提是两路特征通道数一致语义对齐后直接合并计算量小、训练稳定。拼接会加倍通道数后面必须再接卷积来压缩参数量和 GPU 显存占用都会上涨。在很多实际任务里concat 相比 add 的收益并不足以抵消计算开销的增加。5.3 小目标检测提升的真正来源在哪里不少人误以为 FPN 让小目标变准是因为把浅层特征用上了。其实更准确地说是因为把高分辨率特征和高层语义结合在了一起。单独拿 C2 去做预测空间细节丰富但语义不足很容易把背景纹理误判成目标。FPN 通过自顶向下路径让 C2 最终获得的特征里带着 C5 的语义判断两者缺一不可。在迁移到自己的数据集时如果发现小目标仍然很差我通常会优先检查两件事一是 backbone 是否足够大、预训练数据是否和当前域接近二是 RPN 的 anchor 设置是否根据当前数据的 GT 尺寸重新统计过。FPN 能解决的是特征组织问题anchor 和训练数据的问题它背不了锅。5.4 FPN 之后的一些结构演进FPN 的影响远远超出目标检测本身。实例分割模型把它作为 backbone 输出语义分割里也有类似的自顶向下思想比如 U-Net 的跳跃连接就很有横向连接的影子。后续不少工作试图在 FPN 基础上继续优化。PANet 增加了自底向上的额外通路让低层信息也能往上流BiFPN 在加权融合上做了文章给不同层加可学习的权重NAS-FPN 用搜索的方式寻找特征融合结构。这些改进在特定任务上确实有效但 FPN 作为最简洁、最稳定的起点仍然是最值得先吃透的版本。提示第一次复现时建议从开源库的 FPN 实现开始先跑通默认参数再逐步改成自己的需求。不要一上来就改融合方式容易把 baseline 和改动效果混在一起出了问题很难定位。我自己复现 FPN 时最深的体会是有时候一个结构能成为经典不是因为某个组件有多精巧而是因为它在语义和分辨率之间找到了恰当的交点。FPN 没有引入任何新的卷积算子也没有复杂的注意力机制只是把已有的特征重新组织了一遍。如果你现在正在调小目标检测的模型建议先把当前模型的基线搞清楚然后只加 FPN 这一项改动跑一次对比实验。你会很快发现空间细节和语义信息这两件事确实可以通过一条自顶向下路径完整地串起来。