ConvNeXt 特征金字塔网络指南:FPN 与 PAN 如何选,实测差多少
【免费下载链接】ConvNeXtCode release for ConvNeXt model项目地址: https://gitcode.com/gh_mirrors/co/ConvNeXt
检测与分割任务里,骨干输出的多张特征图语义天然错位:浅层看得清位置却不懂含义,深层含义够却丢了细节。本文基于 ConvNeXt 仓库的目标检测与分割两条落地链路,拆解其特征金字塔网络如何把 C2–C5 融成 P2–P5,对比 FPN 与 PAN 的补路差异,给出按实测数据做的选型规则。
⚠️ 小目标为什么会漏:多尺度特征错位
漏检很少是分类器的问题,更多出在"喂给 RPN 的特征"上。以 COCO 为例,同一张图里同时存在 32 像素的鸟和占画面大半的汽车。若只取最后一层特征图,小目标的激活面积可能不足 2×2,直接消失;若只取浅层,大目标又缺乏类别上下文。这就是单尺度特征的根本矛盾:分辨率与语义不可兼得。特征金字塔的解法不是发明新信息,而是把骨干已有的四档分辨率重排成"每档都语义对齐"的均匀输出——这就是 FPN 的全部动机。
🔗 把 C2–C5 接成 P2–P5:ConvNeXt 里的 FPN 链路
ConvNeXt 骨干在object_detection/mmdet/models/backbones/convnext.py中以 4 个 stage 依次降采样:stem 的 4×4 卷积出 1/4 尺度,之后每进一个 stage 经 2×2 卷积再降一档,Tiny 尺寸得到 96/192/384/768 通道、stride 4/8/16/32 的 C2–C5。forward_features对选中的输出逐层做 LayerNorm 后以 tuple 返回,交给 mmdet 框架组装。
真正的融合发生在 neck。基础模型object_detection/configs/_base_/models/mask_rcnn_convnext_fpn.py中 neck 只有三行:type='FPN'、in_channels=[128, 256, 512, 1024](各档 C/2,与 stage 卷积输出的通道数一致)、out_channels=256, num_outs=5。融合链路是:
| 输出 | 来源 | 融合方式 |
|---|---|---|
| P5 | C5 经 1×1 卷积 | 无横向连接,自顶向下起点 |
| P4 | C4 ⊕ up(P5) | 上采样后逐元素相加 + 3×3 卷积 |
| P3 | C3 ⊕ up(P4) | 同上 |
| P2 | C2 ⊕ up(P3) | 同上 |
配置里的 RPN anchor 步长[4, 8, 16, 32, 64]与 P2–P5(加 P6 档)严格对齐,RoI 提取的featmap_strides=[4, 8, 16, 32]也直接消费这四张图——改 neck 通道数时这两处必须同步,否则会静默错位。
➕ PAN 比 FPN 多补的那条路
FPN 的缺陷:P2 虽然分辨率够,但它的"语义浓度"依赖自顶向下的累加,低层的位置先验在多次上采样后被稀释。PAN 的补丁是一条自底向上路径:P2 → P3 → P4 方向再融合一次,让低层的位置细节反向注入高层,小目标定位的框回归精度因此受益(PANet 原文报告小目标提升显著)。注意两点边界:这条路的收益集中在密集小目标场景,中尺度目标收益有限;它给 neck 增加一组 3×3 卷积,FLOPs 与显存开销随之上升。
本仓库的检测配置默认是标准 FPN 而非 PANet;若小目标成为瓶颈,可在 neck 中把类型换成 PANet(MMDet 内置)并沿用同一组in_channels。分割侧是另一种融合思路:semantic_segmentation/configs/_base_/models/upernet_convnext.py的 UPerNet 头用pool_scales=(1, 2, 3, 6)多尺度池化抽取全局上下文,再做自底向上聚合,属于"池化式"的 PAN 思想;另有一个挂在 C3 上的辅助 FCN 头(loss 权重 0.4)提供额外监督。
📊 实测数据:检测与分割两张表
COCO 目标检测(3x 训练,数据取自仓库object_detection/README.md):
| 骨干 | 方法 | 框 mAP | 掩码 mAP | 参数量 |
|---|---|---|---|---|
| ConvNeXt-T | Mask R-CNN + FPN | 46.2 | 41.7 | 48M |
| ConvNeXt-T | Cascade Mask R-CNN + FPN | 50.4 | 43.7 | 86M |
| ConvNeXt-S | Cascade Mask R-CNN + FPN | 51.9 | 45.0 | 108M |
| ConvNeXt-L | Cascade Mask R-CNN + FPN | 54.8 | 47.6 | 255M |
ADE20k 语义分割(160K iters,数据取自semantic_segmentation/README.md):
| 骨干 | 预训练 | 裁剪尺寸 | mIoU | mIoU (ms+flip) | 参数量 |
|---|---|---|---|---|---|
| ConvNeXt-T | 1K | 512 | 46.0 | 46.7 | 60M |
| ConvNeXt-B | 1K | 512 | 49.1 | 49.9 | 122M |
| ConvNeXt-B | 22K | 640 | 52.6 | 53.1 | 122M |
| ConvNeXt-XL | 22K | 640 | 53.6 | 54.0 | 391M |
两组数据共同说明:同一 FPN 链路上,换更深的 Cascade 头比换 PAN 更直接;分割侧多尺度测试(ms+flip)普遍再涨 0.5–0.7 个 mIoU,说明融合结构的精度还有测试期红利可吃。
🧭 三条可直接执行的选型规则
- 条件:单卡推理或参数量预算 < 60M→ 选 FPN。
num_outs=5的标准配置已足够,T 级骨干 48M 即可跑。 - 条件:COCO-style 指标中"小"档 mAP 明显低于中/大档(差距 > 5 点)→ 换 PANet neck,先不动骨干;仍不达标再加 Cascade 头。
- 条件:稠密像素任务(语义分割、全景分割)→ 直接走 UPerNet 池化式融合,它的多尺度全局上下文比纯路径聚合更适合大感受野需求,配合 ms+flip 测试。
🛠️ 三个工程要点
- 预训练加载:backbone 的
init_weights走strict=False加载,检测侧只灌骨干权重、neck 随机初始化。训练命令形如tools/dist_train.sh <config> 8 --cfg-options model.pretrained=<pth 路径>,config 选configs/convnext/下与骨干尺寸匹配的那份。 - 分层学习率:
object_detection/mmcv_custom/layer_decay_optimizer_constructor.py把骨干参数映射到 13 个逻辑层(stage 0–3 展开 + 顶层),越深的层学习率按layer_decay递减,浅层学得快、深层学不动的失衡问题就是靠它压住的。 - 多尺度增强:检测配置名中的
mstrain_480-800表示训练期短边在 480–800 间采样,与 FPN 的多档分辨率相乘后等效于对每个金字塔层级做尺度抖动;分割侧对应 512/640 两档裁剪配置。
FPN 是这套特征金字塔网络的默认起点,PAN 与池化式融合是按瓶颈逐项追加的补丁——先跑通前者,再用小目标数据说话,选型成本最低。
【免费下载链接】ConvNeXtCode release for ConvNeXt model项目地址: https://gitcode.com/gh_mirrors/co/ConvNeXt
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考