5 种生成模型对比:VAE、GAN、AR、Flow 与 Diffusion 原理及实现
对比了五种主流深度生成模型:变分自编码器(VAE)、生成对抗网络(GAN)、自回归模型(AR)、流模型(Flow)和扩散模型(Diffusion)。详细阐述了各模型的核心概念、训练损失函数及优缺点,并提供了基于 PyTorch 的代码实现示例。内容涵盖从原理到应用的完整梳理,适合希望深入理解当前深度学习生成技术的开发者阅读。

对比了五种主流深度生成模型:变分自编码器(VAE)、生成对抗网络(GAN)、自回归模型(AR)、流模型(Flow)和扩散模型(Diffusion)。详细阐述了各模型的核心概念、训练损失函数及优缺点,并提供了基于 PyTorch 的代码实现示例。内容涵盖从原理到应用的完整梳理,适合希望深入理解当前深度学习生成技术的开发者阅读。

随着 Sora、diffusion、GPT 等模型的大火,深度生成模型又成为了大家的焦点。
深度生成模型是一类强大的机器学习工具,它可以从输入数据学习其潜在的分布,进而生成与训练数据相似的新的样本数据,它在计算机视觉、密度估计、自然语言和语音识别等领域得到成功应用,并给无监督学习提供了良好的范式。

本文汇总了常用的深度学习模型,深入介绍其原理及应用:VAE(变分自编码器)、GAN(生成对抗网络)、AR(自回归模型 如 Transformer)、Flow(流模型)和 Diffusion(扩散模型)
| 模型 | 核心目标 | 原理 | 优点 | 缺点 | 应用场景 |
|---|---|---|---|---|---|
| VAE | 学习潜在空间分布,编码器 - 解码器生成与训练数据相似的样本 | 基于变分推断,将输入数据映射到潜在空间的正态分布,解码器重构数据,优化重构误差与 KL 散度 | 训练稳定,支持潜在空间插值;生成样本多样化 | 生成图像模糊;KL 散度约束可能导致信息丢失 | 数据填充、特征提取、图像修复 |
| GAN | 通过生成器与判别器的对抗训练,生成与真实数据难分的样本 | 生成器从噪声生成假数据,判别器区分真假;两者通过零和博弈优化,最终达到纳什均衡 | 生成图像细节丰富;单步推理速度快 | 训练不稳定;生成多样性不足;需精细调参 | 艺术创作、风格迁移、图像超分辨率 |
| AR | 自回归地生成序列数据,逐个预测下一个元素的概率分布 | 基于条件概率分解(如Transformer),自注意力机制捕捉长程依赖,逐像素/逐 token 生成数据 | 建模能力强,支持长序列生成;训练稳定 | 生成速度慢 (逐步采样);高维数据计算成本高 | 文本生成、时序预测、图像生成 |
| Flow | 可逆变换将简单分布转为复杂数据分布,实现精确概率密度估计 | 设计可逆神经网络层,利用变量变换公式计算数据对数似然,优化雅可比行列式。 | 支持精确密度估计;生成与重建可逆 | 高维数据下变换设计复杂;计算雅可比行列式开销大 | 语音合成、密度估计、图像生成 |
| Diffusion | 通过逐步去噪过程从高斯噪声重建数据分布,生成高质量样本 | 正向扩散(逐步加噪)与逆向扩散(学习去噪)结合,基于马尔可夫链建模条件概率 | 生成质量最高;训练稳定 | 推理速度慢;显存占用高 | 高清图像生成、多模态/视频生成 |
VAE 是在自编码器(Auto-Encoder)的基础上,结合变分推断(Variational Inference)和贝叶斯理论提出的一种深度生成模型。VAE的目标是学习一个能够生成与训练数据相似样本的模型。它假设隐变量服从某种先验分布(如标准正态分布),并通过编码器将输入数据映射到隐变量的后验分布,再通过解码器将隐变量还原成生成样本。
补充
- 先验分布的概念
假设我们要通过身高预测体重,贝叶斯理论会要求我们先对体重有一个 "初始认知"(比如平均体重 60kg,波动范围 ±10kg)。这个初始认知就是先验分布,它反映了我们在看到具体数据前对某个变量的信念
- VAE 中的先验设计
在 VAE 中,隐变量(latent variable)z 被假设服从某种简单分布(通常是标准正态分布 N(0,1))。这就像我们在做图像生成任务时,先假设所有图像的潜在特征(比如形状、颜色)都符合 "常见特征分布"

VAE 的训练损失函数包括 重构损失(如均方误差)和 KL 散度(衡量潜在分布与标准正态分布的差异)
损失函数:

优化目标:最大化证据下界(ELBO),同时保证潜在空间的结构化和连续性
补充
- 损失函数的两大核心目标
VAE 的损失函数像一个 "双面裁判",同时监督两个目标:重建能力:"你生成的图片要和原图差不多!"(重构损失)规则意识:"你不能乱想!生成规则要符合常识!"(KL 散度)
- 重构损失:像照镜子的误差
作用:确保解码器能把隐变量 z 还原成接近原图的样子。
数学形式:图像任务常用均方误差(MSE):计算每个像素点的误差平方和。文本任务常用交叉熵:衡量生成分布与真实分布的差异。
- KL 散度:用规则约束想象力
作用:强制隐变量 z 的分布接近先验(如正态分布)。
数学意义:KL 散度 = 0 时,后验分布 q(z|x) 和先验 p(z) 完全一致;数值越大,说明模型越 "不守规矩"。
- β-VAE:用旋钮调节规则强度
β 参数:像音量旋钮一样调节 KL 散度的权重。β=0 时:完全不管规则,可能生成奇形怪状的样本(如猫的耳朵长在尾巴上)。β 很大时:过于遵守规则,生成样本千篇一律(所有猫都长一个样)。
- ELBO:为什么要同时优化这两个目标?
ELBO 公式:ELBO = 重构损失 - KL 散度
(更准确的数学表达需要结合概率模型,但这里简化理解)
核心逻辑:模型需要同时做到:记住训练数据的特征(重构损失小)把这些特征压缩到 "常识空间"(KL 散度小)
这就像学习绘画时,既要准确临摹(重构),又要符合透视、比例等规则(KL 约束)。
import torch
import torch.nn as nn
import torch.nn.functional as F
class VAE(nn.Module):
def __init__(self, input_dim=784, hidden_dim=400, latent_dim=20):
super(VAE, self).__init__()
# 编码器:输入 → 隐藏层 → 均值和方差
self.encoder = nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, latent_dim * 2) # 输出均值和对数方差
)
# 解码器:潜在变量 → 隐藏层 → 重构输入
self.decoder = nn.Sequential(
nn.Linear(latent_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, input_dim),
nn.Sigmoid() # 输出像素值在 [0,1] 区间
)
def reparameterize(self, mu, log_var):
"""重参数化技巧:从 N(μ, σ²) 采样潜在变量 z"""
std = torch.exp(0.5 * log_var) # 计算标准差
eps = torch.randn_like(std) # 生成随机噪声
return mu + eps * std # 返回采样结果
def forward(self, x):
# 编码:x → μ和 logσ²
h = self.encoder(x)
mu, log_var = torch.chunk(h, 2, dim=1) # 分割为均值和方差
# 采样潜在变量 z
z = self.reparameterize(mu, log_var)
# 解码:z → 重构 x
x_recon = self.decoder(z)
x_recon, mu, log_var
():
recon_loss = F.binary_cross_entropy(x_recon, x, reduction=)
kl_div = - * torch.( + log_var - mu.() - log_var.exp())
recon_loss + kl_div
GAN 由两部分组成:**生成器(Generator)**和 判别器(Discriminator)

训练过程:


为什么使用交叉熵作为损失?
交叉熵的特性:它衡量两个概率分布的差异。当判别器对真实样本输出 1、生成样本输出 0 时,交叉熵为 0(理想状态)。
对抗的本质:生成器希望让判别器的输出分布与 "全 1" 分布更接近(对生成样本),而判别器希望让输出分布与 "真实标签分布" 更接近(对真实样本为 1,生成样本为 0)。
class Generator(nn.Module):
"""生成器:从噪声生成图像"""
def __init__(self, noise_dim=100, output_dim=784):
super(Generator, self).__init__()
self.model = nn.Sequential(
nn.Linear(noise_dim, 256),
nn.ReLU(),
nn.Linear(256, 512),
nn.ReLU(),
nn.Linear(512, output_dim),
nn.Tanh() # 输出范围 [-1,1],需在数据预处理时归一化
)
def forward(self, z):
return self.model(z).view(-1, 1, 28, 28) # 输出形状为 (B, 1, 28, 28)
class Discriminator(nn.Module):
"""判别器:区分真实图像与生成图像"""
def __init__(self, input_dim=784):
super(Discriminator, self).__init__()
self.model = nn.Sequential(
nn.Linear(input_dim, 512),
nn.LeakyReLU(0.2),
nn.Linear(512, 256),
nn.LeakyReLU(0.2),
nn.Linear(256, 1),
nn.Sigmoid() # 输出概率值
)
def forward(self, x):
x = x.view(-1, )
.model(x)
():
G = Generator()
D = Discriminator()
criterion = nn.BCELoss()
real_images, _ dataloader:
real_labels = torch.ones(real_images.size(), )
fake_labels = torch.zeros(real_images.size(), )
real_loss = criterion(D(real_images), real_labels)
z = torch.randn(real_images.size(), )
fake_images = G(z)
fake_loss = criterion(D(fake_images.detach()), fake_labels)
d_loss = real_loss + fake_loss
d_loss.backward()
optimizer_D.step()
g_loss = criterion(D(fake_images), real_labels)
g_loss.backward()
optimizer_G.step()
算法原理:自回归模型是一种基于序列数据的生成模型,它通过预测序列中下一个元素的值来生成数据。给定一个序列 (x_1, x_2, ..., x_n),自回归模型试图学习条件概率分布 P(x_t | x_{t-1}, ..., x_1),其中 t 表示序列的当前位置。AR 模型可以通过**循环神经网络(RNN)**或 Transformer 等结构实现。
如下以 Transformer 为例解析。
在深度学习的早期阶段,卷积神经网络(CNN)在图像识别和自然语言处理领域取得了显著的成功。然而,随着任务复杂度的增加,序列到序列(Seq2Seq)模型和循环神经网络(RNN)成为处理序列数据的常用方法。尽管 RNN 及其变体在某些任务上表现良好,但它们在处理长序列时容易遇到梯度消失和模型退化问题。为了解决这些问题,Transformer 模型被提出。而后的GPT、Bert等大模型都是基于 Transformer 实现了卓越的性能!

自回归模型的核心是根据过去的输出预测未来的输出。例如:
Transformer 作为自回归模型:它通过注意力机制捕捉序列中每个位置与之前所有位置的依赖关系,最终输出每个位置的预测概率分布。

为什么使用交叉熵?
分类问题的天然选择:每个位置的预测是多分类任务(选择词表中的一个词)
直观案例:生成句子 "我喜欢晴天"
输入序列:["我", "喜", "欢"]
目标序列:["喜", "欢", "晴"]
损失计算:预测第一个位置("我")的下一个词 "喜",若正确则损失低
预测第二个位置("喜")的下一个词 "欢",若正确则继续
预测第三个位置("欢")的下一个词 "晴",若错误则贡献高损失
训练中的优化技巧
掩码填充(Padding Mask):忽略输入中的无效填充符号(如 "")
学习率调度:使用 warm-up 策略避免初始训练时的不稳定
梯度裁剪:防止长序列反向传播时的梯度爆炸
与 VAE/GAN 损失的对比
class TransformerAR(nn.Module):
"""基于 Transformer 的自回归图像生成模型(Pixel Transformer)"""
def __init__(self, vocab_size=256, embed_dim=128, num_heads=4, num_layers=3):
super(TransformerAR, self).__init__()
# 输入:图像展平为序列(如 28x28 → 784 像素)
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.positional_enc = nn.Parameter(torch.randn(784, embed_dim)) # 位置编码
# Transformer 编码器(仅解码模式)
encoder_layer = nn.TransformerEncoderLayer(
d_model=embed_dim,
nhead=num_heads,
dim_feedforward=512
)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers)
# 输出层:预测每个像素的概率分布
self.fc = nn.Linear(embed_dim, vocab_size)
def forward(self, x):
# x 形状:(B, seq_len) 每个位置是像素值(0-255)
x = self.embedding(x) + self.positional_enc # 嵌入 + 位置编码
# 自注意力掩码(防止看到未来信息)
mask = torch.triu(torch.ones(784, 784), diagonal=1).bool()
# Transformer 处理
out = self.transformer(x, mask=mask)
# 预测每个像素的分布
logits = self.fc(out)
return logits
# 生成示例(逐像素生成)
def generate(self, start_token, max_len=):
generated = start_token
_ (max_len):
logits = (generated)
next_pixel = torch.multinomial(F.softmax(logits[:, -, :], dim=-), )
generated = torch.cat([generated, next_pixel], dim=)
generated
算法原理:流模型是一种基于可逆变换的深度生成模型。它通过一系列可逆的变换,将简单分布(如均匀分布或正态分布)转换为复杂的数据分布。

