一句话 · 五个直觉 · 四个互动演示

你看不见的那一层高维球壳
正是 Flow Matching 学的东西

"Flow-matching 倾向于让网络学习噪声的流动方向(的集合),对应就是有偏置的高维球壳上一段在固定条件控制下的运动轨迹。"

如果你第一次读这句话觉得"每个字都认识、合起来像谜语"——这正常。 它把训练目标高维几何条件生成 这三件互相纠缠的事情压缩成了一句话。我们用 5 个停靠站一一拆开, 每个站都配一个能拖动、能播放的小演示。

阅读约 15 分钟 需要的背景:基本机器学习、基本微积分 更新于 2026

§0这句话到底说了什么?

这句话其实是把三件不同的事叠加在一起讲:

1 · 训练目标

"噪声的流动方向(的集合)"

网络要学的不是一张图、不是一个噪声,而是一个速度场 $u_\theta(x, t)$:在每个时间、每个空间点上,告诉粒子"你下一步往哪走"。

2 · 几何形状

"高维球壳"

高维标准正态分布 $N(0, I_d)$ 的几乎所有样本都集中在半径 $\sqrt{d}$ 的一层薄壳上——这是个反直觉但精确的事实。

3 · 条件控制

"固定条件控制下的轨迹"

你给一个 prompt / class / mask(这就是"条件"),同一个噪声会被推向不同的目的地。条件就是"偏置",决定了流的方向。

本文路线

§1 讲清"流匹配在学什么";§2 揭示"高维噪声是一层壳"这个反直觉事实; §3 把前两站拼起来,看流匹配是怎么"在壳上走路"的;§4 加入条件, 看"偏置"到底偏置了什么;§5 把原句重读一遍,每个词都有了形象的画面; 最后给一段最小可运行代码和论文索引。

§1流匹配在学什么 —— 把噪声"运输"到数据

所有生成模型本质上都在做一件事:把一个简单分布(例如 $N(0, I)$)变成一个复杂分布(例如所有自然图像的分布)。

不同的生成模型只是用不同的方式去做这种"运输":

  • GAN:一步把噪声变成图(生成器是一个大函数 $G: z \mapsto x$)。
  • Diffusion:分很多步,每步去掉一点噪声。
  • Flow Matching:把整个过程看成一个连续时间的常微分方程(ODE),学这个 ODE 的速度场

具体来说,定义一族中间分布 $p_t$,从 $p_0 = N(0, I)$(纯噪声)连续过渡到 $p_1 = $ 数据分布。每个粒子 $x_t$ 在每个时刻有一个速度 $\frac{dx}{dt}$,所有这些速度合起来就是一个向量场 / 速度场

$$\frac{d x_t}{dt} = u_\theta(x_t, t)$$

网络 $u_\theta$ 输入"位置 $+$ 时间",输出"该往哪个方向走多快"。

最简单也最常用的一种设计,叫 Rectified Flow / Conditional Flow Matching with optimal-transport coupling:让粒子走直线。

$$x_t = (1-t)\,x_0 + t\,x_1$$ $$\text{真值速度} = \frac{d x_t}{dt} = x_1 - x_0$$ $$\mathcal{L}(\theta) = \mathbb{E}_{x_0 \sim N(0,I),\,x_1 \sim \text{data},\,t \sim U[0,1]} \big\| u_\theta(x_t, t) - (x_1 - x_0) \big\|^2$$

训练 $=$ 让网络预测"从此刻 $x_t$ 出发,走到目标 $x_1$ 的方向是什么"。这就是"学习噪声的流动方向"的精确含义。

Demo A · 互动演示

看噪声变成"两弯月亮"

下面这个 2D 演示里,teal 色点是 $N(0, I_2)$ 噪声,橙色点是目标"双月"分布。拖动时间滑块,你会看到每个粒子沿直线从 teal 点滑到对应的橙点——这就是 rectified flow 学的轨迹族。背景的小箭头是平均速度场。

