Реализация экспоненциальной генеральной линейной модели в Стане / Рустане - PullRequest
0 голосов
/ 05 июня 2018

У меня есть следующая модель:

enter image description here

Буду признателен, если кто-нибудь сможет продемонстрировать, как реализовать эту модель.


Исследование и попытка

В качестве примера я посмотрел на следующий Пуассоновский GLM:

enter image description here

```{stan output.var="PoissonGLMQR"}
  data{
    int<lower=1> n;     // number of observations
    int<lower=1> p;     // number of predictors
    matrix[n, p] x;     // design matrix
    int<lower=0> y[n];  // responses
  }

  transformed data{
    matrix [n, p] Q_ast;
    matrix [p, p] R_ast;
    matrix [p, p] R_ast_inverse;
    Q_ast = qr_Q(x)[,1:p] * sqrt(n - 1.0);
    R_ast = qr_R(x)[1:p,] / sqrt(n - 1.0);
    R_ast_inverse = inverse(R_ast);
  }

  parameters{
    vector[p] theta;  // regression coefficients for predictors
  }

  transformed parameters{
    vector[p] beta;
    vector[n] mu;
    mu = exp(Q_ast*theta);
    beta = R_ast_inverse * theta;
  }

  model{
     y  ~ poisson(mu);
  }
```

Я понимаю, что в этом примере использовалась репараметризация QR (см. Раздел 9.2 справочного руководства Stan).Однако, так как я новичок в Stan, я нахожу это довольно пугающим, и я не уверен, как реализовать экспоненциальный GLM таким же образом.Нужно ли даже использовать репараметризацию QR для экспоненциального случая?

В любом случае, моя наивная попытка экспоненциального GLM заключается в следующей адаптации пуассоновского кода GLM:

```{stan output.var="ExponentialGLM"}
  data{
    int<lower=1> n;     // number of observations
    int<lower=1> p;     // number of predictors
    matrix[n, p] x;     // design matrix
    real<lower=0> y[n];  // responses
  }

  transformed data{
    matrix [n, p] Q_ast;
    matrix [p, p] R_ast;
    matrix [p, p] R_ast_inverse;
    Q_ast = qr_Q(x)[,1:p] * sqrt(n - 1.0);
    R_ast = qr_R(x)[1:p,] / sqrt(n - 1.0);
    R_ast_inverse = inverse(R_ast);
  }

  parameters{
    vector[p] theta;  // regression coefficients for predictors
  }

  transformed parameters{
    vector[p] beta;
    vector[n] mu;
    mu = exp(Q_ast*theta);
    beta = (R_ast_inverse * theta);
  }

  model{
     y  ~ exponential(mu);
  }
```

Но,как я уже сказал, я совершенно не уверен, что это правильный путь для кодирования такой модели в Stan.Все, что я сделал, это просто попытался приспособить пример Пуассона GLM к вышеупомянутой экспоненциальной GLM.


Я был бы признателен, если бы кто-то мог продемонстрировать экспоненциальную GLM и, если это не слишком много проблем,пожалуйста, найдите время, чтобы уточнить мои неправильные представления выше.


Пример данных

    conc  time
 1   6.1   0.8
 2   4.2   3.5
 3   0.5  12.4
 4   8.8   1.1
 5   1.5   8.9
 6   9.2   2.4
 7   8.5   0.1
 8   8.7   0.4
 9   6.7   3.5
10   6.5   8.3
11   6.3   2.6
12   6.7   1.5
13   0.2  16.6
14   8.7   0.1
15   7.5   1.3

1 Ответ

0 голосов
/ 06 июня 2018

