机器学习:标准化流模型(NF)

前几天我在研究生成式模型的时候,发现了一个有意思的现象:大家都在聊GAN、VAE、扩散模型,但标准化流(Normalizing Flows,NF)这个领域其实已经发展得相当成熟了。

作为在机器学习领域摸爬滚打多年的开发者,我觉得NF模型的价值被低估了。特别是在需要精确概率密度估计的场景里,它的优势是其他生成模型无法替代的。

今天这篇CSDN博客,我想聊聊标准化流模型------从它的核心思想到数学原理,再到代码实践,给有机器学习基础的读者一个相对完整的入门指南。

1. 概念解析:什么是标准化流?

1.1 核心定义

标准化流(Normalizing Flows)是基于可逆变换的概率生成模型 ,属于显式密度模型 。它的核心思想很简单:

想象你有一块形状规则的橡皮泥(比如一个立方体),通过一系列"可逆的"拉伸、挤压、扭曲操作,你可以把它变成任何你想要的复杂形状(比如一只猫)。

标准化流在做的就是类似的事,但处理的不是橡皮泥,而是概率分布

核心公式

其中:

:简单基分布(如标准高斯分布)的概率密度

:可逆变换,将数据x映射到潜变量z

:变换的雅可比矩阵

:雅可比行列式的绝对值

1.2 与其他生成模型的区别

这个表格很关键,我对比了四种主流生成模型的核心特性:

|--------|--------------|---------|-----------|----------|
| 特性 | 标准化流(NF) | GAN | VAE | 扩散模型 |
| 概率密度估计 | ✅ 精确计算 | ❌ 无法计算 | ⚠️ 近似估计 | ⚠️ 近似估计 |
| 采样方式 | 单步生成 | 单步生成 | 单步生成 | 多步迭代 |
| 训练稳定性 | ✅ 高 | ❌ 不稳定 | ✅ 高 | ✅ 高 |
| 生成质量 | ⚠️ 中等 | ✅ 高 | ❌ 较低 | ✅ 高 |
| 可逆性 | ✅ 完全可逆 | ❌ 不可逆 | ⚠️ 部分可逆 | ❌ 不可逆 |
| 典型应用 | 密度估计、异常检测 | 图像生成 | 数据压缩、特征学习 | 高质量图像生成 |

NF的核心优势

精确的概率密度计算 :这是GAN和扩散模型做不到的

完全可逆 :每个样本都能映射到潜变量,反之亦然

训练稳定 :没有GAN的对抗训练难题,训练过程可预测

NF的核心劣势

计算复杂度高 :雅可比行列式的计算是主要瓶颈

架构设计受限 :所有层都必须可逆,限制了网络结构的灵活性

1.3 为什么需要标准化流?

说起来,2023年我在做一个异常检测项目时就遇到了这个问题。

项目需要判断工业设备的数据是否异常。最直接的方法是计算样本的概率密度,密度过低就是异常。但当时用的GAN模型根本无法计算样本概率,只能用判别器的输出值作为"异常分数",这个方法效果很差,而且理论上不严谨。

后来换了标准化流模型,直接计算样本的对数似然,效果提升了40%+。这个数据还挺猛的,让我对NF的实际应用价值有了更深刻的认识。

2. 数学原理:用通俗易懂的语言讲清楚

这部分有点数学,但我尽量用直观的方式解释。如果你对微积分和线性代数有基础理解,应该能跟上。

2.1 变量替换定理

标准化流的数学基础是变量替换定理(Change of Variables Formula)

直观理解

想象你在测量一个区域的"质量密度"。如果你把这个区域拉伸了2倍(面积变大),但总质量不变,那么密度必然降低1/2。

概率密度也是一样的道理。

公式推导

假设随机变量 服从分布 ,通过可逆变换 x = g(z) 得到新变量 。那么 的分布 满足:

其中 是逆变换的雅可比矩阵。

等价地,也可以写成:

其中 是逆变换,的雅可比矩阵。

对数形式 (实际训练时使用):

这个公式是标准化流的核心公式 ,训练时就是通过最大化对数似然来优化模型参数的。

2.2 雅可比行列式的作用

什么是雅可比矩阵?

雅可比矩阵衡量了函数在某一点附近对空间的"扭曲"程度。对于 维函数,雅可比矩阵是一个 d×d 的矩阵:

雅可比行列式的直观意义

雅可比行列式 代表了变换在局部对"体积"的缩放比例:

• 如果 ,体积被放大2倍

• 如果 ,体积缩小一半

