ベイズ推論の事後分布(離散)の更新について
前回の続き
VAEの理解に必要なベイズ学習について - 時給600円
パラメータの事前分布を自分で仮定して、観測データを元により適したパラメータを推定するのがベイズ学習といった話だった。
事前分布として固定値ではなく正規分布といった確率分布を与えてその時にちゃんとパラメータが学習されるのか確認する。
例としてある1枚のコインが存在して、そのコインで100回コイントスをする。
表が10回ほど、裏が90回ほど出たとする。このとき結果から1枚のコインの表が出る確率が求められるか。
100回中10回程度しか表が出てないんだから確率としては でいいじゃん^^と自分は思うがベイズ学習で似たような結果が得られるか確認する。
まずは上記の設定に似たコイントスの結果を生成はこんな感じ
true_mu = 0.16 X = np.random.binomial(n=1,p=true_mu,size=100) # 結果の例 # (array([0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, # 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, # 0, 0, 0, 0, 0, 0, 0, 0]) )
コイントスは二値の値 { 0 , 1 } を取るので、{0,1}を生成してくれるベルヌーイ分布を使う。(1がコインの表とする)
ここではベルヌーイ分布に必要なパラメータの真の値を 0.16 とした。この値に近いものを観測データから求めるのが目標。
前回の通りにやるとパラメータの推定は i.i.d な観測データ {}としたとき、ベイズの定理から
とできる。
タイトル通り事前分布として固定値ではなく確率分布を与える。前提としてベルヌーイ分布のパラメータの取りうる範囲は (0,1) の間でなくてはいけない。
つまり (0, 1) の間で実数を生成してくれるような確率分布を与えればいいんだな。
ベータ分布なるものは となる実数を生成してくれる連続確率分布らしい。これを使ってみよう。定義は以下のようになっている
a,bは非負の実数でなくてはならず、自分で設定するハイパーパラメータ。 殺傷力ありそうな記号がついた関数はガンマ関数。階乗を表現してるそうだ。
a,bを適当に設定したベータ分布をプロットしてみた結果はこんなんになった
ふわ^〜
aとbの値が小さいと下に凹んだ形状で、大きいと正規分布みたいな山のような形になるのかな。いや数式見れば形状に説明つくんだろうけど・・・
観測データの見えない生成規則がベルヌーイ分布
に従うとしているから、この式と上のベータ分布の式を事後分布の右辺に代入してみる
ヴワッ・・・果てしなく汚い式になってしまった・・・
ガンマ関数と分母はパラメータによらないので、定数Aとおいて見ればこの式は
こんな感じで書けるはずなのだ。ようはとでまとめられる。
この式の形はベータ分布と似ている。共役性とやらで実はをベルヌーイ分布に設定して、事前分布をベータ分布にしたとき事後分布はベータ分布と同じ形の分布になるのだ・・・
何が嬉しいって分母、もとい周辺尤度を計算しなくて済む。
多分この周辺尤度ってパラメータが取りうる範囲で積分、つまりは周辺化して得られるはずだから
こうなるはずなんだよな・・・違うかな・・・
積分するものによっては難しいものあるんじゃなかろうか(´・ω・)
気を取り直して観測データの集合Xからパラメータを推定するを計算するにはをベルヌーイ分布にして、事前分布をベータ分布に設定すれば、ベータ分布のハイパーパラメータに観測データの情報を加えたものから得られることがわかった。
ベータ分布のハイパーパラメータを a = 0.1 , b = 0.1 に設定して、が本当に推定されていくのか見ていく
for i in range(0,len(X),10): est_mu = 0 est_mu = scipy.stats.beta.rvs(np.sum(X[0:i+10])+a[0], len(X[0:i+10]) - np.sum(X[0:i+10]) + b[0]) print('観測データの数 : {0}件 、 パラメータμの推定値 : {1} '.format(i,est_mu))
観測するデータを10件ごと増やしていくと50件以降から真のパラメータの値 0.16 に近くなっていくのが若干わかる
学習できていると見ていいんじゃなかろうか
もういっちょ別の確率分布を使ってパラメータの推定をやってみる
ベルヌーイ分布は二値に対してのみだったが、サイコロの目のようなKパターンの確率も推定してみたい
このようなK次元の確率分布としてカテゴリ分布なるものがある
はといった、K次元のうちk番目の値が1で他の値が0といったonehot表記であること、はで、かつとなることが条件
ベルヌーイ分布でのパラメータのように、このカテゴリ分布では がパラメータになる
次元数をK=6 とし、とすれば等確率のサイコロが表せるんだな
同様に
としたとき事前分布はどうすればいいだろう
ベータ分布は、つまり変数1個しか生成してくれない。同じ (0,1) 区間で実数を多次元で、かつそれらの和が 1 となってくれるようなものを生成してくれる分布はないだろうかと悩むところでディクリレ分布というものがその役割を果たしてくれる。すげえ!!!
の要素は正の実数が条件のハイパーパラメータ
何はともあれ事後分布に代入だ。観測データを { }とすると
ヴォエッ・・。こっちも汚くなったが分母とガンマ関数の部分は定数とみなせるのでAとして、総乗の部分はどちらもkのものだからまとめることができる。整理すると
こんな感じで事後分布をディクリレ分布と似た形の式にみることができるのだ。共役性ありがてえ・・・
等確率でないサイコロを作って、そのサイコロの確率、もといパラメータを推定してみる
まずサンプルとなるインチキなサイコロを投げた結果を作る
weights = [0.1,0.1,0.35,0.1,0.25,0.1] dice = [1,2,3,4,5,6] obs_dice = np.random.choice(dice,p=weights,size=300) # カテゴリ分布に投げるためにonehot表記する obs_dice_onehot = [] t = np.zeros(6) for d in obs_dice: t = np.zeros(6) t[d-1] = 1 obs_dice_onehot.append(t)
np.random.choice()
で任意の重みで設定した結果をsizeの数だけくれるようだ。
ここでweights = [0.1,0.1,0.35,0.1,0.25,0.1]
が左からサイコロで1がでる確率、2が出る確率、として、これらの重みを結果から推定する
ディクリレ分布に必要なハイパーパラメータの値を全て 0.2 とする
データを10件ずつ増やしていってパラメータ の推定値がどう変わるか見ていく
for i in range(0,300,10): s = [0] * 6 alpha = [0.2,0.2,0.2,0.2,0.2,0.2] for k in range(0,6): s[k] = np.sum(list(map(lambda x: x[k], obs_dice_onehot[0:i+10]))) alpha = np.add(alpha,s) p = scipy.stats.dirichlet.rvs(alpha) print(i,p)
10件あたりでもそこそこいい値が出ている気がする。データが増えていくほど値が安定していくようなそうでないような
ベルヌーイ分布なら事前分布をベータ分布に、カテゴリ分布なら事前分布をディクリレ分布にといった共役の事前確率分布を取れば計算がかなり簡単になることがわかった。それでも現実の問題によっては共役でない事前分布を取ることとかあるのだろうか?必ず共役な事前分布をとれって訳ではないしな・・・
これはベイズ学習であって自分が知りたい変分ベイズではない。変分ベイズはこのような手順を踏んではいるけどちょっと違う推定の仕方とかそういうのなんだろうか?(´・ω・)というか試したのは離散であって連続値に対する推定ですらなかった
VAEの理解にはまだ遠い