第 5 讲 · 潜空间与神经网络架构 · Latent Spaces & Neural Network Architectures

Generative AI with Stochastic Differential Equations · Lecture 05。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

个人整理的学习笔记,非官方讲义;课程来源与整理说明见课程介绍。配套示例未逐一运行验证。

本篇目录

对应 Maps to:MIT 6.S184 Generative AI with Stochastic Differential Equations(IAP 2026),Lecture 4 · 28-Jan-2026 · Latent Spaces and Neural Network Architectures;Lab 3 中 Diffusion Transformer、VAE 与潜空间扩散的部分(原课用 MNIST,这里换成代码画出来的”像素数字”)。 阅读材料:原课讲义 Holderrieth & Erives, An Introduction to Flow Matching and Diffusion Models(2026 版,arXiv:2506.02070)第 6 章与附录 D;Lecture 4 slides;原始论文 Ronneberger et al. (2015) U-Net、Peebles & Xie (2023) DiT、Kingma & Welling (2014) VAE、Rombach et al. (2022) Latent Diffusion Models、Esser et al. (2024) SD3、Polyak et al. (2024) Movie Gen。 依据说明:本讲以原课讲义第 6 章为主线:网络的输入输出与条件注入、U-Net、DiT、为什么要潜空间、自编码器与 VAE、潜空间扩散的配方、SD3 与 Movie Gen 的案例。“直接输入 t vs 傅里叶嵌入""参数量 vs 计算量""注意力的 token 数""adaLN-Zero 的恒等初始化""同样步数下 MLP / U-Net / DiT 的比较""β 扫描""三种潜空间上的流匹配”等实验是本讲义补充的。其中”普通自编码器的潜空间反而最好生成”一条与常见的直觉不一致,§3.5 如实报告并解释。案例部分的数字来自论文本身,写成”某某报告”。 配套代码 Code:code/lecture05/(5 个脚本 + 2 个模块;PyTorch + matplotlib;每个脚本 4–112 秒,都完整运行过) 预计阅读时间:5 小时(含运行代码)


0. 本讲导航 · How to read this lecture

0.1 学习目标 Learning goals

学完这一讲,你应该能够:

  1. 说清一个流 / 扩散模型的网络要吃什么、吐什么,以及时间 tt 和提示 yy 有哪几种喂法(傅里叶时间嵌入、类别嵌入、文本编码器;加到特征上、adaLN、交叉注意力)。
  2. 画出 U-Net 和 Diffusion Transformer(DiT)的结构,追踪一张图在其中的形状变化,估算参数量和计算量,理解”参数少不等于算得快”、注意力的代价随 token 数平方增长。
  3. 解释为什么几乎所有大模型都在潜空间里生成,写出 VAE 的损失(重建 + KL)并推导两个高斯之间的 KL。
  4. 理解 VAE 的两难:KL 让潜空间”规整”、代价是重建变差;知道潜空间扩散的配方,以及”潜空间要多规整”在实践中怎样取舍。
  5. 读懂 Stable Diffusion 3、Meta Movie Gen 的系统设计,把它们拆回前五讲的组件。

0.2 先修与本讲范围

需要的背景在哪里
流匹配训练(Algorithm 3)、调度与数据尺度第 2 讲 §4、练习 7
四种参数化、换算公式第 3 讲 §2
无分类器引导(Algorithm 5)、像素数字、Fréchet 距离第 4 讲 §3–§4
nn.Module、buffer、训练循环前置讲 P2 §3
高斯的重参数化、KL 散度的定义前置讲 P1 §2.1

本讲不详细讲卷积网络和 Transformer 的一般原理(自注意力、多头、LayerNorm),只讲它们在生成模型里怎样用;需要补课可以看本仓库 CMU10-423 的相关讲次。

0.3 本讲地图 Map

§1  网络的输入与输出:时间嵌入、提示嵌入                         ← 代码 01
§2  网络架构:U-Net、DiT、条件怎样注入;形状与代价;同步数比较   ← 代码 02、03
§3  潜空间:为什么;自编码器与 VAE;β 的两难;潜空间流匹配        ← 代码 04、05
§4  案例:Stable Diffusion 3、Meta Movie Gen
§5  小结   §6  常见误区   §7  练习   §8  延伸阅读   §9  下一讲预告

0.4 记号 Notation(新增的部分)

记号意思
emb(t)\mathrm{emb}(t)时间嵌入:t↦[sin⁡(2πfkt),cos⁡(2πfkt)]kt\mapsto[\sin(2\pi f_k t), \cos(2\pi f_k t)]_k
cc条件向量(时间嵌入 + 提示嵌入),由它调制网络
pppatch(小块)边长;N=(H/p)(W/p)N = (H/p)(W/p) 是 token 数
EE,DD编码器、解码器;z=E(x)z = E(x) 是潜变量,x^=D(z)\hat x = D(z) 是重建
qϕ(z∣x)q_\phi(z\mid x)VAE 的编码器分布 N(μϕ(x),diag⁡σϕ2(x))\mathcal{N}(\mu_\phi(x), \operatorname{diag}\sigma_\phi^2(x))
β\betaVAE 损失里 KL 的权重(和调度 βt\beta_t 无关,沿用 β-VAE 的习惯写法)
kk潜变量维数

1. 网络的输入与输出 · Inputs and outputs

1.1 网络要做什么

不管用流匹配还是得分匹配、预测速度还是噪声,网络的”合同”都一样:

