3. Gibbs采样:条件分布采样、Gibbs与MH的关系、分块Gibbs、代码实现、在金融中的应用场景
Gibbs采样,说白了就是MCMC家族里那个最会「偷懒」的成员。它不需要你去调什么提议分布,也不用纠结接受率的问题。你想想看,在金融建模中,我们经常遇到高维参数空间——比如多因子模型的参数估计,这时候MH采样往往效率感人,而Gibbs采样就派上大用场了。
我个人习惯把Gibbs采样看作是一种「条件分布驱动的马尔可夫链」。它的核心思想很简单:当联合分布太复杂时,我们就拆成一个个条件分布来采样。嗯,这里要注意,Gibbs采样要求每个参数的条件分布是已知的、可采样的。
3.1 条件分布采样:Gibbs的核心机制
假设我们有一个二维的联合分布 p(x, y),想从中采样。Gibbs的做法是:
- 先固定 y,从 p(x|y) 中采样一个新的 x
- 再固定这个新的 x,从 p(y|x) 中采样一个新的 y
- 重复以上步骤
你看,这就像是在玩一个「你一步我一步」的游戏。每次只更新一个维度,其他维度都保持不动。我在项目中遇到过这样的情况:用Gibbs采样估计GARCH模型的参数时,条件分布往往就是一些标准的分布——正态分布、逆伽马分布之类的,采样起来非常方便。
关键点:Gibbs采样的每一步都只涉及条件分布,而这些条件分布通常比联合分布简单得多。这就是它高效的原因。
3.2 Gibbs与MH的关系:谁才是老大?
很多人问我:「Gibbs和MH到底啥关系?」我通常这么回答:Gibbs是MH的一个特例,而且是一个很特殊的特例。
在MH算法中,我们需要设计提议分布 q(x'|x),然后计算接受率 α。而Gibbs采样呢?它相当于把提议分布直接设成了条件分布 p(x_i | x_{-i}),而且接受率永远等于1。你想想看,这意味着什么?意味着Gibbs采样永远不会拒绝样本!
| 特性 | MH采样 | Gibbs采样 |
|---|---|---|
| 提议分布 | 需要手动设计 | 自动使用条件分布 |
| 接受率 | 通常小于1 | 恒等于1 |
| 适用场景 | 任意分布 | 条件分布已知 |
| 效率 | 可能较低 | 通常较高 |
我曾经踩过一个坑:以为Gibbs采样永远比MH好。后来发现,当条件分布很难采样时,Gibbs反而更慢。所以,没有绝对的好坏,只有适合不适合。
3.3 分块Gibbs:处理高维参数的利器
当参数维度很高时,一个一个地采样太慢了。这时候分块Gibbs就登场了。它的思路是:把参数分成几个块,每次更新一个块。
举个例子,在估计一个包含10个因子的资产定价模型时,我不会一个一个因子去采样,而是把相关性强的因子分成一组,整组一起更新。这样做的好处是:
- 减少了迭代次数
- 利用了参数间的相关性结构
- 收敛速度更快
实战技巧:分块时尽量把相关性强的参数放在一起。我在做多因子模型时,通常把市场因子和行业因子分在一组,因为它们之间往往有较强的相关性。
3.4 代码实现:一个完整的Gibbs采样示例
下面我用一个简单的二元正态分布来演示Gibbs采样。这个例子虽然简单,但包含了Gibbs采样的所有核心要素。
import numpy as np
import matplotlib.pyplot as plt
def gibbs_sampler(n_samples, rho=0.8):
"""
Gibbs采样器:从二元正态分布中采样
参数:
n_samples: 采样数量
rho: 相关系数
返回:
samples: 采样结果,形状为 (n_samples, 2)
"""
samples = np.zeros((n_samples, 2))
x, y = 0.0, 0.0 # 初始值
for i in range(n_samples):
# 从条件分布 p(x|y) 采样
# x|y ~ N(rho*y, 1-rho^2)
x = np.random.normal(rho * y, np.sqrt(1 - rho**2))
# 从条件分布 p(y|x) 采样
# y|x ~ N(rho*x, 1-rho^2)
y = np.random.normal(rho * x, np.sqrt(1 - rho**2))
samples[i] = [x, y]
return samples
# 运行采样
samples = gibbs_sampler(5000, rho=0.8)
# 可视化结果
plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.scatter(samples[:, 0], samples[:, 1], alpha=0.5, s=1)
plt.title('Gibbs采样结果')
plt.xlabel('x')
plt.ylabel('y')
plt.subplot(1, 2, 2)
plt.plot(samples[:100, 0], label='x')
plt.plot(samples[:100, 1], label='y')
plt.title('前100次迭代的轨迹')
plt.legend()
plt.tight_layout()
plt.show()
注意:代码中的初始值我设成了(0,0)。在实际应用中,初始值的选择会影响burn-in期的长度。我建议多跑几条链,用不同的初始值,然后对比结果是否一致。
3.5 在金融中的应用场景
Gibbs采样在金融领域用得最多的,我个人觉得是这几个方向:
- 随机波动率模型:SV模型的参数估计是Gibbs采样的经典应用。条件分布往往是标准的,采样效率很高。
- 因子模型:多因子模型的贝叶斯估计,特别是当因子数量很多时,分块Gibbs能大幅提升效率。
- 信用风险模型:比如估计违约概率的相关性结构,Gibbs采样可以处理高维的潜在变量。
- 资产配置:在Black-Litterman模型中,用Gibbs采样来生成后验分布样本,比解析解更灵活。
我记得有一次做信用组合风险模型,需要估计1000个公司的违约相关性。如果用MH采样,调提议分布就够我喝一壶的了。后来改用分块Gibbs,把公司按行业分组,每组内用条件分布采样,问题就迎刃而解了。
核心总结:Gibbs采样是处理高维参数空间的一把好手。它不需要调参,接受率永远是1,但前提是条件分布要容易采样。在实际金融建模中,分块Gibbs往往是更实用的选择。