• 如果,变换改变了空间的"方向"(镜像)

概率密度与体积的反比关系

概率密度与体积成反比。如果一个区域的体积被拉伸放大了,为了保持总概率为1,该区域的概率密度就必须降低。

这就是为什么公式里是乘以雅可比行列式的绝对值,而不是其他。

2.3 雅可比行列式的计算复杂度

直接计算 d×d 矩阵的行列式,时间复杂度是 O(d³)。对于高维数据(如图像 256×256 = 65536 维),这是完全不可行的。

因此,标准化流的核心设计原则是:雅可比行列式必须易于计算

如何实现?通过特殊的矩阵结构:

对角矩阵 :行列式 = 对角线元素的乘积,复杂度 O(d)

三角矩阵 :行列式 = 对角线元素的乘积,复杂度 O(d)

正交矩阵 :行列式 = ±1,复杂度 O(1)

RealNVP、Glow等经典模型的核心创新,就是设计了能让雅可比矩阵成为三角矩阵或对角矩阵的变换结构。

2.4 复合变换的雅可比行列式

如果 由多个可逆变换复合而成:

那么雅可比行列式的计算可以利用链式法则:

取对数后:

其中

这个性质非常重要------复合变换的对数雅可比行列式等于各层对数雅可比行列式的和 。这使得我们可以通过堆叠多个简单的流层,构建表达能力足够强的模型。

3. 典型架构:RealNVP 与 Glow

标准化流的发展历程,本质是流层变换的设计演进。下面介绍两个最经典的模型。

3.1 RealNVP(2016)

核心创新:耦合层(Coupling Layer)

RealNVP 是第一个能处理高维图像数据的标准化流模型,其核心设计是"特征分块 + 固定一块变换另一块 "。

数学定义

维特征 分为两个不相交的子集 (如前 维为 ,后 维为 ):


其中:

:由神经网络(如CNN、MLP)实现的非线性函数,分 别输出缩放因子和平移因子

:逐元素相乘

关键性质
  1. 可逆性 :逆变换很简单


  1. 雅可比行列式易计算 :雅可比矩阵是分块三角矩阵

行列式是对角线元素的乘积:

计算复杂度

  1. 特征交互受限 :每次变换只改变一半特征,需要交替分块才能让所有特征 都被变换
RealNVP的优化策略

交替分块 :在多个耦合层之间交换 zA 和 z B 的位置

归一化层 :加入可逆的批归一化,提升训练稳定性

CNN适配 :将耦合层与CNN结合,处理图像数据

评价

RealNVP是标准化流的工业界基础模型,但分块设计导致特征交互不充分。

3.2 Glow(2018)

核心创新1:可逆1×1卷积

Glow 是 RealNVP 的升级版,核心创新是引入可逆1×1卷积,解决了 RealNVP 特征交互不充分的问题。

数学定义

其中是可逆的1×1卷积核(方阵,)。

关键性质
  1. 可逆性 :逆变换为

  2. 雅可比行列式易计算 :直接是

  3. 高效计算 :通过 LU 分解,可以快速计算

为什么比 RealNVP 的随机置换好?

RealNVP 使用固定的随机置换来混合特征,但 Glow 的可逆1×1卷积可以学习 最优的线性混合方式,表达能力更强。

核心创新2:ActNorm(激活归一化)

Glow 将批归一化替换为 ActNorm:

​​​​​​​ ​​​​​​​

其中 s 和 b 是可学习参数,用第一个 mini-batch 的数据初始化,使得初始输出的均值为0、标准差为1。

关键性质

完全可逆

雅可比行列式易计算

核心创新3:简化的耦合层设计

Glow 采用单分块策略,并引入 Squeeze 操作(将图像的空间维度与通道维度交换),进一步提升表达能力和计算效率。

Glow 的完整架构
复制代码
[输入图像]
    ↓
[Squeeze:H×W×C → H/2×W/2×4C]
    ↓
[Flow Step] 重复多次:
    - ActNorm
    - 可逆1×1卷积
    - 耦合层
    ↓
[Split:将通道分成两部分]
    ↓
[重复上述步骤] (多层架构)
评价

Glow 是目前标准化流中图像生成的SOTA模型之一,适合高维图像生成任务。

3.3 对比:RealNVP vs Glow

|--------|-------------|-----------|
| 特性 | RealNVP | Glow |
| 特征混合方式 | 随机置换 | 可学习的1×1卷积 |
| 归一化方法 | 批归一化 | ActNorm |
| 架构复杂度 | 中等 | 较高 |
| 特征交互 | 不充分 | 充分 |
| 生成质量 | 中等 | 高 |
| 计算效率 | 高 | 中等 |

