【问题标题】:Fixing a parameter to a distribution in JAGS将参数固定到 JAGS 中的分布
【发布时间】:2021-08-13 16:48:20
【问题描述】:

在贝叶斯编程语言 JAGS 中,我正在寻找一种将参数固定为特定分布的方法,而不是常量。下面的段落更明确地提出了这个问题并引用了 JAGS 代码。我也愿意接受使用其他概率编程语言(例如 stan)的答案。

下面的第一个代码块 (model1) 是一个 JAGS 脚本,旨在估计具有不等方差的两组高斯混合模型。我正在寻找一种将其中一个参数(例如 $\mu_2$)修复为特定分布(例如,dnorm(0,0.0001))的方法。我知道如何将 $\mu_2$ 修复为一个常数(例如,参见代码块 2 中的 model2),尽管我找不到将 $\mu_2$ 修复为我之前的信念的方法(例如,参见代码块 3 中的 model3,其中从概念上显示我正在尝试做的事情)。

提前致谢!

代码块 1

model1 = "
model {

for (i in 1:n1){
y1[i] ~ dnorm (mu1 , phi1) 
}

for (i in 1:n2){ 
y2[i] ~ dnorm (mu2 , phi2) 
}

# Priors
phi1 ~ dgamma(.001,.001) 
phi2 ~ dgamma(.001,.001) 
sigma2.1 <- 1/phi1
sigma2.2 <- 1/phi2
mu1 ~ dnorm (0,0.0001) 
mu2 ~ dnorm (0,0.0001)

# Create a variable for the mean difference

delta <- mu1 - mu2

}
"

代码块 2

model2 = "
model {

for (i in 1:n1){
y1[i] ~ dnorm (mu1 , phi1) 
}

for (i in 1:n2){ 
y2[i] ~ dnorm (mu2 , phi2) 
}

# Priors
phi1 ~ dgamma(.001,.001) 
phi2 ~ dgamma(.001,.001) 
sigma2.1 <- 1/phi1
sigma2.2 <- 1/phi2
mu1 ~ dnorm (0,0.0001) 
mu2 <- 1.27

# Create a variable for the mean difference

delta <- mu1 - mu2

}
"

代码块 3

model3 = "
model {

for (i in 1:n1){
y1[i] ~ dnorm (mu1 , phi1) 
}

for (i in 1:n2){ 
y2[i] ~ dnorm (mu2 , phi2) 
}

# Priors
phi1 ~ dgamma(.001,.001) 
phi2 ~ dgamma(.001,.001) 
sigma2.1 <- 1/phi1
sigma2.2 <- 1/phi2
mu1 ~ dnorm (0,0.0001) 
mu2 <- dnorm (0,0.0001) 

# Create a variable for the mean difference

delta <- mu1 - mu2

}
"