utθ:(x, t, y) ⟼ 与 x 同形状的输出.u_t^\theta:\quad (x,\ t,\ y)\ \longmapsto\ \text{与 } x \text{ 同形状的输出}.
  • xx:一张(带噪的)图,或者它的潜变量;
  • tt:一个数;
  • yy:提示(类别、一段文字……),或者空提示 ∅\varnothing;
  • 输出:速度(或噪声、或去噪结果),形状和 xx 完全相同。

原课讲义把网络设计归结为三个问题:怎样处理 xx 的空间结构(§2),怎样让网络感知 tt(本节),怎样注入 yy(本节和 §2.3)。前四讲一直用”把 xx、tt、yy 拼起来喂给 MLP”,在二维玩具上够用;在图像上,三个问题都要认真对待。

1.2 时间:傅里叶嵌入

tt 只是一个数。直接把它拼到输入里,网络第一层看到的 t=0.99t = 0.99 和 t=0.999t = 0.999 几乎一样,而理想的向量场在这两个时刻可能差别很大(第 2 讲:t→1t\to1 时每个峰都在迅速收紧)。标准做法是先把 tt 映射成一个高维向量:

emb(t)=[sin⁡(2πf1t),…,sin⁡(2πfKt), cos⁡(2πf1t),…,cos⁡(2πfKt)],\mathrm{emb}(t) = \big[\sin(2\pi f_1 t), \dots, \sin(2\pi f_K t),\ \cos(2\pi f_1 t), \dots, \cos(2\pi f_K t)\big],

频率 fkf_k 按几何级数排开(nets.FourierTimeEmbedding;原课 Lab 用随机的高斯频率,效果类似)。这和 Transformer 的位置编码是同一个想法。01 号脚本第 1 部分(16 维,频率 0.5–50):

时间差 Δt\Delta t0.0010.010.050.10.30.5
嵌入之间的欧氏距离0.3662.6533.7763.7963.0793.765
相对于 Δt\Delta t 的放大倍数3662657638108
嵌入的余弦相似度0.9920.5600.1090.1000.4070.114

三点:时间差 0.01 时,嵌入之间的距离被放大了两百多倍,网络第一层”看到”的差别大得多;距离只依赖于时间差(三角函数的平移性质),tt 在哪里都一样敏感;余弦相似度并不随 Δt\Delta t 单调下降(各频率分量此起彼伏),保证”远的时刻嵌入不同”的是所有分量合在一起,最低频率 0.5 让整个 [0,1][0, 1] 上的嵌入不会重复。

它真的有用吗? 01 号脚本第 2 部分在第 2 讲的五峰数据上,用同样大小的 MLP(4×64)、同样的训练(5000 步),比较”直接输入 tt“和”输入 emb(t)\mathrm{emb}(t)“,各两个随机种子(速度的相对误差):

模型t=0t = 00.30.60.90.970.99样本能量距离落在峰之间
直接输入 tt(种子 0)3.91%0.98%0.35%0.19%0.60%0.86%0.01000.4%
直接输入 tt(种子 1)2.66%0.50%0.36%0.46%1.00%1.25%0.00100.4%
傅里叶嵌入(种子 0)3.44%0.45%0.40%0.19%0.25%0.35%0.00260.8%
傅里叶嵌入(种子 1)2.69%0.35%0.31%0.09%0.20%0.28%0.00000.7%

向量场的精度上,傅里叶嵌入在 t→1t\to1 附近明显更好(t=0.99t = 0.99:0.28–0.35% vs 0.86–1.25%),正是”理想场随 tt 变化很快”的地方;t=0t = 0 附近两者差不多(那里的误差来自目标的噪声,第 2 讲 §5.3)。但样本质量没有一致的差别:两个种子之间的差距和两种做法之间的差距一样大。在这个二维小问题上,直接输入 tt 已经够用;时间嵌入在大模型里是标准做法,因为那里的网络要在很宽的噪声水平范围内表现出截然不同的行为(几乎纯噪声时要”画出大致构图”,几乎干净时要”修细节”)。

📌 代码 01:python3 01_time_embedding.py(约 20 秒;--quick 是冒烟测试)。

1.3 提示:从类别到文字

第 4 讲的提示是类别,用独热编码;本讲的网络改用 nn.Embedding(一张可训练的查找表,第 yy 行就是类别 yy 的向量,等价于”独热编码乘一个矩阵”),再加上一行给空提示 ∅\varnothing。

真实的提示是一段文字。原课讲义的说法是:用一个预训练的文本编码器把文字变成向量,生成模型本身不从头学语言。常见的两类:

  • CLIP(Radford et al. 2021):图像编码器和文本编码器在 4 亿个图文对上做对比学习,使”配对的图和文”嵌入相近。它的文本嵌入天然是”和图像对齐”的,擅长描述视觉内容。通常取一个汇总向量(pooled embedding)。
  • T5 等语言模型的编码器:只在文本上训练,理解长句、复杂的组合关系(“左边红色的球,右边蓝色的方块”)更好。输出一串 token 嵌入(每个词一个向量)。

两种嵌入的用法不同:一个汇总向量可以像 tt 一样并进条件向量 cc(§2.3 的 adaLN);一串 token 嵌入则要用注意力(交叉注意力或联合注意力)让图像的每个位置去”查阅”文字的每个词。SD3 同时用了三个文本编码器(两个 CLIP、一个 T5),把两种用法都用上(§4.1)。


2. 网络架构 · Architectures

2.1 U-Net

U-Net 最早是为医学图像分割设计的(Ronneberger et al. 2015),DDPM 把它用作去噪网络,Stable Diffusion 1/2 用的也是它。nets.TinyUNet 是一个缩小版:

输入 1×12×12 → 卷积 → [ResBlock] 12×12 ──────────────────────── 跳连 ─┐
                       ↓ 下采样(步长 2 的卷积)                          │
                     [ResBlock] 6×6 ─────────────── 跳连 ─┐               │
                       ↓ 下采样                            │               │
                     [ResBlock] 3×3(中间层)               │               │
                       ↑ 上采样 ×2 + 拼接 ←────────────────┘               │
                     [ResBlock] 6×6                                       │
                       ↑ 上采样 ×2 + 拼接 ←───────────────────────────────┘
                     [ResBlock] 12×12 → 卷积 → 输出 1×12×12
  • 编码器(下行):卷积提取局部特征,下采样让深层特征”看到”大范围的结构(这个数字整体是什么形状)。
  • 解码器(上行):上采样回到原尺寸。
  • 跳连(skip connection):把同尺寸的浅层特征直接拼到解码器上,保留细节(笔画的精确位置)。
  • 条件注入:每个残差块里,条件向量 c=MLP(emb(t))+Emb(y)c = \mathrm{MLP}(\mathrm{emb}(t)) + \mathrm{Emb}(y) 经过一个线性层变成”每个通道一个数”,加到特征图的每个像素上。真实的 U-Net 还会在低分辨率层加自注意力和对文本的交叉注意力。

卷积的两个”先验”——局部性(相邻像素一起看)和平移等变性(同一个卷积核到处用)——正适合图像,这是 U-Net 参数少却效果好的原因(§2.5)。

2.2 Diffusion Transformer(DiT)

DiT(Peebles & Xie 2023)把图像当成一串 token:

  1. Patchify:把图切成不重叠的 p×pp\times p 小块,每块展平成一个向量(12×12 的图、p=3p = 3:16 个 9 维的 token)。
  2. 嵌入:线性层映射到 dd 维,加上位置嵌入(告诉注意力每个 token 在图上的哪个位置;这里用固定的二维正弦编码)。
  3. Transformer 块:自注意力 + MLP,每块都用条件向量 cc 调制(§2.3 的 adaLN-Zero)。所有块的形状都是 (N,d)(N, d),token 的个数不变,只有内容在变。
  4. 输出:每个 token 线性地变回一个 p×pp\times p 的小块,再拼回整张图(depatchify)。

02 号脚本第 1 部分追踪了一批 4 张图的形状:U-Net 是一个”U”(通道 16 → 16 → 32 → 32,空间 12 → 12 → 6 → 3,再对称地回来;上行块的输入通道是”深层 + 浅层”之和);DiT 是 (4,144)→(4,16,9)→(4,16,64)→⋯→(4,16,64)→(4,16,9)→(4,144)(4, 144)\to(4, 16, 9)\to(4, 16, 64)\to\dots\to(4, 16, 64)\to(4, 16, 9)\to(4, 144)。

原课讲义和 slides 指出,近年的大模型(SD3、FLUX、Sora、Movie Gen)几乎都转向了 Transformer:它对不同的模态(图像、视频、文字)一视同仁,并且 Peebles & Xie 发现,DiT 的样本质量随计算量(Gflops)平稳地提升,扩展性很好。

2.3 条件怎样注入

方式做法例子
加到特征上cc 经线性层变成每个通道一个数,加到特征图上DDPM、本讲的 U-Net
adaLN(自适应层归一化)由 cc 算出 LayerNorm 之后的平移 shift 和缩放 scaleDiT
adaLN-Zero在 adaLN 的基础上,再由 cc 算出残差分支的门控 gate;产生这些参数的线性层初始化为 0DiT(效果最好)
交叉注意力图像 token 做 query,文本 token 做 key / valueSD 1/2 的 U-Net
联合注意力(MM-DiT)图像 token 和文本 token 拼成一串,一起做自注意力;两种模态各有一套权重SD3

adaLN-Zero 的一个块是这样的(nets.DiTBlock):

(shift1,scale1,gate1,shift2,scale2,gate2)=Linear(SiLU(c)),(\text{shift}_1, \text{scale}_1, \text{gate}_1, \text{shift}_2, \text{scale}_2, \text{gate}_2) = \mathrm{Linear}(\mathrm{SiLU}(c)), h←h+gate1⋅Attn(LN(h)(1+scale1)+shift1),h←h+gate2⋅MLP(LN(h)(1+scale2)+shift2).h \leftarrow h + \text{gate}_1\cdot\mathrm{Attn}\big(\mathrm{LN}(h)(1 + \text{scale}_1) + \text{shift}_1\big),\qquad h \leftarrow h + \text{gate}_2\cdot\mathrm{MLP}\big(\mathrm{LN}(h)(1 + \text{scale}_2) + \text{shift}_2\big).

“Zero”指那个线性层初始化为 0:一开始 gate = 0,每个块都是恒等映射,深层网络训练从”什么都不做”开始,更稳定。02 号脚本第 4 部分验证:未训练的 DiT 块 max⁡∣block(h,c)−h∣=0\max|\mathrm{block}(h, c) - h| = 0,整个 DiT 的输出恒为 0;对照的 U-Net 输出非零(最大 1.25),而且换一个类别标签输出就变(最大变化 0.99)——条件嵌入在初始化时就在”乱说话”。Peebles & Xie 比较了”拼接 token""交叉注意力""adaLN""adaLN-Zero”四种方式,adaLN-Zero 的效果最好、计算也最省。

2.4 形状与代价

02 号脚本第 2 部分用 PyTorch 的 FlopCounterMode 数每张图一次前向的浮点运算:

