第 3 章:微积分、变分推断与采样方法
梯度是深度学习的通用语言,而"采样"是这门语言中最难翻译的词。本章围绕一个核心矛盾展开:我们需要对含有随机采样步骤的目标函数求梯度,但采样操作本身不可导。Score Function 和 Path Derivative 是解决这一矛盾的两条路径,分别催生了策略梯度和重参数化技巧。在此基础上,我们深入变分推断与 VAE 的数学结构,剖析 PyTorch autograd 机制,推导常见函数的梯度,分析不同 token-level 归一化策略的梯度效应,并将视野扩展到 Neural ODE、能量模型、流形约束优化与因果推断。
3.1 Score Function 与 Path Derivative
两种梯度估计器的本质区别在于:Score Function 把采样当作不可控的"黑盒",在外部用奖励加权概率梯度;Path Derivative 打开采样的"黑盒",将随机性剥离为外部噪声,让梯度直接穿过确定性变换流回参数。
3.1.1 问题的提出
生成模型和强化学习的通用优化目标是最小化期望损失:
对
两种绕过障碍的方法各有哲学:
| 方法 | 别名 | 隐喻 | 适用场景 |
|---|---|---|---|
| Score Function (SF) | REINFORCE 估计器 | "蒙眼射击":不知道弹道如何,但记住高分时的姿势 | |
| Path Derivative (PD) | Reparameterization Trick | "拆解枪支":把手抖(噪声)和瞄准(参数)分开 |
3.1.2 Score Function 估计器
对数导数技巧(Log-Derivative Trick) 是 SF 估计器的数学基础。核心恒等式:
利用此技巧,将梯度从"对分布求导"转化为"在分布下求期望":
直觉解释:
Score Function 期望为零(重要性质):
这意味着减去任何与
SF 的特点:
- 优点:
可以完全不可导,甚至可以是离散的(如 NLP 中离散 token 的奖励,或复杂 RL 环境中不可导的 Reward) - 缺点:方差极高——
的量级直接放大梯度估计的噪声,通常需要 baseline 减方差
3.1.3 Path Derivative 估计器
重参数化技巧(Reparameterization Trick):将随机变量
经典示例:正态分布
根据无意识统计学家定律(Law of the Unconscious Statistician),期望可转化为对噪声的期望:
现在
直觉解释:梯度通过链式法则,从 Loss
PD 的特点:
- 优点:直接利用
的局部几何信息(斜率),方差极小,收敛极快 - 缺点:要求
和 处处可导,不适用于离散采样
3.1.4 计算图对比
两种估计器的梯度流可以从计算图直观理解:
Score Function: θ → p_θ → [Sample x] ···✕ f(x)
↑ 梯度在采样处断裂
SF 绕道:直接问 p_θ "怎样让 x 更可能",用 f(x) 加权
Path Derivative: ε → [Transform g(θ, ε)] → x → f(x)
↑ θ 是变换的参数
梯度流畅通:θ ← g ← x ← fSF 的梯度流到
3.1.5 实验对比
用一个简单问题对比两种估计器:寻找高斯分布的最优均值
import torch
import matplotlib.pyplot as plt
torch.manual_seed(42)
TARGET, SIGMA, LR, ITERATIONS, BATCH_SIZE = 3.0, 1.0, 0.1, 500, 64
def run_optimization(method='path_derivative'):
mu = torch.tensor([0.0], requires_grad=True)
optimizer = torch.optim.Adam([mu], lr=LR)
loss_history, grad_var_history, mu_history = [], [], []
for _ in range(ITERATIONS):
optimizer.zero_grad()
dist = torch.distributions.Normal(mu, SIGMA)
if method == 'path_derivative':
x = dist.rsample((BATCH_SIZE,)) # rsample() 保持梯度流
loss = ((x - TARGET) ** 2).mean()
loss_mean = loss
loss.backward()
grads = 2 * (x - TARGET) # 单样本梯度: d/dmu (x-T)^2 = 2(x-T)
elif method == 'score_function':
x = dist.sample((BATCH_SIZE,)) # sample() 切断梯度流
loss_val = (x - TARGET) ** 2
log_prob = dist.log_prob(x)
surrogate = (log_prob * loss_val.detach()).mean() # 代理损失
surrogate.backward()
loss_mean = loss_val.mean()
grads = loss_val * (x - mu.detach()) # SF 单样本梯度: f(x) * (x-mu)/sigma^2
loss_history.append(loss_mean.item())
mu_history.append(mu.item())
grad_var_history.append(torch.var(grads).item())
optimizer.step()
return loss_history, grad_var_history, mu_history
pd_loss, pd_var, pd_mu = run_optimization('path_derivative')
sf_loss, sf_var, sf_mu = run_optimization('score_function')实验结果解读:
- 收敛速度:Path Derivative 的
在约 50 步内逼近 target = 3.0;Score Function 需要数百步,且曲线抖动明显 - 梯度方差(核心差异):PD 的梯度方差比 SF 低 1--2 个数量级。SF 的
中, 的量级直接叠加到梯度上,导致方差爆炸;PD 直接用 这一精确斜率信息,方差天然受控 - Loss 曲线:PD 平滑单调下降;SF 在前期剧烈波动,需要更大 batch 或 baseline 才能稳定
3.2 变分推断:Reverse KL、Forward KL 与 VAE
变分推断的核心思想是"既然积不出来,就猜一个"——用一个简单分布族中的最优成员去近似复杂的真实后验,将推断问题转化为优化问题。ELBO 是这一转化的数学桥梁。
3.2.1 变分推断(Variational Inference)
贝叶斯统计的终极目标是计算后验分布——透过现象(数据
分母
变分推断的思路:找一个形状简单、易于计算的分布族
3.2.2 为什么选 Reverse KL
标准变分推断最小化 Reverse KL:
关键观察:期望是对
若改用 Forward KL:
Reverse KL vs. Forward KL 的行为差异:
| 类型 | 名称 | 性质 | 行为 |
|---|---|---|---|
| Mode-Seeking | Zero-Forcing | ||
| Mean-Seeking | Mass-Covering |
直觉:Reverse KL 中,当
3.2.3 ELBO 推导
直接最小化
从 KL 散度出发展开:
重新整理得到核心等式:
因为
最大化 ELBO
从 ELBO 到 VAE Loss 的桥接推导:将联合概率
这一分解将 ELBO 从抽象的联合概率形式变成了两个直觉清晰的部分:第一项要求从
ELBO 的计算优势在于它避开了难以计算的
是我们自己设定的简单分布,密度和采样都已知 :先验 由我们设定,似然 由模型定义——两者都是点对点计算,不需要积分
3.2.4 VAE:ELBO 的神经网络实现
VAE(Variational AutoEncoder)用神经网络参数化变分推断的两个组件:
- Encoder
:输入数据 ,输出后验近似的参数(均值 和对数方差 ) - Decoder
:输入潜变量 ,输出重构数据
VAE 的损失函数是负 ELBO,可分解为两个直觉清晰的部分:
- 重构误差:要求 Decoder 能从
大概率还原出 。若假设 为高斯分布,此项等价于 MSE;若为伯努利分布,等价于 Binary Cross Entropy - KL 正则项:要求 Encoder 输出的后验
尽量接近先验 。这防止了潜在空间"记住"个别数据点,保证了生成时的平滑性
当先验为标准正态分布时,KL 散度有解析解:
完整的 VAE 实现:
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().__init__()
# Encoder: x → h → (mu, log_var)
self.fc1 = nn.Linear(input_dim, hidden_dim)
self.fc_mu = nn.Linear(hidden_dim, latent_dim)
self.fc_lv = nn.Linear(hidden_dim, latent_dim)
# Decoder: z → h → x_recon
self.fc3 = nn.Linear(latent_dim, hidden_dim)
self.fc4 = nn.Linear(hidden_dim, input_dim)
def encode(self, x):
h = F.relu(self.fc1(x))
return self.fc_mu(h), self.fc_lv(h)
def reparameterize(self, mu, log_var):
"""重参数化技巧核心:z = mu + sigma * epsilon"""
std = torch.exp(0.5 * log_var) # log_var → std
eps = torch.randn_like(std) # epsilon ~ N(0,1),与 phi 无关
return mu + eps * std # 确定性变换,梯度可流过
def decode(self, z):
return torch.sigmoid(self.fc4(F.relu(self.fc3(z))))
def forward(self, x):
mu, log_var = self.encode(x.view(-1, 784))
z = self.reparameterize(mu, log_var) # 梯度通过这里流回 mu, log_var
return self.decode(z), mu, log_var
def vae_loss(recon_x, x, mu, log_var):
"""负 ELBO = 重构误差 + KL 散度"""
BCE = F.binary_cross_entropy(recon_x, x.view(-1, 784), reduction='sum')
KLD = -0.5 * torch.sum(1 + log_var - mu.pow(2) - log_var.exp())
return BCE + KLD, BCE, KLD训练结果(MNIST,10 epochs):
| Epoch | 总 Loss | 重构误差 | KL 散度 |
|---|---|---|---|
| 1 | 162.70 | 146.81 | 15.88 |
| 5 | 109.50 | 84.55 | 24.95 |
| 10 | 105.96 | 80.58 | 25.38 |
观察:随着训练进行,重构误差持续下降(Decoder 越来越好),KL 散度先升后趋于稳定(Encoder 学到的后验结构在与先验约束之间找到平衡)。训练完成后,从
3.2.5 知识蒸馏中的 Forward KL
知识蒸馏(Knowledge Distillation)是 Forward KL 的成功应用场景,值得与 VI 对比理解。
为何这里可以用 Forward KL?
(Teacher)是已知的、完全透明的——做一次前向传播就能得到完整的概率向量 - 积分域是有限的类别集合(如 10 类或 1000 类),
变成了 ,可以精确计算 - 这与 VI 面对的不可计算高维连续后验是完全不同的处境
因为 Teacher 是固定的(detach),CrossEntropyLoss(student_logits, teacher_probs) 的原因。
3.3 梯度无法通过"采样"传播:Policy Gradient 与 Reparameterization Trick
采样是计算图中的"断桥"。对于离散采样(token 生成),只能绕道——用 Policy Gradient;对于连续采样(潜变量),可以修桥——用 Reparameterization Trick。
3.3.1 问题的根源
LLM 对齐的 RLHF 目标函数:
计算图:
:奖励随动作的变化,可计算 :不可导。 (token)是离散的——概率从 0.49 变到 0.51,输出从 "A" 瞬间跳变为 "B",没有平滑梯度
后果:梯度无法穿过"采样"这个操作反向传播回参数
3.3.2 解决方案一:Policy Gradient(Score Function 的应用)
不尝试通过采样反向传播,而是用第 3.1 节的 Score Function 技巧绕过:
关键设计:
变成了权重系数(detach):不需要对 求关于 的导数,也不需要对 求关于 的导数 : 是神经网络直接输出的概率,完全可导 - 数据
不再必须从当前 采样——可以用旧策略的数据(Off-Policy)
Policy Gradient 的工作流程:
- 采样(Rollout):让模型运行,记录"状态
、动作 、奖励 "。此时不计算梯度 - 更新:如果
高,计算 让模型更可能选 ;如果 低,则降低 的概率
PPO 在此基础上引入重要性采样(Importance Sampling)和 Clip 机制:
3.3.3 解决方案二:Reparameterization Trick(连续情形)
对于连续潜变量(VAE 的
梯度路径完全畅通:
3.3.4 Vanilla PG vs. PPO 的梯度行为
对 logit
Vanilla PG(使用
当
PPO(使用比率
当
这解释了 PPO 为什么比 Vanilla PG 更稳定:它自带"安全阀",不会对已经被放弃的动作施加不合理的大梯度。
3.4 PyTorch Autograd 机制
PyTorch 的动态计算图是"用时建、用后拆"——每次前向传播即时构建图,反向传播沿图求完梯度后立即释放。理解这一机制是正确调试梯度问题的基础。

3.4.1 动态计算图
PyTorch 使用动态计算图(define-by-run):
requires_grad=True:标记需要计算梯度的叶节点(通常是模型参数nn.Parameter).backward():从标量 loss 出发,沿计算图反向传播,把每个叶节点的梯度累加到.grad属性中optimizer.zero_grad():清空累积梯度。不清空则多次 backward 的梯度会叠加(这在梯度累积策略中是有意为之,但通常需要显式管理)
3.4.2 梯度通过形状变换的传播
一个揭示 autograd 行为的经典示例:
import torch
from torch import nn
a = nn.Parameter(torch.rand(1, 4)) # shape: (1, 4),叶节点
b = a.unsqueeze(0) # shape: (1, 1, 4)
c = b.tile(2, 1, 1) # shape: (2, 1, 4),a 被复制了 2 次
d = torch.rand(2, 1, 4) # 随机数据
loss = torch.mean(d - c)
loss.backward()
print(a.grad) # tensor([[-0.2500, -0.2500, -0.2500, -0.2500]])梯度分析:
loss = mean(d - c),总共个元素 (mean 中每个元素的贡献相等,负号来自 ) tile(2, 1, 1)使得的每个元素在 中出现 2 次 - 梯度从两个副本累加回原参数:
核心原则:tile、expand、repeat 等操作相当于参数共享——梯度从所有"副本"处累加回源参数。这与卷积中权重共享的梯度传播机制完全一致。
3.5 常见函数的梯度推导
深度学习中反复出现的几个函数——Softmax、Cross Entropy、Sigmoid——它们的梯度公式简洁优雅,但推导需要商法则和链式法则的配合。掌握这些推导是理解后续章节(如 PPO 梯度分析)的前提。
3.5.1 商法则
3.5.2 Softmax 梯度
对角元素(
令分子
非对角元素(
分子
紧凑形式:
3.5.3 Cross Entropy 对 Logits 的梯度
CE Loss(one-hot 标签
对 logit
这个优雅的结果——"预测概率减去真实标签"——是 Softmax 和 Log 的链式法则恰好相互抵消的结果。它也解释了为什么 Cross Entropy + Softmax 在实践中是天作之合。
3.5.4 Sigmoid 梯度与饱和问题
当
这正是 ReLU (
3.5.5 二分类 Cross Entropy 的梯度
假设标签
当 Loss
3.6 Loss 与梯度分析(Token-Level Mean)
"Loss 是高度,Gradient 是坡度"——高度为零不意味着坡度为零。理解两者的非对称关系是调试训练问题的关键。
3.6.1 Loss 与 Gradient 的非对称关系
一个常见误解是"Loss 降到 0,梯度自然为 0"。这在 MSE、CE 这类以 0 为最小值的凸函数上成立,但在更一般的情形下不成立。
MSE(完美抛物线):
Cross Entropy(渐近收敛):
Plateau(饱和区)——Loss 大但梯度为零:
当
陡峭峡谷(Steep Valley)——Loss 小但梯度巨大:
在最优解附近的微小偏移
PPO Clip——Loss
当概率比
| 场景 | Loss | Gradient | 机制 |
|---|---|---|---|
| MSE 最优点 | 0 | 0 | 凸函数全局最小值 |
| CE 接近收敛 | 渐近趋零 | ||
| Sigmoid 饱和区 | 接近最大值 | 梯度消失 | |
| 陡峭峡谷 | 极小 | 极大 | Hessian 特征值大 |
| PPO Clip 区 | 人为截断保护 | ||
| PPO | 最优点在负无穷 |
import numpy as np
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
# Plateau: High Loss, Low Gradient
x1 = np.linspace(-10, 5, 200)
sigmoid = 1 / (1 + np.exp(-x1))
loss1 = (sigmoid - 1)**2
grad1 = 2 * (sigmoid - 1) * (sigmoid * (1 - sigmoid))
axes[0].plot(x1, loss1, label=r'Loss: $(\sigma(x)-1)^2$', color='blue', linewidth=2)
axes[0].plot(x1, np.abs(grad1), label='|Gradient|', color='red', linestyle='--', linewidth=2)
axes[0].set_title('Plateau: High Loss, Zero Gradient')
# Steep Valley: Low Loss, High Gradient
x2 = np.linspace(-0.5, 0.5, 200)
axes[1].plot(x2, 20*x2**2, label='Loss: $20x^2$', color='blue', linewidth=2)
axes[1].plot(x2, np.abs(40*x2), label='|Gradient|', color='red', linestyle='--', linewidth=2)
axes[1].set_title('Steep Valley: Low Loss, Huge Gradient')
# PPO Clip: Loss != 0, Gradient = 0
r = np.linspace(0, 2.0, 400)
A, epsilon = 1.0, 0.2
objective = np.minimum(r * A, np.clip(r, 1-epsilon, 1+epsilon) * A)
ppo_loss = -objective
ppo_grad = np.where(r < (1+epsilon), -A, 0)
axes[2].plot(r, ppo_loss, label='PPO Loss', color='blue', linewidth=2)
axes[2].plot(r, np.abs(ppo_grad), label='|Gradient|', color='red', linestyle='--', linewidth=2)
axes[2].axvspan(1+epsilon, 2.0, color='yellow', alpha=0.2, label='Clipped Region')
axes[2].set_title('PPO Clip: Loss!=0, Gradient=0')
for ax in axes:
ax.grid(True, alpha=0.3); ax.legend()
plt.tight_layout()三联图输出解读:
左图(Plateau):蓝色 Loss 曲线在
中图(Steep Valley):Loss
右图(PPO Clip):当概率比
3.6.2 Token-Level 归一化策略
在 RLHF 训练中,不同长度回答的 token-level 归一化方式直接影响每个 token 的梯度权重:
GRPO(Naive)——Per-Sample Mean:
每个样本先内部取均值,再跨样本平均。效果:不管回答有 10 tokens 还是 100 tokens,每条回答的总投票权相同(
DAPO——Global Token Mean:
所有 token 放入同一个池子,除以总 token 数。消除了长度歧视,但梯度量级随 batch 内总 token 数变化,可能需要调整学习率。
Dr. GRPO——Sum over Group:
只除以组数
数值对比(10-token 短回答 + 100-token 长回答,
import torch
def analyze_gradient_attenuation():
len_short, len_long, G = 10, 100, 2
total_tokens = len_short + len_long
print(f"{'Method':<15} | {'Short Grad':<15} | {'Long Grad':<15}")
print("-" * 50)
print(f"{'GRPO (Naive)':<15} | {1.0/G:<15.4f} | {1.0/G:<15.4f}")
print(f"{'DAPO':<15} | {len_short/total_tokens:<15.4f} | {len_long/total_tokens:<15.4f}")
print(f"{'Dr. GRPO':<15} | {len_short/G:<15.4f} | {len_long/G:<15.4f}")
analyze_gradient_attenuation()输出:
Method | Short Grad | Long Grad
--------------------------------------------------
GRPO (Naive) | 0.5000 | 0.5000
DAPO | 0.0909 | 0.9091
Dr. GRPO | 5.0000 | 50.0000解读:
- GRPO (Naive) 完全抹平了长度差异,长句子每 token 的贡献被稀释 10 倍,导致模型偏好短回答
- DAPO 消除了长度歧视,但梯度量级(0.09 和 0.91)远小于 Dr. GRPO,需要更大学习率补偿
- Dr. GRPO 梯度量级只与
有关,长推理链做对了理应获得更强正反馈,最符合 Policy Gradient 定理的原始形式
3.7 Neural ODE
如果残差网络的层数可以是实数而非整数,每一层变成了微分方程中无穷小的一步,我们就得到了 Neural ODE——一个把"深度"从离散跳跃推向连续极限的框架。

3.7.1 残差连接即欧拉离散化
残差块的更新公式:
把层数
这不只是形式上的巧合。它意味着:增加残差网络的层数相当于在数值积分中缩小步长,而层数趋于无穷的极限就是连续的 ODE。
3.7.2 从 ResNet 到 Neural ODE
Neural ODE(Chen et al., 2018)将这一洞察推向极致:直接用神经网络
给定初始状态
不再有离散的"第 1 层、第 2 层",取而代之的是一个连续的时间参数
3.7.3 优势与训练
连续深度:层数不再是需要预先设定的超参数,而是由 ODE solver 自适应决定积分步数。
内存高效:训练时使用**伴随法(Adjoint Method)**反向传播——不需要存储中间时刻的所有激活值(不像 ResNet 需要存每一层的激活),内存开销与"深度"无关,只与状态维度和 ODE 参数量有关。
自适应精度:ODE solver(如 Dormand-Prince)根据误差容限自动调整步长——在状态变化剧烈的区域用小步长(等价于更多"层"),在平稳区域用大步长。
3.7.4 与生成模型的联系
Neural ODE 是现代生成模型中多个框架的理论基石:
- 扩散模型(Diffusion):前向加噪/反向去噪过程本质上是 SDE(随机微分方程)。去掉随机项后的确定性版本——概率流 ODE(Probability Flow ODE)——就是 Neural ODE
- Flow Matching:将从先验分布到数据分布的变换视为向量场,用 Neural ODE 来参数化和求解这个向量场
- 连续 Normalizing Flows:利用 ODE 的可逆性和 Jacobian 的 trace 公式实现精确的密度估计
3.8 能量模型(Energy-Based Methods)
能量模型的哲学:不直接建模概率,而是学一个"打分函数"——分数越低越自然,分数越高越违和。概率通过 Boltzmann 分布自动从能量中涌现。
3.8.1 能量函数与 Boltzmann 分布
EBM 的核心公式:
直觉——能量景观(Energy Landscape):
想象一个起伏不平的山地地形:
- 山谷底(低能量):小球放在这里是稳定的,不想动。对应"自然的"、"合理的"数据点,概率高
- 山顶/半山腰(高能量):小球不稳定,会自然滚落。对应"不自然的"、"异常的"数据点,概率低
在 EBM 中,
3.8.2 训练困难:配分函数
训练 EBM 需要最大化对数似然:
第一项(正相)简单:降低训练数据的能量。第二项(负相)困难:需要从模型自身的分布
对比散度(Contrastive Divergence, CD):Hinton 提出的实用方案——不做完整的 MCMC 采样,只跑几步(通常 1 步)从数据点出发的 Gibbs 采样,用得到的"负样本"近似负相梯度。
3.8.3 Score Matching:绕过配分函数
Score(得分函数,注意与第 3.1 节的 Score Function 估计器区分)是对数概率密度对数据
配分函数
Score Matching 的目标:
直接匹配模型的 score 和数据分布的 score,完全避免了
3.8.4 与扩散模型的联系
扩散模型本质上是在学习加噪数据的 score function(
从 EBM 的视角看:score 指向能量景观的"下坡方向",Langevin Dynamics 就是在能量景观上做带噪声的梯度下降,最终收敛到低能量区域(高概率数据)。
3.9 流形约束优化
当参数天然活在弯曲的空间上——如旋转矩阵、单位球面、正定矩阵——在欧氏空间做梯度下降再投影回去既粗糙又低效。流形约束优化的目标是让优化过程"原住民化":直接在弯曲空间上行走。
3.9.1 流形与约束
许多 AI 问题的参数天然存在于某个低维流形上,而非整个欧氏空间:
- 旋转矩阵
: ,行列式为 1。3D 视觉、机器人姿态估计中无处不在 - 单位球面
: 。归一化嵌入(如对比学习中的特征向量) - 正定矩阵流形
:协方差矩阵的参数空间。高斯过程、变分推断的参数
如果忽视约束,在欧氏空间做无约束优化后投影回流形,会导致:更新方向被投影歪曲、投影步骤本身的计算开销、收敛到约束边界附近的振荡。
3.9.2 三种约束处理策略
投影法(Projection):每步梯度更新后投影回流形。简单直接,但投影步骤可能代价高昂(如投影到
参数化法(Parameterization):用满足约束的参数化消除约束。例如正交矩阵可以参数化为
Riemannian 梯度下降:在流形上定义度量张量,计算流形上的梯度(将欧氏梯度投影到切空间),沿测地线(geodesic)更新参数。这是理论上最"原住民"的方法,但实现复杂度较高。
3.9.3 在 AI 中的应用
- Attention 正交基:对注意力头施加正交约束,使不同头关注不同子空间,提高多样性
- 归一化嵌入优化:对比学习中特征向量在训练过程中始终保持在单位球面上,避免 collapse
- Flow Matching 与向量场:将从先验到数据分布的变换视为流形上的向量场,Neural ODE 在流形上求解
3.10 因果推断入门
"冰淇淋销量与溺水事故高度相关"——但没有人会因此禁止卖冰淇淋。传统 ML 只能学到相关性,因果推断要回答的问题是:如果我做了
, 会如何变化?
3.10.1 相关性 vs. 因果性
传统 ML 的局限:学习的是关联性(Association)
因果 ML 的目标:学习干预效果(Intervention)
Pearl 的因果层级(Ladder of Causation):
| 层级 | 问题 | 数学 | 例子 |
|---|---|---|---|
| L1: 关联 | 观测到什么? | 看到冰淇淋销量高,溺水事故多 | |
| L2: 干预 | 如果做了 | 强制增加冰淇淋生产,溺水率变吗? | |
| L3: 反事实 | 如果当时做了 | 那位病人如果接受了治疗,会活吗? |
每个层级严格包含下方的信息,但无法仅从下方的数据推出上方的结论。
3.10.2 因果图(DAG)
因果关系用**有向无环图(DAG)**表示:
- 节点:变量
- 有向边
: 是 的直接原因
关键概念:
- 混淆变量(Confounder):
, 。 同时影响 和 ,制造虚假关联(如"天气热"同时导致"冰淇淋销量高"和"溺水多") - 后门标准(Backdoor Criterion):如果能找到一组变量
阻断 到 的所有后门路径,则条件化 后的条件概率 等于因果效应
3.10.3 因果推断与 AI 的交汇
治疗效果预测:在临床医疗中预测"给患者用药 A vs. 药 B"的效果差异,不能仅靠观测数据——因为医生开药的决策本身就是有偏的(混淆变量:病情严重程度同时影响用药选择和治疗结果)。需要因果推断工具来去混淆。
LLM 中的因果思维:
- Causal Masking:Transformer 的因果掩码保证 token
只能看到之前的上下文——这是自回归生成的"时间因果性" - 反事实数据增强:通过干预因果图中的变量生成对抗样本,提高鲁棒性
奖励模型的混淆问题:RLHF 中,如果人类偏好数据存在混淆变量(如回答长度偏差),直接训练奖励模型会学到"长回答更好"的伪相关而非真正的质量信号。这正是第 3.6 节中 GRPO/DAPO/Dr. GRPO 长度偏差问题的深层根源——需要因果推断的视角来识别和消除这类偏差。
3.11 交叉熵方法(Cross-Entropy Method)
交叉熵方法(CEM)是一种基于采样的零阶优化算法:不需要梯度,只需要反复采样、评估、筛选、拟合。它的核心思想极其朴素——在好的样本上重新拟合分布,让下一轮采样更可能落在高分区域。
3.11.1 算法思想
考虑一个黑盒优化问题:寻找
CEM 的策略是维护一个搜索分布
- 采样:从当前分布
中采 个候选解 - 评估:计算每个候选解的目标值
- 筛选:选取目标值最高的前
个样本(称为 elite set),其中 , 为 elite 比例(通常 10%--20%) - 更新:用 elite 样本重新拟合分布参数
(对高斯分布即重新计算均值和协方差)
重复直到分布收敛(方差足够小)。
3.11.2 为什么叫"交叉熵"
CEM 的名字来源于其理论基础。将 elite 筛选理解为重要性采样:我们希望找到一个新分布
其中
3.11.3 与策略优化的联系
CEM 与强化学习中的策略搜索方法密切相关。在 RL 场景中:
- 搜索分布
对应策略参数的分布(而非策略本身) - 目标值
对应一次 rollout 的累积奖励 - elite 筛选 对应只保留高回报的 episode
这使 CEM 成为一种简单有效的进化策略(Evolution Strategy)。与 Policy Gradient 相比:
| 特性 | CEM | Policy Gradient |
|---|---|---|
| 梯度需求 | 零阶,无需梯度 | 一阶,需要 |
| 样本效率 | 较低(大量采样仅保留 top- | 较高(所有样本都贡献梯度) |
| 适用场景 | 低维参数空间、不可导目标 | 高维参数空间、可导策略 |
| 方差 | 天然低(只用好样本) | 较高(需 baseline 减方差) |
在低维连续控制任务(如 CartPole、MountainCar 等经典环境)中,CEM 凭借实现简单、无需调学习率等优点,常常是首选的 baseline 方法。
3.11.4 实现示例
以下代码用 CEM 求解一个简单的多峰函数优化问题:
import numpy as np
def cem_optimize(f, dim, n_samples=100, elite_frac=0.2, n_iters=50):
"""交叉熵方法优化 f(x) 的最大值"""
mu = np.zeros(dim)
sigma = np.ones(dim) * 5.0 # 初始标准差较大,覆盖广
n_elite = int(n_samples * elite_frac)
for t in range(n_iters):
# 1. 采样
samples = np.random.randn(n_samples, dim) * sigma + mu
# 2. 评估
scores = np.array([f(x) for x in samples])
# 3. 筛选 elite
elite_idx = scores.argsort()[-n_elite:]
elite_samples = samples[elite_idx]
# 4. 更新分布参数
mu = elite_samples.mean(axis=0)
sigma = elite_samples.std(axis=0)
return mu
# 测试:优化 Rastrigin 函数的负值(寻找全局最小值)
def neg_rastrigin(x):
return -(10 * len(x) + sum(xi**2 - 10 * np.cos(2 * np.pi * xi) for xi in x))
best = cem_optimize(neg_rastrigin, dim=2, n_samples=200, n_iters=100)
print(f"Found optimum at: {best}") # 应接近 [0, 0]CEM 的简洁性是它的优势——整个算法不过十几行,没有学习率、没有梯度计算、没有反向传播。但这种简洁也决定了它的局限:在高维空间中,覆盖搜索空间所需的样本数呈指数增长,使得 CEM 难以扩展到上千维的参数空间。
本章小结
| 主题 | 核心公式/思想 | 应用场景 |
|---|---|---|
| Score Function 估计器 | Policy Gradient、REINFORCE | |
| Path Derivative 估计器 | VAE、扩散模型训练 | |
| Reverse KL / ELBO | max ELBO | VAE、变分推断 |
| Reparameterization Trick | VAE Encoder、连续控制 | |
| Loss vs. Gradient | 非单调关系(PPO Clip、Sigmoid 饱和) | PPO 调试、训练诊断 |
| Token-Level 归一化 | GRPO / DAPO / Dr. GRPO 三种策略 | RLHF 长度偏差修正 |
| Neural ODE | 残差 = 欧拉步,极限为 ODE | 连续深度、Flow Matching |
| 能量模型 | 扩散模型(score-based) | |
| 流形约束 | 投影 / 参数化 / Riemannian 梯度 | 正交约束、归一化嵌入 |
| 因果推断 | do-calculus,DAG,后门标准 | 治疗效果、奖励去偏 |
| 交叉熵方法(CEM) | 采样→筛选 elite→重新拟合分布 | 零阶优化、低维策略搜索 |
三条核心直觉:
采样是不可导的屏障——所有绕过它的方法(Score Function 或 Reparameterization)本质上都是将随机性"移出计算图"或"转换视角"。离散采样只能用 SF 绕道(REINFORCE/PPO),连续采样可以用 PD 修桥(Reparameterization)。
ELBO 是推断与生成的桥梁——通过最大化 ELBO,VAE 同时学到了如何压缩数据(Encoder,近似后验
)和如何生成数据(Decoder,似然 ),两者通过潜在空间的概率结构耦合。ELBO 把一个不可解的积分问题变成了可以用 SGD 优化的目标。 能量视角统一了大多数生成模型——扩散模型学习 score(能量曲面的梯度),EBM 直接在能量曲面上爬坡,Flow Matching 找一条从先验到数据的最优路径。它们是在同一个能量景观上的不同"行走策略",Neural ODE 为这些策略提供了连续时间的数学框架。
延伸阅读
- 变分推断:Blei, Kucukelbir & McAuliffe (2017), "Variational Inference: A Review for Statisticians", JASA
- VAE:Kingma & Welling (2014), "Auto-Encoding Variational Bayes", arXiv:1312.6114
- Policy Gradient:Williams (1992), "Simple Statistical Gradient-Following Algorithms for Connectionist Reinforcement Learning";Schulman et al. (2017), "Proximal Policy Optimization Algorithms"
- Neural ODE:Chen et al. (2018), "Neural Ordinary Differential Equations", NeurIPS 2018 Best Paper
- EBM 与 Score Matching:Song & Ermon (2019), "Generative Modeling by Estimating Gradients of the Data Distribution"
- 因果推断:Pearl (2018), The Book of Why;Peters, Janzing & Scholkopf (2017), Elements of Causal Inference(开源)
- Token-Level 归一化:Liu et al. (2025), "Understanding the Length Bias of GRPO"(Dr. GRPO);Yu et al. (2025), "DAPO: An Open-Source LLM Reinforcement Learning System"