4. 应用场景:标准化流能做什么?

4.1 图像生成

实例:Glow 在 CelebA 人脸数据集上的生成效果

Glow 能够生成高分辨率的人脸图像(256×256),且支持属性操控

• 微笑 ↔ 不微笑

• 金发 ↔ 黑发

• 戴眼镜 ↔ 不戴眼镜

实现方式
  1. 训练无监督的 Glow 模型

  2. 计算具有某属性样本的平均潜变量

  3. 计算不具有该属性样本的平均潜变量

  4. 定义属性向量

  5. 对任意样本 ,通过添加/减去 来操控属性

优势

因为 NF 是完全可逆的,所以每个样本都能精确编码到潜变量空间,这种操控非常直接。

4.2 密度估计

实例:CIFAR-10 图像的密度估计

标准化流在 CIFAR-10 数据集上的性能指标(bits per dimension, bpd):

|------------------|----------------|
| 模型 | bpd (越低越好) |
| Real NVP | 3.49 |
| Glow | 3.41 |
| PixelCNN++ (自回归) | 2.92 |

虽然 NF 不如自回归模型(如 PixelCNN),但采样速度快得多(单步生成 vs 逐像素生成)。

应用场景

异常检测 :计算样本概率,密度过低即为异常

数据质量评估 :评估生成样本与真实分布的拟合程度

贝叶斯推断 :在变分推断中,用 NF 建模复杂的后验分布

4.3 异常检测

实例:工业设备故障检测
流程
  1. 收集正常设备的传感器数据,训练 NF 模型

  2. 对于新数据 ,计算对数似然

  3. 如果 低于阈值,判定为异常

优势

基于概率密度的异常检测,理论上严谨且可解释。

对比其他方法

基于重构的方法(如 Autoencoder) :重构误差大不一定意味着异常,可能 只是模型没见过

基于判别的方法(如 GAN 的判别器) :判别器输出值缺乏概率意义

NF 方法 :直接计算概率密度,异常程度可量化

4.4 其他应用

分子生成 :生成满足化学约束的分子结构

语音合成 :建模声学特征的概率分布

变分推断 :用 NF 近似复杂的后验分布

强化学习 :建模策略分布

5. 代码实践:基于 PyTorch 的简化实现

下面给出一个基于 RealNVP 的简化实现,展示 NF 模型的基础训练流程。代码参考了 normflows 和 nflows 库的设计思路。

5.1 环境准备

python 复制代码
import torch
import torch.nn as nn
import torch.nn.functional as F
from sklearn.datasets import make_moons
import matplotlib.pyplot as plt
import numpy as np

5.2 定义耦合层(Coupling Layer)

python 复制代码
class CouplingLayer(nn.Module):
    """
    RealNVP 的仿射耦合层
    Args:
        in_channels: 输入特征维度
        mask: 二进制掩码,1表示不变,0表示变换
        hidden_dim: 隐藏层维度
    """
    def __init__(self, in_channels, mask, hidden_dim=64):
        super().__init__()
        self.mask = mask
        # 缩放和平移网络(只作用于 mask=0 的特征)
        self.net = nn.Sequential(
            nn.Linear(in_channels, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, 2 * in_channels)  # 输出 s 和 t
        )
        # 初始化最后一层为0,使初始变换接近恒等变换
        self.net[-1].weight.data.zero_()
        self.net[-1].bias.data.zero_()
    def forward(self, x, reverse=False):
        """
        Args:
            x: 输入张量,形状 [batch_size, in_channels]
            reverse: 是否逆向传播(采样时用)
        Returns:
            y: 输出张量
            log_det_jac: 对数雅可比行列式
        """
        x_unmasked = x * self.mask
        s_t = self.net(x_unmasked)
        s, t = torch.chunk(s_t, 2, dim=1)
        s = s * (1 - self.mask)
        t = t * (1 - self.mask)
        if not reverse:
            # 前向传播:数据 -> 潜变量
            y = x_unmasked + (x + t) * torch.exp(s) * (1 - self.mask)
            log_det_jac = torch.sum(s, dim=1)
        else:
            # 逆向传播:潜变量 -> 数据(采样时用)
            y = x_unmasked + (x * torch.exp(-s) - t) * (1 - self.mask)
            log_det_jac = -torch.sum(s, dim=1)
        return y, log_det_jac

