MCMCサンプラー

 このページでは、ベイズモデルのパラメータ推定に不可欠な(自分でプログラムを書ける人は別ですが)、MCMCサンプラーの「使い方」について説明します。

 なお、Stan以外はほぼ共通のBUGS言語で動作します。BUGS言語の意味などを知りたい方は、「BUGSで学ぶ階層モデリング入門: 個体群のベイズ解析」をご覧下さい。


  WinBUGS OpenBUGS JAGS Stan
 OS  Windows(頑張ればMacも) Windows(頑張ればMacも) マルチプラットフォーム マルチプラットフォーム
長所 car.normal()が使える。 WinBUGSと同様の操作。 多項モデルの計算が速い。エラー個所の特定が容易。 収束が速い。エラー個所の特定が容易。空間自己相関を考慮可能。
短所 開発は止まっている。エラーメッセージが理解不能。プログラムは非公開。 同左。 特になし(個人的には)。 離散パラメータを推定できない。1stepあたりの計算速度が遅い。
公式サイト The BUGS project OpenBUGS JAGS Stan
インストール方法 北大の久保先生のページ     2012年度統計数理研究所共同研究集会「データ解析環境Rの整備と利用」の石倉さんのスライド、BUGSstan勉強会R stan導入公開版

使うデータ

以下の例で共通して使うデータを用意します。

set.seed(1)
N <- 100
x1 <- rnorm(N, 0, 1)
x2 <- rnorm(N, 0, 1)
y <- x1 + rnorm(N, 0, 1)

WinBUGS

データの定義

データは、データ名のベクトルを与えます。

data <- c("N", "y", "x1", "x2")

初期値の設定

WinBUGSにまつわるエラーの大半はここが原因です。間違いのないように指定しましょう。

inits <- list(list(alpha = 0, bx1 = 0, bx2 = 0, sigma=70),
              list(alpha = -5, bx1 = -5, bx2 = -5, sigma=40),
              list(alpha = 5, bx1 = 5, bx2 = 5, sigma=50)
)

監視対象パラメータの定義

WinBUGSは、あらかじめ指定したパラメータの結果しかRに返してくれません。これを、監視対象パラメータといい、事前に指定します。

para <- c("alpha", "bx1", "bx2", "sigma")

BUGS言語でモデルを記述

解析したいモデルを、BUGS言語で記述します。ここでは非常に単純なモデルを扱いますので、R上に直接記述します(ただし、念のためtestmod.txtという名前のテキストファイルに同時に出力しています)。モデルのファイルを別に用意する場合は、model { }の部分だけ書いてファイルに保存して下さい。

