↓ Skip to main content
  1. Posts/

从 U-Net 到 FPN:重新理解多尺度特征融合

·4614 words·10 mins
Note: This article is available in Chinese only. 本文暂无英文版本。 View original

DDPM 的噪声预测网络到底是什么
#

前两篇文章在推导 DDPM 的前向扩散和反向去噪时,一直在用一个符号 \(\epsilon_\theta(x_t, t)\) 来表示噪声预测网络——给一张带噪图片和当前 timestep,输出对噪声的预测。

当时关注的是训练目标和数学推导,网络架构直接跳过了。但推完公式之后,一个很自然的问题冒出来:\(\epsilon_\theta\) 到底是什么网络?

去查 DDPM 原始论文(Ho et al., 2020),发现作者用的是 U-Net。

我对 U-Net 并不熟悉。之前知道它是一个用于医学图像分割的网络,发表于 2015 年(Ronneberger et al., MICCAI 2015),但从没仔细看过它的结构。一个 2015 年为分割任务设计的网络,怎么到了 2020 年被拿来做图像生成?

带着这个疑问去看 U-Net 的结构,看完之后产生了另一个更大的困惑——后面会讲到。先从一个更基本的问题开始:如果普通 CNN 已经能提取图像特征,为什么还需要 U-Net 这种 U 形架构?

语义信息和空间细节的矛盾
#

之前重温 CNN 时梳理过,CNN 通过多级卷积和下采样逐步扩大感受野、提取更高层次的语义特征。以一张 256×256 的输入图像为例,经过多级处理后 feature map 的空间尺寸会逐步缩小:

1256×256 → 128×128 → 64×64 → 32×32 → 16×16

深层 feature map 的感受野更大,倾向于编码更抽象的语义信息——比如"这块区域像一只猫"。浅层 feature map 空间分辨率更高,保留了更多精细的位置和边缘细节。

对于图像分类来说,只需要最终判断"这张图是什么",用深层语义就够了——空间细节丢了也无所谓,反正不需要定位。

但语义分割不一样。它需要给每个像素都分配一个类别标签。噪声预测也是一样——DDPM 里 \(\epsilon_\theta\) 的输出和输入 \(x_t\) 形状完全相同,每个位置都要给出预测。这类任务统称为 dense prediction(密集预测)。

做 dense prediction 就卡在一个矛盾上:下采样让网络获得了更强的语义理解能力,但同时丢掉了精确定位所需要的空间分辨率。 如果只用最深层的 feature map 做预测,语义够了但分辨率太低;如果只用浅层的 feature map,分辨率够了但语义太弱。

这不是一个非此即彼的选择。需要的是一种方法,能同时用上深层的语义和浅层的细节。

U-Net 的解法
#

U-Net 的结构可以拆成三部分:Encoder(编码器)、Bottleneck(瓶颈层)和 Decoder(解码器)。

Encoder 就是一条标准的 CNN 下采样路径。每一级做若干卷积后接 MaxPool,空间尺寸减半,channel 数加倍。按一个简化的例子来看维度变化:

1Encoder:
2256×256×64 → 128×128×128 → 64×64×256 → 32×32×512
3
4Bottleneck:
516×16×1024

Bottleneck 是分辨率最低、channel 最多的那一层。到这里为止,和普通分类网络没有区别。

关键在 Decoder。Decoder 走的是一条和 Encoder 对称的上采样路径,逐级恢复空间分辨率:

1Decoder:
232×32×512 → 64×64×256 → 128×128×128 → 256×256×64
3
4Output:
51×1 Conv → 256×256×C(C 是类别数或输出通道数)
U-Net 完整结构:左侧 Encoder 下采样,右侧 Decoder 上采样,绿色虚线为 Skip Connection

上面的维度是为了便于理解选的示例值。2015 年原始 U-Net 使用的是 valid padding(无填充卷积),每次卷积后 feature map 尺寸会略微缩小,还需要在 skip connection 时做 crop 对齐。现代实现普遍改用 same padding,这里就不展开原版的裁剪细节了。

为什么 Upsampling 不够
#

到这里可能会想:Decoder 已经通过 upsampling 把分辨率恢复了,直接输出不就行了?

问题在于,upsampling(无论是转置卷积还是双线性插值后接卷积)只能恢复 feature map 的空间尺寸,但无法凭空恢复下采样过程中已经丢掉的细节。16×16 的 feature map 里根本就没有精确的边缘位置信息了,放大回 256×256 也只是把模糊的东西放大了而已。

所以 U-Net 加了一个关键机制:Skip Connection(跳跃连接)。

每一级 Decoder 在做 upsampling 之后,会把 Encoder 中对应尺度的 feature map 拿过来,沿 channel 维度拼接(concat),再做卷积融合。

Skip Connection 融合过程:Encoder 同级特征与 Decoder 上采样特征沿 Channel 拼接后卷积