模型参数量每张图的前向 MFLOPsFLOPs / 参数
MLP(第 4 讲,512×3)679,5681.362.0
U-Net(ch = 16)128,5618.1963.7
U-Net(ch = 32)412,12932.4278.7
DiT(patch 3,16 个 token)436,9376.8015.6
DiT(patch 2,36 个 token)436,29214.6633.6
DiT(patch 1,144 个 token)435,90557.13131.1
  • 参数少 ≠ 算得快。MLP 的每个参数在一次前向里只用一次(FLOPs/参数 = 2,一次乘加);卷积核在每个像素上都用一次,U-Net(ch = 16)的参数只有 MLP 的五分之一,计算量却是它的 6 倍。
  • DiT 的计算量由 token 数决定。patch 从 3 缩到 1,参数几乎不变,计算量涨了 8 倍多。

自注意力还有一个按 token 数平方增长的部分(第 3 部分):

输入token 数注意力矩阵的元素(每个头、每层)
12×12,patch 316256
12×12,patch 114420,736
1024×1024 RGB 像素空间,patch 2262,1446.9×10106.9\times10^{10}
1024×1024 RGB 像素空间,patch 164,0961.7×1071.7\times10^7
VAE 潜空间 128×128×16,patch 24,0961.7×1071.7\times10^7

在像素空间里,1024×1024 的图用 patch 2 会有 26 万个 token,注意力矩阵根本放不下。用很大的 patch(16)也能把 token 降到 4096,但每个 token 要装 16×16×3=76816\times16\times3 = 768 个数,网络要直接生成所有像素级的细节;在 8 倍下采样的潜空间里用 patch 2,同样 4096 个 token,每个只有 2×2×16=642\times2\times16 = 64 个数。这是 §3 的伏笔。

📌 代码 02:python3 02_architectures.py(约 4 秒;耗时那一列每次运行都不同)。

2.5 同样的训练步数:MLP vs U-Net vs DiT

03 号脚本在像素数字上用同样的 CFG 训练(1500 步、批大小 64、puncond=0.1p_{\text{uncond}} = 0.1)比较三种网络(每个设置生成 500 张、欧拉 30 步;两批真实图像之间的 Fréchet 距离 0.58,样本少时 Fréchet 距离偏大,只适合在同样的样本数下比较):

网络参数量训练用时w=1w = 1 判对w=1w = 1 风格多样性w=1w = 1 Fréchetw=2w = 2 判对w=2w = 2 风格多样性w=2w = 2 Fréchet
MLP(512×3)679,5683 秒79.0%0.705.097.8%0.6121.3
U-Net(ch = 16)128,56158 秒85.8%0.891.399.0%0.733.3
DiT(patch 3)436,93719 秒85.2%0.911.398.2%0.743.8

三种网络的样本

同样的训练步数,U-Net 和 DiT 明显好于 MLP:Fréchet 距离 1.3 vs 5.0,风格多样性 0.9 vs 0.7;图上 MLP 的背景满是噪点、笔画边缘发虚,U-Net 和 DiT 的数字干净整齐。U-Net 的参数只有 MLP 的五分之一,靠的是卷积的先验;DiT 没有卷积,靠 patch + 位置嵌入 + 注意力达到差不多的效果。

代价是计算:在 CPU 上 U-Net 的训练用时是 MLP 的约 20 倍,DiT 约 6 倍。同样的”用时”,MLP 可以训练多得多的步数(第 4 讲的 MLP 看过 8 倍的样本,Fréchet 距离 2.4,生成条件不同,只能粗略对比)。在 GPU 上卷积和注意力都高度优化,大规模时 U-Net、DiT 的优势更明显。U-Net 与 DiT 在这个小问题上难分高下;“Transformer 扩展得更好”需要大得多的模型和数据才能看出来。

📌 代码 03:python3 03_train_image_flow.py(约 100 秒;--quick 是冒烟测试)。


3. 潜空间 · Latent spaces

3.1 为什么要在潜空间里生成

一张 1024×1024 的彩色图是 300 万个数。直接在这个空间里做流匹配,有三个问题:

  1. 计算:§2.4 的注意力表。
  2. 显存:每一步训练都要在这个空间里存激活值。
  3. 大部分比特人眼不在乎。Rombach et al.(2022,LDM)的观察:一张图的信息量里,绝大部分是”感知上不重要的高频细节”(纹理的精确噪点),真正决定”这是一张什么图”的语义信息只占很小一部分。在像素空间里训练生成模型,大量的容量花在了建模这些不重要的细节上。

解决办法是分工(原课讲义附录 D 的视角):先训练一个自编码器,编码器 EE 把图像压成小得多的潜变量 z=E(x)z = E(x),解码器 DD 负责还原所有细节;生成模型只在 zz 的空间里工作,专心建模”语义”。SD 1.x 把 512×512×3 的图压成 64×64×4(缩小 48 倍),SD3 把 1024×1024×3 的图压成 128×128×16(缩小 12 倍,通道更多、保留更多细节)。

3.2 普通自编码器的问题

最简单的自编码器:最小化重建误差 ∥x−D(E(x))∥2\lVert x - D(E(x))\rVert^2。它能把图压得很好,但对潜变量长什么样没有任何约束:可能尺度很大、可能挤在奇怪的低维曲面上、可能有大片空洞。原课讲义的担心是:这样的潜分布可能很难学——流模型要从 N(0,I)\mathcal{N}(0, I) 出发搬运到它。

04 号脚本把 12×12 的数字压成 k=8k = 8 维:普通自编码器的潜变量标准差约 3.0,与 N(0,I)\mathcal{N}(0, I) 的能量距离 3.68;直接从 N(0,I)\mathcal{N}(0, I) 采样再解码,得到的是一团乱麻(Fréchet 距离 112.5),因为 N(0,I)\mathcal{N}(0, I) 的大部分地方根本不是它的潜变量会出现的地方。

