查看全课大纲(第 32 / 40 节)
- 1.从规则到学习:深度学习解决什么问题
- 2.表示学习与端到端训练
- 3.张量、计算图与自动微分
- 4.训练三要素:数据、模型、目标函数
- 5.从感知机到多层感知机
- 6.反向传播:手推一个两层网络
- 7.激活函数与初始化
- 8.优化器:SGD → Momentum → Adam
- 9.正则化:权重衰减、Dropout、BN/LN
- 10.卷积的直觉与参数共享
- 11.池化、感受野与下采样
- 12.经典网络演进:LeNet → AlexNet → VGG → ResNet
- 13.残差连接与归一化为什么有效
- 14.迁移学习与特征可视化
- 15.RNN 与 BPTT
- 16.LSTM/GRU:门控如何缓解梯度问题
- 17.Seq2Seq 与编码器-解码器
- 18.注意力前史:Bahdanau 加性注意力
- 19.Self-Attention:QKV 与缩放点积
- 20.多头注意力:并行的多种关系
- 21.位置编码:正弦与可学习
- 22.残差、LayerNorm 与前馈层
- 23.从零实现一个最小 Transformer block
- 24.训练稳定性、复杂度与常见误区
- 25.自监督与语言建模目标
- 26.BERT:掩码语言模型与双向编码
- 27.GPT:自回归生成与缩放定律
- 28.指令微调与对齐:SFT → RLHF/DPO
- 29.参数高效微调:LoRA 与适配器
- 30.自编码器与 VAE
- 31.GAN:对抗训练与模式崩塌
- 32.扩散模型:前向加噪与反向去噪
- 33.扩散模型的应用与采样加速
- 34.对比学习与 CLIP
- 35.ViT:把图像当序列
- 36.多模态架构:对齐、融合与生成
- 37.能力边界、幻觉与评测
- 38.PyTorch 2.x 训练循环与混合精度
- 39.实验管理、评测与复现
- 40.推理部署与成本:量化/蒸馏概览
扩散模型:前向加噪与反向去噪
小象实战讲义 · 深度学习基础:从神经网络到 Transformer
学完本节,你将能回答:扩散模型是如何通过一个逐步加噪的前向过程破坏数据,再训练一个神经网络去学习反向去噪,从而生成全新样本的?本节是第 7 章《生成模型》的第三节,会拆开三个关键机制:前向过程的封闭解、反向过程的噪声参数化、以及那个极其简洁的简化训练目标。先修:变分自编码器(VAE)的概率图模型思想。后续:下一节把扩散过程搬进潜在空间,讲清 Stable Diffusion 这类潜在扩散模型与 DDIM 采样加速。
💡 核心导读
- 扩散模型定义:一种通过训练参数化马尔可夫链(反向过程)来逆转一个固定、逐步加噪的马尔可夫链(前向过程)的生成模型。
- 前向过程:一个固定方差的、逐步向数据添加高斯噪声的过程,其任意时刻的噪声数据可以通过一个封闭公式直接采样得到。
- 反向过程:一个学习去噪的神经网络,其目标被证明可以简化为预测每一步所添加的噪声。
- 训练目标:一个去掉了权重系数(即非加权)、经重参数化简化的均方误差(MSE)损失,直接优化噪声预测的准确性。
- 采样生成:从一个纯高斯噪声开始,使用训练好的反向过程模型,逐步进行去噪采样,最终得到清晰的数据样本。
🧭 前置知识
- 变分下界(ELBO):理解变分自编码器(VAE)中用于优化证据下界的基本思想。不展开推导,见站内数学基础课。
- 高斯分布的性质:了解高斯分布的加法、乘法规则,以及重参数化技巧。
- 马尔可夫链:知道当前状态只依赖于前一状态的基本概念。
- 均方误差(MSE)损失:
torch.nn.functional.mse_loss的基本用法。
扩散模型的基本框架
扩散模型(Diffusion Probabilistic Models)属于潜变量模型,其联合分布 称为反向过程。它被定义为一个从纯噪声 开始,逐步去噪生成数据 的马尔可夫链: 其中 ,均值和方差由神经网络参数化。
与之对应的是一个固定的前向过程(或扩散过程),它也是一个马尔可夫链,逐步向数据 添加高斯噪声: 这里的 是预先定义好的方差调度表(variance schedule),控制着每一步添加的噪声量。
整个过程的概率图模型如下所示(一个双向的马尔可夫链):
前向 q : x_0 --> x_1 --> ... --> x_T (固定加噪)
反向 p_θ : x_T --> x_{T-1} --> ... --> x_0 (学习去噪)我们的目标是训练反向过程 ,使其能够逆转前向过程 ,从而从噪声 采样出逼真的数据 。
前向过程:任意步加噪的封闭解
前向过程的一个关键性质是,我们可以直接从原始数据 采样出任意中间时刻 的加噪数据 ,而无需逐步迭代 次。这极大地提高了训练效率。
定义 , 。根据高斯噪声的叠加性质,可以推导出(推导过程见论文附录):
这意味着, 是原始数据 按系数 缩放后,再加上一个方差为 的高斯噪声。我们可以使用重参数化技巧来采样:
让我们通过代码来直观感受前向过程。我们将设置一个总步数 的简化调度表,并观察一个简单数据点如何被逐步加噪。
import torch
import math
torch.manual_seed(0)
# 1. 定义前向过程参数(简化版,T=5)
T = 5
# 定义一个简单的线性增长的 beta_t,从 0.1 到 0.5
betas = torch.linspace(0.1, 0.5, T)
print("beta_t (噪声方差):", [round(b, 4) for b in betas.tolist()])
alphas = 1. - betas
print("alpha_t (保留系数):", [round(a, 4) for a in alphas.tolist()])
alphas_cumprod = torch.cumprod(alphas, dim=0) # alpha_bar_t
print("alpha_bar_t (累积保留系数):", [round(acp, 4) for acp in alphas_cumprod.tolist()])
# 2. 构造一个简单的 4 维数据点 x_0
x_0 = torch.tensor([1.0, 0.5, -0.5, -1.0])
print("\n原始数据 x_0:", [round(v, 4) for v in x_0.tolist()])
# 3. 演示逐步加噪(迭代方式)
x_prev = x_0.clone()
print("\n--- 逐步加噪演示 (迭代) ---")
for t in range(1, T+1):
beta_t = betas[t-1]
# 根据公式 q(x_t | x_{t-1}) 采样
noise = torch.randn_like(x_prev)
x_t = math.sqrt(1 - beta_t) * x_prev + math.sqrt(beta_t) * noise
print(f"t={t}: x_{t} =", [round(v, 4) for v in x_t.tolist()])
x_prev = x_t
# 4. 演示任意步加噪(封闭解,直接从 x_0 到 x_t)
print("\n--- 任意步加噪演示 (封闭解) ---")
for t in [1, 3, 5]: # 选择几个时间步
alpha_bar_t = alphas_cumprod[t-1]
# 根据公式 q(x_t | x_0) 采样
noise = torch.randn_like(x_0)
x_t_direct = math.sqrt(alpha_bar_t) * x_0 + math.sqrt(1 - alpha_bar_t) * noise
print(f"t={t}: x_{t} (直接采样) =", [round(v, 4) for v in x_t_direct.tolist()])
# 注意:由于噪声随机,这里的值与迭代结果不同,但分布相同。beta_t (噪声方差): [0.1, 0.2, 0.3, 0.4, 0.5]
alpha_t (保留系数): [0.9, 0.8, 0.7, 0.6, 0.5]
alpha_bar_t (累积保留系数): [0.9, 0.72, 0.504, 0.3024, 0.1512]
原始数据 x_0: [1.0, 0.5, -0.5, -1.0]
--- 逐步加噪演示 (迭代) ---
t=1: x_1 = [1.436, 0.3816, -1.1633, -0.7689]
t=2: x_2 = [0.7994, -0.2842, -0.8601, -0.313]
t=3: x_3 = [0.2749, -0.4587, -1.0464, -0.1621]
t=4: x_4 = [-0.3289, 0.3408, -1.488, -0.048]
t=5: x_5 = [-0.633, 0.5048, -1.6829, -1.101]
--- 任意步加噪演示 (封闭解) ---
t=1: x_1 (直接采样) = [1.0658, 0.9349, -0.1771, -0.7037]
t=3: x_3 (直接采样) = [0.8451, 1.245, -1.2638, -1.2671]
t=5: x_5 (直接采样) = [0.3696, -0.4675, 0.2834, -1.5981]可以看到,随着 增大,alpha_bar_t 单调下降, 中的原始信号 成分越来越弱,噪声成分越来越强。上面的 只是便于手算的玩具设置;在真实配置(,且设计使 )下, 几乎完全由噪声构成,其分布接近标准高斯分布 。这正是我们期望的:前向过程最终把任何数据都“扩散”成了纯噪声。
反向过程:从噪声预测到去噪均值
训练扩散模型需要优化变分下界(ELBO)。经过推导(详见 Denoising Diffusion Probabilistic Models(Ho et al., 2020,arXiv:2006.11239)论文第 2、3 节),损失函数可以写成若干 KL 散度项之和。其中最关键的一项 是让反向过程 去匹配一个可计算的前向过程后验分布 。
这个后验分布也是一个高斯分布: 其中均值和方差为:
我们的反向过程也参数化为高斯分布 。论文发现,将方差 设为固定的 或 效果相似,且能简化训练。因此,训练的关键在于让 去逼近 。
然而, 依赖于未知的原始数据 。我们可以利用前向过程的封闭解 将 表示为 。将其代入 的表达式,经过化简(论文正文把 代回式(7)),可以得到一个极其简洁的形式:
这个公式揭示了本质:前向过程后验的均值 ,是在当前噪声数据 的基础上,减去一个由所加噪声 缩放后的项。
因此,一个自然的参数化策略是:让神经网络去预测这个噪声 。即,我们定义: 其中 是一个以 和时间步 为输入,输出与 同维度的神经网络,其目标就是预测在前向过程中添加到 上的噪声 。
将这种参数化代入 KL 散度项 ,并忽略一些权重系数后,可以得到一个非常简单的训练目标(论文式(14)):
这就是扩散模型训练的核心:随机采样一个时间步 、一个数据样本 和一个随机噪声 ,构造出加噪样本 ,然后让神经网络 去预测这个噪声 ,并用均方误差(MSE)作为损失函数。
下面我们用代码实现一个极简的噪声预测网络和训练步骤。
import torch
import torch.nn as nn
import torch.nn.functional as F
torch.manual_seed(0)
# 1. 定义超参数
input_dim = 4 # 数据维度
hidden_dim = 16
T = 5 # 总扩散步数(为了演示简化)
batch_size = 2
# 2. 定义调度参数(与之前一致)
betas = torch.linspace(0.1, 0.5, T)
alphas = 1. - betas
alphas_cumprod = torch.cumprod(alphas, dim=0)
# 用于计算的辅助张量,扩展维度便于批量操作
sqrt_alphas_cumprod = torch.sqrt(alphas_cumprod)
sqrt_one_minus_alphas_cumprod = torch.sqrt(1. - alphas_cumprod)
# 3. 定义一个简单的噪声预测网络
class SimpleNoisePredictor(nn.Module):
def __init__(self, input_dim, hidden_dim):
super().__init__()
# 时间步嵌入层:将标量t映射为向量
self.time_embed = nn.Sequential(
nn.Linear(1, hidden_dim),
nn.SiLU(),
nn.Linear(hidden_dim, hidden_dim)
)
# 主网络:将 [x_t, time_emb] 拼接后预测噪声
self.main = nn.Sequential(
nn.Linear(input_dim + hidden_dim, hidden_dim),
nn.SiLU(),
nn.Linear(hidden_dim, hidden_dim),
nn.SiLU(),
nn.Linear(hidden_dim, input_dim) # 输出预测的噪声,维度与x_t相同
)
def forward(self, x, t):
# x: (batch, input_dim)
# t: (batch, ) 或标量,表示批次内每个样本的时间步
t_emb = self.time_embed(t.view(-1, 1).float()) # (batch, hidden_dim)
x_in = torch.cat([x, t_emb], dim=-1) # (batch, input_dim+hidden_dim)
return self.main(x_in) # (batch, input_dim)
# 4. 实例化模型和优化器
model = SimpleNoisePredictor(input_dim, hidden_dim)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
# 5. 模拟一个极简的训练步骤
def show(name, batch):
"""逐样本打印,避免单行过长在手机端横向溢出"""
for i, row in enumerate(batch.tolist()):
print(f" {name}[{i}]:", [round(v, 4) for v in row])
print("--- 模拟单次训练步骤 ---")
# 生成随机"数据"
x_0_batch = torch.randn(batch_size, input_dim)
print("原始数据批次 x_0 shape:", x_0_batch.shape)
show("x_0", x_0_batch)
# 随机采样时间步 t (从 1 到 T)
t_batch = torch.randint(1, T+1, (batch_size,))
print("采样的时间步 t:", t_batch.tolist())
# 采样随机噪声 epsilon
epsilon_batch = torch.randn_like(x_0_batch)
show("epsilon", epsilon_batch)
# 根据前向过程公式构造 x_t
# 注意:需要根据每个样本不同的 t 选取对应的 alpha_bar_t
alpha_bar_t_batch = alphas_cumprod[t_batch - 1].unsqueeze(-1) # (batch, 1)
sqrt_alpha_bar_t = torch.sqrt(alpha_bar_t_batch)
sqrt_one_minus_alpha_bar_t = torch.sqrt(1. - alpha_bar_t_batch)
x_t_batch = sqrt_alpha_bar_t * x_0_batch + sqrt_one_minus_alpha_bar_t * epsilon_batch
show("x_t", x_t_batch)
# 前向传播:模型预测噪声
predicted_epsilon = model(x_t_batch, t_batch)
show("epsilon_theta", predicted_epsilon)
# 计算简化损失 L_simple (MSE)
loss = F.mse_loss(predicted_epsilon, epsilon_batch)
print(f"损失值 (MSE): {loss.item():.6f}")
# 反向传播与优化(演示用,这里只做一步)
optimizer.zero_grad()
loss.backward()
# 模拟一次参数更新
# optimizer.step()
print("训练步骤完成(优化器 step 已注释,仅演示流程)。")--- 模拟单次训练步骤 ---
原始数据批次 x_0 shape: torch.Size([2, 4])
x_0[0]: [1.1062, -1.7757, -2.7676, -1.236]
x_0[1]: [-0.5107, -0.7649, -0.0038, -1.572]
采样的时间步 t: [4, 5]
epsilon[0]: [1.4628, 0.1323, 0.5416, -0.0844]
epsilon[1]: [0.0426, 0.2129, -0.1061, -0.2153]
x_t[0]: [1.8301, -0.866, -1.0696, -0.7502]
x_t[1]: [-0.1594, -0.1013, -0.0992, -0.8096]
epsilon_theta[0]: [0.1376, 0.2833, -0.3778, 0.1369]
epsilon_theta[1]: [0.1484, 0.2555, -0.3263, 0.1038]
损失值 (MSE): 0.354541
训练步骤完成(优化器 step 已注释,仅演示流程)。可以看到,模型在初始随机权重下预测的噪声 epsilon_theta 与真实的 epsilon 几乎不相关,损失值停在 0.3545 左右——与“直接预测全零”的随机水平(本批噪声的均方值约 0.32)同量级,说明这一步模型还没学到任何东西。训练过程就是通过大量这样的样本,不断调整模型参数,使其预测的噪声越来越准。
采样生成:从噪声到数据
训练好噪声预测模型 后,我们就可以进行采样,从随机噪声 开始,逐步生成数据 。
采样算法(论文 Algorithm 2)如下:
- 采样
- for do:
采样 if , else- end for
- return
其中 是反向过程的方差,我们可以选择 或 。这一步添加的噪声 是为了引入随机性,对应着生成过程的不确定性。当 时,通常不再添加噪声。
下面的代码演示了使用一个未经训练的模型进行采样的过程。由于模型是随机的,生成的“数据”也将是随机的,但这展示了完整的采样流程。
import torch
import torch.nn as nn
import math
torch.manual_seed(42) # 使用不同的种子以便观察
# 1. 重新定义模型和调度参数(确保本代码块自包含)
class SimpleNoisePredictor(nn.Module):
def __init__(self, input_dim=4, hidden_dim=16):
super().__init__()
self.time_embed = nn.Sequential(nn.Linear(1, hidden_dim), nn.SiLU(), nn.Linear(hidden_dim, hidden_dim))
self.main = nn.Sequential(
nn.Linear(input_dim + hidden_dim, hidden_dim),
nn.SiLU(),
nn.Linear(hidden_dim, hidden_dim),
nn.SiLU(),
nn.Linear(hidden_dim, input_dim)
)
def forward(self, x, t):
t_emb = self.time_embed(t.view(-1, 1).float())
return self.main(torch.cat([x, t_emb], dim=-1))
T = 5
betas = torch.linspace(0.1, 0.5, T)
alphas = 1. - betas
alphas_cumprod = torch.cumprod(alphas, dim=0)
# 计算反向过程方差,这里演示两种选择
sigma_t_beta = torch.sqrt(betas) # 对应 sigma_t^2 = beta_t
# 计算 tilde_beta_t
tilde_beta_t = (1. - torch.cat([torch.tensor([1.0]), alphas_cumprod[:-1]])) / (1. - alphas_cumprod) * betas
sigma_t_tilde = torch.sqrt(tilde_beta_t) # 对应 sigma_t^2 = tilde_beta_t
model = SimpleNoisePredictor()
model.eval() # 切换到评估模式
# 2. 采样函数(DDPM 的单步采样:均值 + 按 sigma_t 加噪)
def ddpm_sample_step(x_t, t, use_tilde_var=False):
"""从 x_t 采样 x_{t-1}。使用未训练的模型,仅演示流程。"""
with torch.no_grad():
predicted_noise = model(x_t, torch.tensor([t]))
# 计算均值部分
coef1 = 1. / math.sqrt(alphas[t-1])
coef2 = betas[t-1] / math.sqrt(1. - alphas_cumprod[t-1])
mean = coef1 * (x_t - coef2 * predicted_noise)
# 添加随机噪声
sigma = sigma_t_tilde[t-1] if use_tilde_var else sigma_t_beta[t-1]
if t > 1:
z = torch.randn_like(x_t)
x_prev = mean + sigma * z
else:
x_prev = mean # 最后一步通常不加噪
return x_prev
# 3. 执行采样流程(从纯噪声开始)
print("--- 扩散模型采样生成演示 (使用未训练模型) ---")
x_t = torch.randn(1, 4) # 从标准高斯噪声开始,batch_size=1
print(f"初始噪声 x_{T}:", [[round(v, 4) for v in row] for row in x_t.tolist()])
for t in range(T, 0, -1):
x_t = ddpm_sample_step(x_t, t, use_tilde_var=False)
print(f"去噪后 x_{t-1}:", [[round(v, 4) for v in row] for row in x_t.tolist()])
print("\n由于模型未经训练,预测的噪声不准,因此生成的 x_0 看起来仍是随机噪声。")
print("训练有素的模型能够准确预测噪声,从而逐步去除噪声,还原出有意义的数据分布。")--- 扩散模型采样生成演示 (使用未训练模型) ---
初始噪声 x_5: [[0.1005, 0.2325, 0.7064, 0.3693]]
去噪后 x_4: [[-0.3938, -0.6571, 0.591, 0.7774]]
去噪后 x_3: [[-1.4524, -1.3522, 1.3453, -0.3695]]
去噪后 x_2: [[-1.0207, -2.1272, 1.4005, -0.4243]]
去噪后 x_1: [[-1.6041, -2.7243, 1.5934, -0.2372]]
去噪后 x_0: [[-1.6553, -2.9259, 1.5998, -0.3255]]
由于模型未经训练,预测的噪声不准,因此生成的 x_0 看起来仍是随机噪声。
训练有素的模型能够准确预测噪声,从而逐步去除噪声,还原出有意义的数据分布。(注:模型未经训练时 epsilon_theta 的输出接近 0,采样均值退化为 ,于是 x_t 的幅度被逐步放大,得到的结果自然没有意义。这正说明训练的重要性:只有预测出非平凡的噪声,反向过程才能把纯噪声还原成数据。)
📝 动手练一练
问题:在采样算法中,反向过程的方差 可以选择 或 。请编写代码计算在 T=5,betas = [0.1, 0.2, 0.3, 0.4, 0.5] 的情况下,每个时间步 对应的 和 的值,并说明哪一个通常更小。这如何影响采样生成的结果?
👉 点击查看参考答案
思路:根据公式 进行计算。 是 的累积乘积。 是前向过程后验 的方差,它通常比前向过程单步方差 要小,因为它利用了 的信息。在采样中,使用更小的方差意味着每一步添加的随机噪声更少,采样过程可能更“确定”,但多样性可能降低。
import torch
torch.manual_seed(0)
T = 5
betas = torch.linspace(0.1, 0.5, T)
alphas = 1. - betas
alphas_cumprod = torch.cumprod(alphas, dim=0)
# 计算 tilde_beta_t,注意处理 t=1 时 alpha_bar_0 定义为 1
alpha_bar_prev = torch.cat([torch.tensor([1.0]), alphas_cumprod[:-1]])
tilde_beta_t = (1. - alpha_bar_prev) / (1. - alphas_cumprod) * betas
print("时间步 t | beta_t | tilde_beta_t")
print("-" * 40)
for t in range(T):
print(f" {t+1:2d} | {betas[t]:.6f} | {tilde_beta_t[t]:.6f}")
print(f"\n结论:对于所有 t>1,tilde_beta_t 均小于 beta_t。")
print("在采样时,使用 tilde_beta_t 作为方差会添加更少的随机噪声,")
print("可能使生成过程更稳定,但样本多样性可能略低于使用 beta_t。")时间步 t | beta_t | tilde_beta_t
----------------------------------------
1 | 0.100000 | 0.000000
2 | 0.200000 | 0.071429
3 | 0.300000 | 0.169355
4 | 0.400000 | 0.284404
5 | 0.500000 | 0.410933
结论:对于所有 t>1,tilde_beta_t 均小于 beta_t。
在采样时,使用 tilde_beta_t 作为方差会添加更少的随机噪声,
可能使生成过程更稳定,但样本多样性可能略低于使用 beta_t。常见错误
| 错误理解或写法 | 现象或报错 | 原因 | 正确做法 |
|---|---|---|---|
在训练时对每个样本使用相同的时间步 t | 模型无法学习到不同噪声程度下的去噪能力,生成质量差。 | 损失函数 的期望包含对时间步 的均匀采样。每个 batch 中的样本应关联不同的 t。 | 在每步训练中,使用 torch.randint(1, T+1, (batch_size,)) 为 batch 内每个样本独立采样时间步。 |
采样时忘记在 的步骤添加随机噪声 z | 同一初始噪声永远得到同一结果,样本多样性显著低于预期。 | 反向过程 是一个高斯分布,采样时应从该分布中随机抽取;只取均值等于偷偷把方差改成了 0,与训练时假设的方差设定不一致。 | 若要做 DDPM 采样,严格按算法在 时采样 并乘上 加到均值上;若本来就想做确定性采样(DDIM),应显式说明并按 DDIM 的公式实现。 |
混淆 alpha_bar_t 与 alpha_t 在计算公式中的使用 | 前向加噪或损失计算错误,导致训练不收敛。 | alpha_bar_t () 是累积乘积,用于计算 。alpha_t () 是单步系数,用于计算 。 | 仔细对照论文公式:前向采样用 和 ;计算反向均值用 和 。 |
未对时间步 t 进行嵌入(embedding)直接输入网络 | 模型无法区分不同时间步,去噪性能严重下降。 | 时间步 t 是离散标量,网络难以直接利用其信息。需要将其映射到连续向量空间。 | 设计一个时间步嵌入层(如通过线性层+激活函数),将标量 t 转换为与网络隐藏层维度匹配的向量,再与输入拼接或相加。 |
本章小结
- 扩散模型通过训练一个反向去噪的马尔可夫链,来逆转一个固定的前向加噪链。
- 前向过程允许从原始数据 直接采样任意时刻的加噪数据 ,公式为 。
- 反向过程的核心是训练一个噪声预测网络 ,其训练目标简化为预测前向过程所加噪声的均方误差(MSE)。
- 采样时,从纯噪声开始,利用训练好的噪声预测网络,按照公式 逐步去噪,最终生成数据。
- 反向过程的方差 可以固定为 或 ,后者通常更小,可能使采样更稳定。
📋 行动清单
- 修改前向过程代码,将
T增加到 100,并观察alpha_bar_t在最后几步是否接近 0,理解为何 的分布接近标准正态分布。 - 在“动手练一练”的代码中,把线性
betas换成余弦调度(定义见 Improved Denoising Diffusion Probabilistic Models,Nichol & Dhariwal, 2021,arXiv:2102.09672 的式(17)),重新计算并比较beta_t与tilde_beta_t的变化趋势。 - 查阅 PyTorch 官方文档,了解
nn.Embedding层,思考如何用它来替代我们自定义的time_embed线性层序列,对离散时间步进行嵌入。
延伸阅读
本段 PyTorch 官方文档/教程条目的框架出处:PyTorch: An Imperative Style, High-Performance Deep Learning Library(Paszke et al., 2019,arXiv:1912.01703);页面内容以 PyTorch 2.14.1 为准。
- Denoising Diffusion Probabilistic Models(Ho et al., 2020,arXiv:2006.11239)—— 本节核心论文,提出了噪声预测的参数化和简化损失。
- Improved Denoising Diffusion Probabilistic Models(Nichol & Dhariwal, 2021,arXiv:2102.09672)—— 余弦噪声调度与可学习方差。
- PyTorch 官方文档 mse_loss:https://pytorch.org/docs/stable/generated/torch.nn.functional.mse_loss.html —— 均方误差损失函数的实现。
—— 小象教研组