5.3 定义完整 Flow 模型

python 复制代码
class NormalizingFlow(nn.Module):
    """
    标准化流模型:堆叠多个耦合层
    Args:
        in_channels: 输入维度
        num_layers: 流层数量
        hidden_dim: 隐藏层维度
    """
    def __init__(self, in_channels, num_layers=4, hidden_dim=64):
        super().__init__()
        # 创建交替的掩码
        masks = []
        for i in range(num_layers):
            mask = torch.zeros(in_channels)
            if i % 2 == 0:
                mask[:in_channels//2] = 1  # 前一半不变
            else:
                mask[in_channels//2:] = 1  # 后一半不变
            masks.append(mask)
        # 堆叠耦合层
        self.layers = nn.ModuleList([
            CouplingLayer(in_channels, masks[i], hidden_dim)
            for i in range(num_layers)
        ])
    def forward(self, x, reverse=False):
        """
        Args:
            x: 输入张量,形状 [batch_size, in_channels]
            reverse: 是否逆向传播
        Returns:
            y: 输出张量
            log_det_jac: 总对数雅可比行列式
        """
        log_det_jac = 0
        if not reverse:
            # 前向:数据 -> 潜变量
            for layer in self.layers:
                x, log_det = layer(x, reverse=False)
                log_det_jac += log_det
        else:
            # 逆向:潜变量 -> 数据
            for layer in reversed(self.layers):
                x, log_det = layer(x, reverse=True)
                log_det_jac += log_det
        return x, log_det_jac
    def log_prob(self, x):
        """
        计算输入的对数概率密度
        Args:
            x: 输入张量
        Returns:
            log_prob: 对数概率
        """
        # 前向传播到潜变量空间
        z, log_det = self(x, reverse=False)
        # 基分布:标准高斯
        log_prob_z = -0.5 * (z ** 2 + np.log(2 * np.pi)).sum(dim=1)
        # 变量替换公式
        return log_prob_z + log_det
    def sample(self, num_samples):
        """
        从基分布采样并生成数据
        Args:
            num_samples: 采样数量
        Returns:
            samples: 生成的样本
        """
        # 从标准高斯采样
        z = torch.randn(num_samples, self.layers[0].mask.shape[0])
        # 逆向传播到数据空间
        samples, _ = self(z, reverse=True)
        return samples

5.4 训练流程

python 复制代码
# 生成双月数据集
X, _ = make_moons(n_samples=1000, noise=0.05)
X = torch.tensor(X, dtype=torch.float32)
# 创建模型
flow = NormalizingFlow(in_channels=2, num_layers=6, hidden_dim=64)
# 优化器
optimizer = torch.optim.Adam(flow.parameters(), lr=1e-3)
# 训练循环
num_epochs = 1000
for epoch in range(num_epochs):
    optimizer.zero_grad()
    # 计算负对数似然
    loss = -flow.log_prob(X).mean()
    loss.backward()
    optimizer.step()
    if epoch % 100 == 0:
        print(f'Epoch {epoch}, Loss: {loss.item():.4f}')
# 生成样本并可视化
samples = flow.sample(1000).detach().numpy()
plt.figure(figsize=(12, 5))
# 原始数据
plt.subplot(1, 2, 1)
plt.scatter(X[:, 0], X[:, 1], alpha=0.5)
plt.title('Original Data (Two Moons)')
plt.grid(True)
# 生成样本
plt.subplot(1, 2, 2)
plt.scatter(samples[:, 0], samples[:, 1], alpha=0.5)
plt.title('Generated Samples')
plt.grid(True)
plt.tight_layout()
plt.show()

5.5 实验结果

训练 1000 epochs 后,模型应该能够很好地拟合双月分布。生成样本的分布应该与原始数据高度相似。

关键观察
  1. 损失收敛 :负对数似然应逐渐下降并收敛

  2. 样本质量 :生成样本应覆盖双月的两个分支

  3. 平滑过渡 :在两个月亮之间应该有合理的过渡区域

6. 挑战与展望:标准化流的发展方向

6.1 当前挑战

计算复杂度高

这是 NF 最大的痛点。即使通过三角矩阵优化了雅可比行列式的计算,高维数据的建模仍然面临巨大挑战。

内存消耗 :需要存储中间激活值用于反向传播(虽然可逆网络可以节省内 存)

推理速度 :相比 GAN 的单步生成,NF 的推理速度略慢(但远快于扩散模 型)

训练时间 :在 ImageNet 等大规模数据集上训练需要数天甚至数周

模型设计受限

可逆性约束限制了网络架构的灵活性:

不能用 ResNet 风格的残差连接 :虽然可以用可逆残差,但增加了复杂度

不能用 BatchNorm :虽然可以用 ActNorm,但效果不如标准 BN

不能用 Dropout :可逆网络不支持标准的 Dropout

表达能力瓶颈

尽管 RealNVP、Glow 等模型通过堆叠多层增强了表达能力,但在极高维数据(如 1024×1024 图像)上,仍难以达到扩散模型的生成质量。

6.2 未来研究方向

更高效的高维流层设计

研究更简洁、更高效的高维可逆变换:

连续标准化流 :用常微分方程建模连续变换,通过 ODE 求解器实现

多尺度架构 :通过 Split 和 Squeeze 操作处理不同分辨率

混合架构 :结合自回归、耦合、流等不同变换类型

与其他生成模型的融合

Flow + Diffusion

• 用 NF 提升扩散模型的采样效率

• 用扩散模型的去噪思路改进 NF 的变换设计

Flow + GAN

• 用 NF 为 GAN 引入显式密度估计

• 用 GAN 的对抗训练提升 NF 的生成质量

Flow + VAE

• 用 NF 建模 VAE 的后验分布

• 用 NF 提升 VAE 的潜变量质量

自适应流层与动态流

研究能根据数据分布自适应调整流层数量和类型的动态流:

门控流层 :根据输入特征选择激活哪些流层

深度流 :用 RNN 或 Transformer 动态决定流的深度

条件流 :根据外部条件(如类别、文本)调整变换

大模型与标准化流的结合

将标准化流与 Transformer、大语言模型(LLM)结合:

Text-to-Flow :用文本条件控制 NF 的生成过程

Flow-based Sequence Modeling :用 NF 建模序列数据的概率分布

LLM + Flow for Generation :结合 LLM 的语义理解能力和 NF 的可控生成

实际场景的落地优化

针对工业质检、医疗诊断、分子生成等实际场景:

轻量化模型 :通过知识蒸馏压缩 NF 模型

硬件优化 :利用 GPU/TPU 的并行计算能力

应用导向设计 :针对特定任务优化模型结构

6.3 何时选择标准化流?

根据项目需求选择合适的生成模型:

选择 NF 的场景

• ✅ 需要精确计算样本概率密度

• ✅ 需要完全可逆的编码器-解码器

• ✅ 需要在潜变量空间进行精确操控

• ✅ 训练稳定性比生成质量更重要

选择 GAN 的场景

• ✅ 追求最高的生成质量

• ✅ 生成速度是关键指标

• ✅ 不需要概率密度估计

选择扩散模型的场景

• ✅ 需要最高质量的生成

• ✅ 不介意较长的推理时间

• ✅ 任务对训练稳定性要求高

选择 VAE 的场景

• ✅ 需要连续的潜变量空间

• ✅ 计算资源有限

• ✅ 需要快速原型开发

7. 总结

标准化流是一种优雅且强大的概率生成模型,其核心价值在于精确的概率密度估计完全可逆的生成过程

虽然 NF 在生成质量上暂时不及扩散模型,但它在需要精确概率建模的场景(如异常检测、密度估计)中具有不可替代的地位。

如果你正在做一个需要计算样本概率的项目,不妨试试标准化流。说不定会有意外收获。

相关推荐
程序猿阿伟1 小时前
《游戏AI训练模拟环境:高保真可加速构建实战指南》
人工智能·游戏
狂奔蜗牛飙车1 小时前
Python学习之路-循环语句学习详解
python·学习·python学习·#python学习笔记·循环语句详解
花月mmc1 小时前
CanMV K230 波形识别——整体部署(4)
人工智能·python·嵌入式硬件·深度学习·信号处理
tel_182175397672 小时前
AOI全自动视觉检测生活用纸表面缺陷检测
人工智能·视觉检测·生活
萝卜不爱吃萝卜、2 小时前
智能体来了:从 0 到 1 搭建个人 AI 助手
人工智能
deep_drink2 小时前
【基础知识一】线性代数的核心:从矩阵变换到 SVD 终极奥义
线性代数·机器学习·矩阵
一休哥助手2 小时前
2026年2月2日人工智能早间新闻
人工智能
lang201509282 小时前
Java WebSocket API:JSR-356详解
java·python·websocket
爱吃泡芙的小白白2 小时前
CNN的FLOPs:从理论计算到实战避坑指南
人工智能·神经网络·cnn·flops