2. 先验参数越界导致采样失败的排查

问题定位:先验参数越界如何让采样器崩溃

在 MCMC 采样中,先验分布的超参数(如正态先验的 sigma、Gamma 先验的 alpha/beta、Beta 先验的 a/b)必须落在各自分布的合法定义域内。一旦把 sigma 误设为负值,采样器在计算对数先验密度时会得到 NaN 或直接抛出定义域异常,整条采样链在初始化阶段就中断。排查的关键不是急着改采样器参数,而是先确认先验参数本身是否满足约束。

概率模型定义 先验: Normal(mu, sigma) 先验参数约束检查 sigma > 0 ? alpha,beta > 0 ? sigma = -0.5 越界 → 非法 sigma = 0.5 合法 → 继续 采样器异常 log_prior = NaN 采样正常 链收敛 加入参数合法性校验 assert sigma > 0 修正先验参数 sigma = 0.5 重新采样 验证通过 校验前置 修复后重跑

复现:构造一个会失败的采样模型

下面用 PyMC 构造一个最小可复现模型:观测数据为 5 个样本,先验均值 mu 用正态先验,先验标准差 sigma 被误设为 -0.5。运行采样时,采样器在计算对数先验密度时遇到负的标准差,抛出定义域异常。

import numpy as np
import pymc as pm

# 观测数据(示例)
y_obs = np.array([1.2, 0.8, 1.5, 0.9, 1.1])

# 错误配置:sigma 被误设为负值
with pm.Model() as broken_model:
    mu = pm.Normal("mu", mu=0, sigma=10)
    sigma = pm.HalfNormal("sigma", sigma=-0.5)   # 越界:HalfNormal 的 sigma 必须 > 0
    y = pm.Normal("y", mu=mu, sigma=sigma, observed=y_obs)

    try:
        trace = pm.sample(draws=500, tune=500, chains=2, random_seed=42)
    except Exception as e:
        print("采样失败:", type(e).__name__, e)

预期现象:HalfNormal 的 sigma 参数为负时,PyMC 在构建分布对象阶段就会抛出 ValueError,提示参数必须为正。若换成自定义先验或某些后端,错误可能延迟到 logp 求值时以 NaN 形式出现,表现为采样链全部拒绝、r_hat 异常或直接报 NaN 相关错误。

修复:参数合法性校验 + 修正先验

修复分两层:第一层在模型构建前对先验超参数做显式校验,把错误尽早暴露;第二层把 sigma 修正为合法值。校验函数应覆盖常见先验的约束:正态/半正态的 sigma > 0,Gamma 的 alpha > 0, beta > 0,Beta 的 a > 0, b > 0。

def validate_prior_params(params: dict):
    """检查先验超参数是否落在合法定义域内。"""
    rules = {
        "sigma": lambda v: v > 0,
        "alpha": lambda v: v > 0,
        "beta":  lambda v: v > 0,
        "a":     lambda v: v > 0,
        "b":     lambda v: v > 0,
    }
    for name, value in params.items():
        if name in rules and not rules[name](value):
            raise ValueError(
                f"先验参数 {name}={value} 越界,必须满足约束"
            )
    return True

# 修正后的配置
prior_params = {"mu": 0.0, "sigma": 0.5}
validate_prior_params(prior_params)   # 通过

with pm.Model() as fixed_model:
    mu = pm.Normal("mu", mu=0, sigma=10)
    sigma = pm.HalfNormal("sigma", sigma=prior_params["sigma"])
    y = pm.Normal("y", mu=mu, sigma=sigma, observed=y_obs)
    trace = pm.sample(draws=500, tune=500, chains=2, random_seed=42)

验证与判读

修复后应确认三件事:采样无异常抛出;pm.summary(trace) 中 r_hat 接近 1(经验上小于 1.01);sigma 的后验样本全部为正。若仍出现 NaN 或链不收敛,说明问题不在先验参数越界,应转向其他排查方向(如数据尺度、采样器步长)。

把 validate_prior_params 放在模型构建之前调用,可以让越界错误在采样开始前就暴露,避免浪费采样时间,也让错误信息直接指向具体参数名和非法值。
不要用 abs() 或 max(sigma, 1e-6) 之类的方式“静默修正”越界参数。这会掩盖配置错误,使模型在不知情的情况下使用与预期不同的先验,导致后验解释失真。正确做法是显式报错并修正配置源头。