メトロポリス・ヘイスティングス法
メトロポリス・ヘイスティングス法
M-H法を用いて、
メトロポリス・ヘイスティングス法 (the Metropolis-Hastings algorithm; M-H法) はMCMC法の一つ。直接サンプリングすることが困難な確率分布からサンプルを生成するために使用される。「提案」という操作のアルゴリズムを工夫することで、メトロポリス法、ギブスサンプリング、ハミルトニアン・モンテカルロ法などに帰着する。M-H法はこれらの一般的な形であると解釈すれば良いだろう。
準備
まず「提案分布」と呼ばれる条件付き確率分布
正確には、「確率」と呼ぶためには、これを
アルゴリズム
確率分布
なお、以上において
- 確率
で提案を受理 (accept)、\min( A(y | x), 1 ) 。x \gets y - さもなくば棄却 (reject)、yを捨てる。
したがって、上記をもう少し端折って書くと
- 提案:
y \sim Q(y | x) - メトロポリステスト:
の確率で提案を受理\min( A(y | x), 1 )
と表現できる。
何がすごいの?
アルゴリズムをよく見ると、確率分布を計算する箇所が、
これはなかなかすごいことで、 この式により、
などの形で書かれていて、しかも正規化定数
注意事項
このアルゴリズムは、提案分布
ただし、単にシンプルなだけでは不十分で、受理確率
提案分布をシンプルにしつつ、受理確率も高くするようなものが、優れたM-H法というわけである。
実装例
スカラーをサンプリングするM-H法の Python での実装例。
from typing import Callable
class MetropolisHastings:
def __init__(self,
target_distribution: Callable[[float], float],
proposal_distribution: Callable[[float, float], float],
proposal_sampler: Callable[[], float]
):
"""
Args:
target_distribution (Callable): 目標とする確率分布
proposal_distribution (Callable): 提案分布
proposal_sampler (Callable): 提案分布からのサンプリングを行う関数
"""
def calc_acceptance_ratio(proposed_x, current_x):
"""
Args:
proposed_x (float)
current_x (float)
Returns:
float: 受理確率
"""
acceptance_ratio = \
(target_distribution(proposed_x) * proposal_distribution(current_x, proposed_x)) \
/ (target_distribution(current_x) * proposal_distribution(proposed_x, current_x))
return acceptance_ratio
self.calc_acceptance_ratio = calc_acceptance_ratio
self.proposal_sampler = proposal_sampler
self.samples = []
def sample(self, target_samples, initial_x):
"""
Args:
target_samples (int): 目標サンプル数
initial_x (float): サンプリングの初期値
"""
self.samples.append(initial_x)
for _ in range(target_samples - 1):
current_x = self.samples[-1]
proposed_x = self.proposal_sampler()
acceptance_ratio = self.calc_acceptance_ratio(proposed_x, current_x)
if np.random.rand() < acceptance_ratio:
self.samples.append(proposed_x)
else:
self.samples.append(current_x)
提案分布 proposal_distribution()
と、提案分布からのサンプリングを行う関数 proposal_sampler()
は辻褄があっていないといけない。もしも一方が正規分布を指定するなら、もう一方も全く同じ正規分布を指定していなくてはいけない。もしも一方が連続一様分布なら、もう一方も連続一様分布を指定する。
具体的に、たとえば正規分布を指定する場合を見てみよう:
import numpy as np
import scipy.stats as stats
def target_distribution(x):
return np.exp(-x**4)
def proposal_distribution(proposed_x, current_x):
return stats.norm.pdf(proposed_x, loc=0, scale=1)
def proposal_sampler():
return stats.norm.rvs(loc=0, scale=1)
np.random.seed(0)
initial_x = 0
iterations = 10000
mcmc_sampler = MetropolisHastings(target_distribution, proposal_distribution, proposal_sampler)
mcmc_sampler.sample(iterations, initial_x)
ここでは、提案分布として SciPy
の stats.norm.pdf()
(正規分布の確率密度関数) を指定し、提案分布からのサンプリングには stats.norm.rvs()
(正規分布からのサンプリングを行う関数) を指定した。これをヒストグラムに可視化するには、たとえば次のようにする。
import matplotlib.pyplot as plt
true_x = np.linspace(-2, 2, 1000)
true_y = target_distribution(true_x)
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(6,3))
ax1.hist(mcmc_sampler.samples, bins=30, density=True, color='w', linewidth=2, edgecolor='k')
ax1.set_xlabel('Sampled values')
ax1.set_ylabel('Frequency')
ax1.set_xlim(-2, 2)
ax1.set_ylim(bottom=0)
ax1.set_title('Sample')
ax2.plot(true_x, true_y, color='k')
ax2.set_xlabel('$x$')
ax2.set_xlim(-2, 2)
ax2.set_ylim(bottom=0)
ax2.set_title('Target distribution')
plt.show()
ここまでを実行すれば、冒頭で示した図のような結果が得られる。
Discussion