Encoder 的 feature map 保留着原始分辨率下的空间细节。Decoder 的 feature map 经过深层处理,携带了更强的语义信息。Skip connection 让两者直接融合——Decoder 不需要从头"猜"丢掉的细节,因为 Encoder 已经把这些信息传过来了。

Skip connection 的作用不是"传递梯度"或"让网络更深"——那是 ResNet 里 residual connection 做的事。U-Net 的 skip connection 是在不同抽象层级之间搭桥,让高分辨率的空间细节和高层次的语义信息在同一个 feature map 里汇合。

这里值得区分一下:ResNet 的 residual connection 发生在同一分辨率下,用的是逐元素加法(add),核心作用是缓解深层网络的梯度问题,让网络"至少不比浅层差"。U-Net 的 skip connection 跨越了不同的分辨率层级,用的是 channel 拼接(concat),核心作用是把下采样过程中丢掉的空间信息直接补回来。两者都体现了"直接传递已有信息"的思想,但解决的问题和实现方式都不同。关于 residual connection 的详细讨论,可以参考之前关于 Transformer Block 的笔记。

我看到 U-Net 后的第一反应
#

看完 U-Net 的结构之后,我脑子里冒出的第一个想法是:

这不就是 FPN 吗?

以前在商汤做 CV 的时候,Faster R-CNN、RetinaNet、FCOS 这些目标检测网络天天打交道,FPN(Feature Pyramid Network,特征金字塔网络)是几乎所有检测模型的标配 backbone。之前整理目标检测架构演进的时候也写过 FPN 解决的问题——把深层的 strong semantics 通过 top-down pathway 传回高分辨率的 feature map。以前的 FPN 学习笔记里还有 PyTorch 代码实现。

对比两者的核心结构:

  • Bottom-up pathway(自底向上路径):输入图像经过逐级卷积和下采样,产生多个尺度的 feature map。U-Net 里叫 Encoder,FPN 里就是 backbone 的正向 forward。
  • Top-down pathway(自顶向下路径):从最深层开始逐级 upsample,恢复空间分辨率。U-Net 里叫 Decoder,FPN 里也是这个路径。
  • Lateral connection(横向连接):将 bottom-up 路径中某一级的特征与 top-down 路径中对应尺度的特征进行融合。U-Net 里叫 skip connection,FPN 里叫 lateral connection。
U-Net 与 FPN 架构对比:左侧 U-Net 使用 concat 融合并输出单个全分辨率结果,右侧 FPN 使用 add 融合并在多个尺度输出

我当时跟人讨论这个观察,产生了几个质疑:

FPN 不也有不同尺度的特征吗?U-Net 用于分割,FPN 用于检测——这不就是下游 head 不同吗?Concat 和 add 只是融合操作的具体实现,这能算本质上的架构区别吗?

逐个想下来,这些质疑其实都有道理。U-Net 和 FPN 在宏观拓扑上确实高度相似。两者都是:先通过下采样提取多尺度特征,再通过上采样将深层语义传回浅层,同时用横向连接融合对应尺度的特征。

可以用一个简单的公式概括两者共同的递推结构:

$$ D_i = \operatorname{Fuse}(E_i, \operatorname{Up}(D_{i+1})) $$

\(E_i\) 是编码器/backbone 在第 \(i\) 级的特征,\(D_{i+1}\) 是更深一层的融合结果,\(\operatorname{Up}\) 是上采样,\(\operatorname{Fuse}\) 是特征融合(U-Net 中通常是 concat,FPN 中通常是逐元素 add)。

这不是哪一篇论文规定的精确公式,而是对两者共有的架构思想所做的抽象。具体到每个网络,融合方式、输出接口和模块边界有实际差异:U-Net 最终只在最高分辨率层输出,所有中间层服务于最终的全分辨率预测;FPN 则在多个尺度上都产生输出(P2 到 P5),每个尺度都直接用于检测。但这些差异不应该掩盖它们在宏观网络拓扑上的高度相似性。

FPN 原论文怎么看这种相似性
#

U-Net 发表于 2015 年。FPN 的预印本首次出现在 2016 年 12 月,正式论文发表于 CVPR 2017。

看到两者的相似性后,我去翻了 FPN 的原始论文(Lin et al., Feature Pyramid Networks for Object Detection, CVPR 2017),想看看作者是否讨论过这个问题。

确实讨论了。FPN 论文的 Figure 2 对比了几种利用多尺度特征的架构,其中包括一种"先自底向上再自顶向下、并带有横向连接"的结构。论文的 Related Work 部分也引用了 U-Net,将其归入带有 skip connection 的 encoder-decoder 架构。

但这里需要严谨区分几件事:

  • FPN 论文引用了 U-Net,这是事实。
  • FPN 使用了与 U-Net 相似的 top-down + lateral connection 架构思想,这也是事实。
  • 但仅凭时间先后和引用关系,不能直接断言 FPN 的设计灵感来自 U-Net。类似的 top-down + skip 结构在 U-Net 之前就已经存在,FPN 论文 Figure 2 讨论的也不仅仅是 U-Net。