【问题讨论】:

    标签: r bayesian mcmc jags stan


    【解决方案1】:

    我不知道 JAGS,但这里有两个 Stan 版本。一个在所有迭代中对mu2 进行单个样本;第二个为每次迭代采用不同的mu2 样本。

    无论如何,我没有资格判断这是否真的是一个好主意。 (特别是第二个版本,Stan 团队故意试图避免这种情况,原因描述为here。)但至少是可能的。

    (在这两个示例中,我更改了一些先前的分布以使数据更易于使用,但基本思想是相同的。)

    mu2 的一个样本

    首先,Stan 模型。

    data {
      int<lower=0> n1;
      vector[n1] y1;
      int<lower=0> n2;
      vector[n2] y2;
    }
    
    transformed data {
      // Set mu2 to a single randomly selected value (instead of giving it a prior
      // and estimating it).
      real mu2 = normal_rng(0, 0.0001);
    }
    
    parameters {
      real mu1;
      real<lower=0> phi1;
      real<lower=0> phi2;
    }
    
    transformed parameters {
      real sigma1 = 1 / phi1;
      real sigma2 = 1 / phi2;
    }
    
    model {
      mu1 ~ normal(0, 0.0001);
      phi1 ~ gamma(1, 1);
      phi2 ~ gamma(1, 1);
      y1 ~ normal(mu1, sigma1);
      y2 ~ normal(mu2, sigma2);
    }
    
    generated quantities {
      real delta = mu1 - mu2;
      // We can't return mu2 from the transformed data block.  So if we want to see
      // what it was, we have to copy its value into a generated quantity and return
      // that.
      real mu2_return = mu2;
    }
    

    接下来,R 代码生成假数据并拟合模型。

    # Generate fake data.
    n1 = 1000
    n2 = 1000
    mu1 = rnorm(1, 0, 0.0001)
    mu2 = rnorm(1, 0, 0.0001)
    phi1 = rgamma(1, shape = 1, rate = 1)
    phi2 = rgamma(1, shape = 1, rate = 1)
    y1 = rnorm(n1, mu1, 1 / phi1)
    y2 = rnorm(n2, mu2, 1 / phi2)
    delta = mu1 - mu2
    
    # Fit the Stan model.
    library(rstan)
    options(mc.cores = parallel::detectCores())
    rstan_options(auto_write = T)
    
    stan.data = list(n1 = n1, y1 = y1, n2 = n2, y2 = y2)
    stan.model = stan(file = "stan_model.stan",
                      data = stan.data,
                      cores = 3, iter = 1000)
    

    我们可以从 Stan 模型中提取样本,并看到我们正确地恢复了参数的真实值 - 当然,mu2 的情况除外。

    # Pull out the samples.
    library(tidybayes)
    library(tidyverse)
    stan.model %>%
      spread_draws(mu1, phi1, mu2_return, phi2) %>%
      ungroup() %>%
      dplyr::select(.draw, mu1, phi1, mu2 = mu2_return, phi2) %>%
      pivot_longer(cols = -c(.draw), names_to = "parameter") %>%
      ggplot(aes(x = value)) +
      geom_histogram() +
      geom_vline(data = data.frame(parameter = c("mu1", "phi1", "mu2", "phi2"),
                                   true.value = c(mu1, phi1, mu2, phi2)),
                 aes(xintercept = true.value), color = "red", size = 1.5) +
      facet_wrap(~ parameter, scales = "free") +
      theme_bw() +
      scale_x_continuous("Parameter value") +
      scale_y_continuous("Number of samples")
    

    mu2 每次迭代的新样本

    我们无法在参数、变换参数或模型块中生成随机数;同样,这是一个深思熟虑的设计选择。但是我们可以在转换后的数据块中生成一大堆数字,并为每次迭代获取一个新数字。为此,我们需要一种方法来确定我们在参数块中进行的迭代。我从this discussion on the Stan forums 末尾使用了路​​易斯的解决方案。首先,将以下 C++ 代码保存为 iter.hpp 在您的工作目录中:

    static int itct = 1;
    inline void add_iter(std::ostream* pstream__) {
        itct += 1;
    }
    inline int get_iter(std::ostream* pstream__) {
        return itct;
    }
    

    接下来,定义 Stan 模型如下。函数add_iter()get_iter()iter.hpp中定义;如果您在 RStudio 中工作,则在编辑 Stan 文件时会收到错误符号,因为 RStudio 不知道我们将从其他地方引入这些函数定义。

    functions {
      void add_iter();
      int get_iter();
    }
    
    data {
      int<lower=0> n1;
      vector[n1] y1;
      int<lower=0> n2;
      vector[n2] y2;
      int<lower=0> n_iterations;
    }
    
    transformed data {
      vector[n_iterations + 1] all_mu2s;
      for(n in 1:(n_iterations + 1)) {
        all_mu2s[n] = normal_rng(0, 0.0001);
      }
    }
    
    parameters {
      real mu1;
      real<lower=0> phi1;
      real<lower=0> phi2;
    }
    
    transformed parameters {
      real sigma1 = 1 / phi1;
      real sigma2 = 1 / phi2;
      real mu2 = all_mu2s[get_iter()];
    }
    
    model {
      mu1 ~ normal(0, 0.0001);
      phi1 ~ gamma(1, 1);
      phi2 ~ gamma(1, 1);
      y1 ~ normal(mu1, sigma1);
      y2 ~ normal(mu2, sigma2);
    }
    
    generated quantities {
      real delta = mu1 - mu2;
      add_iter();
    }
    

    请注意,模型实际上为 mu2 生成的随机值比我们需要的多 1 个。当我尝试准确生成n_iterations 随机值时,我收到一条错误消息,通知我Stan 试图访问all_mu2s[1001]。 我觉得这很令人担忧,因为这意味着我不完全了解内部发生了什么——考虑到下面的 R 代码,不应该只有 1000 次迭代吗?但它只是看起来像一个错误的错误,并且拟合的模型看起来很合理,所以我没有进一步追求。

    另外,请注意,这种方法获取的是迭代次数,而不是链。我只跑了一条链;如果您运行多个链,则mu2 的第 i 个值将在每个链中相同。同一个 Stan 论坛讨论有一个区分链的建议,但我没有探索它。

    最后,生成假数据并拟合模型。当我们编译模型时,我们需要从iter.hpp 中潜入函数定义,如here 所述。

    # Generate fake data.
    n1 = 1000
    n2 = 1000
    mu1 = rnorm(1, 0, 0.0001)
    mu2 = rnorm(1, 0, 0.0001)
    phi1 = rgamma(1, shape = 1, rate = 1)
    phi2 = rgamma(1, shape = 1, rate = 1)
    y1 = rnorm(n1, mu1, 1 / phi1)
    y2 = rnorm(n2, mu2, 1 / phi2)
    delta = mu1 - mu2
    n.iterations = 1000
    
    # Fit the Stan model.
    library(rstan)
    stan.data = list(n1 = n1, y1 = y1, n2 = n2, y2 = y2,
                     n_iterations = n.iterations)
    stan.model = stan_model(file = "stan_model.stan",
                            allow_undefined = T,
                            includes = paste0('\n#include "',
                                              file.path(getwd(), 'iter.hpp'),
                                              '"\n'))
    stan.model.fit = sampling(stan.model,
                              data = stan.data,
                              chains = 1,
                              iter = n.iterations,
                              pars = c("mu1", "phi1", "mu2", "phi2"))
    

    再一次,我们很好地恢复了mu1phi1phi2 的值。这一次,我们为mu2 使用了一系列值,它们遵循指定的分布。

    # Pull out the samples.
    library(tidybayes)
    library(tidyverse)
    stan.model.fit %>%
      spread_draws(mu1, phi1, mu2, phi2) %>%
      ungroup() %>%
      dplyr::select(.draw, mu1, phi1, mu2 = mu2, phi2) %>%
      pivot_longer(cols = -c(.draw), names_to = "parameter") %>%
      ggplot(aes(x = value)) +
      geom_histogram() +
      stat_function(dat = data.frame(parameter = "mu2", value = 0),
                    fun = function(.x) { dnorm(.x, 0, 0.0001) * 0.01 },
                    color = "blue", size = 1.5) +
      geom_vline(data = data.frame(parameter = c("mu1", "phi1", "mu2", "phi2"),
                                   true.value = c(mu1, phi1, mu2, phi2)),
                 aes(xintercept = true.value), color = "red", size = 1.5) +
      facet_wrap(~ parameter, scales = "free") +
      theme_bw() +
      scale_x_continuous("Parameter value") +
      scale_y_continuous("Number of samples")
    

    【讨论】:

    • 是的,你正是我想要做的。我想为每次迭代绘制一个新的 mu2 值,而不使其成为常规参数。
    • 另外,在看了 stan 手册之后,我想做的类似于包含一个已知测量误差的常数。
    • 知道了;谢谢。事实证明,一种在每次迭代中获取一个样本的方法(尽管并不简单);我已经相应地更新了帖子。
    • 在 JAGS 中,您可以通过将随机节点定义放在 data{} 块而不是 model{} 块中来获得一个整体的平局(不是通过迭代)。 Here 是关于如何在 JAGS 中进行的讨论,尽管从计算的角度来看它非常不令人满意。
    猜你喜欢
    • 2014-03-03
    • 2014-06-06
    • 2018-03-25
    • 2018-06-23
    • 2018-05-30
    • 1970-01-01
    • 2022-11-28
    • 1970-01-01
    • 2011-07-31
    相关资源
    最近更新 更多