SSブログ

dlmのGibbsサンプラー、それからStan [統計]

Rのdlmパッケージには、Gibbsサンプラーでパラメータ推定するdlmGibbsDIG()もある。

Rによるベイジアン動的線形モデル (統計ライブラリー) の4章を参考にする。スペインの投資額のデータ。

##
## Petris at al. (2009) Dynamic Linear Models with R
## Chapter 4
##
library(dlm)

data.file <- url("http://definetti.uark.edu/~gpetris/dlm/Datasets/invest2.dat")
# data.file <- "Datasets/invest2.dat"
invSpain <- ts(read.table(data.file,
                          colClasses = "numeric")[, 2] / 1000,
               start = 1960)
plot(invSpain, las = 1, xlab = "Year", ylab = "Investiments (×1000)")

Rplot10.png

MCMC

set.seed(5672)
MCMC <- 12000
gibbsOut <- dlmGibbsDIG(invSpain,             # data vector
                        mod = dlmModPoly(2),  # dlm
                        a.y = 1,              # prior mean of observation precision
                        b.y = 1000,           # prior variance of observation precision
                        a.theta = 10,         # prior mean of system precisions
                        b.theta = 1000,       # prior variance of system precisions
                        n.sample = MCMC,
                        thin = 1,
                        save.states = TRUE)

burn <- 2000
theta <- mcmcMean(cbind(gibbsOut$dV[-(1:burn)],
                        gibbsOut$dW[-(1:burn), ]))

計算はあまりはやくない。結果。

> theta
                W.1        W.2   
  0.012197   0.117391   0.329588 
 (0.000743) (0.007682) (0.007833)

推定された状態をプロット。

y <- apply(gibbsOut$theta[, 1, -(1:burn)], 1, mean)
plot(invSpain, las = 1, xlab = "Year", ylab = "Investiments (×1000)")
lines(start(invSpain)[1]:end(invSpain)[1], dropFirst(y), col = 2, lty = 2)

Rplot11.png
観測誤差がちいさい。(Investmentのつづりをまちがえているが、きにしない。)

カルマンフィルタにかけてみる。

build <- function(theta) {
  dlmModPoly(order = 2, dV = theta[1], dW = theta[2:3])
}
mod <- build(theta[1, ])
ks <- dlmSmooth(invSpain, mod)
plot(invSpain, las = 1,
     xlab = "Year", ylab = "Investiments (×1000)")
lines(start(invSpain)[1]:end(invSpain)[1],
      dropFirst(ks$s[, 1]), col = "red", lty = 2)

Rplot12.png

MCMCの部分をStanでやってみる。

library(rstan)

model.text <- "
  data {
  int<lower=1>  N;
  real          y[N];
}
parameters {
  real          mu[N];
  real          mu0;
  real          beta[N];
  real          beta0;
  real<lower=0> sigma[3];
}
model {
  for (t in 1:N) {
    y[t] ~ normal(mu[t], sigma[1]);
  }
  mu[1] ~ normal(mu0, sigma[2]);
  beta[1] ~ normal(beta0, sigma[3]);
  for (t in 2:N) {
    mu[t] ~ normal(mu[t - 1] + beta[t - 1], sigma[2]);
    beta[t] ~ normal(beta[t - 1], sigma[3]);
  }
  sigma[1] ~ uniform(0, 1000);
  sigma[2] ~ uniform(0, 1000);
  sigma[3] ~ uniform(0, 1000);
}
"

model <- stan_model(model_code = model.text)
fit <- sampling(model, data = list(N = length(invSpain),
                                   y = c(invSpain)),
                chains = 3, iter = 6000, warmup = 1000, thin = 5)

結果

> print(fit, pars = "sigma", digits = 2)
Inference for Stan model: model.text.
3 chains, each with iter=6000; warmup=1000; thin=5; 
post-warmup draws per chain=1000, total post-warmup draws=3000.

         mean se_mean   sd 2.5%  25%  50%  75% 97.5% n_eff Rhat
sigma[1] 0.11    0.01 0.07 0.01 0.05 0.10 0.16  0.27   136 1.03
sigma[2] 0.30    0.01 0.17 0.05 0.16 0.28 0.41  0.66   303 1.01
sigma[3] 0.57    0.01 0.15 0.22 0.48 0.58 0.66  0.83   474 1.00

Samples were drawn using NUTS(diag_e) at Sat Jan 18 13:08:20 2014.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at 
convergence, Rhat=1).

カルマンフィルタへ。

sigma <- apply(extract(fit, "sigma")$sigma, 2, mean)
mod <- build(sigma)
ks <- dlmSmooth(invSpain, mod)
plot(invSpain, las = 1, xlab = "Year", ylab = "Investiments (×1000)")
lines(start(invSpain)[1]:end(invSpain)[1],
      dropFirst(ks$s[, 1]), col = "red")

Rplot13.png

Stanのモデルだが、increment_log_prob()をつかおうと、下のようにかいたら、Error in function stan::prob::gaussian_dlm_obs_log(l): columns of Fcolumns of F (2) and rows of y (1) must match in sizeRejecting proposed initial value with zero density.というエラーになった。Fは1行2列のはずなのだが。

[追記] Fは転置したものがincrement_log_prob()関数に渡されるので、2行1列が正しい。下のコードは修正ずみ。

data {
  int<lower=0>  N;
  matrix[1, N]  y;
  matrix[2, 1]  F;
  matrix[2, 2]  G;
  vector[2]     m0;
  cov_matrix[2] C0;
}
parameters {
  real<lower=0> sigma[3];
}
transformed parameters {
  vector[1]     V;
  cov_matrix[2] W;

  V[1] <- sigma[1];
  W[1, 1] <- sigma[2];
  W[2, 2] <- sigma[3];
  W[1, 2] <- 0;
  W[2, 1] <- 0;
}
model {
  increment_log_prob(gaussian_dlm_obs_log(y, F, G, V, W, m0, C0));
  sigma ~ uniform(0, 1.0e+3);
}

[追記] 上のコードだが、VとWには分散がはいる。sigmaは分散とおもってください。

このモデルを使用するRStanのコード

model <- stan_model(model_code = model.text)
fit <- sampling(model, data = list(N = length(invSpain),
                                   y = matrix(c(invSpain), nrow = 1),
                                   F = matrix(c(1, 0), nrow = 2, ncol = 1),
                                   G = matrix(c(1, 0, 1, 1), nrow = 2, ncol = 2),
                                   m0 = c(10, 10),
                                   C0 = matrix(c(10, 0, 0, 10), nrow =2, ncol = 2)),
                chains = 3, iter = 6000, warmup = 1000, thin = 5)

タグ:R STAn
nice!(0)  コメント(0)  トラックバック(0) 
共通テーマ:日記・雑感

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

Facebook コメント

トラックバック 0