機械学習に詳しくなりたいブログ

機械学習や数学について勉強した内容を中心に書きます。100%趣味です。記事は数学的に厳密でなかったり誤りを含んでいるかもしれません。ご指摘頂ければ幸いです。

マルコフ連鎖モンテカルロ法 - メトロポリス・ヘイスティングス法(1)

概要

マルコフ連鎖モンテカルロ法とは、サンプリングしたい分布(目標分布)p(\mathbf x)が不変分布となるようなマルコフ連鎖を設定することでp(\mathbf x)からサンプリングする手法です。マルコフ過程で、ある適当な推移核を定めたとき、各状態に遷移する確率が不変分布になる例を見ました。今回は逆で、不変分布が既知、つまり目標分布であるp(\mathbf x)です。そしてp(\mathbf x)が不変分布となるような推移核を求めることが目標です。

マルコフ連鎖モンテカルロ法を実現するアルゴリズムの1つが、メトロポリス・ヘイスティングス法です。なおマルコフ連鎖モンテカルロ法は略してMCMC法と表記されることが多いようです。

詳細釣り合い条件

マルコフ過程では、状態が離散的なものを考え推移核として行列を用いましたが、連続的な分布では、状態\mathbf xから\mathbf x^{\prime}へ遷移する推移核を k( \mathbf x^{\prime} | \mathbf x)と表します。 マルコフ連鎖が不変分布p(\mathbf x)に収束する十分条件が次の詳細釣り合い条件です。


p(\mathbf x)k( \mathbf x^{\prime} | \mathbf x) = p(\mathbf x^{\prime})k( \mathbf x | \mathbf x^{\prime}) \tag{1}

例えばp(\mathbf x) \gt p(\mathbf x^{\prime})だったとしたとき、k( \mathbf x^{\prime} | \mathbf x)  \lt k( \mathbf x | \mathbf x^{\prime}) となりますから、生起しやすい状態(\mathbf x)から生起しにくい状態(\mathbf x^{\prime})へ遷移する確率は低く、その逆の確率は高いことを意味しています。下図のようなイメージです。


ここで式(1)の両辺を\mathbf x^{\prime}で積分すれば、左辺はp(\mathbf x)になりますから、


\displaystyle p(\mathbf x) = \int p(\mathbf x^{\prime})k( \mathbf x | \mathbf x^{\prime}) d \mathbf x^{\prime} \tag{2}

となります。式(2)は、あらゆる点\mathbf x^{\prime}から推移核を k( \mathbf x | \mathbf x^{\prime})によって\mathbf xへ遷移する確率の和がp(\mathbf x)そのものであることを示しています。つまり詳細釣り合い条件が成り立つ推移核によってサンプリングを続ければ、それは分布p(\mathbf x)に収束するはずです。*1

メトロポリス・ヘイスティングス法のアルゴリズム

概要で書いた通り、いまサンプリングしたい目標分布はp(\mathbf x)ですから、これは既知です。推移核 k( \mathbf x^{\prime} | \mathbf x)がわかっていれば良いのですが不明な状態です。そこでサンプリング可能な既知の提案分布q(\mathbf x^{\prime} | \mathbf x)を用います。この提案分布は欲しい推移核ではありませんから、式(1)の詳細釣り合い条件を満たしておらず、例えば


p(\mathbf x)q( \mathbf x^{\prime} | \mathbf x) \gt p(\mathbf x^{\prime})q( \mathbf x | \mathbf x^{\prime}) \tag{3}

などのように等号が成立していません。ここで、式(3)の等号が成立すればp(\mathbf x)からサンプリングできるのですから、次式のようにある確率aで補正し、\mathbf xから\mathbf x^{\prime}へ遷移する確率を下げてやれば良いはずです。


p(\mathbf x)q( \mathbf x^{\prime} | \mathbf x)a = p(\mathbf x^{\prime})q( \mathbf x | \mathbf x^{\prime}) \tag{4}

つまり\mathbf xから\mathbf x^{\prime}へ遷移したとき、それを確率aで採択すれば詳細釣り合い条件が満たされる、つまりp(\mathbf x)からのサンプリングが実現できるということになります。 式(4)より、a


\displaystyle a =\frac { p(\mathbf x^{\prime})q( \mathbf x | \mathbf x^{\prime})} {p(\mathbf x)q( \mathbf x^{\prime} | \mathbf x)} \tag{5}

です。なお式(3)の等号は逆かもしれず、その場合は式(5)のaが1以上となります。確率が大きく補正されるわけですから、無条件で採択すれば良いはずです。

