チラシの裏の落書き日記

統計とか,研究とか,日常についての備忘録的なもの。

JAGSで対数尤度を得る方法(Stanも)

WAICなどを計算するには,対数尤度のMCMCサンプルが必要。
個々のMCMCサンプルからがんばって計算する方法もあるが,より簡単にはMCMCサンプラーのなかで対数尤度を得ると楽。
Stanではgenerated quantityが容易されているが,JAGSでは少し工夫が必要。

参考
https://sourceforge.net/p/mcmc-jags/discussion/610037/thread/a3bef062/
https://sourceforge.net/p/mcmc-jags/discussion/610037/thread/87902622/

その他参考
ito-hi.blog.so-net.ne.jp
hamada.hatenablog.jp



JAGSのモデルの中でのようなものでも対数尤度が得られる
一変量の分布に関しては,密度関数などがRのように用意されている。

model{
for(i in 1:I) logLik[i] <- log(dnorm(x[i], mu, 1/sigma))
}

そのほか, logdensity.**系の関数が用意されているが,これはマニュアルに書かれていないので必要に応じて調べる。
多分,すべての分布に対してこれらの関数が利用できる?
(未確認)

#
# 相関係数については,以下の頁のものを参考にした。
# https://kyougokumakoto.blogspot.jp/2016/09/jags_91.html
# 
# logdensity.mnormで多変量正規分布の対数密度を得られる。
# https://sourceforge.net/p/mcmc-jags/discussion/610037/thread/a3bef062/
#
library(MASS)
set.seed(1234) 
mu <- c(0,0) 
Sig <- matrix(c(1,0.75,0.75,1),2,2) 
x <- mvrnorm(100,mu,Sig) 
cor.test(x[,1],x[,2])

data = list(x = x, N = nrow(x)) 

# JAGS code 
cat("model{
# likelihood
for(i in 1:N){
x[i,1:2] ~ dmnorm(mu[], prec[ , ]) 

logLik[i] <- logdensity.mnorm(x[i,1:2], mu, prec) 
} 

# corresponding the precision matrix
prec[1:2, 1:2] <- inverse(cov[,]) 

# constructing the covariance matrix
cov[1,1] <- sigma[1]^2
cov[1,2] <- sigma[1]*sigma[2]*rho
cov[2,1] <- sigma[1]*sigma[2]*rho
cov[2,2] <- sigma[2]^2 

#priors
rho ~ dunif(-1,1) 
mu[1] ~ dnorm(0, 1.0e-4) 
mu[2] ~ dnorm(0, 1.0e-4)
sigma[1] ~ dt(0, 1, 1)T(0,)
sigma[2] ~ dt(0, 1, 1)T(0,) 

}", file="cor.jag")

fit_jag <- jags(data=data,
            parameters.to.save=c("mu", "sigma","rho","logLik"), 
            model.file="cor.jag",
            n.chains = 3,
            n.adapt = 100,
            n.iter = 5000,
            n.burnin = 2500,
            n.thin = 2)

library("loo")

# チェインがたくさんある場合には,長い行列をとしてみる?
log_lik1 <- sapply(1:nrow(x), function(i) do.call("rbind",fit_jag$samples)[,paste0("logLik[",i,"]")])
waic(log_lik1)

log_lik2 <- sapply(1:nrow(x), function(i) fit_jag$samples[[1]][,paste0("logLik[",i,"]")])
waic(log_lik2)


library(rstan)

#
# Stanのコード参考:https://qiita.com/hoxo_m/items/0f1b05681f5d6c4b560a
# ベクトルの配列について注意。y[i]で得られるのはi番目の長さが2のベクトル。
# y[i][1]でi番目の配列にあるベクトルの1番目の要素を取り出すということになる。 
#

cat("data{
 int<lower=0> N;
    vector[2] y[N];
    }
    parameters{
    vector[2] mu;
    real<lower=0> var_x;
    real<lower=0> var_y;
    real cov;
    }
    
    transformed parameters{
    matrix[2,2] sigma;
    sigma[1,1] = var_x;
    sigma[2,1] = cov;
    sigma[1,2] = cov;
    sigma[2,2] = var_y;
    }
    
    model{
    for(i in 1:N){
    target += multi_normal_lpdf(y[i]| mu, sigma);
    //target += normal_lpdf(y[i][2]|0, 1);
    //target += normal_lpdf(y[i][1]|0, 1);
    //y[i] ~ multi_normal(mu, sigma);
    }
    }
    
    
    generated quantities {
    real cor;
    real log_lik[N];
    cor = cov/sqrt(var_x * var_y);
    for(i in 1:N) log_lik[i] = multi_normal_lpdf(y[i] | mu, sigma);
    }

", file="cor.stan")
datastan <- list(N=nrow(x), y = x)
fit <- stan(file = "cor.stan", data=datastan, iter = 5000, chains = 2)

log_lik_stan <- extract_log_lik(fit)
str(log_lik_stan)
waic(log_lik_stan)
waic(log_lik1)
waic(log_lik2)

# 値は大体一致している。