一句话总结:训练好的网络 $=$ 把每一个"$(\text{位置},\,\text{时间})$"对映射到"下一步该往哪"。它学的不是一个方向,而是整个空间 $\times$ 整个时间区间上的方向——所以是"流动方向的集合"。

§2反直觉时刻 —— 高维 Gaussian 根本不是"一团云"

问你一个问题:你脑海里的 $N(0, I_d)$($d$ 很大,比如 1024)长什么样?

大多数人的第一反应:"一团绕着原点的雾,大部分概率密度在原点附近,越远越稀疏"——这在 1 维、2 维、3 维都对,但到了高维,这个图像是错的

真相是:对于 $x \sim N(0, I_d)$,

$$\mathbb{E}\big[\|x\|^2\big] = d, \qquad \text{Var}\big[\|x\|^2\big] = 2d$$

用一阶展开可以得到 $\|x\| \approx \sqrt{d}$,并且波动大约是 $1/\sqrt{2}$,几乎不随 $d$ 改变

这意味着:维度越高,所有样本都被"挤"到半径 $\sqrt{d}$ 的一层薄壳上。

这是经典的 测度浓度 (concentration of measure) 现象。形象地说:

维度 $d$典型范数 $\sqrt{d}$相对厚度(壳厚 / 半径)视觉
$2$$1.41$$\sim 50\%$胖团
$10$$3.16$$\sim 22\%$稍微集中的云
$100$$10.0$$\sim 7\%$初见壳形
$1024$$32.0$$\sim 2.2\%$明显的薄壳
$4096$$64.0$$\sim 1.1\%$极薄的壳
$16384$$128.0$$\sim 0.55\%$几乎是个完美球面

SD3 / Flux / DiT 这类现代生成模型,工作维度通常是 latent 的 $4 \times 64 \times 64 = 16384$ 或更大。所谓"采样一个噪声 $x_0$",几何上就是在那层薄薄的 $\sqrt{d}$ 球壳上随机取一个点。

Demo B · 互动演示

亲眼看见"球壳"从云里浮现

拖动滑块改变维度 $d$。上图:原始范数 $\|x\|$ 的直方图,竖线标出 $\sqrt{d}$。下图:归一化范数 $\|x\| / \sqrt{d}$ 的直方图。维度越高,下图越收缩成一根针——所有样本都凑到了 $1$ 附近。

samples=2500

一句话总结:当我们说"从高维 Gaussian 采样"时,几何上你不是在原点附近抓一把雾,而是在一个半径 $\sqrt{d}$ 的薄壳上随手一点。"高维球壳"——精确得不能再精确。

§3把两件事拼起来 —— 流匹配 $=$ 球壳上的散步

现在我们把 §1(速度场)和 §2(球壳)拼起来。

  1. 起点:$x_0 \sim N(0, I_d)$,几乎一定满足 $\|x_0\| \approx \sqrt{d}$——位于噪声壳上。
  2. 终点:$x_1 \sim p_{\text{data}}$。真实数据(图像 latent、speech token、3D 点云……)通常也有自己的典型范数 $\|x_1\| \approx R_{\text{data}}$,所以也大致活在某个"数据壳"上
  3. 中途:$x_t = (1-t) x_0 + t x_1$,它的范数会从 $\sqrt{d}$ 平滑过渡到 $R_{\text{data}}$。

所以一整条轨迹的形状是:

噪声壳出发 $\;\to\;$ 一段 带方向 的连续运动 $\;\to\;$ 到达数据流形
这就是原句里说的"球壳上一段运动轨迹"。

速度场 $u_\theta(x_t, t)$ 在每一点上其实可以分解为两个分量:

  • 径向分量 $u_r$:决定范数怎么变(壳半径的演化);
  • 切向分量 $u_\perp$:决定在壳上朝哪个方向转(这就是"瞄向哪个目标点")。