核心思想:用 "可逆魔法" 转换分布 流模型就像一个 "数据变形大师",它的核心是可逆变换。想象你有一团标准形状的橡皮泥(简单分布,如正态分布),通过一系列可逆向操作的手法(比如拉伸、折叠,但随时能恢复原状),把它捏成跟真实数据(如图像、语音)分布一样复杂的形状。这种 "既能变形,又能变回去" 的特性,就是流模型的关键 —— 通过可逆函数,让简单分布 "流动" 成复杂数据分布。
**生成过程类比:**假设真实数据是 "猫咪图片" 的分布,流模型先从简单的正态分布中采样一个向量 z(像随机选一块标准形状的橡皮泥),然后通过生成器 G 的一系列可逆变换(比如调整颜色、轮廓等操作),把 z 变成一张猫咪图片 x。因为变换可逆,未来也能通过反向操作,从猫咪图片还原出最初的 z。
这里直接放豆包对图中损失函数的解读


class FlowModel(nn.Module):
"""基于 RealNVP 的可逆流模型"""
def __init__(self, input_dim=784, hidden_dim=512):
super(FlowModel, self).__init__()
# 定义可逆变换的参数网络
self.scale_net = nn.Sequential(
nn.Linear(input_dim//2, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, input_dim//2)
)
self.shift_net = nn.Sequential(
nn.Linear(input_dim//2, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, input_dim//2)
)
def forward(self, x):
# 分割输入为两部分(x1 和 x2)
x1, x2 = x.chunk(2, dim=1)
# 计算缩放和偏移参数
s = self.scale_net(x1)
t = self.shift_net(x1)
# 变换 x2
z2 = x2 * torch.exp(s) + t
# 合并结果并计算对数行列式
z = torch.cat([x1, z2], dim=1)
log_det = s.sum(dim=1) # 行列式的对数
return z, log_det
def inverse(self, z):
# 逆变换:从潜在变量恢复输入
z1, z2 = z.chunk(2, dim=1)
s = self.scale_net(z1)
t = self.shift_net(z1)
x2 = (z2 - t) * torch.exp(-s)
x = torch.cat([z1, x2], dim=1)
return x
():
prior_logprob = - * (z ** ).(dim=)
(-prior_logprob - log_det).mean()
Diffusion Model(扩散模型)是一类深度生成模型,它的灵感来源于物理学中的扩散过程。与传统的生成模型(如 VAE、GAN)不同,Diffusion Model 通过模拟数据从随机噪声逐渐扩散到目标数据的过程来生成数据。这种模型在图像生成、文本生成和音频生成等领域都有出色的表现。
DDPM [NeurIPS 2020] 作为扩散模型的开山之作,证明了不用像 VAE 那样学方差,只学个均值,就能有很好的图像生成效果。
扩散模型也可以被认为像 AR,VAE 那样的传统的编码器->解码器结构,但有所不同:

DDPM [NeurIPS 2020] 作为扩散模型的开山之作,证明了不用像 VAE 那样学方差,只学个均值,就能有很好的图像生成效果

DDPM [NeurIPS 2020] 中用到的 U-Net
补充:
扩散模型中 U-Net 的一次完整前向传播(从大图少通道→小特征图多通道→大图少通道)确实对应一个时间步。这一过程在反向去噪阶段的每个时间步独立执行,具体机制可从 U-Net 流程与时间步的对应关系开始理解:
单次时间步的完整处理
输入:每个时间步的输入是 带噪声的图像(如 64×64×3) 和 时间嵌入(Time Embedding)。时间嵌入通过正弦 / 余弦函数将时间步
t编码为高维向量(如 320 维),作为条件信息传递给 U-NetU-Net 处理流程:下采样阶段:通过卷积和池化操作逐步缩小空间尺寸(如 64×64→32×32→16×16→8×8),同时增加通道数(如 3→64→128→256→512),提取不同尺度的特征(如边缘、纹理、全局结构)瓶颈层:在最小空间尺寸(如 8×8)处进行深度特征提取,捕获图像的语义信息上采样阶段:通过转置卷积或插值恢复空间尺寸(8×8→16×16→32×32→64×64),同时减少通道数(512→256→128→64→3),并通过跳跃连接融合下采样阶段的浅层特征,实现细节恢复输出:最终输出与输入尺寸相同的噪声预测图(如 64×64×3),用于更新当前时间步的去噪结果
时间步独立性 (时间嵌入)
参数共享:U-Net 在所有时间步中共享同一套网络参数,但输入的时间嵌入不同(如时间步
t=100和t=500的编码不同),使得模型能根据当前进度动态调整关注点并行计算:每个时间步的 U-Net 处理是独立的,因此可通过 跳跃采样(如 DDIM) 或 并行推理(如 Faster Diffusion) 优化效率。例如,Faster Diffusion 发现相邻时间步的编码器特征相似,通过复用特征将生成速度提升 1.8 倍
加入时间嵌入(temporal embedding)是因为扩散模型用到的 U-Net 模型的每层都是共享参数的,加入时间嵌入(位置编码)能够告诉模型走到哪一步了,什么时候提取细节,什么时候关注整体
这也是 DDPM [NeurIPS 2020] 中的一大贡献点,系统阐述了时间嵌入(temporal embedding)对于原始的扩散模型 [NIPS 2015] 的重要性
DDPM 通过残差连接、注意力机制等方式深度融入 U-Net 的每个残差块和注意力层,确保时间信息在网络中多层传播
a. 核心思想:模拟 "破坏 - 修复" 的物理过程
b. 与其他模型的区别

原始的扩散模型 [NIPS 2015]

图中损失函数的核心是衡量 "预测噪声" 与 "真实噪声" 的差距,常用均方误差(MSE):
逻辑:在正向扩散中,模型知道每个时间步加了多少真实噪声(正向加噪声过程无可学习参数,纯数学加噪声)。训练时,(可学习的)U-net 根据带噪样本预测噪声,损失函数要求预测值尽可能接近真实噪声。就像教孩子 "找不同",每次对比预测结果和真实答案,错得越多,损失越大,模型就会调整参数减少错误。
作用:通过最小化损失,U-net 学会分析带噪数据的特征,最终在反向扩散时,能用预测的噪声逐步还原出清晰数据。
class DiffusionModel(nn.Module):
"""基于 UNet 的扩散模型"""
def __init__(self, image_size=28, channels=1):
super(DiffusionModel, self).__init__()
# 定义噪声预测网络(简化版 UNet)
self.net = nn.Sequential(
nn.Conv2d(channels, 64, 3, padding=1),
nn.ReLU(),
nn.Conv2d(64, 64, 3, padding=1),
nn.ReLU(),
nn.Conv2d(64, channels, 3, padding=1)
)
# 噪声调度参数
self.num_steps = 1000
self.betas = torch.linspace(1e-4, 0.02, self.num_steps)
self.alphas = 1 - self.betas
self.alpha_bars = torch.cumprod(self.alphas, dim=0)
def forward(self, x, t):
"""预测噪声ε"""
return self.net(x)
def train_step(self, x0):
# 随机选择时间步 t
t = torch.randint(0, self.num_steps, (x0.size(0),))
# 计算加噪后的 xt
sqrt_alpha_bar = torch.sqrt(.alpha_bars[t]).view(-, , , )
sqrt_one_minus_alpha_bar = torch.sqrt( - .alpha_bars[t]).view(-, , , )
epsilon = torch.randn_like(x0)
xt = sqrt_alpha_bar * x0 + sqrt_one_minus_alpha_bar * epsilon
epsilon_pred = (xt, t)
loss = F.mse_loss(epsilon_pred, epsilon)
loss
():
xt = torch.randn(num_samples, , , )
t ((.num_steps)):
epsilon_pred = (xt, t)
xt = (xt - .betas[t] * epsilon_pred) / torch.sqrt(.alphas[t])
t > :
xt += torch.sqrt(.betas[t]) * torch.randn_like(xt)
xt
最后,简单回顾一下已经简单介绍过的 5 种常见的深度学习模型:
VAE(变分自编码器)、GAN(生成对抗网络)、AR(自回归模型 如 Transformer)、Flow(流模型)和 Diffusion(扩散模型)
我们可以看到不同模型的优缺点和适用场景:
最后提供一些潜在的问题和方法:
| 研究方向 | 核心问题 | 技术路径 | 典型应用场景 |
|---|---|---|---|
| 混合架构融合 | 单一模型难以兼顾生成质量与推理速度 | • Diffusion-GAN 混合(扩散模型生成质量+GAN 推理速度) | |
| • VAE-Transformer(压缩编码 + 序列建模) | |||
| • 流模型与自回归模型联立训练 | • 高保真图像实时生成 | ||
| • 长视频时序一致性优化 | |||
| 轻量化 | 大模型部署资源消耗过高 | • 知识蒸馏(教师 - 学生模型迁移) | |
| • 隐式神经表示(INR 参数化生成) | |||
| • 稀疏注意力机制与量化压缩 | • 移动端 AI 绘图 APP | ||
| • 边缘计算设备实时生成 | |||
| 物理约束嵌入 | 生成内容违反现实物理规律 | • 刚体动力学方程约束(牛顿力学 + 生成器) | |
| • 流体力学 PDE 求解器集成 | |||
| • 符号逻辑引导的潜在空间优化 | • 科学模拟(气象/材料) | ||
| • 机器人训练环境生成 |

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online