Skip to content

CNF

Continuous Normalizing Flow(连续归一化流)

CNF 是一种用连续时间动力系统构造生成模型的方法。

它做的事情可以概括为:从一个简单分布中采样一个点,让这个点沿着一个可学习的速度场移动,最后得到数据空间里的样本。移动过程中,模型同时记录概率密度怎样变化,因此既可以采样,也可以计算似然。

从 Normalizing Flow 开始

Normalizing Flow 的目标是学习一个可逆变换,把简单分布变成复杂数据分布。

先从简单分布采样:

zp0(z)

再通过一个可逆映射生成数据:

x=fθ(z)

因为 fθ 可逆,给定 x 也能找回:

z=fθ1(x)

根据变量替换公式,数据密度可以写成:

logpX(x)=logp0(z)log|detfθ(z)z|

这条公式说明,生成模型不只要知道 z 在简单分布下的概率,还要知道变换 fθ 对空间体积造成了多少拉伸或压缩。

离散的 Normalizing Flow 通常由很多层可逆网络组成。每一层都要满足两个要求:映射可逆,Jacobian 行列式容易算。这个要求会限制网络结构。

CNF 采用连续时间写法,把变换表示为一个 ODE。

用 ODE 定义一个 flow

CNF 用下面的 ODE 描述样本的位置变化:

dxtdt=vθ(xt,t),t[0,T]

这里:

  • xt 是时刻 t 的样本位置;
  • vθ(xt,t) 是速度场,由神经网络参数化;
  • x0 来自简单分布;
  • xT 是最终生成的样本。

从初始点 x0 出发,解这个 ODE,可以得到一条轨迹:

x0xtxT

这个由 ODE 诱导出来的映射就是 flow map:

xT=ΦT(x0)

在常见的光滑条件下,ODE 轨迹可以反向求解。所以如果知道 xT,也可以沿时间反方向积分回 x0。这就是 CNF 可以做似然计算的基础。

密度也会随 flow 改变

点在移动时,整团概率质量也在移动。概率密度 pt(x) 满足连续性方程:

tpt(x)+(pt(x)vθ(x,t))=0

它表达的是概率质量守恒。某个区域里概率质量减少,说明概率从边界流出;概率质量增加,说明概率从边界流入。

沿着某一条轨迹 xt,密度的变化可以写成更方便的形式:

ddtlogpt(xt)=vθ(xt,t)

右边的散度是:

vθ(x,t)=tr(vθ(x,t)x)

这条式子叫瞬时变量替换公式。

它的含义很直接:

当速度场在某处把空间往外撑开,体积变大,密度会下降;当速度场把空间往内压缩,体积变小,密度会上升。

瞬时变量替换公式怎么来

从连续性方程开始:

tpt(x)+(pt(x)vt(x))=0

把散度展开:

(ptvt)=vtpt+ptvt

所以:

tpt=vtptptvt

两边除以 pt

tlogpt=vtlogptvt

沿着轨迹 xtlogpt(xt),全导数是:

ddtlogpt(xt)=tlogpt(xt)+(dxtdt)logpt(xt)

又因为:

dxtdt=vt(xt)

代入后,前面的两项会抵消,只剩:

ddtlogpt(xt)=vt(xt)

这就是 CNF 做密度计算时使用的公式。

如何计算一个样本的似然

假设数据样本是 xT。为了计算它在模型下的概率,需要先把它沿 ODE 反向积分回 x0,再使用简单分布 p0 的密度。

沿轨迹积分上面的密度变化公式:

logpT(xT)=logp0(x0)0Tvθ(xt,t)dt

这就是 CNF 的似然计算公式。

它包含两部分:

  • logp0(x0):反向积分得到的起点在简单分布下有多可能;
  • 0Tvθ(xt,t)dt:流动过程中密度的变化量。

训练时通常最大化数据的对数似然,或者等价地最小化负对数似然:

LNLL(θ)=Expdata[logpT(x)]

采样怎么做

采样比似然计算更直接。

先从简单分布采样:

x0p0(x)

然后解 ODE:

dxtdt=vθ(xt,t)

t=0 积分到 t=T,得到:

xT=ΦT(x0)

这个 xT 就是模型生成的样本。

因此,CNF 的采样过程就是:先采一个简单噪声,再沿着学到的速度场移动到数据空间。

一个一维例子

看一个简单的一维 ODE:

dxtdt=axt

它的解是:

xt=eatx0

如果 a>0,空间被放大;如果 a<0,空间被压缩。

一维情况下,散度就是导数:

v(x)=ddx(ax)=a

所以密度变化是:

ddtlogpt(xt)=a

积分后得到:

logpT(xT)=logp0(x0)aT

这和直觉一致。a>0 时,空间变宽,同样的概率质量摊到更大的区域里,密度下降;a<0 时,空间变窄,密度上升。

散度计算为什么麻烦

CNF 的似然公式需要计算散度:

vθ(x,t)=tr(vθ(x,t)x)

如果 x 的维度很高,完整 Jacobian 会很大,直接求 trace 代价高。

FFJORD 使用 Hutchinson 估计器来近似 trace:

tr(J)=Eϵ[ϵJϵ]

其中 ϵ 可以取标准高斯噪声或 Rademacher 噪声。这样就不需要显式构造完整 Jacobian,只需要通过自动微分计算向量-Jacobian 相关的量。

这个技巧让 CNF 可以扩展到更高维的问题。

和离散 Normalizing Flow 的差别

离散 Normalizing Flow 通常写成多层可逆变换:

z0z1zK

每一层都要计算 Jacobian 行列式。为了让这个行列式便宜,模型结构常常要专门设计。

CNF 写成连续时间形式:

dxtdt=vθ(xt,t)

它用散度积分代替一层层的 log-determinant:

logpT(xT)=logp0(x0)0Tvθ(xt,t)dt

这样可以使用更自由的神经网络来参数化速度场。代价是每次训练和采样都要调用 ODE 求解器。

CNF 的优点

CNF 的优势主要来自连续时间建模。

它给出了一个可逆的生成过程,可以从简单分布生成样本,也可以反向计算似然。密度变化由散度积分控制,不需要为每一层专门设计容易求行列式的结构。ODE 求解器还可以根据误差容忍度调整步长,在精度和计算量之间做权衡。

CNF 的限制

CNF 的训练和采样通常比普通前向网络更慢,因为每次都需要数值求解 ODE。

似然计算还需要散度项。高维情况下,即使用 Hutchinson 估计器,也会增加额外计算和方差。

另外,ODE 轨迹在常见条件下不会相交,映射保持可逆。这给似然计算带来好处,也会限制某些变换形式。实际表现取决于速度场设计、ODE 求解器设置和训练稳定性。

总结

CNF 用一个 ODE 定义从简单分布到数据分布的连续变换:

dxtdt=vθ(xt,t)

样本沿速度场移动,密度沿轨迹按散度变化:

ddtlogpt(xt)=vθ(xt,t)

最终得到似然公式:

logpT(xT)=logp0(x0)0Tvθ(xt,t)dt

因此,CNF 可以同时用于采样和似然估计。它把生成建模问题写成连续时间流动问题:样本的位置由 ODE 控制,样本密度由散度控制。