对早期 $t$(接近 $0$),$x_t$ 离 $x_0$ 很近、还在噪声壳附近,所以速度场主要在做切向调整——决定"这粒噪声将来要变成猫还是变成狗"。对后期 $t$(接近 $1$),径向分量才开始压缩范数,让样本落到数据流形上。

Demo C · 互动演示

看高维轨迹的"范数轮廓"

这里我们模拟一个 $d$ 维(可调)的 rectified flow,画出 $20$ 条轨迹的 $\|x_t\|$ 随 $t$ 的变化。你会看到:起点都在 $\sqrt{d}$ 附近抱成一束,中途各自滑行,终点落到一个较小的"数据壳"上。每一条曲线 $=$ 一条"球壳上的运动轨迹"

一句话总结:训练流匹配 $=$ 教网络在两个球壳之间画出一束有方向的曲线。每一条曲线就是一个 $x_0 \to x_1$ 的故事。

§4"偏置"是什么 —— 条件信息决定壳上选哪条路

现在最后一块拼图:原句里的"有偏置的"和"固定条件控制下"到底什么意思?

同一个噪声出发点 $x_0$,不能同时变成猫、变成狗、变成宇航员。要让它确定地变成"猫",必须告诉网络一个条件 $c$("猫"这个 prompt / class / image / mask)。

训练时改成条件流匹配

$$\mathcal{L}(\theta) = \mathbb{E}_{x_0,\, x_1,\, t,\, c}\,\big\| u_\theta(x_t, t, c) - (x_1 - x_0) \big\|^2$$

速度场多了一个输入 $c$。同一个 $(x, t)$ 在不同 $c$ 下,速度方向不一样

几何上:原本"无条件速度场"会把每个噪声粒子推向"所有可能数据的某种平均"(图像里就是模糊的平均脸)。一旦你给了条件 $c$,速度场就被偏置(biased)到指向"$c$ 对应的那一小块数据流形"。

这就是为什么 Classifier-Free Guidance(CFG)work:

$$u_{\text{guided}}(x, t) \;=\; u(x, t \mid c) \;+\; w \cdot \big[\, u(x, t \mid c) - u(x, t \mid \varnothing) \,\big]$$

$u(x, t \mid c) - u(x, t \mid \varnothing)$ 就是条件特有的"偏置方向",乘上系数 $w$ 再加回去,等于"让方向更偏向条件一点"。

Demo D · 互动演示

同一束噪声,三种条件,三套轨迹

三个目标聚类(橙 / teal / 黄)放在一个圆上。同一批噪声粒子作为起点。点击不同的"条件"按钮,看同一组粒子被推向完全不同的目的地。这就是"偏置"在做的事。

一句话总结:"偏置"不是模型有 bug,而是条件信号偏置了速度场。同一个噪声壳上的同一个点,在不同条件下走向不同未来——这就是"固定条件控制下的运动轨迹"。

§5重读那句话

现在你有了 4 个停靠站、4 个互动演示作为锚点,再读一次原句:

flow-matching 倾向于让网络学习噪声的流动方向(的集合) $\Rightarrow$ §1:网络学的是速度场 $u_\theta(x, t)$,覆盖整个空间 $\times$ 时间,所以是"方向的集合"。

,对应就是 $\Rightarrow$ 这个速度场对应一族 ODE 轨迹。

有偏置的高维球壳 $\Rightarrow$ §2 $+$ §4:起点在 $\sqrt{d}$ 的噪声壳上;偏置由条件 $c$ 给出,决定壳上"哪一片"被选中。

上一段 $\Rightarrow$ §3:一段从噪声壳到数据流形的连续曲线,参数化在 $t \in [0, 1]$ 上。

在固定条件控制下的运动轨迹 $\Rightarrow$ §4:条件 $c$ 一旦固定,从同一个 $x_0$ 出发的轨迹就被唯一决定(ODE 是确定性的)。

一句话翻译

训练 Flow Matching $=$ 教一个神经网络学习速度场,让它能把"$\sqrt{d}$ 球壳上一个随机点"通过一段连续的、被条件信号偏置的轨迹,送到"数据流形上某个具体的点"。