更值得关注的是 FPN 自身的贡献。FPN 要解决的问题是:目标检测需要在多个尺度上同时做预测(因为 object 大小差异很大),传统做法是构建图像金字塔——把同一张图 resize 成不同分辨率,每个分辨率都跑一遍 backbone,计算开销成倍增加。FPN 的思路是利用 CNN 自身已有的多尺度特征层级,通过 top-down pathway 和 lateral connection 在计算开销几乎不增加的情况下,构建一组语义都很强的多尺度 feature map。

一个已经存在的架构思想,在新的任务场景中经过重新组织和设计,仍然可以是重要的创新。 FPN 的价值不在于 top-down + skip 这个拓扑结构本身,而在于把这个结构高效适配到了多尺度目标检测的需求上。

回到 DDPM——为什么扩散模型使用 U-Net
#

理解了 U-Net 的结构之后,回到最初的问题:DDPM 的噪声预测网络为什么选 U-Net?

噪声预测 \(\epsilon_\theta(x_t, t)\) 接受一张带噪图片 \(x_t\),输出对噪声的预测。输出张量和输入 \(x_t\) 形状完全相同——每个空间位置都要给出预测值。这就是一个 dense prediction 任务。

U-Net 在 DDPM 中的位置:接收带噪图像和 timestep embedding,输出与输入同形状的噪声预测

从这个角度看,U-Net 非常适合:它能结合大尺度结构信息与局部细节,保留空间对应关系,输出和输入尺寸一致。多尺度特征融合为噪声预测提供了有效的归纳偏置——去噪既需要理解图片的整体结构(“这里应该是天空”),也需要处理精细的纹理和边缘。

不过,DDPM 中使用的 U-Net 和 2015 年的原始 U-Net 有明显差异。扩散模型中的 U-Net 通常会加入几个关键扩展:

  1. Timestep Embedding:通过 sinusoidal embedding 将时间步 \(t\) 编码成向量,注入到每个 residual block 中,告诉网络当前噪声有多重。
  2. Residual Block:用 ResBlock 替代原始 U-Net 中的简单卷积块,改善深层特征处理和梯度传播。
  3. Attention:在部分分辨率层加入 self-attention,帮助建模空间上较远位置之间的关联。部分模型(如用于条件生成的 Stable Diffusion)还通过 cross-attention 接收文本等条件信息。

需要注意的是,U-Net 不是扩散模型的唯一选择。后来出现的 DiT(Diffusion Transformer)用 Transformer 替代了 U-Net 作为去噪骨干网络。Latent Diffusion 则把扩散过程从原始像素空间移到了更低维的潜空间,U-Net 在潜空间里工作。架构在不断演化,U-Net 只是这条路上的一个重要节点。

写在最后
#

这次的学习路径挺有意思:最初只是想搞清楚 DDPM 里 \(\epsilon_\theta\) 到底是什么网络,去看了 U-Net 的结构。看完之后立刻想到了自己过去熟悉的 FPN——两者的宏观拓扑几乎一致。翻 FPN 原论文进一步确认了这种关联。

这个过程里最有价值的认识可能是:多尺度特征融合——先下采样提取语义,再上采样恢复分辨率,同时用横向连接补回空间细节——是一种跨任务复用的架构设计思想。 U-Net 把它用在医学图像分割上,FPN 把它用在多尺度目标检测上,DDPM 把它用在噪声预测上。核心架构思想相同,具体适配到不同任务时在融合方式、输出接口和附加模块上有各自的调整。

理解一个网络架构的设计动机,比记住它的名字和层数更有价值。下一步打算深入看 Diffusion U-Net 的具体实现——timestep embedding 怎么注入、attention 加在哪些层、不同分辨率的 channel 怎么配——但那是另一篇的事了。

参考资料
#

Related

9 年后重温 CNN:剥掉算子细节后,真正留下了什么

最近在重新过 MIT 6.S191 Lecture 3(卷积神经网络)。2017 年我刚接触 CV 那会儿,CNN 算是吃饭的家伙,每天都在调。后来精力逐渐转到 ML Infra,成天跟 GPU 显存、通信拓扑和算子优化打交道,卷积网络的很多具体细节就慢慢生疏了——写个 nn.Conv2d 时 weight 的四维形状到底怎么排、Kaiming 初始化的方差怎么推、感受野怎么算,冷不丁被问到,脑子里还得卡壳一下。

从 Q-Learning 到 Policy Gradient:为什么要直接学习策略?

·4692 words·10 mins
上一篇走完了从 Q-Learning 到 DQN 的过程。用神经网络近似 Q-Function,输出各动作的 Q 值,取 argmax 选动作。加上 Experience Replay 和 Target Network,DQN 在 Atari 游戏上工作得很好。 继续跟 MIT 6.S191 Lecture 5 往下走,课程在讲完 DQN 之后,介绍了一种完全不同的思路——不学 Q-Function,直接学 Policy。