3.3 变分自编码器(VAE)

VAE(Kingma & Welling 2014)做两处修改:

  1. 编码器输出一个分布:qϕ(z∣x)=N(μϕ(x),diag⁡σϕ2(x))q_\phi(z\mid x) = \mathcal{N}(\mu_\phi(x), \operatorname{diag}\sigma_\phi^2(x)),训练时从中采样 z=μ+σ⊙ϵz = \mu + \sigma\odot\epsilon(重参数化,P1 §2.1:采样变成了可以求导的确定性运算加一个独立的噪声)。
  2. KL 正则:让每个 qϕ(z∣x)q_\phi(z\mid x) 都靠近先验 N(0,I)\mathcal{N}(0, I)。
LVAE=Ex[ Ez∼qϕ(z∣x)∥x−Dθ(z)∥2 + β KL(qϕ(z∣x) ∥ N(0,I))],\mathcal{L}_{\text{VAE}} = \mathbb{E}_{x}\Big[\ \mathbb{E}_{z\sim q_\phi(z\mid x)}\lVert x - D_\theta(z)\rVert^2\ +\ \beta\,\mathrm{KL}\big(q_\phi(z\mid x)\,\big\Vert\,\mathcal{N}(0, I)\big)\Big],

两个对角高斯之间的 KL 有闭式(练习 1):

KL(N(μ,diag⁡σ2) ∥ N(0,I))=12∑j=1k(μj2+σj2−1−log⁡σj2).\mathrm{KL}\big(\mathcal{N}(\mu, \operatorname{diag}\sigma^2)\,\big\Vert\,\mathcal{N}(0, I)\big) = \frac12\sum_{j=1}^k\big(\mu_j^2 + \sigma_j^2 - 1 - \log\sigma_j^2\big).

这个损失从哪来(原课讲义附录 D 给出了更多视角,这里只写最常见的一个):把解码器看成高斯 pθ(x∣z)=N(Dθ(z),s2I)p_\theta(x\mid z) = \mathcal{N}(D_\theta(z), s^2 I),对数似然的证据下界(ELBO)是

log⁡pθ(x) ≥ Eqϕ(z∣x)[log⁡pθ(x∣z)]−KL(qϕ(z∣x)∥p(z))=−E∥x−Dθ(z)∥22s2−KL+常数.\log p_\theta(x) \ \ge\ \mathbb{E}_{q_\phi(z\mid x)}\big[\log p_\theta(x\mid z)\big] - \mathrm{KL}\big(q_\phi(z\mid x)\Vert p(z)\big) = -\frac{\mathbb{E}\lVert x - D_\theta(z)\rVert^2}{2s^2} - \mathrm{KL} + \text{常数}.

乘以 2s22s^2,就是上面的 LVAE\mathcal{L}_{\text{VAE}},其中 β=2s2\beta = 2s^2。所以 β\beta 就是”假设解码器有多少噪声”:β=1\beta = 1 相当于假设每个像素有标准差 s=0.71s = 0.71 的噪声——对 [−1,1][-1, 1] 的像素来说,这是很大的噪声,解码器就”有理由”输出模糊的图(练习 4)。β≠1\beta \ne 1 的版本叫 β-VAE(Higgins et al. 2017)。

3.4 β\beta 的两难

04 号脚本训练 5 个 VAE(144 → 256 → 256 → 8 → 256 → 256 → 144,各 3000 步):

模型每像素均方误差重建图被认对KL(nats / 图)在用的维度潜变量标准差潜变量 vs N(0,I)\mathcal{N}(0, I)解码 N(0,I)\mathcal{N}(0,I):Fréchet
β=0\beta = 0(普通自编码器)0.005789.5%88.183.003.678112.5
β=0.01\beta = 0.010.006789.3%32.681.570.40242.1
β=0.1\beta = 0.10.008289.0%18.981.080.0268.4
β=1\beta = 10.021088.5%8.681.010.0024.3
β=10\beta = 100.216714.2%0.211.00−0.00189.8

(“在用的维度”= 平均 KL > 0.05 nats 的维度;真实图像被分类器认对的比例本来就只有约 89%。)

VAE 的重建与解码 N(0, I)

  • β\beta 越大,潜变量越接近 N(0,I)\mathcal{N}(0, I)(能量距离 3.7 → 0.002),直接解码 N(0,I)\mathcal{N}(0, I) 也越像样:β=1\beta = 1 时 Fréchet 距离 4.3,VAE 自己就是一个(偏模糊的)生成模型。
  • β\beta 越大,重建越差:β=1\beta = 1 的每像素误差是普通自编码器的 3.7 倍,不过数字仍然认得出来。
  • β\beta 太大会后验坍塌:β=10\beta = 10 时 8 个维度只剩 1 个在用,所有重建都成了同一个模糊的”8”。KL 的代价太高,编码器干脆不传递信息。

3.5 潜空间流匹配

潜空间扩散的配方(Rombach et al. 2022):

  1. 训练自编码器(VAE),然后冻结它;
  2. 把数据编码成潜变量,缩放到单位方差左右(SD 1.x 的”缩放因子”0.18215 就是做这件事),在潜变量上训练流 / 扩散模型——前四讲的一切原样照搬,包括 CFG;
  3. 生成时在潜空间里从 N(0,I)\mathcal{N}(0, I) 模拟 ODE,再解码。

