最近看 SAM 2 的论文和代码时,我在图像编码器里遇到了 FpnNeck。一开始看到“特征金字塔网络”这个名字,我以为它只是把不同分辨率的特征图放在一起;继续读 FPN 原论文和 SAM 2 的实现后才发现,真正关键的并不是“有很多层”,而是怎样让高分辨率特征也拥有足够强的语义,同时保留它精确的空间定位能力。
在进入公式之前,先确定它在整个视觉模型里的位置:
输入图像
↓
Backbone:逐层提取 C2、C3、C4、C5
↓
FPN / Neck:对齐并融合不同尺度,得到 P2、P3、P4、P5
↓
Task Head:完成分类、检测框、关键点或掩码预测
C 表示 backbone 的原始 stage 输出,P 表示经过金字塔融合、准备交给任务头的输出。FPN 不是 backbone,也不直接预测类别或掩码;它是夹在 backbone 和 head 之间的 neck,负责把原本语义强弱不一的多层特征整理成更好用的多尺度接口,我的理解是作为一个多尺度融合的方法。
1. 为什么视觉模型需要多尺度特征
同一种物体在图片中可能只有十几个像素,也可能占据大半张图。对检测和分割来说,这不是简单的缩放问题:物体大小会直接决定它在特征图上还剩多少空间信息。
假设 backbone 每经过一个 stage 就把高、宽缩小一半:
| 特征层 | 相对输入的步长 | 1024×1024 输入对应尺寸 | 典型特点 |
|---|---|---|---|
| $C_2$ | 4 | 256×256 | 定位细,但语义较浅 |
| $C_3$ | 8 | 128×128 | 细节与语义开始折中 |
| $C_4$ | 16 | 64×64 | 语义强,空间较粗 |
| $C_5$ | 32 | 32×32 | 感受野大,但小目标容易消失 |
这里存在一个天然矛盾:
- 浅层特征分辨率高,边缘、纹理和位置清楚,但还不太知道“这是什么”;
- 深层特征语义强、感受野大,却经过多次下采样,不太知道“它精确在哪里”。
大目标即使落到低分辨率特征图上仍然占据很多格子,深层语义足以识别;小目标在 stride 32 的特征图上可能只剩一个点,甚至在下采样中完全消失。多尺度问题的本质,是语义强度与空间分辨率分布在网络的不同深度。
2. FPN 之前的几种办法分别缺什么
2.1 图像金字塔:准确,但计算昂贵
最直接的方法是把同一张图片缩放成多个尺寸,每个尺寸都单独运行一遍完整网络。小物体在放大的图片中变得容易识别,大物体则可以在缩小的图片中处理。
这种方法的优点是每个尺度都能获得完整的深层语义;缺点也很明显:backbone 被重复计算多次,推理时间和显存开销都很大。
2.2 单张深层特征图:速度快,但小目标吃亏
Faster R-CNN 等系统为了效率,通常只在一张深层特征图上做预测。深层特征适合分类,却已经丢掉大量高分辨率信息。给检测头准备不同大小的 anchor,并不能恢复 backbone 下采样时已经消失的细节。
2.3 直接使用 backbone 的多层输出:尺度有了,语义不齐
卷积网络本来就会产生由高分辨率到低分辨率的层级,因此也可以直接在多个 stage 上预测。问题是浅层与深层的语义差距很大:越靠近输入的层越擅长描述纹理和边缘,却未必能稳定表示目标类别。
所以 FPN 要解决的不是“怎样制造不同大小的特征图”——backbone 早就有这些特征图——而是:
能否只计算一次 backbone,再把深层的强语义传回高分辨率层,使每一个尺度都适合做预测?
3. FPN 的核心贡献究竟是什么
FPN 的核心设计由三部分组成:
- Bottom-up pathway:正常运行 backbone,获得 $C_2,C_3,C_4,C_5$;
- Top-down pathway:从最深层开始,逐级上采样,把强语义送向更高分辨率;
- Lateral connection:把上采样结果与同尺寸的 backbone 特征横向融合。
它的改变很克制:没有重新设计复杂 backbone,也没有对多个输入尺度重复计算。它只是重新组织 backbone 已经计算出的层级,让“深层语义”和“浅层定位”在每个尺度相遇。
这也是 FPN 论文最重要的贡献:用很小的额外开销,在单尺度输入、单次 backbone 前向中,构造出所有层都具有强语义的特征金字塔。原论文进一步强调,这些金字塔层不是只为恢复一张最高分辨率输出,而是可以分别承担不同尺度的预测任务。
4. 标准 FPN 的完整工作流程
以 ResNet backbone 为例,FPN 取每个 stage 最后一个 block 的输出:
\[\{C_2,C_3,C_4,C_5\},\qquad \operatorname{stride}=\{4,8,16,32\}\]4.1 Bottom-up:backbone 先产生天然的尺度层级
以 ResNet-50 为例,四个 stage 的输出可以写成:
| 特征 | PyTorch 形状 | stride | 每个网格大致对应的输入区域 |
|---|---|---|---|
| $C_2$ | $[B,256,H/4,W/4]$ | 4 | 细,适合定位 |
| $C_3$ | $[B,512,H/8,W/8]$ | 8 | 开始形成局部结构 |
| $C_4$ | $[B,1024,H/16,W/16]$ | 16 | 语义较强 |
| $C_5$ | $[B,2048,H/32,W/32]$ | 32 | 感受野最大、空间最粗 |
这条从 $C_2$ 走到 $C_5$ 的普通前向过程,就是论文所说的 bottom-up pathway。它没有增加额外结构,只是在命名 backbone 已经存在的计算路径。
这里有两个不匹配:相邻层高、宽不同,通道数也不同。例如 $C_4$ 是 1024 通道,而 $C_5$ 是 2048 通道;如果想让两层逐元素相加,必须同时完成空间和通道对齐。
4.2 Lateral projection:用 1×1 卷积统一通道
不同 stage 的通道数通常不同,例如 ResNet-50 的 $C_2$ 到 $C_5$ 可以是 256、512、1024、2048 通道。逐元素相加要求通道一一对应,所以先做横向投影:
\[L_i=\operatorname{Conv}_{1\times1}(C_i),qquad L_i\in\mathbb{R}^{H_i\times W_i\times d}\]原始 FPN 将所有层统一为 $d=256$。这里的 1×1 卷积不是简单地“截掉多余通道”。在任意空间位置 $(x,y)$,它都在做一个可学习的线性投影:
\[\mathbf l_i(x,y)=W_i\mathbf c_i(x,y)+\mathbf b_i\]如果输入是 1024 通道、输出是 256 通道,那么 $W_i\in\mathbb R^{256\times1024}$。它学习怎样把每个 stage 自己的通道语义翻译到统一的 FPN 特征空间,同时不改变高和宽。
所以 1×1 卷积有两个作用:
- 让横向分支与 top-down 分支都变成 256 通道,满足逐元素相加的形状要求;
- 对 backbone 特征重新组合,而不是把两套含义完全不同的通道生硬地对齐。
4.3 Top-down:从最深层开始逐级上采样
最粗的一层直接由 $C_5$ 投影得到:
\[M_5=L_5\]然后递归计算:
\[M_i=L_i+\operatorname{Up}_2(M_{i+1}),qquad i=4,3,2\]$\operatorname{Up}_2$ 把高、宽各放大 2 倍,使它与 $L_i$ 的空间尺寸一致。原始论文为了简单使用最近邻插值,再做逐元素相加。
需要特别注意:上采样只扩大网格,不会凭空恢复细节。一张 32×32 的深层特征被复制成 64×64 后,空间坐标变多了,但其中并没有重新出现下采样时丢失的边缘。它更像把一张“语义明确但模糊”的小图放大。
真正补回当前尺度空间信息的是横向连接 $L_i$。因此:
\[M_i(x,y)= \underbrace{L_i(x,y)}_{\text{当前尺度的空间线索}} + \underbrace{\operatorname{Up}_2(M_{i+1})(x,y)}_{\text{更深层传来的语义}}\]top-down 负责把“它是什么”送回来,lateral connection 负责告诉它“更准确地在哪里”。两条路径缺一不可:只上采样会得到放大的粗特征,只用 lateral features 又会回到浅层语义不足的问题。
为什么是相加而不是拼接?相加计算简单、内存开销小,而且两边已经通过 1×1 卷积对齐到相同通道。它迫使每个输出通道同时综合“当前尺度的定位信息”和“更深层传来的语义信息”。拼接当然也能实现融合,但会增加通道和后续计算,并不是原始 FPN 的设计。
4.4 为什么经典 FPN 选择相加而不是拼接
假设 lateral 与 top-down 分支都是 256 通道:
- 相加后仍是 256 通道,不需要额外扩大内存和预测头;
- 拼接后会变成 512 通道,通常还要再加卷积压回 256 通道;
- 相加要求两边在同一通道空间表达可兼容的信息,这也是前面 1×1 投影存在的意义。
相加并不意味着两边在概念上永远同等重要,只表示经典 FPN 使用了最简洁的无参数融合。后来的 BiFPN 会给不同输入增加可学习权重,正是在放宽这一假设。
4.5 用 3×3 卷积生成最终输出
原始 FPN 在每个融合结果后再接一层 3×3 卷积:
\[P_i=\operatorname{Conv}_{3\times3}(M_i)\]论文给出的目的,是减轻上采样带来的混叠效应。逐元素相加只融合同一个坐标的两个向量,而 3×3 卷积还会重新观察它周围的 3×3 邻域,因此也能整理最近邻复制形成的块状模式。
原论文还特意说明,在这些额外卷积层后没有加入非线性激活。FPN 在这里更像一个保持信息连续的特征重组器:先让横向特征与 top-down 特征直接相加,再交给下游任务头学习如何使用。
最后得到 $P_2,P_3,P_4,P_5$:它们分别与 $C_2,C_3,C_4,C_5$ 同尺寸,但都具有统一的 256 通道和更接近的语义强度。这里最好区分两个符号:
- $M_i$:top-down 递归中的中间融合特征,还要继续向更高分辨率传播;
- $P_i$:经过 3×3 卷积整理后,真正交给任务头的金字塔输出。
有些实现会复用变量名,看起来没有区分 $M_i$ 和 $P_i$,但理解论文时把两者分开更清楚。
4.6 用 1024×1024 输入完整走一遍
假设输入为 1024×1024,ResNet backbone 产生:
C2: [B, 256, 256, 256]
C3: [B, 512, 128, 128]
C4: [B, 1024, 64, 64]
C5: [B, 2048, 32, 32]
从最深层开始:
M5 = Conv1×1(C5) → [B, 256, 32, 32]
M4 = Conv1×1(C4) + Up(M5, 64×64) → [B, 256, 64, 64]
M3 = Conv1×1(C3) + Up(M4, 128×128) → [B, 256, 128, 128]
M2 = Conv1×1(C2) + Up(M3, 256×256) → [B, 256, 256, 256]
最后分别施加 3×3 卷积:
P2: [B, 256, 256, 256] stride 4
P3: [B, 256, 128, 128] stride 8
P4: [B, 256, 64, 64] stride 16
P5: [B, 256, 32, 32] stride 32
这里“金字塔”指的是空间尺寸逐级减半,而不是通道数逐级减少。恰恰相反,FPN 通常把每层通道统一,方便同一个预测头跨尺度复用。
工程实现里,最好把上采样目标直接写成 lateral feature 的实际尺寸,而不是永远假设 scale_factor=2。输入尺寸不是 32 的整数倍时,连续下采样可能出现 25→13 这样的奇数尺寸;13×2 得到 26,却未必能与 25×25 的横向特征相加。显式指定 size=lateral.shape[-2:] 更稳妥。
4.7 为什么最后连 $P_2$ 也有较强语义
把递归关系展开就能看见答案。记 $L_i=\operatorname{Conv}_{1\times1}(C_i)$,忽略书写中的重复上采样:
\[M_2=L_2+\operatorname{Up}\left( L_3+\operatorname{Up}\left( L_4+\operatorname{Up}(L_5) \right)\right)\]所以 $P_2$ 并不是把浅层 $C_2$ 单独拿来预测。它直接保留 $C_2$ 的高分辨率线索,又间接接收了 $C_3,C_4,C_5$ 的信息。所谓“高分辨率层拥有强语义”,说的正是这条递归的信息路径,而不是上采样本身产生了新语义。
同样也不要把它机械地理解成“浅层只提供细节、深层只提供类别”。所有分支传递的都是学习到的特征向量;“定位”和“语义”只是对它们统计倾向的解释,最终哪些通道被保留、怎样相加,都由训练目标共同决定。
4.8 把公式对应到 PyTorch 前向过程
忽略模块初始化,标准 FPN 的 forward 基本就是公式的逐行翻译:
def forward(self, c2, c3, c4, c5):
# lateral2...5 都是 1×1 卷积,输出统一为 256 通道
m5 = self.lateral5(c5)
m4 = self.lateral4(c4) + F.interpolate(
m5, size=c4.shape[-2:], mode="nearest"
)
m3 = self.lateral3(c3) + F.interpolate(
m4, size=c3.shape[-2:], mode="nearest"
)
m2 = self.lateral2(c2) + F.interpolate(
m3, size=c2.shape[-2:], mode="nearest"
)
# output2...5 是带 padding=1 的 3×3 卷积,不改变 H、W
return {
"p2": self.output2(m2),
"p3": self.output3(m3),
"p4": self.output4(m4),
"p5": self.output5(m5),
}
这段代码里最容易写错的并不是卷积,而是上采样尺寸。使用下一层 lateral feature 的 shape[-2:] 作为目标,比连续假设恰好放大两倍更能适应奇数尺寸输入。
5. 金字塔输出怎样解决大小目标
FPN 让不同大小的目标在不同层上接受预测:
- $P_2$ 分辨率最高,适合小目标和精细边界;
- $P_3$、$P_4$ 处理逐渐变大的目标;
- $P_5$ 感受野最大,更适合大目标;
- RPN 中还可以从 $P_5$ 下采样得到 $P_6$,覆盖更大的 anchor。
在原论文的 RPN 实验中,不同 pyramid level 分别使用 32、64、128、256、512 像素尺度的 anchor,预测头参数在各层共享。共享参数能够工作,本身也说明 FPN 把不同层拉到了较接近的语义空间。
5.1 RPN:每一层在自己的网格上寻找候选框
在 Faster R-CNN + FPN 中,RPN 会分别扫过 $P_2$ 到 $P_6$。每个位置都预测两件事:这里有没有目标,以及 anchor 应怎样平移和缩放。小 anchor 放在稠密的高分辨率层,大 anchor 放在稀疏的低分辨率层:
| 金字塔层 | stride | 论文中的 anchor 尺度 |
|---|---|---|
| $P_2$ | 4 | 32 像素 |
| $P_3$ | 8 | 64 像素 |
| $P_4$ | 16 | 128 像素 |
| $P_5$ | 32 | 256 像素 |
| $P_6$ | 64 | 512 像素 |
这里的关键不是“每层只认识一种尺寸”,而是让目标在负责它的特征图上占据合理数量的网格。20×20 的小目标放到 stride 32 层,宽高都不足一个网格;放到 stride 4 层,大约还能覆盖 5×5 个位置,结构信息明显更完整。
5.2 RoI Head:根据候选框面积选择读取哪一层
在 Fast R-CNN 中,宽为 $w$、高为 $h$ 的 RoI 会按照面积分配到合适层级:
\[k=\left\lfloor k_0+\log_2\left(\frac{\sqrt{wh}}{224}\right)\right\rfloor\]直觉上,目标缩小一半,就转到分辨率高一倍的金字塔层。这样每个目标在对应特征图上都能占据比较合适的面积。
层级确定后,RoIAlign 只从对应的 $P_k$ 提取固定尺寸特征,再交给分类和边框回归 head。也就是说,FPN 不负责决定最终类别;它负责给每个尺寸的目标准备合适的“观察底图”。
5.3 单阶段检测器:直接在每层做密集预测
RetinaNet、FCOS 这类单阶段检测器不先生成 RoI,而是直接在每个金字塔层的所有位置预测分类和边框。不同实现会用 anchor 尺度或回归距离区间把目标分配到不同层,但原则相同:
高分辨率层用更多网格描述小目标,低分辨率层用更大感受野描述大目标。
这也解释了 FPN 为什么必须保留一整组 $P_i$,而不是最终只输出分辨率最高的 $P_2$。
6. 这项工作的贡献和边界
FPN 论文并不是第一个使用多尺度、上采样或 skip connection 的工作。它真正把几件事组合成了一个简洁而通用的方案:
- 计算复用:只输入一个图像尺度,复用 backbone 内部天然产生的层级;
- 语义回流:通过 top-down pathway,把深层语义逐级传播到高分辨率层;
- 定位补充:通过 lateral connection,保留浅层精确的空间结构;
- 多层预测:不是只恢复一张精细特征图,而是在所有 pyramid level 上分别服务不同尺度目标;
- 接口统一:各层使用相同通道数,使检测头可以共享。
原论文在强单尺度 Faster R-CNN baseline 上报告:RPN 的 AR 提升 8.0 个点,小目标 AR 提升 12.9 个点,目标检测的 COCO AP 提升 2.3 个点。这些消融结果说明,收益不只是来自“多放几层特征”,top-down 的语义增强尤其重要。
FPN 也不是凭空恢复细节。若一个极小目标在 backbone 的浅层就已经不可分辨,后续融合无法创造不存在的信息;上采样本身也只是扩大网格,不会自动增加真实分辨率。FPN 的能力来自保留尚未丢失的高分辨率线索,并用深层语义解释这些线索。
经典 FPN 还有几个明确的局限:
- 信息主要单向传播:语义从 $P_5$ 向 $P_2$ 传播,但低层定位信息没有一条同样直接的路径重新回到高层;
- 融合默认等权:$L_i+\operatorname{Up}(M_{i+1})$ 没有显式学习两条分支各占多大权重;
- 跨层对齐很粗:最近邻上采样后直接相加,默认同一网格坐标能够自然对应;
- 无法挽救已消失的小目标:如果早期下采样已经抹掉目标,后面的 top-down pathway 只能传语义,不能重建原始证据。
后续工作大多围绕这些边界继续改造:PANet/PAFPN 增加 bottom-up 路径增强,让定位信息更快返回深层;BiFPN 使用双向连接和可学习融合权重,并允许多次堆叠;NAS-FPN 则通过架构搜索寻找更复杂的跨尺度连接。它们改变的是“怎样融合”,而 FPN 奠定的 backbone→neck→多尺度 head 接口仍然保留下来。
8. SAM 2 中的 FPN 是什么样的
SAM 2 的图像编码器可以概括为:
图像 → Hiera trunk → FpnNeck → 图像特征 / 高分辨率特征
Hiera 是一个分层视觉 Transformer。与保持单一 patch 分辨率的普通 ViT 不同,它像 CNN 一样分 stage 降低空间分辨率、增加通道,因此天然输出 stride 4、8、16、32 的四级特征。这正好给 FPN neck 提供了多尺度输入。
以 SAM 2.1 的 Hiera-L 和 1024×1024 输入为例,trunk 送给 neck 的四层大致是:
| Hiera 输出 | 原始通道 | 空间尺寸 | stride |
|---|---|---|---|
| Stage 1 | 144 | 256×256 | 4 |
| Stage 2 | 288 | 128×128 | 8 |
| Stage 3 | 576 | 64×64 | 16 |
| Stage 4 | 1152 | 32×32 | 32 |
这和 ResNet 的 $C_2$ 到 $C_5$ 扮演相似角色,只是特征提取单元从卷积 block 换成了分层 Transformer block。FpnNeck 首先用四个独立的 1×1 卷积把 144、288、576、1152 通道都投影为 d_model=256。
但 SAM 2 没有照搬标准 FPN 的 P2–P5 全层 top-down 融合,也没有在每个尺度上放检测头。论文和官方配置显示,它按下列方式使用这些特征:
- Hiera Stage 4 的 stride 32 深层特征先投影到 256 通道;
- 将它上采样 2 倍,与 Stage 3 的 stride 16 横向特征相加;
- 得到的 stride 16、256 通道特征成为主要 image embedding,进入 memory attention;
- Stage 1 和 Stage 2 的 stride 4、8 特征只做横向通道投影,不接收 top-down 融合;
- 这两张高分辨率特征图作为 skip features 加入 mask decoder 的上采样层,帮助恢复边界细节;
- stride 32 的输出完成融合后被丢弃,不作为最终
backbone_fpn输出。
因此它真正的 top-down 融合只有一跳:
\[F_{16}=\operatorname{Conv}_{1\times1}(C_{16})+ \operatorname{Up}_2\left(\operatorname{Conv}_{1\times1}(C_{32})\right)\]而两个高分辨率旁路是:
\[F_8=\operatorname{Conv}_{1\times1}(C_8),\qquad F_4=\operatorname{Conv}_{1\times1}(C_4)\]它们没有继续与 $F_{16}$ 相加。这个区别非常重要:如果只看类名 FpnNeck,很容易误以为 stride 32 的语义会像经典 FPN 一样一路传到 stride 4;官方循环中的重置逻辑明确阻止了这件事。
9. 对照 SAM 2 官方代码
在 SAM 2.1 Hiera-L 配置中,可以看到:
neck:
d_model: 256
backbone_channel_list: [1152, 576, 288, 144]
fpn_top_down_levels: [2, 3]
fpn_interp_model: nearest
image_encoder:
scalp: 1
backbone_channel_list 按 top-down 处理顺序列出 Hiera-L 四个 stage 的通道数。每一级先用 1×1 卷积变成 d_model=256。fpn_top_down_levels: [2, 3] 表示只有对应 stride 16 和 32 的层参与 top-down 传播;stride 4 和 8 只保留各自的 lateral features。
这里的 level 编号仍按 Hiera 正向输出列表计算:0、1、2、3 分别对应 stride 4、8、16、32;代码只是通过 reversed(range(len(xs))) 从 level 3 倒着处理。第一次处理 level 3 时还没有 prev,所以只是建立 stride 32 的起点;处理 level 2 时才真正发生一次“上采样 + 相加”。
官方 FpnNeck.forward 的核心逻辑可以简化为:
prev = None
for level in reversed(levels): # 从低分辨率走向高分辨率
lateral = conv1x1(backbone[level]) # 统一到 d_model
if level in top_down_levels and prev is not None:
prev = lateral + interpolate(prev, scale_factor=2)
else:
prev = lateral
output[level] = prev
这个 else 很关键。当循环走到 stride 8 和 stride 4 时,prev 会重新变成当前层的 lateral feature,而不是继续把 stride 16 的语义一路向上传。因此,SAM 2 的高分辨率旁路并不是经典 FPN 那种“所有层都被深层语义增强”的 P2、P3。
把循环按实际形状展开,会更直观:
level 3 / stride 32:
1152×32×32 --1×1--> 256×32×32
prev 为空,不融合
level 2 / stride 16:
576×64×64 --1×1--> 256×64×64
prev: 256×32×32 --nearest up--> 256×64×64
两者相加 → 256×64×64 主 image embedding
level 1 / stride 8:
288×128×128 --1×1--> 256×128×128
不在继续融合的路径上,prev 重置为当前 lateral feature
level 0 / stride 4:
144×256×256 --1×1--> 256×256×256
同样只保留当前 lateral feature
配置中的 scalp: 1 会删除最低分辨率的最后一级输出。对于 1024×1024 输入,最终主要保留:
stride 4 : [B, 256, 256, 256] → mask decoder 高分辨率旁路
stride 8 : [B, 256, 128, 128] → mask decoder 高分辨率旁路
stride 16: [B, 256, 64, 64] → image embedding / memory attention
SAM 2 的 FpnNeck 还与原始 FPN 有两个实现差异:代码注释明确写着去掉了各层最后的 output convolution,也就是经典 FPN 的 3×3 平滑卷积;SAM 2.1 配置使用最近邻插值,并为每个输出特征生成正弦位置编码。
随后,SAM2Base.forward_image 会把 stride 4 和 stride 8 两层预先投影成 mask decoder 需要的通道数,并把它们作为 high_res_features 保存;stride 16 则成为视觉主干特征,展平后进入 memory attention,与当前提示和历史视频记忆交互。也就是说,neck 输出的三层并不是交给三个相同的预测头,而是被分流到两个完全不同的计算预算中。
10. 为什么 SAM 2 要这样改
SAM 2 的目标不是在四个尺度上分别检测大小目标,而是同时满足两种下游需求:
- 记忆与语义建模需要紧凑特征:stride 16 的 64×64 特征足够保留空间结构,又能控制视频 memory attention 的 token 数量;
- 掩码边界需要高分辨率细节:stride 4 和 8 特征直接送入 mask decoder,可以在上采样时补回轮廓、细小结构和局部定位信息。
如果把 stride 4 特征全部送进视频记忆注意力,token 数会比 stride 16 多 16 倍,时间和显存代价会非常高;如果只保留 stride 16,又容易让最终掩码边缘过粗。SAM 2 的设计因此把“深层语义与时序记忆”和“高分辨率空间细节”分给不同路径处理。
从这个角度看,SAM 2 使用 FPN 思想的地方,不只是出现了上采样和横向相加,而是延续了 FPN 最核心的原则:不同深度的特征各有所长,neck 的任务是按照下游需求重新分配语义与分辨率。
11. 最后总结
现在再看 FPN,我会把它概括成下面几句话:
- backbone 天然产生多尺度特征,但浅层定位准、语义弱,深层语义强、定位粗;
- FPN 用 top-down pathway 传递深层语义,用 lateral connection 保留当前尺度细节;
- 1×1 卷积负责通道对齐,2× 上采样负责空间对齐,相加完成融合,原始 FPN 再用 3×3 卷积平滑;
- 它的贡献不是“发明金字塔”,而是以很小额外成本构造语义一致、可分别预测的多尺度特征;
- SAM 2 只在 stride 32→16 之间做 top-down 融合,stride 4/8 则作为 mask decoder 的高分辨率旁路;
- 所以 SAM 2 的
FpnNeck是对 FPN 原理的任务化改造,而不是经典检测 FPN 的原样复制。