modelFilename = "testmod.txt"
cat("
model {
 for (i in 1:N) {
      y[i] ~ dnorm(mu[i], tau)
      mu[i] <- alpha + bx1*x1[i] + bx2*x2[i]
 }
 
 alpha ~ dnorm(0.0, 1.0E-3)
 bx1 ~ dnorm(0.0, 1.0E-3)
 bx2 ~ dnorm(0.0, 1.0E-3)

 tau <- pow(sigma, -2)
 sigma ~ dunif(0, 100)
}
", fill=TRUE, file=modelFilename)

計算の実行

必要なパッケージを読み込み、解析を実施します。

library(R2WinBUGS)
res <- bugs(data, inits, para,
            model=modelFilename,
            # ファイルから読み込む場合は、model="testmod.txt",
            n.burnin = 500, n.iter=6500, n.chains=3, n.thin=6,
            bugs.directory="C:/WinBUGS14/", 
            # WinBUGSをデフォルト以外の場所にインストール
            # している場合のみ指定
            working.directory=getwd(),
            debug=TRUE
)

結果の確認

推定された係数の値や収束診断などを、要約量を見たり図を書いたりして確認します。

print(res$summary, digits=3)
plot(res)

OpenBUGS

基本的にWinBUGSと同じです。計算の実行関数の一部の引数が異なるだけです。そのため、詳しくは説明しません。

# データの定義
data <- c("N", "y", "x1", "x2")
# 初期値の定義
inits <- list(list(alpha = 0, bx1 = 0, bx2 = 0, sigma=70),
              list(alpha = -5, bx1 = -5, bx2 = -5, sigma=40),
              list(alpha = 5, bx1 = 5, bx2 = 5, sigma=50)
)
# 監視対象パラメータの定義
para <- c("alpha", "bx1", "bx2", "sigma")
# モデルの記述
modelFilename = "testmod.txt"
cat("
model {
 for (i in 1:N) {
      y[i] ~ dnorm(mu[i], tau)
      mu[i] <- alpha + bx1*x1[i] + bx2*x2[i]
 }
 
 alpha ~ dnorm(0.0, 1.0E-3)
 bx1 ~ dnorm(0.0, 1.0E-3)
 bx2 ~ dnorm(0.0, 1.0E-3)

 tau <- pow(sigma, -2)
 sigma ~ dunif(0, 100)
}
", fill=TRUE, file=modelFilename)
# 計算の実行
library(R2OpenBUGS)
res2 <- bugs(data, inits, para, model=modelFilename,
            n.burnin = 500, n.iter=6500, n.chains=3, n.thin=6,
            OpenBUGS.pgm="C:/OpenBUGS322/OpenBUGS.exe",
            # プログラムまでフルパスを指定
            working.directory=getwd(),
            debug=TRUE
)
# 結果の確認
print(res$summary, digits=3)
plot(res)

JAGS

前者2つと比べると、多少データの指定方法などが異なります。

データの定義

データは、リスト形式で準備して下さい。

list.data <- list(N=N, y=y, x1=x1, x2=x2)

初期値

乱数の種類や種を指定するので、若干WinBUGSと異なります。

inits <- list(alpha = 0, bx1 = 0, bx2 = 0, sigma = 50)
inits <- list(inits, inits, inits)
inits[[1]]$.RNG.name <- "base::Mersenne-Twister"
inits[[1]]$.RNG.seed <- 1
inits[[2]]$.RNG.name <- "base::Mersenne-Twister"
inits[[2]]$.RNG.seed <- 12
inits[[3]]$.RNG.name <- "base::Mersenne-Twister"
inits[[3]]$.RNG.seed <- 123

監視対象パラメータの定義

ここはWinBUGSと一緒です。

para <- c("alpha", "bx1", "bx2", "sigma")

BUGS言語でモデルを記述

ここもWinBUGSと一緒です。

# モデルの記述
modelFilename = "testmod.txt"
cat("
model {
 for (i in 1:N) {
      y[i] ~ dnorm(mu[i], tau)
      mu[i] <- alpha + bx1*x1[i] + bx2*x2[i]
 }
 
 alpha ~ dnorm(0.0, 1.0E-3)
 bx1 ~ dnorm(0.0, 1.0E-3)
 bx2 ~ dnorm(0.0, 1.0E-3)

 tau <- pow(sigma, -2)
 sigma ~ dunif(0, 100)
}
", fill=TRUE, file=modelFilename)

計算の実行(runjags)

いくつかパッケージがあります。まず、library(runjags)の場合です。

library(runjags)
res <- autorun.jags(model=modelFilename, data=list.data, inits=inits,
                    monitor=para,n.chains=3, method="parallel",
                    thin.sample=TRUE, psrf.target=1.1)
# methodで計算に使うパッケージを指定します。
# psrf.targetに指定したRhat値以下になるようにします。

計算の実行(rjags)

続いて、library(rjags)の場合です。

# 計算回数の設定
n.chains <- 3
n.iter <- 5000
n.update <- 2000
thin <- 5
# パッケージの読み込み
library(rjags)
# 計算
m <- jags.model(file = modelFilename, data = list.data,
                inits = inits, n.chain = n.chains
)
update(m, n.update)
x <- coda.samples(m, para,
                  thin = thin, n.iter = n.iter
)

結果の確認(runjags)

このパッケージの場合はすでに収束していることは明らかですが、推定値などを見ておきます。

# 要約統計量を見る
res$summary
# MCMCの過程や収束状況などを図で見る
par(ask=TRUE)
plot(res)

結果の確認(rjags)

推定値や収束指標などを見ておきます。

# 計算結果の出力
res <- data.frame(summary(x)$statistics)
ci <- data.frame(summary(x)$quantiles)
# 95%信用区間が 0 をまたぐかどうかを計算
res$sig <- abs(sign(ci[, 1]) + sign(ci[, 5])) == 2
# Rhat 値の計算
rhat <- gelman.diag(x)[["psrf"]][, 1]
res$Rhat <- rhat
# 収束状況の図化
plot(x)

Stan

Stanは、モデルの書き方がBUGS言語と多少異なります。

# データの定義
list.data <- list(N=N, y=y, x1=x1, x2=x2)
# 初期値の定義
inits <- list(alpha = 0, bx1 = 0, bx2 = 0, sigma = 50)
inits <- list(inits, inits, inits)
inits[[1]]$.RNG.name <- "base::Mersenne-Twister"
inits[[1]]$.RNG.seed <- 1
inits[[2]]$.RNG.name <- "base::Mersenne-Twister"
inits[[2]]$.RNG.seed <- 12
inits[[3]]$.RNG.name <- "base::Mersenne-Twister"
inits[[3]]$.RNG.seed <- 123
# 監視対象パラメータの定義
para <- c("alpha", "bx1", "bx2", "sigma")

モデルの記述

ここが結構異なります。慣れるまで手間取るかも。

modelfile <- '
 data {
 int N; //整数はint
 real y[N]; //実数はreal
 real x1[N];
 real x2[N];
 }

 parameters {
 real alpha;
 real bx1;
 real bx2;
 real sigma; //分散パラメータなので、負にならないように範囲を指定
 }
 
 model {
 for (i in 1:N)
      y[i] ~ normal(alpha + bx1*x1[i] + bx2*x2[i], sigma);
//事前分布を指定しない場合、自動的に一様分布が事前分布として与えられます
 }
'

計算の実行

以下のようにして、計算を実行します。

library(rstan)
rstan_options(auto_write = TRUE)
options(mc.cores = parallel::detectCores())
fit <- stan(model_code = modelfile, data = list.data, par = para,
            iter = 1000, chains = 3, thin=1, init=inits)

結果の確認

結果の見方も、BUGS系列と多少異なります。

print(fit, digits=3)
plot(fit)
#パラメータが多くて部分表示したい場合は
#一度データフレーム型に変換すると便利
res <- as.data.frame(summary(fit))[1:10]
colnames(res) <- c("mean", "se", "sd", "ci2.5", "ci25",
                   "ci50", "ci75", "ci97.5", "neff", "Rhat")
head(res)
#同じように、パラメータが多くて一部のトレースだけを
#確認したい場合は以下のようにする
traceplot(fit, pars=c("alpha", "bx1"))

空間的自己相関

Hirokiさんによると、Stanでは空間的自己相関も考慮できるようです。