このページに対応するRmdファイル:GitHub
library(tidyverse)
設定:歪んだコインを使って何回かコイントスを行い,表が出る(真の)確率 \(p\) を推定する.
確率変数 \(X\) を,表が出た場合に \(X=1\),裏が出た場合に \(X=0\) と定義すれば,表が出る確率 \(p\) は \(X\) の期待値に等しい.
\[ E(X) = \sum_i x_i \Pr(X = x_i) = \Pr(X = 1) = p \]
最もシンプルな推定量は標本平均 \(\hat{p} = (1/n) \sum_i x_i\) であるが,ここでは別の方法を考える.
確率変数 \(X\) は0または1のどちらかのみを取るので,ベルヌーイ分布に従う.
\[ \Pr(X = x) = p^x (1-p)^{1-x} \quad (x = 0, 1) \]
この確率分布は \(p\) に依存して次のような形状を持つ.
では,\(p\) がどのような値を取れば実際に観察されたデータ \(x\) を最も自然に説明できるだろうか?
たとえば,コイントスを1000回して表が400回出たら,\(p=0.4\) と考えるのが自然だろう.
\(p=0.4\) のときに「1000回中表が400回出る」というデータが観察されるのは自然であるが,\(p=0.1\) のときに「1000回中表が400回出る」というデータが観察されるのはかなり不自然であるし,\(p=0.9\) のときに「1000回中表が400回出る」というデータが観察されるのも相当不自然である.
つまり,鍵となるのは「パラメタ \(p\) を所与としてデータが観察される確率 \(\Pr(X = x \mid p)\)」である.
2回コイントスを行い \(x_1 = 1, x_2 = 0\) だったとしよう.
\[ \Pr(X_1=1, X_2 = 0 \mid p) = p^1 (1-p)^{1-1} \times p^0 (1-p)^{1-0} = p (1-p) \]
\[ \Pr(X_1=1, X_2 = 0 \mid p = 0.1) = 0.1 \times 0.9 = 0.09 \]
\[ \Pr(X_1=1, X_2 = 0 \mid p = 0.2) = 0.2 \times 0.8 = 0.16 \]
同様に,\(p\) を 0.1 刻みで増加させると次のようになる.
各行一つ目の値が \(p\),二つ目が \(p(1-p)\) を示す.
for (p in seq(from = 0.3, to = 0.9, by = 0.1)) {
print(c(p, p*(1-p)))
}
## [1] 0.30 0.21
## [1] 0.40 0.24
## [1] 0.50 0.25
## [1] 0.60 0.24
## [1] 0.70 0.21
## [1] 0.80 0.16
## [1] 0.90 0.09
よって,\(\Pr(X_1 = 1, X_2 = 0) = p (1-p)\) を最大にするのは \(p = 0.5\) あるいはそれに近い値であることが予想される.
見方を変えれば,\(p(1-p)\) という関数の最大化問題を解いていることが分かるはずだ.
(関数 \(p (1-p)\) の最大化問題を解析的に解けば厳密に \(p = 0.5\) で最大化されることが分かる.しかしこの例のポイントは,この関数の最大化問題を厳密に解くことができなかったとしても,数値的に最大化問題を解けば求めたい \(p\) が近似的に得られることである.)
続いて \(x_1 = 1, x_2 = 0, x_3 = 1\) だった場合について,上と同様に考えてみよう.
\[\Pr(X_1 = 1, X_2 = 0, X_3 = 1 \mid p) = p^1 (1-p)^{1-1} \times p^0 (1-p)^{1-0} \times p^1 (1-p)^{1-1} = p^2(1-p) \]
\[\Pr(X_1 = 1, X_2 = 0, X_3 = 1 \mid p = 0.1) = 0.1^2 \times 0.9 = 0.009\]
先ほどと同様に計算してみよう. 各行一つ目の値が \(p\),二つ目が \(p^2(1-p)\) を示す.
for (p in seq(from = 0.2, to = 0.9, by = 0.1)) {
print(c(p, p^2*(1-p)))
}
## [1] 0.200 0.032
## [1] 0.300 0.063
## [1] 0.400 0.096
## [1] 0.500 0.125
## [1] 0.600 0.144
## [1] 0.700 0.147
## [1] 0.800 0.128
## [1] 0.900 0.081
よって,\(p^2 (1-p)\) を最大化するのは \(0.6 < p < 0.7\) を満たす \(p\) であると予想される.
(解析的に解けば \(p = 2/3\) で最大化される.)
以上のように,データにもっとよく当てはまる \(p\) の値は「\(p\) を所与としてデータ \(x\) が観察される確率(同時確率または同時確率密度)」を最大化するものとして求められる.
我々はパラメタ \(p\) を動かして同時確率を最大化しようとするため,この同時確率を「データ \(x\) を所与としたパラメタ \(p\) の関数」とみなす.これを尤度関数と呼ぶ.
最尤法とはこの尤度関数(実際にはその対数)を最大化するようにパラメタを推定する方法である.
尤度関数を \(L\),対数尤度関数を \(\log L\) と表す.
\[ L(p \mid x) = \Pr(\mbox{Heads})^2 \times \Pr(\mbox{Tails})^1 = p^2 (1-p)^1, \] \[ \log L(p \mid x) = 2 \log (p) + \log (1-p) \]
一階の条件(\(L(\hat{p} \mid x)' = 0\) または \(\log L(\hat{p} \mid x)' = 0\))より \(\hat{p} = 2/3\) が得られる.
\[ \frac{d L(p \mid x)}{d p} = 2p (1-p) + p^2 (-1) = p(2-3p) = 0, \] \[ \frac{d \log L(p \mid x)}{d p} = \frac{2}{p} + \frac{-1}{1-p} = \frac{2(1-p) - p}{p(1-p)} = \frac{2-3p}{p(1-p)} = 0, \]
l_func <- function (p) p^2 * (1-p)^1 # 尤度関数
plot(l_func, xlim = c(0, 1), xlab = "p"); abline(v = 2/3, lty = 2, col = 2)
ll_func <- function (p) 2 * log(p) + log(1-p) # 対数尤度関数
plot(ll_func, xlim = c(0, 1), xlab = "p"); abline(v = 2/3, lty = 2, col = 2)
標本 \(\{-3, -2, 0.5, 0.5, 1.5\}\) に基づいて母平均 \(\mu\) と母分散 \(\sigma^2\) を推定.
この標本が正規母集団からサンプリングされていると仮定すると,対数尤度関数は次のように書ける.
\[ f (\mu, \sigma^2 \mid y_i) = \frac{1}{\sqrt{2 \pi \sigma^2}} \exp \left[- \frac{(y_i - \mu)^2}{2 \sigma^2} \right] \] \[ \log f (\mu, \sigma^2 \mid y_i) = - \frac{1}{2} \log 2 \pi - \frac{1}{2} \log \sigma^2 - \frac{(y_i - \mu)^2}{2 \sigma^2} \]
\[ \log L (\mu, \sigma^2 \mid y_1, \ldots, y_n) = - \frac{n}{2} \log 2 \pi - \frac{n}{2} \log \sigma^2 - \frac{1}{2 \sigma^2} \sum_i (y_i - \mu)^2 \]
よって,最尤推定量は次のように解析的に求められる.
\[ \hat{\mu} = \frac{1}{n} \sum_i y_i , \quad \hat{\sigma}^2 = \frac{1}{n} \sum_i (y_i - \bar{y})^2 \]
y <- c(-3, -2, 0.5, 0.5, 1.5) # 標本
mean(y) # 標本平均(mu の最尤推定量と同じ)
## [1] -0.5
var(y) # 標本分散
## [1] 3.625
sum((y - mean(y))^2) / length(y) # 分散(sigma^2 の最尤推定量と同じ)
## [1] 2.9
対数尤度関数を定義する. 最初の引数がパラメタベクトルになっている(後述の最適化のため).
ll_func_n <- function (param, y) { # 対数尤度関数
mu <- param[1]
sigma2 <- param[2]
n <- length(y) # number of obs.
-n/2*log(2*pi) - n/2*log(sigma2) - 1/(2*sigma2)*sum((y-mu)^2)
}
様々な値をパラメタの候補として与えてみると,どのあたりに最適値がありそうかが分かるだろう.
ll_func_n(param = c(0, 1), y = y) # まずは sigma2 = 1 に固定して mu を探索する
## [1] -12.46969
ll_func_n(param = c(-1, 1), y = y)
## [1] -12.46969
ll_func_n(param = c(-0.5, 1), y = y)
## [1] -11.84469
ll_func_n(param = c(-0.5, 2), y = y) # 次は mu=-0.5 に固定して sigma2 を探索する
## [1] -9.952561
ll_func_n(param = c(-0.5, 3), y = y)
## [1] -9.75789
ll_func_n(param = c(-0.5, 4), y = y)
## [1] -9.872929
\(\sigma^2 = 1\) に固定した場合の \(\mu\) と対数尤度の関係をプロットする.
mu_list <- seq(-2, 2, length = 100)
ll_mu_list <- sapply(X = mu_list,
FUN = function(x) ll_func_n(param = c(x, 1), y = y))
plot(x = mu_list, y = ll_mu_list, xlim = c(-2, 2), type = "l")
3Dで描画する.(outer 関数を使って z
を計算するつもりだったが,なぜか outer
関数にかませる尤度関数中で sum((y-mu)^2) が評価できないので
for 文をネストして計算する.)
grid_mu <- seq(-2, 2, length = 20) # 探索する範囲を grid で表す
grid_sigma2 <- seq(1, 10, length = 20)
z <- matrix(0, nrow = length(grid_mu), ncol = length(grid_sigma2))
for (i in 1:length(grid_mu)) { # 探索 grid に対応する対数尤度を計算
for (j in 1:length(grid_sigma2)) {
z[i, j] <- ll_func_n(param = c(grid_mu[i], grid_sigma2[j]), y = y)
}
}
which_max_ml <- which(z == max(z), arr.ind = T) # 尤度を最大化する grid の index
grid_mu[which_max_ml[1]]; grid_sigma2[which_max_ml[2]] # ML推定量 (gridが粗いので精度は低い)
## [1] -0.5263158
## [1] 2.894737
persp(grid_mu, grid_sigma2, z, theta = 50, phi = 20) -> pmat_persp
points(trans3d(x = grid_mu[which_max_ml[1]], y = grid_sigma2[which_max_ml[2]],
z = z[which_max_ml], pmat = pmat_persp), col = 2, pch = 19)
汎用の最適化関数 optim を使って最尤推定する.
$par が最適化されたパラメタの値(最尤推定値).
$value は最大化された対数尤度.
$convergence は 0 ならば収束したことを意味する.
optim
はデフォルトでは与えられた関数を最小化するため,最大化するためには目的関数を
-1 倍した値を最小化するように設定する(引数
control = list(fnscale = -1)).
optim(par = c(0, 1), fn = ll_func_n, y = y, control = list(fnscale = -1))
## $par
## [1] -0.5000029 2.8995297
##
## $value
## [1] -9.75647
##
## $counts
## function gradient
## 65 NA
##
## $convergence
## [1] 0
##
## $message
## NULL
誤差項が正規分布に従うと仮定した場合の回帰モデルの対数尤度関数.
\[ y_i = \beta_0 + \beta_1 x_{i1} + \beta_2 x_{i2} + u_i, \quad u_i \sim N(0, \sigma^2) \]
\[ \log L (\beta \mid x_1, \ldots, x_n, y_1, \ldots, y_n) = - \frac{n}{2} \log (2 \pi \sigma^2) - \frac{1}{2 \sigma^2} \sum_i (y_i - x_i' \beta)^2 \]
ここで \(x_{i}' \beta = \beta_0 + \beta_1 x_{i1} + \beta_2 x_{i2}\) である.
引数 param は k+1 個のパラメタベクトル. k
は定数項を含めた説明変数の数. 1 つ目から k 個目までが回帰係数で,k+1
個目は \(\sigma^2\).
ml_gauss <- function (y_gauss, x_gauss) {
LL_gauss <- function (param) { # 対数尤度関数
- (n / 2) * log(2 * pi * param[k+1]) -
sum((y_gauss - x_gauss %*% param[1:k])^2) / (2 * param[k+1])
}
n <- nrow(x_gauss) # サンプルサイズ
k <- ncol(x_gauss) # 説明変数の数(定数項を含む)
optim(par = rep(1, k+1), fn = LL_gauss, control = list(fnscale = -1)) # 尤度最大化
}
y_swiss <- swiss$Fertility # y
x_swiss <- cbind(1, swiss$Agriculture, swiss$Examination) # X
ml_gauss(y_gauss = y_swiss, x_gauss = x_swiss)
## $par
## [1] 94.66923521 -0.09443892 -1.19762789 86.69205312
##
## $value
## [1] -171.5454
##
## $counts
## function gradient
## 357 NA
##
## $convergence
## [1] 0
##
## $message
## NULL
答え合わせ.
lm(Fertility ~ Agriculture + Examination, swiss)
##
## Call:
## lm(formula = Fertility ~ Agriculture + Examination, data = swiss)
##
## Coefficients:
## (Intercept) Agriculture Examination
## 94.610 -0.094 -1.195
推定の原理や方法は解説しないので,ベイズ統計学の知識がない方は信頼できる資料で勉強してほしい.
入門レベルの和書で有名なもの:
Web上で公開されている日本語の資料:
モデルが複雑で尤度関数の最大化が安定しない場合はベイズ推定が役に立つ場合がある.
ベイズ推定においてはパラメタ \(\theta\) は確率変数であり分布すると仮定する.
データを \(Y\), パラメタを \(\theta\) としたとき,ベイズの定理は次のように表される:
\[ \Pr (\theta \mid Y) = \frac{\Pr (Y \mid \theta) \times \Pr (\theta)}{\Pr (Y)} \]
\(\Pr (Y) = \int \Pr (Y \mid \theta) \Pr (\theta) d \theta\) は \(\theta\) に依存しない定数で,周辺尤度や正規化定数と呼ばれる. 正規化定数そのものは推定結果には影響を与えず,一般に定数として無視される. そのため,ベイズ推定においては次の比例関係を用いる.
\[ \underbrace{\Pr (\theta \mid Y)}_{\mbox{Posterior}} \propto \underbrace{\Pr (\theta)}_{\mbox{Prior}} \times \underbrace{\Pr (Y \mid \theta)}_{\mbox{Likelihood}}\]
なお,通常は連続な確率変数を考えるため,事前分布は密度関数 \(p\) を用いて \(p(\theta)\) のように表し,尤度も \(L(\theta)\) のように表す(尤度関数は \(p(Y \mid \theta)\) を \(\theta\) の関数とみなしたもの).
計算手順の一例:
\[ \mbox{Acceptance rate} = \min \left\{ 1, \frac{p (\theta^*) \cdot L(\theta^*) \cdot Q (\theta^{(t)} \mid \theta^*)}{p (\theta^{(t)}) \cdot L(\theta^{(t)}) \cdot Q (\theta^* \mid \theta^{(t)})} \right\} \]
提案分布が正規分布や一様分布なら \(Q(\theta^* \mid \theta^{(t)}) = Q(\theta^{(t)} \mid \theta^*)\) であり,さらに事前分布が一様であれば,次の確率で暫定パラメタを採択することとなる(これを Metropolis 法と呼ぶ).
\[ \mbox{Acceptance rate} = \min \left\{ 1, \frac{ L(\theta^*) }{ L(\theta^{(t)}) } \right\} \]
正規分布 \(N(\mu, \sigma^2)\) から得られた標本に基づいて母平均パラメタ \(\mu\) を推定する. ただし \(\sigma^2 = 3.6\) と分かっているものとしよう.
提案分布:次のように \(\mu^{(t)}\) から少しズラすように設定する. このような方法を random walk MH と呼ぶ.
\[ \mu^* \sim N (\mu^{(t)}, \sigma^2) \quad \Leftrightarrow \quad \mu^{*} = \mu^{(t)} + \sigma v, \quad v \sim N (0, 1) \]
事前分布:分散を大きく設定して弱情報にする.
\[ \mu \sim N(0, 100) \] \[ p (\mu) = \frac{1}{\sqrt{2 \pi \cdot 100}} \exp \left( - \frac{(\mu - 0)^2}{2 \times 100} \right), \quad \ln p (\mu) = (\mbox{const.}) - \frac{1}{2} \left( \frac{(\mu - 0)^2}{100} \right) \]
尤度:
\[ L (\mu) = \prod_i \frac{1}{\sqrt{2 \pi \sigma^2}} \exp \left( - \frac{(y_i - \mu)^2}{2 \times 3.6} \right), \quad \ln L (\mu) = (\mbox{const.}) - \frac{1}{2} \sum_i \left( \frac{(y_i - \mu)^2}{3.6} \right) \]
採択確率:
\[ \mbox{Acceptance rate} = \min \left\{ 1, \frac{\mbox{unnormalized posterior at } \mu^*}{\mbox{unnormalized posterior at } \mu^{(t)}} \right\} = \min \left\{ 1, \frac{p (\mu^*) \cdot L (\mu^*)}{p (\mu^{(t)}) \cdot L (\mu^{(t)})} \right\} \]
\[ \ln \left( \frac{p (\mu^*) \cdot L (\mu^*)}{p (\mu^{(t)}) \cdot L (\mu^{(t)})} \right) = \ln p (\mu^*) + \ln L (\mu^*) - \left[ \ln p (\mu^{(t)}) + \ln L (\mu^{(t)}) \right] \]
y <- c(-3, -2, 0.5, 0.5, 1.5) # 標本
n <- length(y) # 後々使う
mean(y) # 標本平均 = -0.5
## [1] -0.5
sigma2 <- 3.6 # 母分散は既知とする
# prior: N(0, 100)
prior_mu <- 0
prior_sigma2 <- 100
# posterior ∝ prior * likelihood
# ⇔ log(posterior) ∝ log(prior) + log(likelihood)
log_posterior <- function (mu) {
log_prior <- -(1/2) * (mu - prior_mu)^2 / prior_sigma2
log_likelihood <- -(1/2) * sum((y - mu)^2 / sigma2)
return(log_prior + log_likelihood)
}
iter <- 1000 # 繰り返し回数(マルコフ連鎖の長さ)
mu_history <- numeric(iter) # 採択された mu を格納するベクトルの初期化
accept <- numeric(iter) # 採択されたら 1 を記録するベクトルの初期化
for (t in 2:iter) {
set.seed(t)
mu_tmp <- rnorm(n = 1, mean = mu_history[t-1], sd = 2) # candidate
accept_prob <- log_posterior(mu_tmp) - log_posterior(mu_history[t-1])
if (log(runif(1)) < accept_prob) {
mu_history[t] <- mu_tmp
accept[t] <- 1
} else {
mu_history[t] <- mu_history[t-1]
}
}
mean(accept) # acceptance rate
## [1] 0.453
head(data.frame(accept, mu_history), 10)
## accept mu_history
## 1 0 0.0000000
## 2 0 0.0000000
## 3 0 0.0000000
## 4 1 0.4335097
## 5 1 -1.2482012
## 6 1 -0.7089893
## 7 0 -0.7089893
## 8 1 -0.8781614
## 9 0 -0.8781614
## 10 1 -0.8406691
plot(mu_history, type = "l") # trace line
abline(h = mean(y), col = 2) # sample mean
本来は log_posterior(mu_history[mu_tmp])
をどこかに一時保存しておいて,採択された場合は次の for loop
で保存された値を参照すれば計算量が少し減らせる.
このページではコードの分かりやすさを優先して for loop ごとに posterior
を計算しているが,大規模データ・多次元パラメタなど尤度関数の計算に時間がかかる場合はこの再利用方式を試すとよい.
Burn-in 期間はほとんど不要だと思われるが,一応最初の10%とする.
mu_after_burnin <- mu_history[round(iter/10, 0):iter]
c(mean(mu_after_burnin), sd(mu_after_burnin))
## [1] -0.4603851 0.8015664
hist(mu_after_burnin, breaks = 30, main = "Posterior of mu")
abline(v = mean(mu_after_burnin), col = "red")
【補足】事前分布 \(N(\mu_0, \tau^2)\) と尤度 \(N(\mu, \sigma^2)\) のどちらも正規分布で与えている場合,事後分布も正規分布となり,事後分布の分散と平均は解析的に解ける(このような事前分布を共役事前分布と呼ぶ.式の導出は省略). そのため,実はMHによるMCMCはせずとも \(\mu\) の事後分布は計算できる.
\[ p (\mu \mid Y) \propto p (\mu) \cdot p (Y \mid \mu) \propto \exp \left[ - \frac{1}{2 \tau^2} (\mu - \mu_0)^2 - \frac{1}{2 \sigma^2} \sum_i (Y_i - \mu)^2 \right], \] \[ \sigma^2_{\mbox{post}} = \left( \frac{1}{\tau^2} + \frac{n}{\sigma^2} \right)^{-1}, \quad \mu_{\mbox{post}} = \sigma^2_{\mbox{post}} \cdot \left( \frac{\mu_0}{\tau^2} + n \frac{\bar{y}}{\sigma^2} \right) \]
正規分布 \(N(\mu, \sigma^2)\) から得られた標本に基づいて母平均パラメタ \(\mu\) を推定する. ただし \(\sigma^2 = 3.6\) と分かっているものとしよう.
先ほどは事前分布を正規分布にしたが,今回は一様分布にしよう. そうすると事後分布は解析的に導出できない(テクニカルに言うと,一様分布は正規分布と異なり指数分布族でないため). このようなケースはMCMCによる事後分布のシミュレートが必要.
事前分布:
\[ \mu \sim U(\min(Y), \max(Y)) \] \[ p (\mu) = \frac{1}{\max(Y) - \min(Y)} , \quad \ln p (\mu) = (\mbox{const.}) \]
採択確率:\([\min, \max]\) においては事前分布 \(p(\mu)\) が定数なので,次のように簡略化される.
\[ \mbox{Acceptance rate} = \min \left\{ 1, \frac{ L(\theta^*) }{ L(\theta^{(t)}) } \right\} \]
# prior: U(min, max) ... U(-100, 100) でもよい
prior_lower <- min(y) # lower bound = -3
prior_upper <- max(y) # upper bound = 1.5
# posterior
log_posterior <- function (mu) {
if (mu < prior_lower || mu > prior_upper) return(-Inf) # [min, max] 外は常に棄却
log_likelihood <- -(1/2) * sum((y - mu)^2 / sigma2)
return(log_likelihood)
}
iter <- 1000 # 繰り返し回数
mu_history <- numeric(iter) # 初期化
accept <- numeric(iter)
for (t in 2:iter) { # 先ほどと中身は一緒.違うのは log_posterior の定義だけ.
set.seed(t)
mu_tmp <- rnorm(n = 1, mean = mu_history[t-1], sd = 2)
accept_prob <- log_posterior(mu_tmp) - log_posterior(mu_history[t-1])
if (log(runif(1)) < accept_prob) {
mu_history[t] <- mu_tmp
accept[t] <- 1
} else {
mu_history[t] <- mu_history[t-1]
}
}
mean(accept) # acceptance rate
## [1] 0.456
後は先ほどとほとんど同じなので省略.
plot(mu_history, type = "l")
abline(h = mean(y), col = 2) # sample mean
mu_after_burnin <- mu_history[round(iter/5, 0):iter]
c(mean(mu_after_burnin), sd(mu_after_burnin))
hist(mu_after_burnin, breaks = 30, main = "Posterior of mu")
abline(v = mean(mu_after_burnin), col = "red")
正規分布 \(N(\mu, \sigma^2)\) から得られた標本に基づいて母分散パラメタ \(\sigma^2\) を推定する. ただし \(\mu = -0.5\) と分かっているものとしよう.
事前分布:逆ガンマ分布(\(\alpha\): shape param., \(\beta\): scale param.)
\[ \sigma^2 \sim InvGamma(\alpha, \beta) \] \[ p (\sigma^2) = \frac{\beta^\alpha}{\Gamma(\alpha)} \left( \frac{1}{\sigma^2} \right)^{\alpha + 1} \exp \left(- \frac{\beta}{\sigma^2} \right) , \quad \ln p (\sigma^2) = (\mbox{const.}) - (\alpha + 1) \log \sigma^2 - \frac{\beta}{\sigma^2} \]
mu <- -0.5 # 母平均は既知と設定
# prior
prior_alpha <- prior_beta <- 0.001 # 弱情報
# posterior
log_posterior <- function (sigma2) {
if (sigma2 <= 0) return(-Inf) # 分散は非負なので,負の値は常に棄却
log_prior <- - (prior_alpha + 1) * log(sigma2) - prior_beta / sigma2
log_likelihood <- -(n/2) * log(sigma2) -(1/(2 * sigma2)) * sum((y - mu)^2)
return(log_prior + log_likelihood)
}
iter <- 1000 # 繰り返し回数
sigma2_history <- numeric(iter) # 初期化
sigma2_history[1] <- 1
accept <- numeric(iter)
for (t in 2:iter) {
set.seed(t)
sigma2_tmp <- exp(rnorm(n = 1, mean = log(sigma2_history[t-1]), sd = 1))
accept_prob <- log_posterior(sigma2_tmp) - log_posterior(sigma2_history[t-1])
if (log(runif(1)) < accept_prob) {
sigma2_history[t] <- sigma2_tmp
accept[t] <- 1
} else {
sigma2_history[t] <- sigma2_history[t-1]
}
}
mean(accept) # acceptance rate
## [1] 0.533
plot(sigma2_history, type = "l")
abline(h = var(y), col = 2) # sample variance ≒ 3.6
Burn-in 後の事後分布.
sigma2_after_burnin <- sigma2_history[round(iter/10, 0):iter]
c(mean(sigma2_after_burnin), sd(sigma2_after_burnin))
## [1] 2.969630 2.443163
hist(sigma2_after_burnin, breaks = 30, main = "Posterior of sigma2")
abline(v = mean(sigma2_after_burnin), col = "red")
正規分布 \(N(\mu, \sigma^2)\) から得られた標本に基づいて母平均パラメタ \(\mu\) と母分散パラメタ \(\sigma^2\) を推定する.
事前分布は,\(\mu\) については正規分布,\(\sigma^2\) については逆ガンマ分布とする.
# prior
prior_mu <- 0 # for mu ~ N
prior_sigma2 <- 100
prior_alpha <- prior_beta <- 0.001 # for sigma2 ~ InvG
# posterior
log_posterior <- function (mu, sigma2) {
# prior
if (sigma2 <= 0) return(-Inf) # 分散は非負
log_prior_mu <- -(1/2) * (mu - prior_mu)^2 / prior_sigma2
log_prior_sigma2 <- - (prior_alpha + 1) * log(sigma2) - prior_beta / sigma2
# likelihood
log_likelihood <- -(n/2) * log(sigma2) -(1/(2 * sigma2)) * sum((y - mu)^2)
return(log_prior_mu + log_prior_sigma2 + log_likelihood)
}
iter <- 1000 # 繰り返し回数
mu_history <- sigma2_history <- numeric(iter) # 初期化
sigma2_history[1] <- 1
accept <- numeric(iter)
for (t in 2:iter) {
set.seed(t)
mu_tmp <- rnorm(n = 1, mean = mu_history[t-1], sd = 1)
sigma2_tmp <- exp(rnorm(n = 1, mean = log(sigma2_history[t-1]), sd = 1))
accept_prob <- log_posterior(mu_tmp, sigma2_tmp) -
log_posterior(mu_history[t-1], sigma2_history[t-1])
if (log(runif(1)) < accept_prob) {
mu_history[t] <- mu_tmp
sigma2_history[t] <- sigma2_tmp
accept[t] <- 1
} else {
mu_history[t] <- mu_history[t-1]
sigma2_history[t] <- sigma2_history[t-1]
}
}
mean(accept) # acceptance rate
## [1] 0.398
yl <- range(c(mu_history, sigma2_history))
plot(mu_history, type = "l", ylim = yl, ylab = "mu, sigma2")
par(new = TRUE)
plot(sigma2_history, type = "l", ylim = yl, axes = FALSE, ann = FALSE, col = 2, lty = 2)
# abline(h = mean(y), col = 1) # sample mean ≒ -0.5
# abline(h = var(y), col = 2) # sample variance ≒ 3.6
legend(x = "topright", legend = c("mu", "sigma2"), col = 1:2, lty = 1:2)
事後分布が2つあるので,2次元平面にその軌跡(MCMC開始直後のみ)をプロットしてみる.
mu_beginning <- unique(mu_history[1:20]) # 全く同じ値に偶然戻ってくることはないと仮定
sigma2_beginning <- unique(sigma2_history[1:20])
plot(mu_beginning, sigma2_beginning, type = "b") # chain
text(x = mu_beginning + .05, y = sigma2_beginning, labels = 1:length(mu_beginning), col = 8)
Burn-in 後の事後分布.
mu_after_burnin <- mu_history[round(iter/10, 0):iter]
sigma2_after_burnin <- sigma2_history[round(iter/10, 0):iter]
c(mean(mu_after_burnin), sd(mu_after_burnin),
mean(sigma2_after_burnin), sd(sigma2_after_burnin))
## [1] -0.3796998 0.8789160 3.8906061 3.4283113
hist(mu_after_burnin, breaks = 20, main = "Posterior", xlab = "", xlim = yl, probability = TRUE)
hist(sigma2_after_burnin, breaks = 100, add = TRUE, xlim = yl, probability = TRUE, col = rgb(1,0,0,.2))
# abline(v = mean(mu_after_burnin)); abline(v = mean(sigma2_after_burnin), col = "red")
legend(x = "topright", legend = c("mu", "sigma2"), fill = c(rgb(0,0,0,.2), rgb(1,0,0,.2)))
線形回帰モデルの係数パラメタ \(\beta_0, \beta_1, \beta_2\) および誤差分散 \(\sigma^2\) を推定する.
\[ \mbox{Fert} = \beta_0 + \beta_1 \mbox{Agri} + \beta_2 \mbox{Exam} + \epsilon, \quad \epsilon \sim N(0, \sigma^2) \]
事前分布は,\(\beta\) については正規分布,\(\sigma^2\) については逆ガンマ分布とする.
本来は提案分布からサンプリングされた \((\beta_0, \beta_1, \beta_2, \sigma^2)\) の組に対して採択/棄却を一括判定するものと思われるが,その方法ではこのケースでうまく推定できないため,逐次的に更新する方法を採用する(componentwise MH).
# data
Y <- swiss$Fertility
X <- cbind(1, swiss$Agriculture, swiss$Examination)
n_swiss <- length(Y)
k_swiss <- ncol(X)
# prior
prior_beta_mu <- rep(0, k_swiss) # for beta ~ N
prior_beta_sigma2 <- rep(1000, k_swiss) # 値が小さいとうまく推定できない
prior_alpha <- prior_beta <- 0.001 # for sigma2 ~ InvG
# posterior
log_posterior <- function (param) {
betas <- param[1:k_swiss]
sigma2 <- param[1+k_swiss]
# prior
if (sigma2 <= 0) return(-Inf) # 分散は非負
log_prior_betas <- sum( -(1/2) * (betas - prior_beta_mu)^2 / prior_beta_sigma2 )
log_prior_sigma2 <- - (prior_alpha + 1) * log(sigma2) - prior_beta / sigma2
# likelihood
SSR <- sum((Y - X %*% betas)^2) # sum of squared residuals
log_likelihood <- -(n_swiss/2) * log(sigma2) -(1/(2 * sigma2)) * SSR
return(log_prior_betas + log_prior_sigma2 + log_likelihood)
}
iter <- 10000 # 繰り返し回数
# マルコフ連鎖を保存する行列の初期化
# column 1=t, 2=beta0, 3=beta1, 4=beta2, 5=sigma2, 6~9=acceptance(flag)
param_history <- as.data.frame(matrix(0, ncol = 1+(k_swiss+1)*2, nrow = iter))
names(param_history) <- c("t", "b0", "b1", "b2", "s", "a0", "a1", "a2", "as")
betas_current <- rep(0, k_swiss)
sigma2_current <- 1
param_history$s[1] <- sigma2_current # sigma^2 at t=1
log_posterior_ref <- log_posterior(c(betas_current, sigma2_current))
start_time <- proc.time()[3]
for (t in 2:iter) {
set.seed(t)
# update beta
betas_new <- betas_current
sigma2_new <- sigma2_current
accept_betas <- rep(0, k_swiss)
accept_sigma2 <- 0
for (k in 1:k_swiss) {
betas_tmp <- betas_new
betas_tmp[k] <- rnorm(n = 1, mean = betas_current[k], sd = 1)
log_posterior_tmp <- log_posterior(c(betas_tmp, sigma2_current))
accept_prob <- log_posterior_tmp - log_posterior_ref
if (log(runif(1)) < accept_prob) {
betas_new[k] <- betas_tmp[k]
accept_betas[k] <- 1
log_posterior_ref <- log_posterior_tmp
}
}
# update sigma2
sigma2_tmp <- exp(rnorm(n = 1, mean = log(param_history$s[t-1]), sd = 1))
log_posterior_tmp <- log_posterior(c(betas_new, sigma2_tmp))
accept_prob <- log_posterior_tmp - log_posterior_ref
if (log(runif(1)) < accept_prob) {
sigma2_new <- sigma2_tmp
accept_sigma2 <- 1
log_posterior_ref <- log_posterior_tmp
}
betas_current <- betas_new
sigma2_current <- sigma2_new
param_history[t, ] <- c(t, betas_new, sigma2_new, accept_betas, accept_sigma2)
if (t %% round(iter/2, 0) == 0) {
cat(t, ", b0=", betas_new[1], ", b1=", betas_new[2], ", b2=", betas_new[3],
", s2=", sigma2_new, ", elasped=", proc.time()[3] - start_time, "[s] \n", sep = "")
}
}
## 5000, b0=96.50189, b1=-0.159943, b2=-1.230562, s2=85.25378, elasped=1.78[s]
## 10000, b0=87.6099, b1=-0.04860907, b2=-0.9189937, s2=84.70792, elasped=2.91[s]
apply(param_history[, 6:9], 2, mean) # acceptance rate
## a0 a1 a2 as
## 0.7894 0.0356 0.0947 0.2489
plot(param_history$b0, type = "l")
abline(h = lm(Fertility ~ Agriculture + Examination, swiss)$coef[1], col = 2) # OLS estimates
# plot(param_history$b1, type = "l")
# plot(param_history$b2, type = "l")
# plot(param_history$s, type = "l")
Burn-in 後.
apply(param_history[(iter/5):iter, 2:(2+k_swiss)], 2, mean)
## b0 b1 b2 s
## 90.48952816 -0.05399481 -1.07490044 91.38357921
# for comparison
lm(Fertility ~ Agriculture + Examination, swiss)$coef
## (Intercept) Agriculture Examination
## 94.60968827 -0.09399751 -1.19502875
summary(lm(Fertility ~ Agriculture + Examination, swiss))$sigma^2
## [1] 92.56226