Skip to content

CNF ​

Continuous Normalizing Flow(连续归一化流) ​

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

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

从 Normalizing Flow 开始 ​

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

先从简单分布采样:

z∼p0(z)

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

x=fθ(z)

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

z=fθ−1(x)

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

log⁡pX(x)=log⁡p0(z)−log⁡|det∂fθ(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,可以得到一条轨迹:

x0→xt→xT

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

xT=ΦT(x0)

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

密度也会随 flow 改变 ​

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

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

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

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

ddtlog⁡pt(xt)=−∇⋅vθ(xt,t)

右边的散度是:

∇⋅vθ(x,t)=tr(∂vθ(x,t)∂x)

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

它的含义很直接:

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

瞬时变量替换公式怎么来 ​

从连续性方程开始:

∂tpt(x)+∇⋅(pt(x)vt(x))=0

把散度展开:

∇⋅(ptvt)=vt⊤∇pt+pt∇⋅vt

所以:

∂tpt=−vt⊤∇pt−pt∇⋅vt

两边除以 pt:

∂tlog⁡pt=−vt⊤∇log⁡pt−∇⋅vt

沿着轨迹 xt 看 log⁡pt(xt),全导数是:

ddtlog⁡pt(xt)=∂tlog⁡pt(xt)+(dxtdt)⊤∇log⁡pt(xt)

又因为:

dxtdt=vt(xt)

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

ddtlog⁡pt(xt)=−∇⋅vt(xt)

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

如何计算一个样本的似然 ​

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

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

log⁡pT(xT)=log⁡p0(x0)−∫0T∇⋅vθ(xt,t)dt

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

它包含两部分:

  • log⁡p0(x0):反向积分得到的起点在简单分布下有多可能;
  • −∫0T∇⋅vθ(xt,t)dt:流动过程中密度的变化量。

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

LNLL(θ)=−Ex∼pdata[log⁡pT(x)]

采样怎么做 ​

采样比似然计算更直接。

先从简单分布采样:

x0∼p0(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

所以密度变化是:

ddtlog⁡pt(xt)=−a

积分后得到:

log⁡pT(xT)=log⁡p0(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 通常写成多层可逆变换:

z0→z1→⋯→zK

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

CNF 写成连续时间形式:

dxtdt=vθ(xt,t)

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

log⁡pT(xT)=log⁡p0(x0)−∫0T∇⋅vθ(xt,t)dt

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

CNF 的优点 ​

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

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

CNF 的限制 ​

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

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

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

总结 ​

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

dxtdt=vθ(xt,t)

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

ddtlog⁡pt(xt)=−∇⋅vθ(xt,t)

最终得到似然公式:

log⁡pT(xT)=log⁡p0(x0)−∫0T∇⋅vθ(xt,t)dt

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