05 号脚本照做:三个自编码器(β=0\beta = 0、0.1、1)的 8 维潜空间上各训练一个带 CFG 的条件 MLP 流模型,和第 4 讲的像素空间 MLP 比较(同样 3000 步、批大小 256;每个设置生成 1000 张,欧拉 50 步):

生成方式流模型参数量w=1w = 1 判对w=1w = 1 风格多样性w=1w = 1 Fréchetw=2w = 2 判对w=2w = 2 Fréchet
像素空间 MLP679,56890.2%0.782.4499.2%13.26
潜空间(普通自编码器)139,01688.6%0.900.5499.7%2.36
潜空间(VAE β=0.1\beta = 0.1)139,01688.0%0.880.8399.7%1.28
潜空间(VAE β=1\beta = 1)139,01682.3%0.841.7999.1%1.99
对照:β=1\beta = 1 的 VAE 直接解码 N(0,I)\mathcal{N}(0, I)——1.174.79——

潜空间 vs 像素空间

  1. 潜空间全面好于像素空间:Fréchet 距离 0.54–1.79 vs 2.44,风格多样性 0.84–0.90 vs 0.78;w=2w = 2 时差距更大(1.3–2.4 vs 13.3)。图上像素空间的背景满是噪点,潜空间的数字干净——解码器只会画”像数字的图”,噪点被它过滤掉了。流模型只需建模 8 个数,参数也只有五分之一。
  2. 出乎意料:普通自编码器的潜空间反而最好生成(w=1w = 1 时 0.54),β=1\beta = 1 的 VAE 最差(1.79)。β=1\beta = 1 的解码器更模糊,生成的数字也更模糊、更难认(判对 82%)。在 8 维这么低的维度里,MLP 流模型完全学得会自编码器那个”形状奇怪”的潜分布,KL 正则没有带来好处;§3.2 担心的”潜分布难学”在这里并没有发生。
  3. 这和大模型的实践其实一致:Stable Diffusion 的 VAE 的 KL 权重非常小(LDM 的配置里约 10−610^{-6}),几乎就是自编码器。KL 项只是防止潜变量的尺度和形状失控;“把潜分布变成 N(0,I)\mathcal{N}(0, I)“这件事交给扩散 / 流模型去做。原课讲义说的”重建质量与生成质量的权衡”是真实存在的,但最优点通常在”很小的 β\beta“那一端。
  4. VAE 先验的缝隙:β=1\beta = 1 的 VAE 直接解码 N(0,I)\mathcal{N}(0, I) 的 Fréchet 距离 4.79,加上潜空间流模型后降到 1.79。VAE 的”汇总后验” ∫q(z∣x)p(x)dx\int q(z\mid x)p(x)dx 和先验 N(0,I)\mathcal{N}(0, I) 之间总有缝隙(“先验空洞”),流模型把它补上了。
  5. 训练用时没有体现出潜空间的优势(14 秒 vs 20 秒),因为这里每一步都要现画数字、过一遍编码器。真实系统会预先把整个数据集编码好存起来,之后训练流模型的代价只取决于潜空间的大小。

(缩放重要吗?把普通自编码器的潜变量不缩放、直接训练,w=1w = 1 时 Fréchet 距离 0.615,和缩放后的 0.54 相差不大,练习 5。这里潜变量的标准差只有 3;第 2 讲练习 7 说明,尺度差得更多时调度的表现会明显变化。)

📌 代码 04、05:python3 04_vae.py(约 80 秒);python3 05_latent_flow.py(约 110 秒)。两者都有 --quick。


4. 案例 · Case studies

原课的最后一部分用两个真实系统把前五讲串起来。下面的数字都来自论文本身。

4.1 Stable Diffusion 3(Esser et al. 2024)

组件SD3 的选择对应本课
概率路径与参数化Rectified Flow:CondOT 路径 xt=tz+(1−t)ϵx_t = tz + (1-t)\epsilon,预测速度 z−ϵz - \epsilon第 2 讲、第 3 讲 §2.3
训练时 tt 的分布不是均匀的:logit-normal 分布,在中间的时刻多采样第 2 讲 §5.3(中段最关键)
潜空间自编码器,8 倍下采样,16 个通道(SD 1/2 只有 4 个)本讲 §3
网络MM-DiT:图像 token 和文本 token 拼在一起做联合注意力,两种模态各有一套权重;时间和汇总的文本向量用 adaLN 注入;最大约 80 亿参数本讲 §2
文本编码器两个 CLIP(汇总向量 + token 序列)和 T5-XXL(token 序列)本讲 §1.3
引导训练时随机丢弃文本,采样时用 CFG第 4 讲
采样欧拉法(或类似的 ODE 求解器),几十步第 1 讲

SD3 论文做了大规模的对比实验:比较了包括 CondOT、VP 在内的多种路径与时间采样方式,发现 rectified flow 配合 logit-normal 时间采样表现最好;比较了潜空间的通道数,发现通道越多重建越好,而在模型足够大时生成质量也随之提高(练习 7)。

4.2 Meta Movie Gen(Polyak et al. 2024)

视频生成把同样的配方推到了更大的规模:

  • 流匹配(CondOT 路径)训练,和 SD3 一样预测速度;
  • 时空自编码器(TAE):在时间、高、宽三个方向上各压缩 8 倍,视频在潜空间里变成一串”时空 token”;
  • 约 300 亿参数的 Transformer(结构接近 Llama 这样的语言模型),用多个文本编码器的输出作为条件;
  • 生成十几秒的高清视频,再用单独的模型做超分辨率。

