4. Metropolis-Hastings算法原理

Metropolis-Hastings(简称MH)算法是MCMC方法族中最具代表性的一种构造性采样方法。它的核心思想是:当我们无法直接从目标分布 p(x) 中抽样时,可以借助一个容易采样的提议分布 q(x'|x),构造一条马尔可夫链,使该链的平稳分布恰好等于目标分布。理解MH的关键在于三件事:提议分布如何选、接受率如何推导、算法如何落地实现。

一、从"提议"到"接受":MH的构造逻辑

设当前状态为 x,我们从提议分布 q(x'|x) 中生成候选状态 x'。如果直接接受所有候选,链的平稳分布将是 q 而非 p,因此必须引入一个接受概率 α(x, x') 来"修正"提议分布带来的偏差。MH的接受率定义为:

α(x, x') = min{ 1, [p(x') · q(x | x')] / [p(x) · q(x' | x)] }

这个式子的直观含义是:如果候选状态在目标分布下更"合理"(p(x') ≥ p(x)),则一定接受;如果候选状态更"不合理",则以一定概率接受,接受概率由目标分布比值与提议分布反向比值的乘积决定。注意这里出现了 q(x|x') 与 q(x'|x) 的比值,这正是MH相比原始Metropolis算法(要求提议分布对称)的推广之处——它允许使用非对称提议分布。

二、接受率的推导思路

接受率的推导依赖细致平衡条件(detailed balance):p(x)·T(x→x') = p(x')·T(x'→x),其中 T(x→x') = q(x'|x)·α(x,x') 是转移核。将转移核代入细致平衡式,得到:

p(x) · q(x'|x) · α(x, x') = p(x') · q(x | x') · α(x', x)

若令 α(x,x') = min{1, R(x,x')},其中 R = [p(x')q(x|x')]/[p(x)q(x'|x)],可以验证当 R ≥ 1 时 α=1,反向接受率 α(x',x)=1/R,等式成立;当 R < 1 时 α=R,反向接受率为1,等式同样成立。因此该接受率恰好满足细致平衡,从而保证 p 是链的平稳分布。实际实现时,由于 α 中只出现 p(x')/p(x) 的比值,目标分布中的归一化常数会被约掉——这正是MH能处理未归一化密度的根本原因。

当前状态 x 目标密度 p(x) 提议分布 q(x'|x) 采样候选 x' (如正态随机游走) 计算接受率 α R = p(x')q(x|x') ÷ p(x)q(x'|x) α = min{1, R} (归一化常数约去) u ≤ α ? u ~ U(0,1) 接受:x ← x' 记录样本 x' 拒绝:保留 x 重复记录 x 迭代循环:形成马尔可夫链,平稳分布收敛到 p(x)

三、算法流程与实现要点

将上述逻辑整理为可执行的迭代流程:初始化 x₀;对第 t 步,从 q(·|x_{t-1}) 采样候选 x';计算接受率 α;生成均匀随机数 u~U(0,1);若 u ≤ α 则接受 x_t = x',否则 x_t = x_{t-1}(注意拒绝时也要把当前状态重复记入样本链,这是保证平稳分布正确的关键,不能跳过)。

实现时有几个容易踩坑的细节。第一,提议分布的标准差(步长)直接决定接受率与混合速度:步长过小则接受率高但链移动缓慢、自相关强;步长过大则大量候选被拒、链停滞。随机游走MH的经验目标是接受率落在0.2~0.5之间。第二,必须丢弃前若干步的"预热期"(burn-in)样本,因为链从初始点出发尚未收敛到平稳分布。第三,为降低自相关,可对链进行稀释(thinning),每隔若干步取一个样本。

四、手写实现一维目标分布采样

下面以目标分布 p(x) ∝ exp(-x²/2)·(1 + 0.5·sin(3x)) 为例(一个被轻微调制的标准正态,未归一化),用对称正态随机游走提议 q(x'|x)=N(x, σ²) 实现MH。由于提议对称,q(x|x')/q(x'|x)=1,接受率简化为 min{1, p(x')/p(x)}。

import numpy as np

def target_logpdf(x):
    # 未归一化对数密度,避免数值下溢
    return -0.5 * x**2 + np.log(1 + 0.5 * np.sin(3 * x))

def mh_sample(n_iter=20000, sigma=1.2, x0=0.0, burn_in=2000, thin=5, seed=42):
    rng = np.random.default_rng(seed)
    x = x0
    samples = []
    accepted = 0
    for t in range(n_iter):
        x_prop = x + rng.normal(0, sigma)          # 对称提议
        log_r = target_logpdf(x_prop) - target_logpdf(x)
        if np.log(rng.random()) < log_r:           # 等价于 u <= alpha
            x = x_prop
            accepted += 1
        if t >= burn_in and (t - burn_in) % thin == 0:
            samples.append(x)
    return np.array(samples), accepted / n_iter

samples, acc_rate = mh_sample()
print("接受率:", round(acc_rate, 3))
print("样本均值:", round(samples.mean(), 3), "样本标准差:", round(samples.std(), 3))

这里用对数密度之差与 log(u) 比较,等价于 u ≤ α,但避免了极小概率下的下溢问题,是工程实现的标准做法。以 σ=1.2 运行时,接受率通常落在0.4~0.6附近(示例参数下的合理预期,实际数值随随机种子略有波动)。若接受率明显偏高(如0.9以上),说明步长偏小,应增大 σ;若低于0.15,说明步长过大,应减小 σ。

五、结果判读与收敛检查

得到样本后,不能直接当作目标分布的样本使用,需要做基本诊断。可将样本直方图与目标密度曲线叠加对比,观察形状是否吻合;绘制样本轨迹图(trace plot),看链是否在均值附近稳定波动而无明显漂移;计算样本自相关函数,若滞后若干步后自相关迅速衰减到接近0,说明混合良好。对于本例,样本均值应接近0附近,直方图应呈现带轻微波动的钟形。若轨迹图显示链长期卡在某一区域,往往意味着提议步长不当或目标分布存在多峰,此时可考虑调整步长或改用自适应MH。

MH的接受率公式中只出现目标密度的比值,因此目标分布无需归一化——这是它相对拒绝采样、重要性采样在处理复杂后验时的核心优势。实现时优先在对数域计算比值,可显著提升数值稳定性。