はじめての MCMC、
はじめての Stan

@y__mattu

2018/6/9 Tokyo.R #70 <ベイズ特集会>

はじめに

誰?

icon

著書(共著)

book

  • 2 部構成
  • 第一部(基礎編)
    • R 入門
    • R のデータハンドリング基礎
  • 第二部(応用編)
    • テキストマイニング
    • R のパフォーマンス向上
    • 地理データ解析
    • ベイズ最適化入門
    • …etc

著書 2

R ユーザのための RStudio[実践]入門
− tidyverse によるモダンな分析フローの世界−

rstudiobook

  • 通称: 「宇宙本
  1. RStudio 入門(@y__mattu)
  2. スクレイピングによるデータ取得(@y__mattu)
  3. dplyr を中心としたデータハンドリング(@yutannihilation)
  4. ggplot2 による可視化(@kyn02666)
  5. R Markdown によるレポーティング(@kazutan)

本題

この発表の目的

  • ベイズ実践編の方々(LT, 応用セッション)に繋げる
  • 繋げる: 全部は理解できなくても、何をしているのかくらいはだいたい分かるようにする
  • ベイズ推定で頻繁に用いられる MCMC の基礎を理解
  • 最近人気の Stan の基礎を理解

おことわり

  • 数学的な解説は最小限に留めます(教科書に書いてあるから)。
  • 「何をやっているのか」を直感的に理解することを優先

このスライドを見るには

https://github.com/ymattu/TokyoR70

  • このスライドはrevealjs パッケージで作成しています

MCMC 導入

なぜ MCMC か


