ZIP - 隐马尔可夫模型 r Stan

ZIP - Hidden Markov model r Stan

我正在尝试与 Stan 一起调整零膨胀泊松隐马尔可夫模型。对于过去论坛中的 Poisson-HMM,显示了此设置。 .

同时调整 ZIP 与经典理论有据可查的代码和模型。


邮编
library(ziphsmm)
set.seed(123)
prior_init <- c(0.5,0.5)
emit_init <- c(20,6)
zero_init <- c(0.5,0)
tpm <- matrix(c(0.9, 0.1, 0.2, 0.8),2,2,byrow=TRUE)
result <- hmmsim(n=100,M=2,prior=prior_init, tpm_parm=tpm,emit_parm=emit_init,zeroprop=zero_init)
y <- result$series
serie <- data.frame(y = result$series, m = result$state)

fit1 <-  fasthmmfit(y,x=NULL,ntimes=NULL,M=2,prior_init,tpm,
                    emit_init,0.5, hessian=FALSE,method="BFGS", 
                    control=list(trace=1))
fit1
$prior
            [,1]
[1,] 0.997497445
[2,] 0.002502555

$tpm
          [,1]       [,2]
[1,] 0.9264945 0.07350553
[2,] 0.3303533 0.66964673

$zeroprop
[1] 0.6342182

$emit
          [,1]
[1,] 20.384688
[2,]  7.365498

$working_parm
[1] -5.9879373 -2.5340475  0.7065877  0.5503559  3.0147840  1.9968067

$negloglik
[1] 208.823
斯坦
library(rstan)

ZIPHMM <- 'data {
    int<lower=0> N;
    int<lower=0> y[N];
    int<lower=1> m;
  }

parameters {
    real<lower=0, upper=1> theta; //
    positive_ordered[m] lambda; //
    simplex[m] Gamma[m]; // tpm
  }

model {
  vector[m] log_Gamma_tr[m];
  vector[m] lp;
  vector[m] lp_p1;

  // priors
  lambda ~ gamma(0.1,0.01);
  theta ~ beta(0.05, 0.05);

  // transposing tpm and taking the log of each entry
  for(i in 1:m)
    for(j in 1:m)
      log_Gamma_tr[j, i] = log(Gamma[i, j]);

  lp = rep_vector(-log(m), m); // 

    for(n in 1:N) {
      for(j in 1:m){
        if (y[n] == 0)
          lp_p1[j] = log_sum_exp(log_Gamma_tr[j] + lp) +
                     log_sum_exp(bernoulli_lpmf(1 | theta),
                     bernoulli_lpmf(0 | theta) + poisson_lpmf(y[n] | lambda[j]));
        else
          lp_p1[j] = log_sum_exp(log_Gamma_tr[j] + lp) + 
                     bernoulli_lpmf(0 | theta) + 
                     poisson_lpmf(y[n] | lambda[j]);
                   }
      lp = lp_p1;
                  }
    target += log_sum_exp(lp);
}'
mod_ZIP <- stan(model_code = ZIPHMM, data=list(N=length(y), y=y, m=2), iter=1000, chains=1)
print(mod_ZIP,digits_summary = 3)
               mean se_mean    sd     2.5%      25%      50%      75%    97.5% n_eff  Rhat
theta         0.518   0.002 0.052    0.417    0.484    0.518    0.554    0.621   568 0.998
lambda[1]     7.620   0.039 0.787    6.190    7.038    7.619    8.194    9.132   404 1.005
lambda[2]    20.544   0.039 0.957   18.861   19.891   20.500   21.189   22.611   614 1.005
Gamma[1,1]    0.664   0.004 0.094    0.473    0.604    0.669    0.730    0.841   541 0.998
Gamma[1,2]    0.336   0.004 0.094    0.159    0.270    0.331    0.396    0.527   541 0.998
Gamma[2,1]    0.163   0.003 0.066    0.057    0.114    0.159    0.201    0.312   522 0.999
Gamma[2,2]    0.837   0.003 0.066    0.688    0.799    0.841    0.886    0.943   522 0.999
lp__       -222.870   0.133 1.683 -227.154 -223.760 -222.469 -221.691 -220.689   161 0.999
真值
real = list(tpm = tpm, 
     zeroprop = nrow(serie[serie$m == 1 & serie$y == 0, ]) / nrow(serie[serie$m == 1,]),
     emit = t(t(tapply(serie$y[serie$y != 0],serie$m[serie$y != 0], mean))))
real
$tpm
     [,1] [,2]
[1,]  0.9  0.1
[2,]  0.2  0.8

$zeroprop
[1] 0.6341463

$emit
       [,1]
1 20.433333
2  7.277778

估计给某人很奇怪可以帮助我知道我做错了。正如我们看到的,stan zeroprop 的估计值 = 0.518,而实际值为 0.634,另一方面,t.p.m 的值。在 stan 中,它们相距很远,lambda1 = 7.62 和 lambda2 = 20.54 的平均值虽然它们足够接近,但与实际的 20.43 和 7.27 的顺序不同。我想我在 Stan 中定义模型时犯了一些错误,但我不知道是哪个。

虽然我不知道 ZIP-HMM 拟合算法的内部工作原理,但您在 Stan 模型中实现的内容与 ZIP-HMM 优化算法对自身的描述存在一些明显差异。解决这些似乎足以产生类似的结果。

模型之间的差异

初始状态概率

ZIP-HMM 估计的值,特别是 fit1$prior,表明它具有学习初始状态概率的能力。然而,在 Stan 模型中,这被固定为 1:1

lp = rep_vector(-log(m), m);

这应该更改为允许模型估计初始状态。

参数先验(可选)

