Mímisbrunnr知恵の泉

← シミュレーション 一覧

🎓 レベル:標準 | 重要度:A(必須)

📎 前提:メトロポリス・ヘイスティングス法 | ベイズ:収束診断

要点(BLUF)

1. なぜ診断が要るか

MCMC サンプルには2つの厄介な性質があります。

しかも「収束したか」は連鎖を見ても確定できません(多峰分布の片方の山に嵌って気づかない、など)。そこで複数の診断を併用します。

2. R-hat(Gelman–Rubin 統計量)

異なる初期値から複数の連鎖を走らせ、(a) 連鎖のばらつき BB と (b) 連鎖のばらつき WW を比べます。収束していれば、どの連鎖も同じ定常分布を見ているので両者が一致し、比

R^=Var^+W,Var^+=L1LW+1LB\hat{R} = \sqrt{\frac{\widehat{\text{Var}}^+}{W}},\qquad \widehat{\text{Var}}^+ = \frac{L-1}{L}\,W + \frac{1}{L}\,B

1に近づくLL は連鎖長)。未収束だと連鎖がバラバラの場所にいて BWB \gg WR^1\hat{R} \gg 1 になります。目安は R^<1.01\hat{R} < 1.01

import numpy as np

# 乱数シードを固定
rng = np.random.default_rng(43)

def mh_chain(seed, n, step=2.0, x0=0.0):
    """標準正規を狙うランダムウォークMH"""
    r = np.random.default_rng(seed)
    x = x0; out = np.empty(n)
    for i in range(n):
        xp = x + r.normal(0, step)
        if np.log(r.random()) < (-0.5*xp**2) - (-0.5*x**2):
            x = xp
        out[i] = x
    return out

M, n = 4, 50_000
# 異なる初期値(散らばった出発点)から4連鎖
chains = np.array([mh_chain(50+m, n, x0=rng.normal(0, 3)) for m in range(M)])
burn = n // 2
ch = chains[:, burn:]                  # 後半だけ使う(バーンイン除去)

m, L = ch.shape
chain_means = ch.mean(axis=1)
chain_vars = ch.var(axis=1, ddof=1)
B = L * chain_means.var(ddof=1)        # 連鎖間分散
W = chain_vars.mean()                  # 連鎖内分散
var_hat = (1 - 1/L)*W + B/L
Rhat = np.sqrt(var_hat / W)
print(f"R-hat = {Rhat:.4f}  (収束なら ~1.0)")

出力:

R-hat = 1.0002  (収束なら ~1.0)

出力の意味:4本の連鎖を散らばった初期値から出したのに R-hat = 1.0002。連鎖間と連鎖内のばらつきがほぼ一致=全連鎖が同じ標準正規を見ている=収束した、と判断できます。これが 1.05 や 1.2 なら、連鎖がまだバラバラの場所にいて収束していない(バーンインを伸ばす・連鎖を長くする必要がある)。

3. 有効標本サイズ(ESS)

自己相関があると、LL サンプルの情報量は独立 LL 個より少ない。有効標本サイズ

ESS=Lτ,τ=1+2k=1ρk\text{ESS} = \frac{L}{\tau},\qquad \tau = 1 + 2\sum_{k=1}^{\infty}\rho_k

τ\tau積分自己相関時間ρk\rho_k はラグ kk の自己相関)。「実質的に独立サンプルが何個ぶんあるか」を表します。

import numpy as np

def mh_chain(seed, n, step=2.0, x0=0.0):
    r = np.random.default_rng(seed)
    x = x0; out = np.empty(n)
    for i in range(n):
        xp = x + r.normal(0, step)
        if np.log(r.random()) < (-0.5*xp**2) - (-0.5*x**2):
            x = xp
        out[i] = x
    return out

def ess(x):
    x = x - x.mean(); n = len(x)
    acf = np.correlate(x, x, mode='full')[n-1:] / (x.var() * np.arange(n, 0, -1))
    tau = 1.0
    for k in range(1, n):
        if acf[k] < 0: break            # 自己相関が負になったら打ち切り
        tau += 2*acf[k]
    return n/tau, tau

chain = mh_chain(60, 50_000)[25_000:]   # バーンイン後の25000サンプル
e, tau = ess(chain)
print(f"積分自己相関時間 tau = {tau:.1f}")
print(f"有効標本サイズ ESS (L=25000) = {e:.0f}")

出力:

積分自己相関時間 tau = 4.7
有効標本サイズ ESS (L=25000) = 5292

出力の意味:25000サンプルあっても、自己相関時間 τ=4.7\tau=4.7 のせいで実質的に独立なのは約5300個ぶん。つまり連続する約4.7サンプルが「ほぼ1個ぶん」の情報。歩幅 step を最適化すれば τ\tau が下がり ESS が増えます。推定の精度(標準誤差)は LL ではなく ESS で決まる(σ/ESS\sigma/\sqrt{\text{ESS}})ので、ESS こそが MCMC の「実質サンプル数」です。

4. 実装の作法

数式の直観的意味

R-hat は「みんな同じ景色を見ているか」のチェックです。違う場所から出発した登山者(連鎖)が、十分歩いた後に全員が同じ高度分布を報告すれば、その分布が真の地形(定常分布)。報告がバラついていれば、まだ各自が出発点近くの局所にいる証拠(BWB \gg W)。ESS は「サンプルの水増し度」の補正です。自己相関した連続サンプルは「同じ情報の繰り返し」を含むので、額面の LLτ\tau で割って実質の独立数に直す。τ\tau は「前の状態を忘れるのにかかる歩数」で、これが MCMC の効率そのもの。R-hat で「収束したか」、ESS で「どれだけ精度があるか」——この2つで MCMC の信頼性が定量化されます。

⚠️ よくある誤解・落とし穴

対応シミュレーション参照

本文の R-hat(4連鎖・default_rng(43)default_rng(50+m))と ESS(default_rng(60)τ=4.7\tau=4.7, ESS≈5300)。応用はベイズへ。

関連ノート