原课 slides 强调的一点是:从 MNIST 到 Movie Gen,算法完全一样——条件概率路径、流匹配损失、CFG、ODE 采样。差别全在规模、数据和工程:潜空间、网络架构、分布式训练。


5. 小结 · Summary

  1. 网络的合同:(x,t,y)↦(x, t, y)\mapsto 与 xx 同形状的输出。时间用傅里叶嵌入(对 tt 的小变化更敏感,在 t→1t\to1 附近向量场更准);提示用嵌入表或预训练的文本编码器(CLIP、T5)。
  2. U-Net:卷积编码器 / 解码器 + 跳连,局部性与平移等变性适合图像,参数少但计算量大。DiT:patchify + 位置嵌入 + Transformer 块 + depatchify,计算量由 token 数决定,注意力按 token 数平方增长,扩展性好。
  3. 条件注入:加到特征上、adaLN、adaLN-Zero(恒等初始化,DiT 中效果最好)、交叉注意力、联合注意力(MM-DiT)。
  4. 潜空间:生成模型只建模语义,解码器负责细节;token 数和计算量都大幅下降。像素数字上,潜空间流匹配全面好于像素空间。
  5. VAE:编码器输出分布、重参数化、KL 正则;β=2s2\beta = 2s^2 是”假设的解码器噪声”。β\beta 越大潜空间越规整、重建越差,太大会后验坍塌。实践中 β\beta 取很小的值,把”变成 N(0,I)\mathcal{N}(0, I)“交给流模型。
  6. 案例:SD3 = CondOT 流匹配 + logit-normal 时间采样 + 16 通道潜空间 + MM-DiT + 三个文本编码器 + CFG;Movie Gen = 同样的算法 + 时空自编码器 + 300 亿参数的 Transformer。

6. 常见误区 · Common pitfalls

  1. “参数越少,训练越快。” 卷积核和注意力在每个位置都要算一遍,U-Net 参数只有 MLP 的五分之一,计算量却是 6 倍(§2.4)。
  2. “DiT 的 patch 越小越好。” patch 越小,token 越多,计算量线性增长、注意力平方增长;在本讲的小问题上,patch 2 比 patch 3 慢一倍、也没有更好(练习 6)。
  3. “VAE 的 KL 越大越好,潜空间越接近 N(0,I)\mathcal{N}(0, I) 越好生成。” 本讲的实验里恰恰相反,普通自编码器的潜空间最好生成(§3.5);大模型的 KL 权重也非常小。
  4. “潜空间扩散要把 VAE 和生成模型一起训练。” 先训练、冻结 VAE,再在固定的潜空间里训练生成模型(§3.5)。
  5. ”β=1\beta = 1 才是’正确’的 VAE。” β=1\beta = 1 只对应”解码器噪声标准差 0.71”这个具体的假设([−1,1][-1, 1] 的像素),没有特殊地位(§3.3)。
  6. “潜变量不用缩放。” 本讲的例子里影响不大(练习 5),但尺度差得多时会影响调度的表现(第 2 讲练习 7),SD 系列都有一个缩放因子。
  7. “大模型用的是和本课不同的算法。” SD3、Movie Gen 用的就是 CondOT 流匹配 + CFG + ODE 采样(§4)。

7. 练习 · Exercises

练习 1(两个高斯的 KL)。推导 KL(N(μ,σ2)∥N(0,1))=12(μ2+σ2−1−log⁡σ2)\mathrm{KL}(\mathcal{N}(\mu, \sigma^2)\Vert\mathcal{N}(0, 1)) = \frac12(\mu^2 + \sigma^2 - 1 - \log\sigma^2)(一维;对角高斯对各维求和)。

参考答案

KL=Eq[log⁡q−log⁡p]\mathrm{KL} = \mathbb{E}_q[\log q - \log p]。log⁡q(z)=−12log⁡(2πσ2)−(z−μ)22σ2\log q(z) = -\frac12\log(2\pi\sigma^2) - \frac{(z-\mu)^2}{2\sigma^2},log⁡p(z)=−12log⁡(2π)−z22\log p(z) = -\frac12\log(2\pi) - \frac{z^2}{2}。在 qq 下 E[(z−μ)2]=σ2\mathbb{E}[(z-\mu)^2] = \sigma^2,E[z2]=μ2+σ2\mathbb{E}[z^2] = \mu^2 + \sigma^2。所以 KL=−12log⁡σ2−12+12(μ2+σ2)\mathrm{KL} = -\frac12\log\sigma^2 - \frac12 + \frac12(\mu^2 + \sigma^2)。它在 μ=0\mu = 0、σ=1\sigma = 1 时为 0;μ2\mu^2 项把均值拉向 0,σ2−1−log⁡σ2\sigma^2 - 1 - \log\sigma^2 项把方差拉向 1。

练习 2(patchify = 步长为 pp 的卷积)。证明”把图切成 p×pp\times p 的小块、每块展平后乘同一个矩阵 W∈Rd×p2W\in\mathbb{R}^{d\times p^2}“等价于一个卷积核大小 pp、步长 pp、输出 dd 个通道的卷积。

参考答案

步长 pp、核大小 pp 的卷积在每个不重叠的 p×pp\times p 窗口上计算 ∑a,bKc,a,b xip+a, jp+b\sum_{a,b}K_{c,a,b}\,x_{i p + a,\,j p + b},对每个输出通道 cc 都用同一个核。把核 Kc,⋅,⋅K_{c,\cdot,\cdot} 展平成 WW 的第 cc 行,窗口展平成 token,两者逐项相同。DiT 的官方实现正是用一个 Conv2d(kernel_size=p, stride=p) 来做 patch 嵌入。