以上をまとめます。

メトロポリス・ヘイスティングス法のアルゴリズム

  1. 目標分布をp(\mathbf x)、提案分布をq(\mathbf x^{\prime} | \mathbf x)とする。
  2. 提案分布から\mathbf x^{\prime}をサンプリングする。
  3.  \displaystyle a =\frac { p(\mathbf x^{\prime})q( \mathbf x | \mathbf x^{\prime})} {p(\mathbf x)q( \mathbf x^{\prime} | \mathbf x)}の値を計算する。
  4. aが1以上なら\mathbf x^{\prime}を採択する。aが1未満なら、その確率で\mathbf x^{\prime}を採択する。採択されなければ同じ値(\mathbf x)をサンプリング点とする。
  5. 手順2に戻り、再び次の候補点をサンプリングする。

なお、q( \mathbf x | \mathbf x^{\prime})=q( \mathbf x^{\prime} | \mathbf x)のような提案分布を用いれば、

 \displaystyle a =\frac { p(\mathbf x^{\prime})} {p(\mathbf x)} \tag{5}

です。例えばランダムウォークと呼ばれるものはq( \mathbf x | \mathbf x^{\prime})=q( \mathbf x^{\prime} | \mathbf x)が成り立ちます。

式(5)で考えれば、イメージはしやすいアルゴリズムですよね。既知の提案分布から次の候補点をサンプリングし、それが目標分布において確率が高い方向なら採択、低い方向なら、低い度合いに合った確率で採択する、という感じでしょうか。ただし採択されなかったら捨てるのではなく、同じ値がサンプリングされたとするところが棄却サンプリングの採択部分とは違います。また、初期値は任意に決めますが、発生確率が低いところから出発してしまうと、本来サンプルされにくい部分からいきなり始まってしまいます。初期値への依存を薄めるため、最初のいくつかのサンプルは捨ててしまうのが良いようで、これをバーンインと呼ぶようです。

サンプリング実験

コードを書いて実験してみました。とりあえず簡単のために1次元の分布です。提案分布は、正規分布を用いたランダムウォークです。目標分布は p(x) =\cos^{2} 0.5 \pi xです。

ちゃんとサンプルできました。

次に以下のような目標分布で試してみました。

MCMC法の性質上、サンプル点は前回の値の影響を受けます。したがって、このような確率の高い山が複数ある分布からサンプリングをすると、1つの山から集中的にサンプルし、ある確率で別の山に移るという挙動になるはずです。それを確認したのが次のアニメーションです。50サンプルごとにヒストグラムを描画しています。やはり各山から交互にサンプルしているような動きになっていました。

しかしサンプル数を十分に大きくすれば、目的の分布に近いものになりました。もっと意地悪な形状の分布にするとサンプリングがうまくいかない場合などもあるかもしれません。

次回は2次元の目標分布からサンプリングの実験をしてみたいと思います。(次回:マルコフ連鎖モンテカルロ法 - メトロポリス・ヘイスティングス法(2)) 

今回のコードです。提案分布の分散を変えるとサンプリングが失敗することもありました。これも1次元程度ならいろいろ調整しながらやれますが多次元だとうまくいったかどうか、どう判断すればいいんでしょうかね。

# マルコフ連鎖モンテカルロ法(メトロポリスヘイスティングズ法)のテスト
import numpy as np
import matplotlib.pyplot as plt

def randomwalk(x):
    z = np.random.normal(0,0.5)
    return x + z

# サンプルしたい関数
def p(x):
    if x < -1 or 1 < x:
        return 0
    return np.cos(0.5*np.pi*x)**2

# MCMC
def MCMC(x):
        # サンプル候補
        z = randomwalk(x)
        if p(z)/p(x) > np.random.uniform(0, 1):
            return z
        else:
            return x


# ランダムシードを固定
np.random.seed(0)

# サンプル数
N = 100000

# バーンイン
B = 1000

# 初期値
x = 0.1

samples = np.empty(N)

# MCMC
for i in range(N):
    x = MCMC(x)
    samples[i] = x

plt.hist(samples[B:], bins=50, normed=True, alpha=0.5)
x = np.linspace(-1, 1, 500)
y = np.vectorize(p)(x)
plt.plot(x, y)
plt.show()

*1:記事の中で\mathbf xから\mathbf x^{\prime}へ遷移したり、\mathbf x^{\prime}から\mathbf xに遷移したりいろいろな書き方をしてしまってややこしいですが、どっちでも同じことです