概述

归一化流(Normalizing Flows)是一种通过可逆变换构造复杂概率分布的方法。1 其核心思想是将简单的基础分布(如高斯分布)通过一系列可逆映射变换为复杂的目标分布。

在变分推断的框架下,归一化流提供了更灵活的后验近似族,使得我们可以用更复杂的分布来逼近真实后验,从而提高变分推断的精度。


变量变换公式

定理

服从分布 是双射(可逆且光滑),令 ,则:

或等价地:

对数行列式的性质

因此,我们更常用:

雅可比行列式计算的核心挑战

计算 的复杂度为 ,其中 是维度。因此,设计高效的归一化流需要精心设计变换,使雅可比行列式易于计算。


归一化流的基本构建块

1. 仿射变换(Affine Transformation)

最简单的变换形式:

其中 是平移向量, 是缩放向量。

特点

  • 雅可比行列式:
  • 计算复杂度:
  • 表达能力有限

2. 逐通道仿射耦合(Affine Coupling)

由Dinh et al. (2015)在Real NVP中提出。2

结构

其中 是任意神经网络。

雅可比行列式

计算复杂度(对角矩阵)

3. 置换层(Permutation)

通过固定置换操作来混合变量:

class Permutation(nn.Module):
    def __init__(self, dim, permutation=None):
        super().__init__()
        if permutation is None:
            self.permutation = torch.randperm(dim)
        else:
            self.permutation = permutation
    
    def forward(self, z):
        return z[:, self.permutation]
    
    def inverse(self, x):
        inv_permutation = torch.argsort(self.permutation)
        return x[:, inv_permutation]

4. 激活归一化(ActNorm)

通过数据依赖的初始化实现高效训练:

其中 通过数据批量初始化,使输出分布均值为0、方差为1。


主流归一化流架构

Real NVP (Real-valued Non-Volume Preserving)

由Dinh et al. (2015)提出,是首个成功的深度归一化流。2

架构