练习 3(数 FLOPs)。U-Net(ch = 16)里,12×12 分辨率上一个 16 → 16 通道的 3×3 卷积,每张图需要多少次浮点运算?占整个网络(8.19 MFLOPs)的多少?

参考答案

每个输出位置、每个输出通道要做 16×916\times9 次乘加,共 144×16×16×9=331,776144\times16\times16\times9 = 331{,}776 次乘加,即约 0.66 MFLOPs(乘加算 2 次),占 8%。网络里有好几个这样的 12×12 卷积(下行块 1 两个、上行块 1 两个且输入通道是 48),12×12 分辨率的层占了大部分计算——这也是为什么真实的 U-Net 只在低分辨率层做注意力。

练习 4(β\beta 与解码器噪声)。由 §3.3,β=2s2\beta = 2s^2。β=0.01\beta = 0.01、0.1、1 分别对应多大的解码器噪声标准差 ss?和 §3.4 表里的重建误差对照。

参考答案

s=β/2s = \sqrt{\beta/2}:0.071、0.22、0.71。表里的每像素均方误差是 0.0067、0.0082、0.021,即均方根误差 0.082、0.091、0.145。β\beta 小时,假设的噪声(0.07)和实际的重建误差(0.08)相当,模型”相信”自己应该重建得很准;β=1\beta = 1 时假设的噪声(0.71)远大于实际误差,KL 的分量相对很重,编码器宁可少传一些信息。

练习 5(编程:不缩放潜变量)。在 05_latent_flow.py 里,对普通自编码器用 LatentEncoder(vae, sample=False, scale=1.0)(潜变量标准差约 3.3)。生成质量变化大吗?

参考答案(作者机器上的结果)

w=1w = 1:判对 87.6%、风格多样性 0.91、Fréchet 距离 0.615(缩放后:88.6%、0.90、0.54);w=2w = 2:Fréchet 距离 2.10(缩放后 2.36)。差别不大。8 维、尺度 3 的潜分布对 CondOT 路径来说还在”可以接受”的范围;第 2 讲练习 7 里尺度从 1 变到 8 时调度的排序就变了。实践中缩放是一个便宜的保险。

练习 6(编程:DiT 的 patch 大小)。在 03_train_image_flow.py 里把 DiT 的 patch 从 3 改成 2(36 个 token)。训练时间和质量怎样变化?

参考答案(作者机器上的结果)

训练用时约 40 秒(patch 3:19 秒)。w=1w = 1:判对 93.4%、风格多样性 0.85、Fréchet 距离 1.76(patch 3:85.2%、0.91、1.3);w=2w = 2:Fréchet 距离 5.68(patch 3:3.8)。计算量翻倍,样本却没有更好(判对率更高但多样性更低、离真实分布更远)。在 12×12 的小图上 patch 3 已经足够细;patch 大小的收益要在大图上才明显(DiT 论文里 patch 越小越好,代价是计算量)。

练习 7(思考:潜空间的通道数)。SD3 把潜空间的通道数从 4 增加到 16。用 §3 的”分工”解释:通道多有什么好处、什么坏处?为什么 SD3 论文发现”模型足够大时”通道多才更好?

参考答案

通道多,潜空间装得下更多信息,解码器的重建更好(细节、文字、小物体);但生成模型要建模的东西也更多、更”细碎”(更接近像素空间的困难)。小模型容量不够,学不好这些额外的信息,生成质量反而可能下降;大模型有余力,就能把更好的重建转化为更好的生成。这和本讲 §3.5 是同一个权衡的另一面:潜空间越”信息丰富”越难学,越”规整简单”重建越差。


8. 延伸阅读 · Further reading

  • 原课讲义第 6 章、附录 D 和 Lab 3(https://github.com/eje24/iap-diffusion-labs ,2026 分支:DiT + VAE + 潜空间扩散)。
  • Ronneberger, Fischer & Brox, U-Net(MICCAI 2015);Ho et al., DDPM(2020)的 U-Net;Dhariwal & Nichol(2021)的 U-Net 改进。
  • Peebles & Xie, Scalable Diffusion Models with Transformers(DiT,ICCV 2023)。
  • Kingma & Welling, Auto-Encoding Variational Bayes(ICLR 2014);Higgins et al., β-VAE(ICLR 2017)。
  • Rombach et al., High-Resolution Image Synthesis with Latent Diffusion Models(CVPR 2022):潜空间扩散、交叉注意力。
  • Radford et al., CLIP(ICML 2021);Raffel et al., T5(JMLR 2020)。
  • Esser et al., Scaling Rectified Flow Transformers for High-Resolution Image Synthesis(SD3,ICML 2024)。
  • Polyak et al., Movie Gen: A Cast of Media Foundation Models(2024)。
  • 本仓库 CMU10-423 第 12、14 讲从另一个角度讲了 VAE、潜空间扩散和 DiT。

9. 下一讲预告 · Next lecture

到现在为止,数据都是连续的向量:图像、潜变量、二维点。文字却是离散的:一串 token,每个 token 是词表里的一个编号。离散空间里没有”沿向量场走一小步”这回事。第 6 讲要回答:

  1. 离散空间里,ODE / SDE 的对应物是什么?——连续时间马尔可夫链(CTMC)和它的速率矩阵。
  2. 流匹配的四步(条件路径、条件”向量场”、边缘化技巧、条件损失)能不能原样搬过来?——能,连续性方程变成 Kolmogorov 前向方程。
  3. 用 [MASK] 作为”噪声”,就得到掩码扩散语言模型。它和自回归语言模型各有什么优劣?
← 返回课程目录