当观察到的节点是潜在节点的最大值

时间:2016-11-11 23:13:45

标签: r statistics jags stan

我有以下潜变量模型:Person j有两个潜在变量,X j1 和X j2 。我们唯一要注意的是它们的最大值,Y j = max(X j1 ,X j2 )。潜在变量是双变量正态;它们各自具有平均μ,方差sigma 2 ,它们的相关性为rho。我想仅使用Y j 来估计三个参数(mu,sigma 2 ,rho),来自n个患者的数据,j = 1,...,n。

我已经尝试在JAGS中使用这个模型(所以我对参数进行了先验),但我无法获得编译代码。这是我用来调用JAGS的R代码。首先,我给出了数据(潜在和观察到的变量),给出了一些真实的参数值:

# true parameter values
mu <- 3
sigma2 <- 2
rho <- 0.7

# generate data
n <- 100
Sigma <- sigma2 * matrix(c(1, rho, rho, 1), ncol=2)
X <- MASS::mvrnorm(n, c(mu,mu), Sigma) # n-by-2 matrix
Y <- apply(X, 1, max)

然后我定义了JAGS模型,并编写了一个小函数来运行JAGS采样器并返回样本:

# JAGS model code
model.text <- '
model {
  for (i in 1:n) {
    Y[i] <- max(X[i,1], X[i,2]) # Ack!
    X[i,1:2] ~ dmnorm(X_mean, X_prec)
  }

  # mean vector and precision matrix for X[i,1:2]
  X_mean <- c(mu, mu)
  X_prec[1,1] <- 1 / (sigma2*(1-rho^2))
  X_prec[2,1] <- -rho / (sigma2*(1-rho^2))
  X_prec[1,2] <- X_prec[2,1]
  X_prec[2,2] <- X_prec[1,1]

  mu ~ dnorm(0, 1)
  sigma2 <- 1 / tau
  tau ~ dgamma(2, 1)
  rho ~ dbeta(2, 2)
}
'

# run JAGS code. If latent=FALSE, remove the line defining Y[i] from the JAGS model
fit.jags <- function(latent=TRUE, data, n.adapt=1000, n.burnin, n.samp) {
  require(rjags)
  if (!latent)
    model.text <- sub('\n *Y.*?\n', '\n', model.text)
  textCon <- textConnection(model.text)
  fit <- jags.model(textCon, data, n.adapt=n.adapt)
  close(textCon)
  update(fit, n.iter=n.burnin)
  coda.samples(fit, variable.names=c("mu","sigma2","rho"), n.iter=n.samp)[[1]]
}

最后,我打电话给JAGS,只给它观察数据:

samp1 <- fit.jags(latent=TRUE, data=list(n=n, Y=Y), n.burnin=1000, n.samp=2000)

可悲的是,这会导致错误消息:“Y [1]是一个逻辑节点,无法观察到”。 JAGS不喜欢我使用“&lt; - ”为Y [i]赋值(我用“Ack!”表示违规行)。我理解投诉,但我不确定如何重写模型代码来解决这个问题。

另外,为了证明其他所有东西(除了“Ack!”行)都没问题,我再次运行模型,但这次我把X数据提供给它,假装它实际被观察到了。这完美运行,我得到了很好的参数估计:

samp2 <- fit.jags(latent=FALSE, data=list(n=n, X=X), n.burnin=1000, n.samp=2000)
colMeans(samp2)

如果你能找到一种方法来在STAN中编程这个模型而不是JAGS,那对我来说没问题。

2 个答案:

答案 0 :(得分:3)

理论上,你可以使用dsum发行版在JAGS中实现这样的模型(在这种情况下,在建模最大值而不是两个变量的总和时使用一些黑客攻击)。但是下面的代码确实可以编译和运行(虽然它没有“真正意义上的工作” - 见后面):

set.seed(2017-02-08)

# true parameter values
mu <- 3
sigma2 <- 2
rho <- 0.7

# generate data
n <- 100
Sigma <- sigma2 * matrix(c(1, rho, rho, 1), ncol=2)
X <- MASS::mvrnorm(n, c(mu,mu), Sigma) # n-by-2 matrix
Y <- apply(X, 1, max)

