餡子付゛録゛

ソフトウェア開発ツールの便利な使い方を紹介。

R/FortranでHamiltonian Monte Carlo法によるベイズ推定を書いてみたよ!

マルコフ連鎖モンテカルロ法(MCMC)には様々なアルゴリズムがあり、メトロポリスヘイスティングス法は尤度関数があれば推定ができる使い勝手のよい方法だと知られています。しかし、高次元で効率が悪いことが知られており、また多峰の確率分布には上手く対応できないそうで、最近のベイズ推定ではHamiltonian Monte Carlo法や、その発展であるNo-U-Turn Samplerが使われています。

Hamiltonian Monte Carlo法は教科書的な手法のようで、Rにもhmclearnと言うHamiltonian Monte Carlo法の教育用パッケージがあるのですが、参考にしつつFortranで書いてみました。推定モデルにLinear Mixed EffectsがなくOrdered Logitがあったり、初期値とステップ幅を最尤法の結果から設定するようにするなど*1、細部では変更があります。

https://github.com/uncorrelated/HMCFortran

Linux環境でgit cloneしたディレクトリでR CMD INSTALL . でRにインストールして使うようになっていますが、Fortranのライブラリを2つ(と片方に内蔵されたCのライブラリを1つ)をリンクします。つまり、lbfgsbdSFMT_F03_interfaceを、どこかにディレクトリー(e.g. /var/tmp)を作ってgit cloneして、MakefileのSHSRC=/var/tmpを適時書き換える必要があります*2

cd /var/tmp
git clone https://github.com/uncorrelated/HMCFortran.git
git clone https://github.com/jacobwilliams/lbfgsb.git
git clone https://github.com/jsspencer/dSFMT_F03_interface.git
cd HMCFortran
R CMD INSTALL .

使い方は以下のように、なるべくR的にしました。モデル式を書いて、summaryやplotなどのジェネリック関数で結果が見られるようにしています。

library(hmcfortran)

# data generator for practice
mkdata <- function(nr, nc, sd = 1){
	X <- matrix(c(rep(1, nr), runif(nr*(nc-1), -2, 2)), nr)
	colnames(X) <- c("Const.", letters[1:(nc - 1)])
	beta <- c(0.5, rep(1, nc - 1))*(-1)^(1:nc)
	y <- X %*% beta + rnorm(nr, sd = sd)
	cbind(y, as.data.frame(X))
}

np <- 4 # number of parameters
data <- mkdata(150, np - 1)

# prior/hyper parameters
ig.alpha <- 1
ig.gamma <- 1
beta.mean <- rep(0, np - 1)
beta.Sigma <- diag(1e+10, np - 1, np - 1)

# do Hamiltonian Monte Carlo method to estimate a liner model
r_hmc_lm <- hmc.blm(y ~ a + b, ig.alpha, ig.gamma, beta.mean, beta.Sigma, data = data, N = 3000, nchains = 3)

# get the marginal likelihood
mlf_1 <- log_ml(r_hmc_lm)

# alternative model: third parameter, a, is zero.
r_hmc_lm_0 <- hmc.blm(y ~ b, 1, 1, rep(0, np - 2), diag(1e+10, np - 2, np - 2), data = data, N = 3000)

# get the marginal likelihood of alternative model
mlf_0 <- log_ml(r_hmc_lm_0)

cat("Laplace Approximated BF_{10}:", exp(mlf_1 - mlf_0), "\n")

BF <- exp(log_BF(r_hmc_lm, 3, 0))
cat("IWAMDE Savage-Dickey ratio BF_{01}:", BF, " BF_{10}:", 1/BF, "\n")

# show the prediction, where explanatory variables are the mean of X.
prdct <- predict(r_hmc_lm, X = apply(r_hmc_lm$input$X, 2, mean))
hist(prdct, breaks = 20)

# call coda::plot
plot(r_hmc_lm)

# calcurate R-hat
gelman.diag(r_hmc_lm)

# call other functions of coda package
# gelman.plot(r_hmc_lm$mcmc.list)
# geweke.plot(r_hmc_lm$mcmc.list)
# crosscorr.plot(r_hmc_lm$mcmc.list)
# autocorr.diag(r_hmc_lm$mcmc.list)
# autocorr.plot(r_hmc_lm$mcmc.list)

# call coda::summary
summary(r_hmc_lm)

# call coda::HPDinterval
HPDinterval(r_hmc_lm)

# statistics of the sample of MCMC
mean(r_hmc_lm)
median(r_hmc_lm)
quantile(r_hmc_lm, 0.5)


線形回帰、二項ロジット、順序ロジット、混合ロジット、ポアソン回帰の他、Rのユーザー定義の目的関数を使えるようにしています。プリセットのモデルでは周辺尤度のラプラス近似と、MCMCサンプルから制約モデルのベイズファクター(IWMDE/Chen (1992) Savage-Dickey density ratio)が計算でき、codaパッケージを利用することでMCMCの収束判定も教科書的に可能なので、MCMCpackと同程度に気軽にベイズ推定ができます。ドキュメントがまったく整っていないので、何とかしたいですね!

今後、気力と体力があればLinear Mixed Effectsと欠損値処理は実装したいのですが、どうも無さそうなので期待しないでください('-' )\(--;)BAKI

*1:Find Reasonable EpsilonやDual Averagingは試したのですが、かなりの頻度で異常な推定結果がもたらされるので諦めました。

*2:ライセンス的には、まとめて配布しても問題ないのですが、まだまだ開発途上なので分けています。