§∞实践中的样子 —— 一段最小可读代码 $+$ 论文索引

下面是最小化的条件 rectified flow 训练 $+$ 采样片段。50 行内就能跑通,可以一对一对应到上面五站的概念:

# 条件 Rectified Flow —— 最小版本(PyTorch 风格伪代码)
import torch
import torch.nn.functional as F

def train_step(model, x1, c, optimizer):
    # x1: 一个 batch 的真实数据 (B, D); c: 条件 (B, ...)
    x0 = torch.randn_like(x1)                # ← 高维球壳上的样本 (§2)
    t  = torch.rand(x1.size(0), 1, device=x1.device)

    xt     = (1 - t) * x0 + t * x1           # ← 一段轨迹 (§3)
    target = x1 - x0                         # ← "噪声的流动方向" (§1)
    pred   = model(xt, t.squeeze(-1), c)     # ← u_theta(x, t, c) 学到的速度

    loss = F.mse_loss(pred, target)          # ← 条件流匹配损失 (§4)
    optimizer.zero_grad(); loss.backward(); optimizer.step()
    return loss.item()


@torch.no_grad()
def sample(model, c, D, steps=50, cfg_scale=0.0):
    # 推理:从噪声壳上一点出发,沿 ODE 积分到 t=1
    x = torch.randn(c.size(0), D)            # ← 起点:随机一个噪声壳上的点
    dt = 1.0 / steps
    for k in range(steps):
        t = torch.full((c.size(0),), k * dt, device=x.device)
        v_c = model(x, t, c)                 # ← 条件方向
        if cfg_scale > 0:
            v_u = model(x, t, torch.zeros_like(c))   # ← 无条件方向
            v = v_c + cfg_scale * (v_c - v_u)        # ← CFG 偏置 (§4 公式)
        else:
            v = v_c
        x = x + v * dt                       # ← 欧拉法走一小步
    return x

每一行代码都对应到原句的某个词:x0 = torch.randn_like(x1) 在视觉化层面就是选了高维球壳上一个点target = x1 - x0 就是这一粒噪声的"流动方向";模型的输入 $c$ 就是那个"偏置";从 $t=0$ 走到 $t=1$ 的循环就是那"一段运动轨迹"

进一步阅读

从入门到现代实践的推荐顺序:

  • 入门读物:"An Introduction to Flow Matching" — Cambridge MLG blog. 如果你只读一篇,读这个。
  • 原始论文:Lipman et al., Flow Matching for Generative Modeling, ICLR 2023.
  • 等价 / 同期工作:Liu et al., Flow Straight and Fast: Learning to Generate and Transfer Data with Rectified Flow, 2022/23.
    Albergo & Vanden-Eijnden, Building Normalizing Flows with Stochastic Interpolants, 2023.
  • OT 耦合改进:Tong et al., Improving and Generalizing Flow-Based Generative Models with Minibatch Optimal Transport, TMLR 2024.
  • 大规模实践:Esser et al. (Stability AI), Scaling Rectified Flow Transformers for High-Resolution Image Synthesis, 2024 (SD3 paper).
    Black Forest Labs, Flux, 2024.
  • 高维几何 / 浓度测度(理解"球壳"那部分的数学):Vershynin, High-Dimensional Probability, 2018. 看第 3 章就够了。
  • 蒸馏与一步采样:Liu et al., InstaFlow; Yin et al., One-Step Diffusion Distillation. 这些工作都是在尝试"把那段球壳上的轨迹拉直成一步跳"。
留给你的最后一个图像

下次当你看到训练日志上流过的 loss 数字时,记住——网络正在干一件几何上非常优雅的事:它在学怎么在一层维度为几千的薄薄球壳上画地图,每一张地图对应一个 prompt。你看到的每一张 Flux 出图,都是从那层壳上随手摸的一个点,沿着这张地图、走过一段轨迹、最终来到你眼前的图像分布上。