第六章 变分推断(优化版)
Chapter 6 Variational Inference (Optimized)
本章导航 / Chapter Navigator
一句话总结(TL;DR)
MAP 只找后验分布的”顶点”;GVI(高斯变分推断)用一个高斯分布去最好地”覆盖”整个后验,同时还能自动学习模型参数。
阅读路径建议:
- 🟢 快速浏览:读完”直觉图解”即可把握核心思想
- 🔵 理解算法:阅读”概念层”的公式和解释
- ⚫ 深度推导:展开”推导层”查看完整数学
6.1 为什么 MAP 不够用?
🎯 直觉:山谷的顶点 vs. 山谷的形状
想象你要研究一个山谷(后验分布 ):
- MAP 的做法:找到谷底最低点。只告诉你”最可能在哪里”。
- GVI 的做法:用一个椭球(高斯分布)去最好地覆盖整个山谷。不仅告诉你”最可能在哪里”,还告诉你”周围有多大范围都是可能的”。
MAP 的两个局限:
- 非线性模型下,后验不是高斯的,MAP 的拉普拉斯协方差只是粗糙近似
- MAP 无法自动学习噪声协方差等模型参数
GVI 解决了这两个问题。
🔑 概念层:三种方法的精度阶梯
| 方法 | 用几个点近似期望 | 从哪里取样 | 精度 |
|---|---|---|---|
| MAP / EKF | 1 个(仅均值处) | — | 最低 |
| SPKF(UKF) | 个 sigma 点 | 先验 | 中 |
| GVI | 个 sigma 点 | 后验 (迭代更新) | 最高 |
关键区别:GVI 的 sigma 点从当前估计的后验中取,而 SPKF 从先验取。后验通常比先验更窄,对非线性的近似更精确。
6.2 KL 散度:如何度量”两个分布有多接近”
🎯 直觉:单向测量
KL 散度衡量”用 近似 有多大的信息损失”。它不是对称的!
为什么选 而不是 ?
- :期望对 (我们自己的高斯) 取 → 可计算
- :期望对 (真实后验) 取 → 不可计算(真实后验本来就是未知的!)
🔑 概念层:损失函数
展开 ,去掉常数项,得到我们实际要最小化的目标:
其中 是联合分布的负对数似然。
两项的直觉:
- 第一项:鼓励高斯 集中在数据似然高的区域(往真实后验靠近)
- 第二项:防止 变成”无限窄的尖峰”(防止过度自信)
- 两者的平衡 = 最优高斯近似
这个 等于负 ELBO(证据下界)。最小化 等价于最大化 ELBO。
📐 推导层:KL 散度展开
展开:从 KL 散度到损失函数
其中 是常数(与 无关)。去掉常数,最小化:
对于高斯 ,其熵为:
因此 。
6.3 GVI 优化:迭代牛顿法
🎯 直觉:比 MAP 更聪明的迭代
MAP 的牛顿迭代:每步在当前均值点处计算梯度和 Hessian。
GVI 的迭代:每步在当前后验分布的多个 sigma 点处计算期望,得到更准确的梯度和 Hessian。
两者的更新形式相同,差别只在于”用一个点”还是”用多个点”来近似期望。
🔑 概念层:GVI 更新公式
对 关于均值 和精度矩阵 求导,令其为零:
新的精度矩阵(= 期望 Hessian):
均值更新(解线性方程组):
与 MAP 的联系:若用 的均值处的函数值近似期望(即只用 1 个点),则 GVI 退化为 MAP 的牛顿迭代。MAP 是 GVI 的极端退化情形。
🧮 数值示例:立体相机测距
场景:立体相机观测距离 米处的目标,观测模型非线性:
真实后验均值 ≈ 24.777 m(通过 Monte Carlo 计算)
| 方法 | 估计均值 | 偏差 |
|---|---|---|
| MAP / EKF | 24.569 m | -33 cm(大偏差) |
| ISPKF | 24.741 m | -3.8 cm |
| GVI | 24.779 m | +0.3 cm(极小!) |
结论:同样的数据,GVI 的偏差比 MAP 小 100 倍以上。
6.4 ESGVI:大规模问题的稀疏性
🎯 直觉:时间链的局部依赖
对于一条机器人轨迹:时刻 只和时刻 、 直接相关(马尔可夫性)。
这意味着:
- 联合似然可以因子化:
- 精度矩阵 是块三对角(大部分是零!)
- 我们不需要计算完整的 协方差矩阵
🔑 概念层:精确稀疏 GVI(ESGVI)
关键性质:当似然可以因子化时,期望计算只需要局部边缘分布:
其中 是局部边缘高斯——比全局 维数小得多!
ESGVI 算法流程:
初始化 μ, Σ⁻¹(可由 MAP 提供)
重复直到收敛:
① 对每个因子 k:
- 从局部边缘 q_k = N(μ_k, Σ_kk) 生成 sigma 点
- 计算局部期望(φ_k 的一阶、二阶期望)
② 组装全局精度矩阵 Σ⁻¹(块三对角)
③ LDL^T 分解 Σ⁻¹
④ 前后向代入求解 Σ⁻¹ δμ = -梯度
⑤ Takahashi 后向代入恢复所需的 Σ 子块
⑥ 更新 μ ← μ + δμ
计算复杂度:与 MAP 同阶 ,只有常数倍差距。
📐 推导层:Takahashi 算法提取协方差子块
展开:如何从稀疏 Σ⁻¹ 高效恢复协方差子块
对 分解后,可以用后向代换提取 的非零子块,无需显式求逆整个大矩阵。
从最后一个块开始:
然后向前递推():
“四角法则”保证所有需要的子块都可以被完整计算:若 且 ,则 。
6.5 Sigma 点:无导数计算期望
🎯 直觉:用有限个代表点近似积分
计算 本来需要对所有可能的 积分。Sigma 点用少数几个”代表性采样点”来近似这个积分:
sigma 点的生成(,维度 ):
生成 个点(含中心点),权重 ,。
🔑 概念层:Stein 引理——避免显式计算导数
利用 Stein 引理,可以完全避免显式计算 的导数:
实际意义:只需要能调用”给定 ,计算 的值”,不需要推导 的解析导数!这极大简化了实现。
6.6 参数估计:EM 算法
🎯 直觉:鸡和蛋的问题
问题:如果我们不知道模型参数(如噪声协方差 ),如何估计状态?反之,如果不知道状态,如何学习参数?
答案:EM 算法——轮流做两件事:
重复直到收敛:
E 步:固定参数 θ,用 ESGVI 估计状态轨迹 x
M 步:固定状态估计 q(x),更新参数 θ
两步都让目标函数(负对数似然)单调不增,保证收敛(到局部最优)。
🔑 概念层:M 步的闭合解
固定状态估计后,参数更新有解析解。以噪声协方差为例:
其中 是运动残差。
重要细节:这里的期望包含了状态估计不确定性的修正项(区别于简单地用平均残差平方)。
🧮 数值示例:同时估计轨迹和噪声协方差
场景:机器人沿直线运动,我们不知道过程噪声有多大。
初始猜测:(真实值 )
| EM 迭代 | 估计 | 位置估计误差 |
|---|---|---|
| 0(初值) | 0.10 | 大(过度相信运动模型) |
| 1 | 0.42 | 中 |
| 3 | 0.87 | 小 |
| 5 | 0.98 | 很小(接近真实值) |
EM 迭代同时改善了参数估计和状态估计,两者相互促进。
6.7 方法对比:升级版精度阶梯
🔑 概念层:完整比较表
| 方法 | 本质 | 计算 sigma 点 | 后验质量 | 计算代价 |
|---|---|---|---|---|
| MAP | GVI 的 1 点近似 | 1 个(均值) | 仅均值精确 | 最低 |
| EKF | 在线 MAP | 1 个 | 偏差大 | 低 |
| SPKF / UKF | 先验 sigma 点 | 个(先验) | 均值+协方差近似 | 中 |
| ISPKF | 迭代先验 sigma | 个(先验,迭代) | 更好 | 中高 |
| ESGVI | 后验 sigma 点 | 个(后验,迭代) | 最接近真实后验 | 高 |
核心规律:越多 sigma 点 + 越靠近真实后验取点 → 越好的近似质量。
📐 推导层:GVI 与线性高斯情形的一致性
展开:线性高斯下 GVI 一步收敛到批量 MAP
对于线性高斯系统 ,期望 Hessian 等于 Hessian 本身(因为是二次函数):
这是一个常数,与 无关!因此 GVI 在一步迭代后就精确收敛,且结果与批量 MAP(Cholesky 平滑器)完全一致。
结论:GVI 是批量线性高斯估计的严格推广。在线性情形下它们等价;在非线性情形下 GVI 更准确。
本章自检清单
阅读完本章,你应该能回答:
- 为什么选 KL 而不是 KL?(期望的可计算性)
- GVI 与 MAP 的本质区别是什么?(用多个 sigma 点 vs. 用 1 个点近似期望)
- 为什么 ESGVI 可以处理大规模轨迹问题?(因子化 → 精确稀疏 → 块三对角)
- Stein 引理的作用是什么?(将导数期望化为函数值期望,避免显式求导)
- EM 算法的 E 步和 M 步分别做什么?(E 步=估计状态,M 步=更新参数)
如果以上都清楚,你掌握了变分推断的核心框架!
延伸阅读与代码
Python 实现(ESGVI 核心):
import numpy as np
def sigma_points(mu, Sigma, kappa=0):
"""生成 UKF sigma 点"""
n = len(mu)
L = np.linalg.cholesky((n + kappa) * Sigma)
points = [mu]
weights = [kappa / (n + kappa)]
for i in range(n):
points.append(mu + L[:, i])
points.append(mu - L[:, i])
weights.extend([1/(2*(n+kappa)), 1/(2*(n+kappa))])
return np.array(points), np.array(weights)
def gvi_update_step(mu, Sigma_inv, phi_func, Sigma_blocks):
"""
ESGVI 单次迭代
phi_func: 接收 x,返回 phi(x) 的值(无需导数!)
Sigma_blocks: 当前协方差子块(用于 sigma 点生成)
"""
# 生成 sigma 点
n = len(mu)
Sigma = np.linalg.inv(Sigma_inv) # 简化;实际用 Takahashi 提取子块
pts, wts = sigma_points(mu, Sigma)
# 计算期望(用 Stein 引理,只需 phi 值)
phi_vals = np.array([phi_func(x) for x in pts])
E_phi = np.sum(wts * phi_vals)
# 期望梯度(Stein 引理)
deviations = pts - mu # (2n+1) x n
E_grad = Sigma_inv @ np.sum(
wts[:, None] * deviations * phi_vals[:, None], axis=0
)
# 期望 Hessian(Stein 引理)
outer = deviations[:, :, None] * deviations[:, None, :] # (2n+1) x n x n
E_hess = (Sigma_inv @
np.sum(wts[:, None, None] * outer * phi_vals[:, None, None], axis=0)
@ Sigma_inv - Sigma_inv * E_phi)
# 更新精度矩阵和均值
Sigma_inv_new = E_hess
delta_mu = np.linalg.solve(Sigma_inv_new, -E_grad)
mu_new = mu + delta_mu
return mu_new, Sigma_inv_new
def esgvi(mu0, Sigma_inv0, phi_func, n_iter=10):
"""完整 ESGVI 迭代"""
mu = mu0.copy()
Sigma_inv = Sigma_inv0.copy()
for i in range(n_iter):
Sigma_blocks = None # 实际实现用 Takahashi 提取
mu_new, Sigma_inv_new = gvi_update_step(mu, Sigma_inv, phi_func, Sigma_blocks)
if np.linalg.norm(mu_new - mu) < 1e-6:
break
mu = mu_new
Sigma_inv = Sigma_inv_new
return mu, Sigma_inv
def em_step_covariance(residuals, state_covariances):
"""
EM M 步:更新噪声协方差
residuals: 每个时刻的残差期望 [K x m]
state_covariances: 每个时刻的状态协方差 [K x n x n]
"""
K = len(residuals)
# Q_new = (1/K) Σ_k [e_k e_k^T + 协方差修正项]
Q = np.mean([
residuals[k:k+1].T @ residuals[k:k+1] + state_covariances[k]
for k in range(K)
], axis=0)
return Q下一章转向三维几何:旋转矩阵和位姿不是普通向量,需要特殊的数学工具(Lie 群)。