model.text <- '
model {
  for (i in 1:n) {
    Y[i] ~ dsum(max_X[i])
    max_X[i] <- max(X[i,1], X[i,2])
    X[i,1:2] ~ dmnorm(X_mean, X_prec)
    ranks[i,1:2] <- rank(X[i,1:2])
    chosen[i] <- ranks[i,2]
  }

  # mean vector and precision matrix for X[i,1:2]
  X_mean <- c(mu, mu)
  X_prec[1,1] <- 1 / (sigma2*(1-rho^2))
  X_prec[2,1] <- -rho / (sigma2*(1-rho^2))
  X_prec[1,2] <- X_prec[2,1]
  X_prec[2,2] <- X_prec[1,1]

  mu ~ dnorm(0, 1)
  sigma2 <- 1 / tau
  tau ~ dgamma(2, 1)
  rho ~ dbeta(2, 2)

  #data# n, Y
  #monitor# mu, sigma2, rho, tau, chosen[1:10]
  #inits# X
}
'

library('runjags')

results <- run.jags(model.text)
results
plot(results)

有两点需要注意:

  1. JAGS不够智能初始化X的矩阵,同时满足dsum(max(X [i,]))约束 - 所以我们必须使用合理的值为JAGS初始化X 。在这种情况下,我使用了作弊的模拟值 - 你得到的答案高度依赖于X的初始值的选择,而在现实世界中你不会让模拟值回落上。

  2. max()约束导致我无法在一般框架内考虑解决方案的问题:与通常的dsum约束不同,它允许一个参数减少而另一个参数增加,因此两个参数都是在任何时候都使用,X [i,]的min()值被忽略,因此采样器可以自由地随意做。这将很少(即从未)导致min(X [i,])的值恰好与Y [i]相同,这是采样器切换所需的条件。在两个X [i,]之间。所以切换永远不会发生,并且在初始化时选择的X []保持最大值 - 我已经添加了一个跟踪参数&#39;选择&#39;这说明了这一点。

  3. 据我所知,其他可能的解决方案是如何对此进行编码的。问题将落入基本相同的非混合陷阱,我认为这是一个根本问题(虽然我可能是错的,非常欢迎工作BUGS / JAGS / Stan代码,否则说明)。

    混合失败的解决方案更难,尽管类似于Carlin&amp; amp;用于模型选择的Chibb方法可以起作用(迫使min(pseudo_X)参数等于Y以促进切换)。这可能是一个棘手的工作,但如果你可以从具有合理的BUGS / JAGS经验的人那里获得帮助,你可以尝试一下 - 参见: Carlin,B.P.,Chib,S.,1995。贝叶斯模型选择通过马尔可夫链蒙特卡罗方法。 J. R. Stat。 SOC。序列。 B 57,473-484。

    或者,您可以尝试略微区别问题,并将X直接模拟为矩阵,第一列全部缺失,第二列全部等于Y.然后您可以使用dinterval()为缺失设置约束它们必须低于相应的最大值。我不确定这在估算mu / sigma2 / rho方面有多好,但值得一试。

    顺便说一下,我意识到这并不一定能回答你的问题,但我认为它是一个有用的例子,说明它之间的区别是可编码的&#39;并且&#39;它是否可行&#39;。

    马特

    PS。一个更聪明的解决方案是直接考虑最多两个正常变量的分布 - 我不确定这样的分布是否存在,但它确实存在,你可以得到一个PDF然后分配可以直接使用零/一招无需考虑最小值。

答案 1 :(得分:0)

我相信你可以用Stan语言对此进行建模,将可能性视为具有相同权重的双组分混合物。 Stan代码看起来像

data {
  int<lower=1> N;
  vector[N] Y;
}
parameters {
  vector<upper=0>[2] diff[N];
  real mu;
  real<lower=0> sigma;
  real<lower=-1,upper=1> rho;
}
model {
  vector[2] case_1[N];
  vector[2] case_2[N];
  vector[2] mu_vec;
  matrix[2,2] Sigma;
  for (n in 1:N) {
    case_1[n][1] = Y[n]; case_1[n][2] = Y[n] + diff[n][1];
    case_2[n][2] = Y[n]; case_2[n][1] = Y[n] + diff[n][2];
  }
  mu_vec[1] = mu; mu_vec[2] = mu;
  Sigma[1,1] = square(sigma);
  Sigma[2,2] = Sigma[1,1];
  Sigma[1,2] = Sigma[1,1] * rho;
  Sigma[2,1] = Sigma[1,2];
  // log-likelihood
  target += log_mix(0.5, multi_normal_lpdf(case_1 | mu_vec, Sigma), 
                         multi_normal_lpdf(case_2 | mu_vec, Sigma));
  // insert priors on mu, sigma, and rho
}