ポアソン混合分布の推論 (Gibbs Samling)
はじめに
ベイズ推論による機械学習を読んだので、実装の練習も兼ねていくつかさらっとメモを残しておこうと思います。
今回は、ポアソン混合分布のギブスサンプリングによる推論です。
ポアソン混合分布の推定
ポアソン分布は離散非負の値をとり、以下のヒストグラムは2つのポアソン分布からサンプリングしたものです。
数式では、以下の通りです。
は潜在変数であり、 (0,0,1,0,...,0) のようなクラスタ数Kあるonehot表現されたベクトルです。つまり、というのは、n番目のデータはk番目の分布からサンプリングされたデータであることを示しています
また、潜在変数はカテゴリ分布に従います。 は混合率と言って、混合分布から観測されたデータに対して、各分布の混合の比率をを示すパラメータです。
そして、ポアソン分布のパラメータに対してポアソン分布の共役事前分布であるガンマ分布を導入しますが、今回はガンマ分布のパラメータは固定とします。
ギブスサンプリング
ギブスサンプリングのざっくり流れとしては、適当なパラメータ があるとして、 をサンプルするときすでにサンプルされた値 で分布を条件付けしてサンプリングします。続けて、 を得るために で分布を条件付けして〜というようにサンプリングを繰り返すことで、各パラメータごと更新していく手法です
ポアソン混合分布の生成に関わるパラメータ と潜在変数 の2つの事後分布に分けてサンプルすることで簡単な確率分布を導出できるのでギブスサンプリングを適用できます。
ここからは実装に必要なこれらの確率分布の計算結果だけを載せていきます。
潜在変数の事後分布
データとパラメータが与えられた元では、条件付き独立が成立し、を独立にサンプリングが可能になることが分かります。
上記はポアソン分布とカテゴリ分布であり、展開して でまとめるとカテゴリ分布になります。
パラメータの事後分布
ベイズの定理を用いると、
ここでも が与えられた元では、各パラメータの分布が独立に分解されます。
まず の分布は、上記の に関係ある項に対数を取り、
展開して、 でまとめてみるとガンマ分布になります。
の分布は、上記の に関係ある項に対数を取り、
展開して、 でまとめてみるとディリクレ分布になります。
実装
学習に使うデータは、 のパラメータの2つのポアソン分布に従う1次元データ(冒頭のヒストグラム)で、それぞれ150個、250個の合計400個のデータを得ていると仮定します。
x1 = np.random.poisson(lam = 25,size = 150) x2 = np.random.poisson(lam = 10, size = 250)
ソースコード
以下がコードです.
#ギブスサンプリング import numpy as np import matplotlib.pyplot as plt import math np.random.seed(1103) def softmax(X): l = [] for x in X: sum_ex = np.sum(np.exp(x)) l.append([np.exp(i)/sum_ex for i in x]) return np.array(l) N = 400 #number of class K = 2 #paramter of poisson dist lam = np.ones(K).reshape(1,-1) #parameter of category dist pi = np.array([0.5,0.5]).reshape(1,-1) #latent var S = np.zeros([N,K]) #parameters of gamma dist a = np.ones(K) b = np.ones(K) #parameters of dirichlet alpha = np.ones(K) #train data x1 = np.random.poisson(lam = 25,size = 150) x2 = np.random.poisson(lam = 10, size = 250) X = np.r_[x1,x2].reshape(-1,1) max_iter = 30 lam_sample,pi_sample =[],[] for i in range(max_iter): #S ex = X.dot(np.log(lam)) - lam + np.log(pi) ex = softmax(ex) for n in range(N): S[n] = np.random.multinomial(1,ex[n],1) #λ a_hat = X.T.dot(S) + a b_hat = S.sum(axis = 0) + b lam = np.random.gamma(a_hat,1/b_hat) lam_sample.append(lam[0]) #π alpha_hat= S.sum(axis = 0) + alpha pi = np.random.dirichlet(alpha_hat) pi_sample.append(pi) fig = plt.figure(figsize=(10,5)) pi_sample = np.array(list((map(list,pi_sample)))) lam_sample = np.array(list((map(list,lam_sample)))) plt.subplot(1,2,1) for i in range(K): pi_m =np.mean(pi_sample[:,i]) plt.hist(pi_sample[:,i],bins=10,alpha=0.6,label=f"{i}: {round(pi_m,2)}") plt.xlabel("$π$") plt.legend() plt.subplot(1,2,2) for i in range(K): lam_m =np.mean(lam_sample[:,i]) plt.hist(lam_sample[:,i],bins=10,alpha=0.6,label=f"{i}: {round(lam_m,2)}") plt.xlabel("$π$") plt.legend()
結果
パラメータ の値を30回サンプリングしてヒストグラムで可視化してみます。
(左)混合率 の平均はそれぞれ0.380, 0.620 となり、実際に学習に使用したデータの比率は 0.375, 0.625 となるので、良い感じですね。
(右) の平均は 10.01, 24.80 となり、実際に分布を生成した値は 10, 25 なのでこれも良い感じです
ヒストグラムから分かるように、この場合ではパラメータを確率変数とすることで, 各パラメータが取りうる値の不確実性を表現しています.