Part V · 第 19 章 生成模型 · 19.3

Normalizing Flow(归一化流)

把一个简单的高斯分布,经过一串可逆、可微的变形,捏成你想要的任何形状:这就是 normalizing flow。因为每一步都可逆,它既能一次前向就采样,也能精确算出任意一点的概率密度,代价只是每一层雅可比行列式的对数。本节从 1.5 节的行列式和 3.3 节的雅可比出发,推出换元公式,搭起 RealNVP 的耦合层并在浏览器里现场训练,再看连续时间的流(通向 19.7 节的 flow matching),最后用一个条件 flow 给冗余机械臂一次采样出许多组逆运动学解。

约 40 分钟 前置:1.5 行列式 · 3.3 雅可比 · 4.6 最大似然 · 16.3 反向传播 6 个交互

    1为什么机器人需要它

    机器人经常需要的不是“一个答案”,而是“答案的分布”。一个 7 自由度机械臂够到同一个杯子有无数种姿态;同一张桌面能有很多个好的抓取位姿;示教数据里,同一个场景下人有时从左绕、有时从右绕。我们想要一个模型,能做两件事:采样(给我 50 个不同的可行姿态),以及打分(这个姿态在数据里有多常见,\(p_\theta(\mathbf x)\) 是多少)。打分还能用来做异常检测:读数的似然低得离谱,说明遇到了没见过的情况。

    前两节的生成模型各缺一半。19.1 节的 VAE 能采样,但只给出似然的一个下界(ELBO);19.2 节的 GAN 采样质量好,却完全没有密度可算。Normalizing flow 的做法很直接:拿一个能精确算密度的分布(标准高斯 \(p_Z\)),再用一个可逆的神经网络 \(f\) 把它变形成数据的样子。采样就是 \(\mathbf x=f(\mathbf z)\);打分就是把 \(\mathbf x\) 倒推回 \(\mathbf z=f^{-1}(\mathbf x)\),读出那里的高斯密度,再按 \(f\) 把体积放大了多少倍做修正。这个“修正”就是 1.5 节说过的那句话:变换之后,密度要除以 \(|\det|\)。

    本节的关键问题是工程上的:一般的神经网络既不可逆,雅可比行列式也要 \(O(D^3)\) 才能算。RealNVP 的耦合层用一个巧妙的结构同时解决了两个问题,于是可以直接用最大似然(4.6 节)训练。在机器人里,这让 flow 成了逆运动学采样器、策略分布和位姿不确定性模型的一个选项;它的连续时间版本则直接引出了今天机器人策略里常用的 flow matching(19.7 节)。

    2直觉:概率像橡皮泥,捏哪里哪里就变稀

    先看一维。\(z\) 服从标准正态,再取一个单调递增的函数 \(x=f(z)\)。把 \(z\) 轴想成一条橡皮筋,上面均匀撒着“概率沙子”,\(f\) 把橡皮筋的各段拉长或压短。沙子总量不变:落在 \([z,z+dz]\) 里的沙子,变换后全部落在 \([f(z),f(z)+f'(z)\,dz]\) 里。所以那一段被拉长 \(f'(z)\) 倍,沙子的密度就变为原来的 \(1/f'(z)\):

    $$ \hl{px}{p_X(x)}\,|dx| \;=\; \hl{pz}{p_Z(z)}\,|dz| \quad\Longrightarrow\quad p_X(x)=\frac{p_Z(z)}{\hl{dfdz}{|f'(z)|}},\qquad z=f^{-1}(x). $$

    下图的 \(f\) 是一条折线,经过 7 个节点 \(z=-3,-2,\dots,3\)。你可以拖动每个节点在下方 \(x\) 轴上的落点(保持单调)。每一段的斜率就是 \(f'\):段被拉长(斜率 > 1)的地方密度变低,被压扁(斜率 < 1)的地方密度变高。4.9 节的逆 CDF 采样其实就是一个一维 flow:它把均匀分布捏成目标分布,用的正是这个公式。

    FIG 1一维换元:拉长的地方变稀,压扁的地方变稠
    上:\(z\) 轴和标准正态密度 \(p_Z\)(蓝色)。中:青色线把每个 \(z\) 刻度连到它在 \(x\) 轴上的落点 \(f(z)\),琥珀色圆圈是可拖动的节点。下:按公式算出的 \(p_X\)(琥珀色曲线),和把 6000 个 \(z\) 样本真的推过 \(f\) 之后的直方图(紫色柱):两者吻合,说明公式就是“沙子守恒”。绿色带是探针:拖动上方的绿色手柄选一段宽 0.25 的 \(z\) 区间,它在两边圈住的面积(概率)相同。折线在 \(z=\pm3\) 之外按端段的斜率延伸。
    试一试
    • 拖动节点,把 \(|z|\le2\) 范围内的某一段压到斜率小于 0.25:那一段上的 \(p_X\) 至少是 \(p_Z\) 的 4 倍,直方图也跟着冒出一个尖峰。
    • 只靠拖动节点,把单峰的高斯捏成双峰:\(p_X\) 有两个峰,中间的谷低于较矮峰的一半。提示:把中间几段拉长,把两侧压扁。
    核心洞见

    一个单调(可逆)的函数,配上“除以斜率”这条规则,就能把一个简单分布变成复杂分布,并且新分布的密度处处可算:先倒推 \(z=f^{-1}(x)\),再算 \(p_Z(z)/|f'(z)|\)。高维时,“斜率”换成雅可比行列式 \(|\det J_f|\)(3.3 节:一个小区域的面积被放大 \(|\det J|\) 倍),其余一字不改。Normalizing flow 的全部难点,都在于造出一个既灵活、又可逆、行列式又便宜的 \(f\)。

    3动手:耦合层,然后现场训练一个 flow

    二维时,flow 需要一个可逆的 \(f:\R^2\to\R^2\),并且要能便宜地算出 \(\det J_f\)。RealNVP(Dinh 等,2017)的仿射耦合层是这样的:把坐标分成两半,第一半原样通过,第二半被缩放再平移,而缩放量 \(\hl{s}{s}\) 和平移量 \(\hl{t}{t}\) 由第一半算出来(可以是任意神经网络):

    $$ x_1=z_1,\qquad x_2=z_2\,e^{\hl{s}{s(z_1)}}+\hl{t}{t(z_1)}\qquad\Longleftrightarrow\qquad z_1=x_1,\qquad z_2=\big(x_2-t(x_1)\big)\,e^{-s(x_1)} $$

    两个好处一眼可见。可逆:因为 \(z_1=x_1\) 原样保留,倒推时先拿到 \(z_1\),就能重新算出 \(s\) 和 \(t\),再把缩放平移撤销;\(s\)、\(t\) 本身完全不需要可逆。行列式便宜:\(x_1\) 不依赖 \(z_2\),所以雅可比是下三角的,行列式就是对角线的乘积 \(1\cdot e^{s(z_1)}\),不管 \(s\) 和 \(t\) 多复杂。下图用两个手写的函数 \(s(z_1)=\alpha\sin(1.3z_1)\)、\(t(z_1)=\beta(\tfrac12z_1^2-0.8)\) 代替神经网络,让你看清一层到底做了什么。

    FIG 2一个仿射耦合层:只动一半坐标,雅可比是三角的
    左:\(z\) 空间,蓝点是 300 个标准正态样本,青色是坐标网格,琥珀色小方块是可拖动的探针(边长 0.2)。右:同样的点、网格和方块经过 \(f\) 之后。竖直的网格线仍是竖直的(\(x_1=z_1\)),每条竖线被整体缩放 \(e^{s}\) 倍并上下平移 \(t\)。读数里“面积比”是右侧琥珀色区域的真实面积(多边形公式)除以 0.04,“\(\det J\)”来自三角结构的公式在方块中心的值;两者只差方块有限大小带来的误差(一层时不到 1%,方块越小越接近相等;两层且 \(\alpha,\beta\) 取到极端时方块本身被强烈弯曲,可差到 15% 左右)。“往返误差”是 \(\|f^{-1}(f(\mathbf z))-\mathbf z\|\)。悬停公式里的 \(s\)、\(t\)、\(J\) 会分别高亮方块的面积、平移箭头和雅可比的两列(红、绿箭头)。“两层”模式再叠一层角色互换的耦合层(\(x_1\) 由 \(x_2\) 缩放平移):总雅可比不再是三角的(四个元素都不为零),但行列式仍是两层行列式之积 \(e^{s_1+s_2}\)。
    • 调节 \(\alpha\) 并拖动探针,找到一个位置使方块的像面积超过原来的 2 倍(\(\det J>2\))。默认的 \(\alpha=0.5\) 在一层模式下做不到:\(e^{0.5}\approx1.65\)。
    • 把 \(\alpha\) 调到 0,并让 \(|\beta|\ge0.5\):网格照样弯曲,但无论探针放在哪里,\(\det J\) 都恰好是 1。这是 NICE(Dinh 等,2015)的“加性耦合”,保体积。

    现场训练:最大似然

    现在把 \(s\) 和 \(t\) 换成小神经网络(每层一个 \([1,16,16,2]\) 的 tanh MLP,同时输出 \(s\) 和 \(t\)),叠 8 层,两半坐标轮流被变换,一共 2704 个参数。训练目标就是 4.6 节的最大似然:对每个数据点倒推到 \(\mathbf z\),累加每层的对数行列式,最小化平均负对数似然(NLL,单位 nat):

    $$ \hl{nll}{L(\theta)}=-\frac1N\sum_{i=1}^N\log p_\theta(\mathbf x_i),\qquad -\log p_\theta(\mathbf x)=\tfrac12\|\mathbf z\|^2+\log 2\pi+\sum_{k=1}^{8}\sum_j s_{k,j},\quad \mathbf z=f_\theta^{-1}(\mathbf x) $$

    第一项 \(\tfrac12\|\mathbf z\|^2\) 要求数据被推到高斯的中心,单独用它,网络会把所有点压成一个点(逆变换的缩放 \(e^{-s}\to0\),即 \(s\to+\infty\));第二项 \(\sum s\) 惩罚这种压缩,因为压缩就是 \(\det J_{f^{-1}}\) 很小。两项拉锯的结果是:把数据均匀地铺满一个标准高斯。梯度用 16.3 节的反向传播逐层算出(页面里与有限差分对过,误差约 \(10^{-9}\)),优化器是 Adam(学习率 0.005,2000 步后降到 0.002),每步 64 个样本。

    FIG 3现场训练 RealNVP:8 个耦合层,精确似然
    左:背景是模型密度 \(p_\theta(\mathbf x)\)(越青 = 越高,每 \(56\times56\) 格逐行刷新),琥珀点是 1000 个训练数据,紫点是 500 个模型样本 \(f_\theta(T\mathbf z)\),\(T\) 是温度。右:负对数似然随训练步数下降,紫线是训练集(滑动平均),琥珀线是 500 个测试点;绿色虚线是数据真实分布的熵 \(H(p_{\mathcal D})\),任何模型的期望 NLL 都不可能低于它;灰色虚线是“最好的单个高斯”的 NLL。全部随机数都固定种子,所以同样的步数给出同样的数字。
    • 在“双月”上按“训练”,直到测试 NLL 低于 1.40(默认种子约 1150 步)。初始时它是 2.51;最好的单个高斯是 2.25,真实熵是 1.14。
    • 训练到测试 NLL < 1.40 之后,把温度 \(T\) 降到 0.6 以下:样本向两条月牙的“脊线”收拢,散在月牙之间的样本几乎消失。
    • 把耦合方式换成“总是变换 \(x_2\)”,在双月上训练至少 2000 步:NLL 停在 1.75 附近下不去,因为 \(x_1=z_1\) 始终原样通过,模型的 \(x_1\) 边缘分布永远是标准正态。
    有意思的细节

    (1) 训练集 NLL(约 1.19)比测试集(约 1.30)低:2704 个参数对 1000 个点已经开始过拟合,精确似然让你能像监督学习一样量出泛化差距(16.2 节)。 (2) 到 4000 步,测试 NLL 约 1.30,离真实熵 1.14 还差 0.16 nat:两条月牙之间总留着一道淡淡的密度“桥”。flow 是连续可逆映射,不能把一个连通的高斯撕成两块,只能把中间拉得很薄(误区 2)。 (3) “三团”数据最难看清这一点:要把一个高斯分成三个孤立的团,中间的桥永远去不掉,测试 NLL 还会随训练上下波动。 (4) 螺旋需要的变形最剧烈,8 层小网络 4000 步只到 2.26 左右,真实熵是 1.79。

    4另外两个视角:逐层看,以及无穷多层

    逐层看:数据怎样被“高斯化”

    “Normalizing”这个名字来自密度估计的方向:从数据 \(\mathbf x\) 出发,一层一层倒推,直到分布变成标准正态(normal)。下图取 FIG 3 正在训练的那个模型,把 500 个测试点逐层推过去,按它们来自哪条月牙(或哪个团)着色。反过来选“\(\mathbf z\to\) 数据”,看到的就是采样:一团高斯一层层被捏成数据的形状。

    FIG 4逐层:数据 → 高斯(密度方向)与 高斯 → 数据(采样方向)
    使用 FIG 3 当前的模型和数据集(先在 FIG 3 训练,这里才有看头)。青色虚线圆是标准正态的 1σ、2σ 等高线。“数据 → \(\mathbf z\)”时点是测试数据,按类别着色(红/蓝,三团时还有绿);“\(\mathbf z\to\) 数据”时点是新的高斯样本,按起点 \(z_1\) 的正负着色(红 = 负,蓝 = 正)。读数:当前阶段的平均 \(\tfrac12\|\mathbf h\|^2\)(标准正态时期望为 1)和到这一层为止累积的平均 \(\log|\det|\);在 \(\mathbf z\) 端,二者与 \(\log2\pi\) 合起来正好是测试 NLL。
    • FIG 3 的模型训练至少 2000 步后,在“数据 → \(\mathbf z\)”方向把阶段滑块从 0 拉到 8:两条月牙被逐层拉直、摊开,最后各占圆形高斯的一边,平均 \(\tfrac12\|\mathbf z\|^2\) 接近 1。

    注意 \(\mathbf z\) 端的颜色:两类数据被分到高斯的两边,中间只有一条细缝。flow 学到的 \(\mathbf z\) 是数据的一种坐标系,和数据同维度、一一对应,不像 VAE 的隐变量那样可以更低维、并带着噪声(19.1 节)。按默认种子训练到 4000 步时,平均 \(\tfrac12\|\mathbf z\|^2\) 约为 1.16,略大于 1:模型还没把数据完全“高斯化”。

    无穷多层:连续时间的流

    如果每一层只做一个极小的变形 \(\mathbf x\leftarrow\mathbf x+\Delta t\,\mathbf v(\mathbf x)\),叠无穷多层,flow 就变成了一个常微分方程(2.5 节):每个样本沿着向量场 \(\hl{vf}{\mathbf v(\mathbf x,t)}\) 漂流(3.6 节)。这时“对数行列式之和”变成了沿轨迹对散度的积分:一小块区域的面积按 \(\tfrac{d}{dt}\log A=\hl{div}{\nabla\cdot\mathbf v}\) 变化(3.6 节说过:散度就是面积的增长率),密度则按相反的符号变化。这就是 Neural ODE(Chen 等,2018)提出的瞬时换元公式。下图用三个解析的向量场演示它;底色是散度。

    $$ \frac{d\mathbf x}{dt}=\mathbf v(\mathbf x,t),\qquad \frac{d}{dt}\log p_t\big(\mathbf x(t)\big)=-\nabla\cdot\mathbf v\big(\mathbf x(t),t\big)\quad\Longrightarrow\quad \log p_1(\mathbf x_1)=\log p_0(\mathbf x_0)-\int_0^1\nabla\cdot\mathbf v\,dt $$
    FIG 5连续时间的流:面积按散度的积分变化
    底色是散度 \(\nabla\cdot\mathbf v\):越琥珀 = 越正(面积膨胀),越蓝 = 越负(面积收缩)。青色小箭头是向量场。紫点是 400 个从 \(\mathcal N(\mathbf 0,0.7^2I)\) 出发的样本,被场带着走:这就是一个 flow 在“采样”。绿色方块(边长 0.3,\(t=0\) 时可拖动)随流变形;读数比较它的真实面积比 \(A(t)/A(0)\) 和沿方块中心积分得到的 \(e^{\int\nabla\cdot\mathbf v\,dt}\)。积分用 RK4,固定步长 0.01。方块很小时两者几乎相等;方块被拉得很大、很弯之后,中心处的散度不再代表整个方块,两者才会分开。
    • 选“旋转成环”场,把绿色方块拖到底色琥珀的区域(离原点小于 1.4),按“运行”到 \(t\ge1\):面积比超过 2,并且与 \(e^{\int\nabla\cdot\mathbf v\,dt}\) 相差不到 3%。同时看紫色样本:高斯被这个场捏成了一个半径 2 的环。
    一个想法,几种看法

    几何:一个可逆变形,概率质量守恒,密度按体积缩放(1.5、3.3 节)。 统计:精确似然下的最大似然估计,等价于最小化 \(\mathrm{KL}(p_{\mathcal D}\,\|\,p_\theta)\)(4.6、4.11 节)。 坐标:学一套和数据一一对应的新坐标 \(\mathbf z\),在这套坐标下数据是标准高斯。 动力学:无穷多层的极限是一个向量场,对数行列式变成散度的积分;19.6 节的扩散模型和 19.7 节的 flow matching 学的也是这样的场(扩散模型通过 score 间接给出它),只是不再用最大似然去训练。

    5再看数学

    提示:把鼠标悬停在公式中带底色的符号上,图中对应的元素会被高亮(FIG 1 的两条密度曲线与探针带,FIG 2 的面积、平移箭头与雅可比列,FIG 3 的损失曲线,FIG 4 的高斯等高线,FIG 5 的散度底色与箭头,FIG 6 的目标点)。

    换元公式

    设 \(f:\R^D\to\R^D\) 是可逆且可微的(微分同胚),\(\mathbf z\sim p_Z\),\(\mathbf x=f(\mathbf z)\)。对任意区域 \(\Omega\),\(\mathbf x\) 落在 \(f(\Omega)\) 的概率等于 \(\mathbf z\) 落在 \(\Omega\) 的概率;而 3.3 节告诉我们,\(f\) 把 \(\mathbf z\) 附近的小体积放大 \(|\det J_f(\mathbf z)|\) 倍。于是

    $$ \hl{px}{p_X(\mathbf x)}=\hl{pz}{p_Z\big(f^{-1}(\mathbf x)\big)}\,\big|\det J_{f^{-1}}(\mathbf x)\big| = \frac{p_Z(\mathbf z)}{\hl{dfdz}{|\det J_f(\mathbf z)|}},\qquad \mathbf z=f^{-1}(\mathbf x) $$

    两种写法等价,因为反函数的雅可比是雅可比的逆(反函数定理:\(J_{f^{-1}}(\mathbf x)=J_f(\mathbf z)^{-1}\)),行列式取倒数。把 \(f\) 写成 \(K\) 层的复合 \(f=f_K\circ\cdots\circ f_1\),中间量 \(\mathbf h_0=\mathbf z,\ \mathbf h_k=f_k(\mathbf h_{k-1}),\ \mathbf h_K=\mathbf x\)。链式法则让雅可比相乘,行列式也相乘,取对数后变成求和:

    $$ \log p_\theta(\mathbf x)=\hl{base}{\log p_Z(\mathbf h_0)}-\sum_{k=1}^K\log\big|\det J_{f_k}(\mathbf h_{k-1})\big|,\qquad \log p_Z(\mathbf z)=-\tfrac12\|\mathbf z\|^2-\tfrac D2\log2\pi $$

    这就是 FIG 4 读数里的那笔账:在 \(\mathbf z\) 端,平均 \(\tfrac12\|\mathbf z\|^2\) 加上 \(\tfrac D2\log2\pi\)(\(D=2\) 时就是 \(\log2\pi\)),减去累积的平均 \(\log|\det J_{f^{-1}}|\),正好是测试 NLL。

    最大似然 = 最小化 KL

    训练目标 \(\hl{nll}{L(\theta)}=-\tfrac1N\sum_i\log p_\theta(\mathbf x_i)\) 是期望 \(-\mathbb E_{p_{\mathcal D}}[\log p_\theta]\) 的样本估计,即交叉熵 \(H(p_{\mathcal D},p_\theta)=H(p_{\mathcal D})+\mathrm{KL}(p_{\mathcal D}\|p_\theta)\)(4.11 节)。熵 \(H(p_{\mathcal D})\) 与 \(\theta\) 无关,所以最小化 NLL 就是最小化 KL,并且 NLL 永远不低于 \(H(p_{\mathcal D})\):这就是 FIG 3 那条绿色虚线。两者之差就是模型离真实分布还有多远,以 nat 计。注意 KL 的这个方向(数据在前)惩罚的是“数据有、模型没有”,所以最大似然训练的模型倾向于覆盖所有模式,宁可在模式之间留一些多余的密度,这和 GAN 容易丢模式(19.2 节)正好相反。

    耦合层:三角雅可比

    一般地,把 \(\mathbf z\) 分成 \((\mathbf z_a,\mathbf z_b)\),仿射耦合层是 \(\mathbf x_a=\mathbf z_a\),\(\mathbf x_b=\mathbf z_b\odot e^{\hl{s}{\mathbf s(\mathbf z_a)}}+\hl{t}{\mathbf t(\mathbf z_a)}\)(\(\odot\) 是逐元素乘)。它的雅可比是

    $$ \hl{J}{J_f}=\frac{\partial(\mathbf x_a,\mathbf x_b)}{\partial(\mathbf z_a,\mathbf z_b)}=\begin{bmatrix} I & 0\\[2pt] \dfrac{\partial\mathbf x_b}{\partial\mathbf z_a} & \operatorname{diag}\!\big(e^{\mathbf s(\mathbf z_a)}\big)\end{bmatrix},\qquad \log|\det J_f|=\sum_j s_j(\mathbf z_a) $$

    左下块 \(\partial\mathbf x_b/\partial\mathbf z_a\) 牵涉 \(\mathbf s\) 和 \(\mathbf t\) 网络的导数,可能非常复杂,但三角矩阵的行列式只看对角线,所以根本用不着它。这就是全部窍门:网络的表达能力放在 \(\mathbf s,\mathbf t\) 里,可逆性和行列式由结构保证。代价是每层只变换一半坐标,所以必须轮换哪一半被变换(FIG 3 的“总是变换 \(x_2\)”就是反例);高维时在层之间插入固定的置换,Glow(Kingma 和 Dhariwal,2018)则学一个可逆的 \(1\times1\) 卷积来混合通道,用 LU 分解让它的行列式也便宜。

    成本对比。一般的 \(D\times D\) 雅可比求行列式要 \(O(D^3)\) 次运算;耦合层只要 \(O(D)\),加上一次网络前向。对一张 \(64\times64\) 的 RGB 图像,\(D=12288\),\(D^3/3\approx6\times10^{11}\):每个样本、每一层都要这么多次运算,是不可能用于训练的。

    其他结构。自回归 flow(MAF,Papamakarios 等,2017;IAF,Kingma 等,2016)让第 \(i\) 个坐标的 \(s_i,t_i\) 依赖前面所有坐标,雅可比同样是三角的,但两个方向里有一个必须逐坐标串行计算:MAF 算密度快、采样慢,IAF 反之(19.4 节的自回归模型是同一种因式分解)。样条 flow(Durkan 等,2019)把仿射的 \(e^sz+t\) 换成单调的有理二次样条,表达力更强,FIG 1 的折线就是它最简陋的版本。

    梯度

    训练时走的是密度方向:每层 \(w_b=(u_b-t)e^{-s}\),损失 \(\ell=\tfrac12\|\mathbf z\|^2+\sum s\)。设 \(g=\partial\ell/\partial w_b\)(最后一层 \(g=z_b\)),反向传播一层需要的三个局部导数是

    $$ \frac{\partial\ell}{\partial u_b}=g\,e^{-s},\qquad \frac{\partial\ell}{\partial s}=1-g\,w_b,\qquad \frac{\partial\ell}{\partial t}=-g\,e^{-s} $$

    后两个再经网络反传到 \(\mathbf u_a\),与直通的 \(\partial\ell/\partial w_a\) 相加。注意 \(\partial\ell/\partial s\) 里的 “1”:它来自对数行列式,没有它就没有阻止塌缩的力。页面里的 \(s\) 是 \(2\tanh(\cdot/2)\) 限幅后的网络输出,防止 \(e^{s}\) 溢出(误区 5)。

    连续时间的流,以及为什么要 flow matching

    把一层 \(\mathbf x\leftarrow\mathbf x+\Delta t\,\mathbf v\) 的雅可比写成 \(I+\Delta t\,\partial\mathbf v/\partial\mathbf x\),则 \(\log\det(I+\Delta t A)=\Delta t\operatorname{tr}A+O(\Delta t^2)\),而 \(\operatorname{tr}(\partial\mathbf v/\partial\mathbf x)\) 正是散度 \(\hl{div}{\nabla\cdot\mathbf v}\)。让 \(\Delta t\to0\),就得到 FIG 5 上方那个公式。连续 flow 的好处是 \(\mathbf v\) 可以是任意神经网络,不需要特殊结构就自动可逆(ODE 的解在合理条件下唯一,倒着积分就是逆)。坏处是按最大似然训练它要在每一步都把 ODE 积分一遍,还要算散度;FFJORD(Grathwohl 等,2019)用 Hutchinson 随机迹估计降低了散度的成本,但积分仍然昂贵。19.7 节的 flow matching 换了一条路:不再最大化似然,而是直接对一个事先选好的概率路径上的速度场做回归,训练时完全不用积分 ODE。

    flow 与其他生成模型

    VAE(19.1):可以降维,似然只有下界。GAN(19.2):样本锐利,没有似然。自回归(19.4):精确似然,采样要逐个生成。EBM(19.5):密度只到一个未知常数。扩散(19.6)与 flow matching(19.7):样本质量最好,也能通过一个 ODE 求似然,只是贵。离散 flow 的位置:精确似然、一次前向采样、一次前向求密度,代价是隐变量必须与数据同维,网络结构受限。

    6机器人中的它:一次采样许多组 IK 解

    冗余机械臂的逆运动学(7.5 节)有无穷多个解。数值 IK 每次从一个初值出发收敛到一个解;想要很多个不同的解(比如避障时挑一个离障碍最远的),只能换很多初值反复跑。条件 flow 换个思路:学一个分布 \(p_\theta(\mathbf q\mid\hl{cond}{\mathbf c})\),给定末端目标 \(\mathbf c\),一次就采出一批关节角。条件的做法很简单:把 \(\mathbf c\) 拼进每个耦合层 \(\mathbf s,\mathbf t\) 网络的输入,\(\mathbf s(\mathbf q_a,\mathbf c)\)、\(\mathbf t(\mathbf q_a,\mathbf c)\);可逆性只针对 \(\mathbf q\),所以三角结构照旧成立。

    训练数据来自正运动学,几乎不要钱:随机采关节角 \(q_i\sim\mathcal N(0,0.9^2)\),算出末端位置,加上 0.04 m 的噪声作为 \(\mathbf c\)。下图是一个三连杆平面臂(杆长 1.0、0.8、0.6 m),目标是二维位置,所以每个目标对应一条一维的解曲线(自运动流形)。模型有 6 个耦合层(每层只变换一个关节角,三个关节轮流),每层一个 \([4,24,24,2]\) 的网络,共 4620 个参数。

    FIG 6条件 flow 做逆运动学:一个目标,一批姿态
    左:琥珀色圆圈是可拖动的末端目标 \(\mathbf c\),紫色折线是从 \(p_\theta(\mathbf q\mid\mathbf c)\) 采出的 24 个姿态,小点是它们的末端,虚线圆是最大可达半径 2.4 m。右:40 个固定测试目标上(每个 10 个样本)的平均末端误差,随训练下降(对数纵轴)。读数里“当前目标”的误差和“肘部散布”(第二个关节位置的标准差)由 200 个样本算出。“+ 精修”对每个样本再做 5 步 7.5 节的阻尼最小二乘(\(\lambda=0.05\))。训练数据只覆盖 \(q_i\sim\mathcal N(0,0.9^2)\) 能到达的地方,主要在基座右侧;把目标拖到左侧或基座附近,模型就不可靠了。
    • 按“训练”,直到 40 个测试目标上的平均末端误差低于 0.20 m(默认种子约 2250 步,开始时约 2.1 m)。姿态明显各不相同:flow 采到的是一整条解曲线,而不是一个解。
    • 训练到误差 < 0.20 m 后打开“+ 精修”:当前目标的误差降到 2 cm 以下,而肘部散布几乎不变。flow 负责“多样的好初值”,雅可比 IK 负责最后几厘米。
    • 训练到误差 < 0.20 m 后,把目标拖到离基座超过 2.2 m 的地方:解曲线变短,姿态向伸直的手臂收拢,肘部散布降到 0.4 m 以下(默认目标处约 0.6 m)。
    真实系统里

    逆运动学:Ardizzone 等(ICLR 2019)用可逆网络分析逆问题,例子之一就是平面机械臂的 IK 后验;IKFlow(Ames、Morgan 和 Konidaris,IEEE RA-L 2022)为 7 自由度机械臂训练条件 normalizing flow,一次批量生成大量不同的 IK 解;再用几步数值 IK 把这些样本精修到高精度(FIG 6 的做法),是很自然的搭配。 策略分布:SAC(20.4 节)默认用高斯策略,它只有一个峰;有些工作在高斯隐变量之上用可逆的 flow 层构造策略(例如 Haarnoja 等 2018 年的分层潜空间策略),表达力比单个高斯强,同时保留精确的 \(\log\pi(a\mid s)\),熵正则需要它。 旋转与位姿:flow 可以定义在流形上(例如环面和球面,Rezende 等,2020),也被用来建模 \(\mathrm{SO}(3)\) 上的旋转不确定性(例如 Liu 等,CVPR 2023),比如对称物体有多个等价朝向时。 异常检测:精确似然可以拿来给传感器读数或状态打分,但要小心误区 4。 今天的主流:在大规模机器人策略里,离散 flow 大多让位给了扩散模型(Diffusion Policy,22.2 节)和 flow matching(例如 π0 的动作头,19.7 节),它们学的仍然是“把高斯变成动作分布”的流,只是训练方式不同。

    7常见误区

    误区 1:忘了对数行列式,或者符号弄反

    只最小化 \(\tfrac12\|\mathbf z\|^2\),模型会把所有数据压到原点附近,损失看起来一路下降,其实密度没有归一化,“似然”毫无意义。符号反了则相反:模型拼命放大体积。检查方法:在一个已知答案的例子上核对,比如 \(f(\mathbf z)=A\mathbf z\) 时 \(\log p_X(\mathbf x)\) 应等于 \(\mathcal N(\mathbf 0,AA^\top)\) 的对数密度;或者像 FIG 1 那样,拿样本直方图和公式比。还要分清方向:密度方向(\(\mathbf x\to\mathbf z\))加 \(\log|\det J_{f^{-1}}|\),采样方向(\(\mathbf z\to\mathbf x\))减 \(\log|\det J_f|\)。

    误区 2:以为 flow 能表示任何形状

    flow 是连续的可逆映射,它保持拓扑:一个连通的高斯永远变不成严格分离的几块,也挖不出一个真正的洞。FIG 3 里双月和三团之间总有一道密度桥,NLL 离真实熵始终差一截。要逼近这类分布,只能叠更多层、用更强的层(样条),让桥越来越细,代价是 \(s\) 很大、数值上更陡峭。另外,标准 flow 的隐变量必须与数据同维:数据如果其实躺在一个低维曲面上(例如 FIG 6 的解曲线),密度在曲面上是无穷大,flow 只能靠噪声把它“撑厚”(我们给目标加了 0.04 m 噪声,部分原因就是这个)。

    误区 3:不轮换变换哪一半

    每层不动的那一半,如果每一层都是同一半,它就从头到尾原样通过:FIG 3 的“总是变换 \(x_2\)”里,模型的 \(x_1\) 永远是标准正态,NLL 停在 1.75 附近,而轮换时 1150 步就到 1.40 以下。高维时同理,层与层之间要换掩码或插入置换(RealNVP 的棋盘与通道掩码,Glow 的 \(1\times1\) 卷积),保证每个坐标都会被其他坐标影响到。

    误区 4:把似然当作万能的“正常度”

    似然高不等于“像训练数据”。Nalisnick 等(ICLR 2019)发现,在 CIFAR-10 上训练的 Glow 给 SVHN(门牌号图片)打出的似然反而更高:似然很大程度上由图像的简单程度(平滑、低方差)决定,而不是语义。在机器人上用 flow 做异常检测前,要在真实的分布外数据上验证。另外,离散数据(8 位像素、编码器计数)直接拟合连续密度时,模型可以把无穷大的密度堆在离散值上,必须先去量化(加 \([0,1)\) 的均匀噪声)。

    误区 5:数值上的可逆不等于理论上的可逆

    \(e^{s}\) 不加限制时,几个大的 \(s\) 就能溢出,或者让逆变换的误差层层放大。常见的保护:把 \(s\) 用 \(c\tanh(\cdot/c)\) 限幅(本页 \(c=2\)),把最后一层初始化为接近零让 flow 从恒等映射开始,把数据标准化到单位尺度,并定期检查往返误差 \(\|f^{-1}(f(\mathbf z))-\mathbf z\|\)(FIG 2 的读数)。另外别忘了:flow 只在训练数据覆盖的区域可靠,FIG 6 把目标拖到基座左侧,采出来的姿态就够不着了。

    8小练习

    1. \(z\sim\mathcal N(0,1)\),\(x=3z+1\)。\(p_X(1)\) 等于多少?
    \(x=1\) 对应 \(z=f^{-1}(1)=0\),斜率 \(f'=3\),所以 \(p_X(1)=p_Z(0)/3=0.3989/3\approx0.133\)。直线把 \(z\) 轴拉长了 3 倍,同样的概率摊在 3 倍长的区间上。选项 B 把除法写成了乘法,选项 D 忘了先倒推到 \(z\)。
    2. 一个三维仿射耦合层:\(x_1=z_1\),\((x_2,x_3)=(z_2,z_3)\odot e^{\mathbf s(z_1)}+\mathbf t(z_1)\),其中 \(\mathbf s(z_1)=(0.3,-0.5)\)。\(\log|\det J_f|\) 是多少?
    雅可比是下三角的,对角线是 \((1,e^{0.3},e^{-0.5})\),行列式是乘积 \(e^{0.3-0.5}\),取对数得 \(0.3+(-0.5)=-0.2\)。\(\mathbf t\) 和 \(\mathbf s\) 对 \(z_1\) 的导数只出现在左下块,不影响三角矩阵的行列式(选项 C 错);选项 D 把对数之和误当成了乘积。
    3. 耦合层里的 \(\mathbf s\) 和 \(\mathbf t\) 网络需要满足什么条件,整层才可逆?
    倒推时 \(\mathbf z_a=\mathbf x_a\) 先直接得到,再把它送进 \(\mathbf s\)、\(\mathbf t\) 重新算出同样的值,然后 \(\mathbf z_b=(\mathbf x_b-\mathbf t)\odot e^{-\mathbf s}\)。我们只是重新计算它们,从未求它们的逆。\(e^{\mathbf s}\) 总是正的,所以 \(\mathbf s\) 可正可负(选项 B 错);三角性来自“\(\mathbf x_a\) 不依赖 \(\mathbf z_b\)”的结构,与 \(\mathbf s,\mathbf t\) 是否线性无关(选项 C 错)。
    4. 一个二维连续 flow 的向量场处处满足 \(\nabla\cdot\mathbf v=0\)(例如 FIG 5 的“剪切”场)。沿着一条轨迹,样本处的密度 \(p_t(\mathbf x(t))\) 怎样变化?
    \(\tfrac{d}{dt}\log p_t(\mathbf x(t))=-\nabla\cdot\mathbf v=0\),所以每个样本随身携带的密度值不变,体积也不变(保体积的流,类似 NICE 的加性耦合)。但样本在移动,分布的形状会被扭曲,所以选项 C 不对:空间中某个固定点的密度一般会变。旋度只决定局部旋转,不影响面积(选项 D 错)。
    5. 你想用生成模型给机器人的 2000 维观测(例如压缩后的图像特征)建模,需要把它压到 32 维的隐变量。为什么标准 normalizing flow 不适合直接做这件事?
    可逆映射必须一一对应,\(\R^{2000}\) 不能可逆地映到 \(\R^{32}\)。需要降维时应选 VAE(19.1 节)或先用自编码器(18.2 节)压缩,再在低维隐空间里训练 flow。选项 D 错:三角矩阵的行列式就是对角线乘积,只要 \(O(D)\)。

    9延伸阅读

    • G. Papamakarios, E. Nalisnick, D. J. Rezende, S. Mohamed & B. Lakshminarayanan, Normalizing Flows for Probabilistic Modeling and Inference, JMLR, 2021 — 最全面的综述:换元、耦合与自回归 flow、连续 flow
    • E. G. Tabak & C. V. Turner, A Family of Nonparametric Density Estimation Algorithms, Communications on Pure and Applied Mathematics, 2013 — “normalizing flow”这一思想的早期来源之一
    • D. J. Rezende & S. Mohamed, Variational Inference with Normalizing Flows, ICML 2015 — 把 flow 用于变分推断的后验,让这个名字流行起来
    • L. Dinh, D. Krueger & Y. Bengio, NICE: Non-linear Independent Components Estimation, ICLR 2015 (workshop) — 加性耦合层
    • L. Dinh, J. Sohl-Dickstein & S. Bengio, Density Estimation Using Real NVP, ICLR 2017 — 仿射耦合层、掩码与多尺度结构
    • D. P. Kingma & P. Dhariwal, Glow: Generative Flow with Invertible 1×1 Convolutions, NeurIPS 2018
    • G. Papamakarios, T. Pavlakou & I. Murray, Masked Autoregressive Flow for Density Estimation, NeurIPS 2017; D. P. Kingma, T. Salimans, R. Jozefowicz, X. Chen, I. Sutskever & M. Welling, Improved Variational Inference with Inverse Autoregressive Flow, NeurIPS 2016
    • C. Durkan, A. Bekasov, I. Murray & G. Papamakarios, Neural Spline Flows, NeurIPS 2019
    • R. T. Q. Chen, Y. Rubanova, J. Bettencourt & D. Duvenaud, Neural Ordinary Differential Equations, NeurIPS 2018; W. Grathwohl, R. T. Q. Chen, J. Bettencourt, I. Sutskever & D. Duvenaud, FFJORD: Free-Form Continuous Dynamics for Scalable Reversible Generative Models, ICLR 2019 — 连续时间的 flow 与瞬时换元公式
    • E. Nalisnick, A. Matsukawa, Y. W. Teh, D. Gorur & B. Lakshminarayanan, Do Deep Generative Models Know What They Don't Know?, ICLR 2019 — 误区 4
    • L. Ardizzone et al., Analyzing Inverse Problems with Invertible Neural Networks, ICLR 2019; B. Ames, J. Morgan & G. Konidaris, IKFlow: Generating Diverse Inverse Kinematics Solutions, IEEE Robotics and Automation Letters, 2022 — FIG 6 的真实版本