$$ \begin{aligned} p(\alpha \mid x) &= \frac{p(x \mid \alpha) \times p(\alpha)}{p(x)} \end{aligned} $$

  • 知りたいのは事後分布p(α ∣ x)
  • 例: y = βx + eβの事後分布を求めたい
  • でも p(x)=∫p(x ∣ αp(α)dα は一般に計算が大変
    → MCMC をつかう

MCMC(Markov Chain Monte Carlo)

  • すごくざっくり:「サンプルをたくさん発生させて事後分布のかわりにしよう
  • ざっくり: データxが与えられたとき、事後分布p(α ∣ x)からパラメータαをサンプリングする手法

細かいことは抜きにして:

  • Markov Chain: 前の状態で次が決まる
  • Monte Carlo: 確率的なアルゴリズム

細かいことは抜きにして。。。

  1. 初期点を決める
  2. パラメータをしらみつぶしに変化させて尤度を計算していく
  3. パラメータをプラス方向かマイナス方向に変えてみて、尤度が大きくなるようならその方向に続けて変えていく
  4. 逆に尤度が小さくなるようなら、反対方向にパラメータを変えていく
  5. これを収束するまで繰り返す
  6. MCMC サンプルが完成→これをパラメータの分布にしよう!

よく見る「あれ」が出来上がります

  • これを traceplot といいます
  • なんか 1 回じゃ心もとないから複数回(chain)やったほうがいいよね trace1

ここで疑問

  1. なんでこうなるの?
  2. 収束ってどういうこと?

疑問 1
なんでこうなるのか

仮定

  • マルコフ連鎖では任意の確率変数からスタートした時に、最終的な確率変数の分布が不変になることを仮定
  • この分布を不変分布という

お天気の例

https://www.slideshare.net/teramonagi/ss-5190440

MCMC のアイディア

  • サンプルを得たい確率密度関数が p(x)
  • これを不変分布に持つようなマルコフ連鎖を生成(推移確率を与える)することで、実際のサンプリングをしよう
  • サンプルが不変分布に近づく十分条件に詳細釣り合い条件というものがある

詳細釣り合い条件

ある状態 xt から xt + 1 に移るとき、次の式が成り立つ


p(xt)q(xt + 1 ∣ xt)=p(xt + 1)q(xt ∣ xt + 1)

この q(x ∣ y) は提案分布と呼ばれ、現在の状態 xt から次の状態 xt + 1 を得るために用いる確率分布

MCMC アルゴリズムの種類

  • メトロポリス・ヘイスティングス法(M-H 法)
  • ギブスサンプリング

M-H法

アイディア

  • 必ずしも詳細つり合い条件を満たさない提案分布q(x ∣ y)に対して、詳細釣り合い条件を成り立たせるための重み関数$a(x y) $が満たす条件を考える


p(xtt)q(xt + 1 ∣ xt)w(xt + 1 ∣ xt)=p(xtt)q(xt + 1 ∣ xt)

  • 提案分布q(x ∣ y)によりサンプルされたxa(x ∣ y)の確率で採択することで、詳細つり合い条件を満たすようにサンプリングをしよう

このときの採択確率


$$ a(x^{t+1} \mid x^t) = \min \left[1, \frac{p(x^t)q(x^{t+1} \mid x^t)}{p(x^{t+1})q(x^t \mid x^{t+1})}\right] $$

うん、イメージがわかない

ギブスサンプリング

アイデア

  • あるパラメータ以外を固定した条件付き分布からサンプリング
  • 採択確率は常に1
  • 提案分布と不変分布の関係を仮定できる


$$ q(x \mid x^t) = \begin{cases} p(x_i \mid x_{-i}^t) \; &x_{-i} = x_{-i}^t \\ 0 \; &otherwise \end{cases} $$

  • これも可視化を見たほうが分かりやすい

収束について

バーンイン

  • 最初の方は初期値依存があるので事後分布の形成から省くことが多い trace1

MCMCでの収束

  • Geweke の方法
  • Geweke統計量Z値を計算し、MCMCのburn-in期間(前半10%)と後半50%を比較し、|Z|値が十分に小さければMCMCが収束したと考える

trace1

Stan 入門

Stan とは

  • Hamiltonian Monte Carlo 法(HMC)とそれを応用した No-U-Turn Sampler(NUTS)用いたサンプラー
    • 注: マルコフ連鎖を使っていないので厳密には MCMC ではない
  • 要は「モデル式を書けば勝手に MC(Monte Carlo)してサンプリングしてくれるもの」
    • モデル式: 尤度関数と事前分布
    • C++で動くので速い
  • 公式マニュアルが丁寧(PDF 注意)

R で Stan を使ってみる

おことわり

Stan のインストール

rstan パッケージのインストール

  • CRAN から
install.packages("rstan")

R で Stan を使う流れ

  1. Stan コードを書く(sample.stan)
  2. C++でコンパイル
stanmodel <- rstan::stan_model("sample.stan")
  1. サンプリング
datlist <- list(...)
fit <- rstan::sampling(stanmodel, datlist, iter=10000, ...)
  1. 結果を出力

Stan の書き方

とりあえず重回帰

  • y = b0 + b1x1 + b2x2 + e,  e ∼ N(μ, σ)
  • サンプルデータ
    • b0=2, b1=5, b2=-3 のデータを 400 個生成
n <- 400
b0 <- 2
b1 <- 5
b2 <- -3
sigma <- 2

set.seed(71)
xx1 <- rnorm(n, 0, 1)
xx2 <- rnorm(n, 2, 2)
bx <- b0 + b1*xx1 + b2*xx2
yy <- rnorm(n, bx, sigma)

df <- data.frame(y = yy, x1 = xx1, x2 = xx2)

サンプルデータ

y x1 x2
14.4201782 -0.4318422 -3.4241758
-21.6955991 -0.4471872 6.5855335
-5.3511933 -0.4785726 0.1395514
0.7327234 0.4171454 0.3261460
-5.7041057 -0.4179006 1.1598209
-7.6903685 -1.1871639 1.6534544
-7.8104954 -1.0722855 2.5685698
4.6245938 1.0032838 1.1200353
-6.3806865 -1.1084783 0.6905064
6.4814420 1.2634307 0.1718988

Stan の文法

  • function{}
  • data{}
  • transformed data{}
  • parameters{}
  • transformed parameters{}
  • model{}
  • generated quantities{}

注: 順番はこの順でなければならない

重回帰を書いてみる

これを sample.stan として保存

data {
  int N ; // num of obs
  real x1[N] ; // independent variable 1
  real x2[N] ; // independent variable 2
  real y[N] ; // dependent variable
}

parameters {
  real b0 ;
  real b1 ;
  real b2 ;
  real<lower=0> sigma ;
}

model {
  for (i in 1:N) {
    y[i] ~ normal(b0 + b1 * x1[i] + b2 * x2[i], sigma) ;
  }
}

実行はRで

library(rstan)

# コンパイル
stanmodel <- stan_model("sample.stan")

datlist <- list(y = df$y,
                x1 = df$x1,
                x2 = df$y,
                N = nrow(df))

fit <- sampling(stanmodel, datlist)

収束を確認1

トレースプロットを見る

traceplot(fit)

収束を確認2

  • Rhat という指標を見る
  • これが 1.05(1.1とも)以下ならOK
summary(fit)$summary
             mean      se_mean         sd        2.5%         25%
b0       2.025081 0.0028269389 0.14803756    1.740184    1.923768
b1       4.834611 0.0016919143 0.10700606    4.628631    4.760272
b2      -2.933941 0.0009892484 0.05203129   -3.036034   -2.968873
sigma    2.046228 0.0011815352 0.07382550    1.906331    1.995531
lp__  -484.974192 0.0321805712 1.39814965 -488.418567 -485.714889
              50%         75%       97.5%    n_eff      Rhat
b0       2.025092    2.123802    2.321898 2742.275 0.9999702
b1       4.835004    4.906985    5.048263 4000.000 0.9999912
b2      -2.933319   -2.898268   -2.835307 2766.422 0.9996749
sigma    2.044362    2.096117    2.199582 3904.086 1.0002324
lp__  -484.645567 -483.942269 -483.207848 1887.643 1.0013678

Stan の書き方いろいろ

モデルを短く書きたい

for (i in 1:N) {
    y[i] ~ normal(b0 + b1 * x1[i] + b2 * x2[i], sigma) ;
  }

data {
  int N ; // num of obs
  real x1[N] ; // independent variable 1
  real x2[N] ; // independent variable 2
  real y[N] ; // dependent variable
}

parameters {
  real b0 ;
  real b1 ;
  real b2 ;
  real<lower=0> sigma ;
}

transformed parameters {
  real mu[N] ;
  for (i in 1:N) {
    mu[N] = b0 + b1 * x1[i] + b2 * x2[i]
  }
}

model {
  for (i in 1:N) {
    y[i] ~ normal(mu[i], sigma) ;
  }
}

行列で書きたい

  • データやパラメータを行列やベクトルで宣言する必要あり
data {
  int N ;
  int M ; \\ num of independent variables
  vector[N] y ; \\ dependent variable
  matrix[N, M] x ; \\ independent variable matrix
}

parameters {
  vector[M] b ; \\ parameter vector
  real<lower=0> sigma ;
}

model {
  y ~ normal(x*b, sigma) ;
}

R側も行列を作ります

datlist2 <- list(N = nrow(df),
                 y = df$y,
                 x = cbind(1, df$x1, df$x2),
                 M = ncol(x))

あれ、そういえば事前分布は?

  • 何も指定しなければ無情報事前分布(一様分布)
  • でも指定してあげたほうがいい(ベイズだしね!)
  • model ブロックに以下を書く(さっきの行列の例)
y ~ normal(x*b, sigma) ;
beta ~ normal(0, 100) ; 
sigma ~ cauchy(0, 5) ;

 サンプリングの他の書き方

  • y ~ normal(mu, sigma) は他の書き方もできる
  • target += normal_lpdf(y | mu, sigma)
  • ***lpdf は対数確率を足し上げる作業
    • 実はこっちがStan内部でやっていることで、 ~ のほうが簡略表記

ここまでやったところで

Stan は楽でいいね!

  • Stan を使えばモデルを書くだけでパラメータ推定ができる!
  • 尤度さえわかればモデルが書ける

一方で

  • モデルが正しく書けないとどうしようもない
    • モデル: 分布、行列etc…
  • 発展的なモデル(階層モデル、状態空間モデル)を勉強するときはサボらずに尤度、行列表記くらいまではちゃんと理解できるよう努めましょう。

まとめ

なにをやったか

  • MCMC をざっくり理解
  • Stan の書き方をざっくり理解

※正直なところ、30-40分では「ちゃんと」は理解できないと思ってます

初心者セッション 1-3 をもっと詳しく知りたい人は

R で楽しむベイズ統計

dicebook

  • 通称: サイコロ本
  • ベイズの定理から MCMC まで 数学的な面も分かりやすく解説
  • わかりやすさと詳細さのバランスがとれた本

初心者セッション 1-3 をもっと詳しく知りたい人は

ベイズ統計モデリング: R,JAGS,Stan によるチュートリアル

4dogs

  • 通称: 犬 4 匹本
  • ベイズの定理から MCMC, Stan までわかりやすく解説
  • 鈍器なみに分厚いです

初心者セッション 1-3 をもっと詳しく知りたい人は

Stan と R でベイズ統計モデリング

duckbook

  • 通称: アヒル本
  • Stan によるベイズモデリングをしっかり、分かりやすく解説
  • Stan 使うなら必携

理論面を理解したい人

  • 『データ解析のための統計モデリング入門』(通称: 緑本)
  • 『計算統計 II-マルコフ連鎖モンテカルロ法とその周辺』(ガチなやつ)
  • https://www.youtube.com/watch?v=-H28H1unn0M (MCMC全般)

Enjoy!