Stan 模型在 lambdatheta 上有不平坦的先验,但大概 ZIP-HMM 没有加权它到达的特定值。如果想更真实地模仿 ZIP-HMM,那么平坦的先验会更好。然而,在 Stan 中拥有非平坦先验的能力确实是一个机会,可以开发比标准 HMM 推理算法更优化的模型。

零-Inflation 状态 1

来自 fasthmmfit 方法的文档

Fast gradient descent / stochastic gradient descent algorithm to learn the parameters in a specialized zero-inflated hidden Markov model, where zero-inflation only happens in State 1. [emphasis added]

Stan 模型假设所有状态为零inflation。这可能是估计的 theta 值相对于 ZIP-HMM MAP 估计值缩小的原因。

状态排序

在 Stan 中估计离散潜在状态或集群时,可以使用 有序向量 作为 mitigate against label switching issues 的技巧。这是通过

有效实现的
positive_ordered[m] lambda;

但是,由于 ZIP-HMM 在第一个状态只有零-inflation,因此在 Stan 中正确实现此行为需要事先了解 lambda 的等级是 "first"状态。这对于泛化这段代码来说似乎很有问题。现在,假设我们总能以某种方式恢复这些信息,让我们继续前进。在这种特定情况下,我们将假设 HMM 中的状态 1 具有更高的 lambda 值,因此将是 Stan 模型中的状态 2。

更新了 Stan 模型

在模型中加入上述变化应该是这样的

斯坦模型

data {
  int<lower=0> N;    // length of chain
  int<lower=0> y[N]; // emissions
  int<lower=1> m;    // num states
}

parameters {
  simplex[m] start_pos;         // initial pos probs
  real<lower=0, upper=1> theta; // zero-inflation parameter
  positive_ordered[m] lambda;   // emission poisson params
  simplex[m] Gamma[m];          // transition prob matrix
}

model {
  vector[m] log_Gamma_tr[m];
  vector[m] lp;
  vector[m] lp_p1;

  // transposing tpm and taking the log of each entry
  for (i in 1:m) {
    for (j in 1:m) { 
      log_Gamma_tr[j, i] = log(Gamma[i, j]);
    }
  }

  // initial position log-lik
  lp = log(start_pos);

  for (n in 1:N) {
    for (j in 1:m) {
      // log-lik for state
      lp_p1[j] = log_sum_exp(log_Gamma_tr[j] + lp);

      // log-lik for emission
      if (j == 2) { // assuming only state 2 has zero-inflation
        if (y[n] == 0) {
          lp_p1[j] += log_mix(theta, 0, poisson_lpmf(0 | lambda[j]));
        } else {
          lp_p1[j] += log1m(theta) + poisson_lpmf(y[n] | lambda[j]);
        }
      } else {
        lp_p1[j] += poisson_lpmf(y[n] | lambda[j]);
      }
    }
    lp = lp_p1; // log-lik for next position
  }
  target += log_sum_exp(lp);
}

地图估计

将上面的内容作为字符串变量加载code.ZIPHMM,我们首先编译它并运行一个MAP估计(因为MAP估计的行为最像HMM拟合算法):

model.ZIPHMM <- stan_model(model_code=code.ZIPHMM)

// note the use of some initialization on the params,
// otherwise it can occasionally converge to strange extrema
map.ZIPHMM <- optimizing(model.ZIPHMM, algorithm="BFGS",
                         data=list(N=length(y), y=y, m=2),
                         init=list(theta=0.5, lambda=c(5,10)))

检查估计参数

> map.ZIPHMM$par
start_pos[1] start_pos[2]        
9.872279e-07 9.999990e-01 

theta    
6.342449e-01 

lambda[1]    lambda[2]   
7.370525e+00 2.038363e+01 

Gamma[1,1]   Gamma[2,1]   Gamma[1,2]   Gamma[2,2] 
6.700871e-01 7.253215e-02 3.299129e-01 9.274678e-01

表明它们密切反映了 fasthmmfit 推断的值,除了状态顺序被切换。

后验采样

这个模型也可以运行用MCMC来推断一个完整的后验,

samples.ZIPHMM <- stan(model_code = code.ZIPHMM, 
                       data=list(N=length(y), y=y, m=2), 
                       iter=2000, chains=4)

采样良好并产生相似结果(并且没有任何参数初始化)

> samples.ZIPHMM
Inference for Stan model: b29a2b7e93b53c78767aa4b0c11b62a0.
4 chains, each with iter=2000; warmup=1000; thin=1; 
post-warmup draws per chain=1000, total post-warmup draws=4000.

                mean se_mean   sd    2.5%     25%     50%     75%   97.5% n_eff Rhat
start_pos[1]    0.45    0.00 0.29    0.02    0.20    0.43    0.69    0.97  6072    1
start_pos[2]    0.55    0.00 0.29    0.03    0.31    0.57    0.80    0.98  6072    1
theta           0.63    0.00 0.05    0.53    0.60    0.63    0.67    0.73  5710    1
lambda[1]       7.53    0.01 0.72    6.23    7.02    7.49    8.00    9.08  4036    1
lambda[2]      20.47    0.01 0.87   18.83   19.87   20.45   21.03   22.24  5964    1
Gamma[1,1]      0.65    0.00 0.11    0.43    0.57    0.65    0.72    0.84  5664    1
Gamma[1,2]      0.35    0.00 0.11    0.16    0.28    0.35    0.43    0.57  5664    1
Gamma[2,1]      0.08    0.00 0.03    0.03    0.06    0.08    0.10    0.16  5605    1
Gamma[2,2]      0.92    0.00 0.03    0.84    0.90    0.92    0.94    0.97  5605    1
lp__         -214.76    0.04 1.83 -219.21 -215.70 -214.43 -213.43 -212.25  1863    1