data { int<lower=1> N; // number of total observations int<lower=0> Pixels[N]; // pixel count data (object area from a 640x480 img) int<lower=1> J; // number of dyads observed int<lower=1> I; // number of objects used int<lower=1> K; // number of effects from design matrix matrix[N, K] X; // population predictor (design) matrix matrix[N, K*J] Z_dyad; // dyad predictor matrix matrix[N, K*I] Z_obj; // object predictor matrix vector<lower=0>[N] Exposure; // exposure (offset) real sig_T; // sigma truncation = 3 * residual sd using mean (~ 15) real b_T; // beta truncation = log(10 x image area) (~ 17)}transformed data { real sigma_T; // truncated cauchy - upper limit vector[K] mu_zeros; // group means for level 2 betas (all zeros)
sigma_T <- atan(sig_T); mu_zeros <- rep_vector(0.0, K); // used for both grouping factors since estimating same K set of effects}parameters { real<lower=-b_T, upper=b_T> beta_mu; // prior mean for all population beta parameters real<lower=0, upper=sigma_T> sigma_pop_unif; // prior sd for all population beta parameters vector<lower=-b_T, upper=b_T> [K] beta_pop_raw; // matt trick adjustment for each population beta parameter matrix<lower=-b_T, upper=b_T> [K, J] beta_dyad_raw; // group effect beta parameters (dyad errors) matrix<lower=-b_T, upper=b_T> [K, I] beta_obj_raw; // group effect beta parameters (object errors) vector<lower=0, upper=sigma_T>[K] sigma_dyad_unif; // group effect sd uniform (for cauchy) vector<lower=0, upper=sigma_T>[K] sigma_obj_unif; // group effect sd uniform (for cauchy) corr_matrix[K] Rho_dyad; // dyad corr matrix corr_matrix[K] Rho_obj; // obj corr matrix real<lower=0, upper=sigma_T> epsilon_unif; // sd epsilon (for overdispersion) vector<lower=-b_T, upper=b_T> [N] epsilon_raw; // matt trick adjust for each residual real<lower=0, upper=1> theta; // probability of zero count}transformed parameters { matrix[K, J] mu_d; // temp matrix dyad (for reshaping matrix to vec) matrix[K, I] mu_o; // temp matrix object (for reshaping matrix to vec) vector[K] sigma_dyad; // final dyad sd for corr matrix vector[K] sigma_obj; // final object sd for corr matrix vector<lower=-b_T, upper=b_T>[K*J] Beta_dyad; // final dyad parameters vector<lower=-b_T, upper=b_T>[K*I] Beta_object; // final object parameters vector<lower=-b_T, upper=b_T>[K] Beta_mu; // final tranformed population Beta means
for (k in 1:K) { sigma_dyad[k] <- tan(sigma_dyad_unif[k]); // cauchy reparameterize sigma_obj[k] <- tan(sigma_obj_unif[k]); mu_d[k] <- row(beta_dyad_raw, k) - mean(beta_dyad_raw[k]); mu_o[k] <- row(beta_obj_raw, k) - mean(beta_obj_raw[k]); }
Beta_dyad <- to_vector(mu_d); Beta_object <- to_vector(mu_o); Beta_mu <- beta_mu + tan(sigma_pop_unif) * beta_pop_raw;}model { matrix[K, K] S_d; // diagonal sd matrix dyad matrix[K, K] S_o; // diagonal sd matrix object matrix[K, K] L_d; // chol decomp of corr matrix for dyad matrix[K, K] L_o; // chol decomp of corr matrix for obj vector[N] epsilon_raw_adj; // uncentered residuals vector[N] epsilon_i; // residuals centered at zero vector[N] lambda; // predicted count
// implicit: Rho_dyad ~ lkj_corr(1.0) // implicit: Rho_obj ~ lkj_corr(1.0) // implicit: sigma_pop_unif ~ uniform(0, sigma_T); // implicit: sigma_dyad_unif ~ uniform(0, sigma_T); // implicit: sigma_obj_unif ~ uniform(0, sigma_T); // implicit: epsilon_unif ~ uniform(0, sigma_T); // implicit: theta ~ uniform(0, 1);
epsilon_raw ~ normal(0, 1); beta_mu ~ normal(0, 5); beta_pop_raw ~ normal(0, 1);
S_d <- diag_matrix(sigma_dyad); L_d <- cholesky_decompose(Rho_dyad); S_o <- diag_matrix(sigma_obj); L_o <- cholesky_decompose(Rho_obj); for (j in 1:J) { col(beta_dyad_raw, j) ~ multi_normal_cholesky(mu_zeros, S_d * L_d); }
for (i in 1:I) { col(beta_obj_raw, i) ~ multi_normal_cholesky(mu_zeros, S_o * L_o); }
epsilon_raw_adj <- tan(epsilon_unif) * epsilon_raw; epsilon_i <- epsilon_raw_adj - mean(epsilon_raw_adj);
lambda <- Exposure .* exp(X * Beta_mu + Z_dyad * Beta_dyad + Z_obj * Beta_object + epsilon_i);
for (n in 1:N) { if (Pixels[n] == 0) { increment_log_prob(log_sum_exp(bernoulli_log(1, theta), bernoulli_log(0, theta) + poisson_log(Pixels[n], lambda[n])));
} else { increment_log_prob(bernoulli_log(0, theta) + poisson_log(Pixels[n], lambda[n]));
} }}generated quantities { real epsilon;
epsilon <- tan(epsilon_unif);}I don't understand the model's parameterization as is, but
maybe someone else will have a clue. In particular, I'm not
sure what's going on with all the centering you're doing,
such as
mu_d[k] <- row(beta_dyad_raw, k) - mean(beta_dyad_raw[k]);
mu_o[k] <- row(beta_obj_raw, k) - mean(beta_obj_raw[k]);
epsilon_i <- epsilon_raw_adj - mean(epsilon_raw_adj);
Another approach I've used for this in large IRT models that
aren't inherently identified is to give the epsilon_raw_adj
a prior centered at 0 (the uniform does this, but it doesn't favor
a centered result).
Does the model at least converge to a reasonable estimate?
And does it converge to the same place from multiple starting
points? If so, does it converge well before the warmup phase
is over? Adaptation needs to spend some time after convergence
before it can estimate the scales properly.
Is there any way to start with a simpler model and work up
to the complex model you posted? That should let you both
assess whether the model's getting reasonable results and
as you add more complexity, put your finger on where the poor
mixing arises.
If there are only a few hundred parameters, you could also
try a dense mass matrix --- it could be that there's not a reasonable
way to scale all the parameters without a rotation that makes sense.
You only have uniform-in-an-interval priors on many of
the parameters --- is the range reasonable given what you know
about the problem? Adding something more peaked in the way of
priors can help with mixing. Also, initialization (assuming
you don't initialize yourself) will be centered at the center
of the intervals specified as constraints.
If you hit the maximum tree depth, that can be for two reasons.
One is that you just turned the step size down so low due to
jitter that it goes out to the max depth without making a U-turn.
The other is that you really need a small step size because of
high curvature in the posterior or because you're way out in
a tail and have to go a long way before turning around.
To make the model more efficient, I'd suggest not making a diagonal
matrix S_d, but doing the scaling in a loop that's *outside* the
multi_normal_cholesky loop.
Another efficiency issue is in the exp for lambda. This can
be problematic numerically, and we provide an alternative.
Instead of
lambda <- Exposure .* exp(X * Beta_mu + Z_dyad * Beta_dyad + Z_obj * Beta_object + epsilon_i);
and then
poisson_log(Pixels[n], lambda[n]))
you can set
lambda <- log(Exposure) + X * ....
and then use:
poisson_log_log(Pixels[n], lambda[n])
The possion_log distribution takes its parameter on the log scale and does
the exponentiation implicitly inside, which is both more efficient and
more arithmetically stable.
> mu_d[k] <- row(beta_dyad_raw, k) - mean(beta_dyad_raw[k]);
>
> mu_o[k] <- row(beta_obj_raw, k) - mean(beta_obj_raw[k]);
>
> epsilon_i <- epsilon_raw_adj - mean(epsilon_raw_adj);
By identified here, I really just meant are there different
values of the parameters that lead to the same transformed parameter
vector?
Although the simplex is constrained to positive
values that sums to 1, (beta_ratios - 1.0 / K) is
a set of values that sums to 0 and whose absolute
values sum to 1. It's only going to be non-negative
in the symmetric case.
For example, if you look at the simplex (0.3, 0.7),
then (0.3, 0.7) - 0.5 = (-0.2, 0.2).
In this particular case, we're going to put a prior on the parameters, not
on the transformed parameters. That's what I meant by it being a bit odd
because of the asymmetry.