class RealNVP(nn.Module):
    def __init__(self, dim, hidden_dim=512):
        super().__init__()
        self.mask = self._create_mask(dim)
        # 两层网络用于s和t
        self.scale_net = nn.Sequential(
            nn.Linear(dim // 2, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, dim // 2)
        )
        self.translate_net = nn.Sequential(
            nn.Linear(dim // 2, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, dim // 2)
        )
    
    def _create_mask(self, dim):
        mask = torch.zeros(dim)
        mask[:dim // 2] = 1
        return mask
    
    def forward(self, z):
        # 分割输入
        z1 = z * self.mask
        z2 = z * (1 - self.mask)
        # 计算仿射参数
        s = self.scale_net(z1)
        t = self.translate_net(z1)
        # 应用变换
        x2 = z2 * torch.exp(s) + t
        # 合并
        x = self.mask * x + (1 - self.mask) * x2
        # 返回雅可比行列式
        log_det = torch.sum(s * (1 - self.mask))
        return x, log_det

特点

  • 可并行计算
  • 易于存储和反转
  • 需要多层堆叠以增加表达能力

Glow (Generative Flow with Invertible 1x1 Convolutions)

由Kingma & Dhariwal (2018)提出,改进了Real NVP。3

关键创新:可逆的1x1卷积

class Invertible1x1Conv(nn.Module):
    def __init__(self, dim):
        super().__init__()
        # 初始化为随机正交矩阵
        self.W = nn.Parameter(torch.linalg.qr(torch.randn(dim, dim))[0])
    
    def forward(self, z):
        log_det = torch.logdet(self.W) * z.shape[1]  # 批次维度
        return torch.einsum('bij,jk->bik', z, self.W), log_det
    
    def inverse(self, x):
        W_inv = torch.inverse(self.W)
        return torch.einsum('bij,jk->bik', x, W_inv)

Masked Autoregressive Flow (MAF)

基于自回归模型构建归一化流。4

变换

特点

  • 雅可比为三角矩阵,对角元素为
  • 采样容易(逐样本生成)
  • 密度估计需要顺序计算(慢)

Neural Spline Flows (NSF)

使用分段线性/三次样条作为非线性变换。5

B-样条变换

其中 是B-样条基函数。

优势

  • 表达能力更强
  • 可以精确逆变换(通过查表)
  • 在密度估计任务上效果优异

归一化流与变分推断

增强变分后验

标准VAE使用高斯后验近似:

使用归一化流可以增强后验近似:

class FlowVAE(nn.Module):
    def __init__(self, encoder, decoder, flow_layers):
        super().__init__()
        self.encoder = encoder
        self.decoder = decoder
        self.flow = PlanarFlow(num_layers=flow_layers)
    
    def forward(self, x):
        # 编码
        q0_mean, q0_logvar = self.encoder(x)
        q0_std = torch.exp(0.5 * q0_logvar)
        
        # 从高斯采样
        z0 = q0_mean + q0_std * torch.randn_like(q0_mean)
        
        # 通过流变换
        zK, log_det = self.flow(z0)
        
        # ELBO
        log_pzK = self.prior.log_prob(zK)
        log_qz0 = -0.5 * (q0_logvar + (z0 - q0_mean)**2 / q0_std**2)
        log_px_z = self.decoder(zK)
        
        elbo = log_px_z + log_pzK - log_qz0 + log_det
        return elbo.mean()

Planar流

最简单的可学习流变换:

其中 是激活函数(如tanh)。

径向流(Radial Flow)

另一种常用变换:

ELBO中的流变换

使用归一化流后,ELBO变为:


连续归一化流(CNF)

神经微分方程视角

将离散的流层推广为连续的变换过程:

概率流ODE

可以证明,这对应于某个确定性随机过程的轨迹。

FFJORD (Free-form Jacobian of Reversible Dynamics)

使用Jacobian-vector积避免显式计算雅可比行列式。6

class FFJORD(nn.Module):
    def __init__(self, dim, hidden_dim=64):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(dim + 1, hidden_dim),  # +1 for time
            nn.SiLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.SiLU(),
            nn.Linear(hidden_dim, dim)
        )
    
    def drift(self, z, t):
        # f(z, t) 用于 dz/dt = f(z, t)
        return self.net(torch.cat([z, t * torch.ones_like(z[:, :1])], dim=-1))
    
    def forward(self, z0, t_span):
        # 使用ODE求解器
        solution = odeint(self.drift, z0, t_span)
        return solution[-1]

实现框架

normflows库

import normflows as nf
 
# 定义基础分布
base = nf.distributions.base.DiagGaussian(2)
 
# 定义流变换
flows = [
    nf.flows.AffineCouplingBlock(
        nf.nets.MLP([1, 64, 64, 1])
    ),
    nf.flows.Permute(2),
    # ... 更多层
]
 
# 构建流模型
nf_model = nf.NormalizingFlow(base=base, flows=flows)
 
# 训练
optimizer = torch.optim.Adam(nf_model.parameters(), lr=1e-4)
for data in dataloader:
    optimizer.zero_grad()
    loss = -nf_model.log_prob(data).mean()
    loss.backward()
    optimizer.step()

nflows库

另一个流行的归一化流实现库。


归一化流的优缺点

优点

优点说明
精确对数密度计算避免变分近似的误差
可逆性易于从隐空间采样和重建
可解释性变换路径提供了分布转化的直观理解
灵活的表达能力通过堆叠多层增加表达能力

缺点

缺点说明
高计算成本需要或精心设计的变换
表达能力限制仍然受限于可逆变换的结构
大内存开销保存所有中间激活用于逆向传播

应用场景

1. 变分自编码器增强

使用归一化流增强VAE的后验近似,提高生成质量。

2. 图像生成

Glow在高质量图像生成上的应用。

3. 密度估计

NSF在表格数据和低维数据上的密度估计。

4. 数据增强

利用流的逆变换生成高质量合成样本。


参考


相关主题

Footnotes

  1. Kobyzev et al. (2021). “Normalizing Flows: An Introduction and Review of Current Methods”. IEEE TPAMI.

  2. Dinh et al. (2015). “Density Estimation Using Real NVP”. ICLR 2017. 2

  3. Kingma & Dhariwal (2018). “Glow: Generative Flow with Invertible 1x1 Convolutions”. NeurIPS 2018.

  4. Papamakarios et al. (2017). “Masked Autoregressive Flow for Density Estimation”. NeurIPS 2017.

  5. Durkan et al. (2019). “Neural Spline Flows”. NeurIPS 2019.

  6. Grathwohl et al. (2019). “FFJORD: Free-form Jacobian of Reversible Dynamics”. ICML 2019.