Как насчет чего-то подобного?

  1. Мы начнем с чтения в примере данных.

    df <- read.table(text =
        "    conc  time
     1   6.1   0.8
     2   4.2   3.5
     3   0.5  12.4
     4   8.8   1.1
     5   1.5   8.9
     6   9.2   2.4
     7   8.5   0.1
     8   8.7   0.4
     9   6.7   3.5
    10   6.5   8.3
    11   6.3   2.6
    12   6.7   1.5
    13   0.2  16.6
    14   8.7   0.1
    15   7.5   1.3", header = T);
    
  2. Мы определим модель Стэна, как простая гамма (экспоненциальная) модель с лог-связью.

    model <- "
    data {
        int N;                                       // Number of observations
        int K;                                       // Number of parameters
        real y[N];                                   // Response
        matrix[N,K] X;                               // Model matrix
    }
    
    parameters {
        vector[K] beta;                              // Model parameters
    }
    
    transformed parameters {
        vector[N] lambda;
        lambda = exp(-X * beta);                     // log-link
    }
    
    model {
        // Priors
        beta[1] ~ cauchy(0, 10);
        for (i in 2:K)
            beta[i] ~ cauchy(0, 2.5);
    
        y ~ exponential(lambda);
    }
    "
    

    Здесь я предполагаю (стандартный) слабоинформативный арифметику Коши по параметрам бета-модели.

  3. Теперь мы подгоняем модель к данным.

    library(rstan);
    options(mc.cores = parallel::detectCores());
    rstan_options(auto_write = TRUE);
    
    X <- model.matrix(time ~ conc, data = df);
    model.stan <- stan(
        model_code = model,
        data = list(
            N = nrow(df),
            K = ncol(X),
            y = df$time,
            X = X),
        iter = 4000);
    
  4. Для сравнения точечных оценок мы также подгоняем Gamma GLM к даннымс использованием glm.

    model.glm <- glm(time ~ conc, data = df, family = Gamma(link = "log"));
    
  5. Оценки параметров модели Стэна равны

    summary(model.stan, "beta")$summary;
    #              mean     se_mean         sd       2.5%        25%        50%
    #beta[1]  2.9371457 0.016460000 0.62017934  1.8671652  2.5000356  2.8871936
    #beta[2] -0.3099799 0.002420744 0.09252423 -0.5045937 -0.3708111 -0.3046333
    #               75%      97.5%    n_eff     Rhat
    #beta[1]  3.3193334  4.3078478 1419.629 1.002440
    #beta[2] -0.2461567 -0.1412095 1460.876 1.002236        
    

    Оценки параметров модели GLM равны

    summary(model.glm)$coef;
    #              Estimate Std. Error   t value     Pr(>|t|)
    #(Intercept)  2.8211487 0.54432115  5.182875 0.0001762685
    #conc        -0.3013355 0.08137986 -3.702827 0.0026556952
    

    Мы видим хорошее согласие между оценками точек Стэна для средних значений и оценками параметров Gamma-GLM.

  6. Мы строим оценки параметров из моделей Стэна и glm.

    library(tidyverse);
    as.data.frame(summary(model.stan, "beta")$summary) %>%
        rownames_to_column("Parameter") %>%
        ggplot(aes(x = `50%`, y = Parameter)) +
        geom_point(size = 3) +
        geom_segment(aes(x = `2.5%`, xend = `97.5%`, yend = Parameter), size = 1) +
        geom_segment(aes(x = `25%`, xend = `75%`, yend = Parameter), size = 2) +
        labs(x = "Median (plus/minus 95% and 50% CIs)") +
        geom_point(
            data = data.frame(summary(model.glm)$coef, row.names = c("beta[1]", "beta[2]")) %>%
                rownames_to_column("Parameter"),
            aes(x = Estimate, y = Parameter),
            colour = "red", size = 4, shape = 1)
    

    enter image description here

    glm оценки показаны красным.


Обновление (модель Fit Stan с использованием QRповторная параметризация)

  1. Модель Стэна с повторной параметризацией QR.

    model.QR <- "
    data {
        int N;                                       // Number of observations
        int K;                                       // Number of parameters
        real y[N];                                   // Response
        matrix[N,K] X;                               // Model matrix
    }
    
    transformed data {
        matrix[N, K] Q;
        matrix[K, K] R;
        matrix[K, K] R_inverse;
        Q = qr_Q(X)[, 1:K];
        R = qr_R(X)[1:K, ];
        R_inverse = inverse(R);
    }
    
    parameters {
        vector[K] theta;
    }
    
    transformed parameters {
        vector[N] lambda;
        lambda = exp(-Q * theta);                     // log-link
    }
    
    model {
        y ~ exponential(lambda);
    }
    
    generated quantities {
        vector[K] beta;                              // Model parameters
        beta = R_inverse * theta;
    }
    "
    

    В разложении QR мы имеем lambda = exp(- X * beta) = exp(- Q * R * beta) = exp(- Q * theta), где theta = R * beta и, следовательно,beta = R^-1 * theta.

    Обратите внимание, что мы не указываем никаких приоров для тета;в Stan это по умолчанию плоские (то есть однородные) априоры.

  2. Подгонка модели Стэна к данным.

    library(rstan);
    options(mc.cores = parallel::detectCores());
    rstan_options(auto_write = TRUE);
    
    X <- model.matrix(time ~ conc, data = df);
    model.stan.QR <- stan(
        model_code = model.QR,
        data = list(
            N = nrow(df),
            K = ncol(X),
            y = df$time,
            X = X),
        iter = 4000);
    
  3. Оценка параметров

    summary(model.stan.QR, "beta")$summary;
    #              mean     se_mean         sd       2.5%        25%        50%
    #beta[1]  2.9637547 0.009129250 0.64383609  1.8396681  2.5174800  2.9194682
    #beta[2] -0.3134252 0.001321584 0.09477156 -0.5126905 -0.3740475 -0.3093937
    #               75%      97.5%    n_eff      Rhat
    #beta[1]  3.3498585  4.3593912 4973.710 0.9998545
    #beta[2] -0.2478029 -0.1395686 5142.408 1.0003236
    

    Вы можете видеть, что оценки параметров между двумя моделями Стэна (с и без повторной параметризации QR) очень хорошо согласуются.

    Если бы вы спросили меня, делает ли повторная параметризация QR(большая / любая) разница, я бы сказал «вероятно, не в этом случае»;Эндрю Гельман и другие часто подчеркивали, что использование даже очень слабоинформативных априоров поможет конвергенции и должно быть предпочтительным по сравнению с плоскими (одинаковыми) априорами;Я бы всегда старался использовать слабоинформативные априоры по всем параметрам и начинать с модели без повторной параметризации QR;если конвергенция плохая, я бы попытался оптимизировать свою модель на следующем этапе.

...