01 - 概率论基础¶
学习时间: 3小时 重要性: ⭐⭐⭐⭐⭐ 扩散模型的数学基石
🎯 学习目标¶
完成本章后,你将能够: - 理解高斯分布及其在扩散模型中的核心作用 - 掌握条件概率和贝叶斯定理的直观意义 - 理解KL散度的含义及其在训练目标中的作用 - 用代码验证概率论概念
1. 高斯分布(正态分布)¶
1.1 直观理解¶
高斯分布是扩散模型中最重要的概率分布,没有之一。
为什么重要? - 扩散模型的前向过程就是不断添加高斯噪声 - 反向过程学习的是去噪分布,也是高斯分布 - 数学上易于处理,有闭合形式的解
直观理解: 想象你向靶心射箭,大多数箭会集中在靶心附近,少数会偏离。这种"中间多、两边少"的分布就是高斯分布。
1.2 数学定义¶
一维高斯分布的概率密度函数:
其中: - \(\mu\):均值(分布的中心位置) - \(\sigma\):标准差(分布的"宽度") - \(\sigma^2\):方差
1.3 多维高斯分布¶
对于 \(d\) 维向量 \(\mathbf{x}\):
其中: - \(\boldsymbol{\mu}\):\(d\) 维均值向量 - \(\Sigma\):\(d \times d\) 协方差矩阵
扩散模型中:图像可以看作是高维向量(例如 \(32 \times 32 \times 3 = 3072\) 维),每个像素值服从高斯分布。
1.4 代码实践¶
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
# 设置随机种子,保证可重复
np.random.seed(42)
# ========== 1. 一维高斯分布 ==========
print("=" * 50)
print("一维高斯分布")
print("=" * 50)
# 定义参数
mu = 0 # 均值
sigma = 1 # 标准差
# 采样
samples = np.random.normal(mu, sigma, 10000)
# 绘制直方图和理论曲线
plt.figure(figsize=(12, 4))
plt.subplot(1, 3, 1)
plt.hist(samples, bins=50, density=True, alpha=0.7, label='Samples')
# 理论曲线
x = np.linspace(-4, 4, 100)
y = stats.norm.pdf(x, mu, sigma)
plt.plot(x, y, 'r-', linewidth=2, label='Theory')
plt.title(f'Gaussian: μ={mu}, σ={sigma}')
plt.xlabel('x')
plt.ylabel('Probability Density')
plt.legend()
plt.grid(True, alpha=0.3)
# ========== 2. 不同参数的比较 ==========
plt.subplot(1, 3, 2)
params = [(0, 0.5), (0, 1), (0, 2), (2, 1)]
colors = ['blue', 'red', 'green', 'orange']
for (mu, sigma), color in zip(params, colors): # zip按位置配对
y = stats.norm.pdf(x, mu, sigma)
plt.plot(x, y, color=color, linewidth=2,
label=f'μ={mu}, σ={sigma}')
plt.title('Different Parameters')
plt.xlabel('x')
plt.ylabel('Probability Density')
plt.legend()
plt.grid(True, alpha=0.3)
# ========== 3. 二维高斯分布 ==========
plt.subplot(1, 3, 3)
# 生成二维高斯样本
mean = [0, 0]
cov = [[1, 0.5], [0.5, 1]] # 协方差矩阵
samples_2d = np.random.multivariate_normal(mean, cov, 1000)
plt.scatter(samples_2d[:, 0], samples_2d[:, 1], alpha=0.5, s=10)
plt.title('2D Gaussian Distribution')
plt.xlabel('x1')
plt.ylabel('x2')
plt.axis('equal')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('gaussian_distribution.png', dpi=150)
plt.show()
# 打印统计信息
print(f"样本均值: {np.mean(samples):.4f} (理论: {mu})")
print(f"样本标准差: {np.std(samples):.4f} (理论: {sigma})")
运行结果分析: - 样本均值接近理论均值 \(\mu=0\) - 样本标准差接近理论标准差 \(\sigma=1\) - 直方图形状与理论曲线吻合
1.5 高斯分布的性质¶
性质1:线性变换 如果 \(X \sim \mathcal{N}(\mu, \sigma^2)\),那么 \(aX + b \sim \mathcal{N}(a\mu + b, a^2\sigma^2)\)
性质2:独立高斯变量的和 如果 \(X \sim \mathcal{N}(\mu_1, \sigma_1^2)\),\(Y \sim \mathcal{N}(\mu_2, \sigma_2^2)\),且独立,那么: $\(X + Y \sim \mathcal{N}(\mu_1 + \mu_2, \sigma_1^2 + \sigma_2^2)\)$
扩散模型中的应用:
# 在扩散模型中,加噪过程就是不断添加高斯噪声
x_0 = np.random.normal(0, 1, 1000) # 原始数据
epsilon = np.random.normal(0, 1, 1000) # 噪声
# x_t = sqrt(1-beta) * x_{t-1} + sqrt(beta) * epsilon
beta = 0.1
x_t = np.sqrt(1-beta) * x_0 + np.sqrt(beta) * epsilon
print(f"x_0: mean={np.mean(x_0):.4f}, std={np.std(x_0):.4f}")
print(f"x_t: mean={np.mean(x_t):.4f}, std={np.std(x_t):.4f}")
# x_t 仍然是高斯分布!
2. 条件概率与贝叶斯定理¶
2.1 条件概率¶
定义:在事件 \(B\) 发生的条件下,事件 \(A\) 发生的概率:
扩散模型中的应用: - \(p(x_t | x_{t-1})\):已知上一步 \(x_{t-1}\),当前步 \(x_t\) 的概率 - \(p(x_{t-1} | x_t)\):已知当前步 \(x_t\),上一步 \(x_{t-1}\) 的概率(反向过程)
2.2 贝叶斯定理¶
直观理解: 贝叶斯定理告诉我们如何"反转"条件概率。在扩散模型中,我们需要从 \(p(x_t | x_{t-1})\) 推导出 \(p(x_{t-1} | x_t)\)。
2.3 代码示例¶
# 用简单的例子理解条件概率
# 假设有两个盒子
# 盒子A:3红球,2蓝球
# 盒子B:1红球,4蓝球
# 随机选择一个盒子,然后抽一个球
P_A = 0.5 # 选择盒子A的概率
P_B = 0.5 # 选择盒子B的概率
# 条件概率:P(红|A) = 3/5, P(蓝|A) = 2/5
P_red_given_A = 3/5
P_blue_given_A = 2/5
# 条件概率:P(红|B) = 1/5, P(蓝|B) = 4/5
P_red_given_B = 1/5
P_blue_given_B = 4/5
# 联合概率:P(红, A) = P(红|A) * P(A)
P_red_and_A = P_red_given_A * P_A
P_red_and_B = P_red_given_B * P_B
# 边缘概率:P(红) = P(红, A) + P(红, B)
P_red = P_red_and_A + P_red_and_B
print(f"P(红) = {P_red:.4f}")
# 贝叶斯定理:P(A|红) = P(红|A) * P(A) / P(红)
P_A_given_red = P_red_given_A * P_A / P_red
print(f"P(A|红) = {P_A_given_red:.4f}")
# 验证:P(A|红) + P(B|红) = 1
P_B_given_red = P_red_given_B * P_B / P_red
print(f"P(B|红) = {P_B_given_red:.4f}")
print(f"Sum = {P_A_given_red + P_B_given_red:.4f}")
3. KL散度(Kullback-Leibler Divergence)¶
3.1 直观理解¶
KL散度衡量两个概率分布之间的"差异"。
扩散模型中的应用: - 训练目标是最小化模型分布与真实分布之间的KL散度 - 用于衡量生成的图像分布与真实图像分布的差异
注意:KL散度不是距离,因为它不对称:\(D_{KL}(P||Q) \neq D_{KL}(Q||P)\)
3.2 数学定义¶
对于离散分布: $\(D_{KL}(P||Q) = \sum_i P(i) \log\frac{P(i)}{Q(i)}\)$
对于连续分布: $\(D_{KL}(P \Vert Q) = \int p(x) \log\frac{p(x)}{q(x)} dx\)$
3.3 代码实践¶
import numpy as np
import matplotlib.pyplot as plt
# ========== KL散度计算 ==========
def kl_divergence(p, q):
"""
计算KL散度 D_KL(P||Q)
p, q: 概率分布(已归一化)
"""
# 避免log(0),添加小量
epsilon = 1e-10
p = np.clip(p, epsilon, 1)
q = np.clip(q, epsilon, 1)
return np.sum(p * np.log(p / q))
# 创建两个高斯分布
x = np.linspace(-5, 5, 1000)
# 分布P:标准正态
mu_p, sigma_p = 0, 1
p = stats.norm.pdf(x, mu_p, sigma_p)
p = p / np.sum(p) # 归一化
# 分布Q:不同的均值
mu_q, sigma_q = 1, 1
q = stats.norm.pdf(x, mu_q, sigma_q)
q = q / np.sum(q)
# 计算KL散度
kl_pq = kl_divergence(p, q)
kl_qp = kl_divergence(q, p)
print(f"D_KL(P||Q) = {kl_pq:.6f}")
print(f"D_KL(Q||P) = {kl_qp:.6f}")
print(f"不对称!D_KL(P||Q) != D_KL(Q||P)")
# 可视化
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(x, p, 'b-', linewidth=2, label=f'P: μ={mu_p}, σ={sigma_p}')
plt.plot(x, q, 'r-', linewidth=2, label=f'Q: μ={mu_q}, σ={sigma_q}')
plt.fill_between(x, p, alpha=0.3)
plt.fill_between(x, q, alpha=0.3)
plt.title(f'KL(P||Q) = {kl_pq:.4f}')
plt.xlabel('x')
plt.ylabel('Probability')
plt.legend()
plt.grid(True, alpha=0.3)
# 比较不同差异程度的KL散度
plt.subplot(1, 2, 2)
mu_q_values = np.linspace(-3, 3, 50)
kl_values = []
for mu_q in mu_q_values:
q = stats.norm.pdf(x, mu_q, sigma_q)
q = q / np.sum(q)
kl = kl_divergence(p, q)
kl_values.append(kl)
plt.plot(mu_q_values, kl_values, 'g-', linewidth=2)
plt.axvline(x=0, color='r', linestyle='--', alpha=0.5, label='P=Q')
plt.xlabel('μ_Q (Q的均值)')
plt.ylabel('KL Divergence')
plt.title('KL散度随分布差异的变化')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('kl_divergence.png', dpi=150)
plt.show()
print("\n观察:当两个分布越接近时,KL散度越小")
print("当P=Q时,KL散度为0")
3.4 高斯分布的KL散度(闭合形式)¶
对于两个高斯分布 \(P = \mathcal{N}(\mu_1, \sigma_1^2)\) 和 \(Q = \mathcal{N}(\mu_2, \sigma_2^2)\):
扩散模型中的重要性: 扩散模型的训练目标可以转化为计算高斯分布之间的KL散度,而高斯分布的KL散度有闭合形式,这使得计算变得可行!
def kl_gaussian(mu1, sigma1, mu2, sigma2):
"""
计算两个高斯分布之间的KL散度 D_KL(N1||N2)
"""
return (np.log(sigma2 / sigma1) +
(sigma1**2 + (mu1 - mu2)**2) / (2 * sigma2**2) - 0.5)
# 验证
mu1, sigma1 = 0, 1
mu2, sigma2 = 1, 2
kl_closed = kl_gaussian(mu1, sigma1, mu2, sigma2)
print(f"闭合形式计算: D_KL = {kl_closed:.6f}")
# 数值计算验证
x = np.linspace(-10, 10, 10000)
p = stats.norm.pdf(x, mu1, sigma1)
q = stats.norm.pdf(x, mu2, sigma2)
p = p / np.sum(p)
q = q / np.sum(q)
kl_numerical = kl_divergence(p, q)
print(f"数值计算: D_KL = {kl_numerical:.6f}")
4. 重参数化技巧(Reparameterization Trick)¶
4.1 问题背景¶
在扩散模型中,我们需要从条件分布 \(p(x_t | x_0)\) 中采样。直接采样需要计算复杂的积分,但重参数化技巧可以让我们用标准高斯噪声来表示。
4.2 核心思想¶
如果 \(x \sim \mathcal{N}(\mu, \sigma^2)\),那么: $\(x = \mu + \sigma \cdot \epsilon, \quad \epsilon \sim \mathcal{N}(0, 1)\)$
好处: - 将随机性从参数中分离出来 - 可以使用梯度下降优化 - 扩散模型前向过程的核心技巧
4.3 代码验证¶
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
# 目标分布参数
mu = 2.0
sigma = 1.5
# 方法1:直接从目标分布采样
samples_direct = np.random.normal(mu, sigma, 10000)
# 方法2:重参数化技巧
epsilon = np.random.normal(0, 1, 10000)
samples_reparam = mu + sigma * epsilon
# 比较两种方法
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.hist(samples_direct, bins=50, density=True, alpha=0.5,
label='Direct Sampling', color='blue')
plt.hist(samples_reparam, bins=50, density=True, alpha=0.5,
label='Reparameterization', color='red')
plt.title('Sampling Comparison')
plt.xlabel('x')
plt.ylabel('Density')
plt.legend()
plt.grid(True, alpha=0.3)
# 统计比较
plt.subplot(1, 2, 2)
stats_data = {
'Direct': [np.mean(samples_direct), np.std(samples_direct)],
'Reparam': [np.mean(samples_reparam), np.std(samples_reparam)],
'Theory': [mu, sigma]
}
x_pos = np.arange(2)
width = 0.25
for i, (method, values) in enumerate(stats_data.items()): # enumerate同时获取索引和元素
plt.bar(x_pos + i*width, values, width, label=method)
plt.xlabel('Statistic')
plt.ylabel('Value')
plt.title('Mean and Std Comparison')
plt.xticks(x_pos + width, ['Mean', 'Std'])
plt.legend()
plt.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig('reparameterization.png', dpi=150)
plt.show()
print("统计比较:")
print(f"直接采样: mean={np.mean(samples_direct):.4f}, std={np.std(samples_direct):.4f}")
print(f"重参数化: mean={np.mean(samples_reparam):.4f}, std={np.std(samples_reparam):.4f}")
print(f"理论值: mean={mu:.4f}, std={sigma:.4f}")
4.4 在扩散模型中的应用¶
# 扩散模型中的前向过程
# x_t = sqrt(alpha_t) * x_0 + sqrt(1 - alpha_t) * epsilon
def q_sample(x_0, t, alphas):
"""
从 q(x_t | x_0) 采样
使用重参数化技巧
参数:
x_0: 原始数据
t: 时间步
alphas: 累积alpha值
"""
# 计算均值和方差
alpha_t = alphas[t]
mean = np.sqrt(alpha_t) * x_0
std = np.sqrt(1 - alpha_t)
# 重参数化采样
epsilon = np.random.normal(0, 1, x_0.shape)
x_t = mean + std * epsilon
return x_t, epsilon
# 示例
x_0 = np.array([1.0, 2.0, 3.0]) # 原始数据
alphas = np.linspace(0.99, 0.01, 1000) # 随时间递减的alpha
alphas_cumprod = np.cumprod(alphas)
# 在不同时间步采样
for t in [0, 250, 500, 750, 999]:
x_t, eps = q_sample(x_0, t, alphas_cumprod)
noise_level = np.sqrt(1 - alphas_cumprod[t])
print(f"t={t:4d}: x_t={x_t}, noise_level={noise_level:.4f}")
5. 本章总结¶
核心概念回顾¶
- 高斯分布
- 扩散模型的基础分布
- 线性变换后仍是高斯分布
-
便于数学处理
-
条件概率与贝叶斯定理
- 理解前向和反向过程的关键
-
从 \(p(x_t|x_{t-1})\) 推导 \(p(x_{t-1}|x_t)\)
-
KL散度
- 训练目标的数学基础
- 衡量两个分布的差异
-
高斯分布有闭合形式
-
重参数化技巧
- 使梯度下降成为可能
- 扩散模型前向过程的核心
关键公式¶
| 概念 | 公式 |
|---|---|
| 高斯分布 | \(p(x) = \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{(x-\mu)^2}{2\sigma^2}\right)\) |
| 贝叶斯定理 | \(P(A \mid B) = \frac{P(B \mid A)P(A)}{P(B)}\) |
| KL散度 | \(D_{KL}(P \Vert Q) = \int p(x) \log\frac{p(x)}{q(x)} dx\) |
| 重参数化 | \(x = \mu + \sigma \cdot \epsilon, \quad \epsilon \sim \mathcal{N}(0, 1)\) |
📝 自测问题¶
基础问题¶
- 高斯分布
- 为什么扩散模型选择高斯噪声而不是其他分布?
-
如果 \(X \sim \mathcal{N}(0, 1)\),\(Y = 2X + 3\),\(Y\) 服从什么分布?
-
条件概率
- 解释 \(p(x_t | x_{t-1})\) 和 \(p(x_{t-1} | x_t)\) 的区别
-
在扩散模型中,哪个是前向过程,哪个是反向过程?
-
KL散度
- KL散度为0意味着什么?
-
为什么KL散度不对称?这在实际中有什么影响?
-
重参数化
- 重参数化技巧解决了什么问题?
- 写出扩散模型中 \(x_t\) 的重参数化形式
编程练习¶
- 实现一个函数,计算两个任意分布的KL散度
- 用重参数化技巧从多元高斯分布中采样
- 可视化扩散过程中噪声水平的变化
思考题¶
- 如果扩散模型使用均匀分布作为噪声,会有什么不同?
- 为什么高斯分布的KL散度有闭合形式很重要?
- 重参数化技巧对梯度计算有什么帮助?
🔗 下一步¶
你已经掌握了概率论的基础知识,接下来我们将学习随机过程与马尔可夫链,这是理解扩散模型动态过程的关键。
→ 下一步:02-随机过程与马尔可夫链.md