Hello Bob and all,
I feel like something of a broken record... Because I keep running into similar problems: inefficiency!
I have successfully fit a multi-level Rasch model with regressions at multiple levels, and it worked extremely well. I employed all of the suggestions I have received thus far, including vectorizing my code, separating out the calculation of means as transformed parameters versus including them in sampling statements, using the if_else construction with design variables instead of relying on matrix calculations, and adjusting my priors.
My next step is to fit a 2 PL model. My concern is that while I have code that runs, it is frustratingly inefficient. All of the examples that I have seen and reviewed on this site, in assorted documentation, etc. parameterize a 2 PL model by specifying some version of vector[2] xi[K]; where xi is the vector of alpha-beta pairs over all of K items.
When this specification is extended into multiple levels, unfortunately, the model becomes... Really bogged down. My code successfully runs, but extremely slowly, and I'm looking for any suggestions as to how I can vectorize my code.
The problem that I seem to be running into is that I'm permitting the alphas and betas to be correlated. Right now, each alpha beta pair is drawn from a normal distribution with some (group-specific) mean vector and (group-specific) correlation. Unfortunately, when I attempt to vectorize this portion over the code so that I don't have to loop over items, I run into errors with the types of data structures permitted with multi_normal_cholesky. Also, because xi is a vector of length two in an array the length of all the items, I am also having difficulty vectorizing at level 1, and so I'm looping over observations.
Please help? I'm wondering if folks can suggest something - or if my weeks of working on this just means I'm too close to the problem and can't see the forest through the trees.
My model code is attached, and also pasted below.
Thanks so much,
Shauna
model<- "
data {
int<lower=1> N; // number of responses
int<lower=1> J; // number of students
int<lower=1> K; // number of items
int<lower=1> P; // number of families
int<lower=1> M; // number of item models
int peeps[N]; // student giving response n
int<lower=1> item[N]; // item for response n
int<lower=0,upper=1> score[N]; // correctness for response n
int<lower=0,upper=J> pid[J]; // Person ID number
int<lower=0,upper=K> iid[K]; // Item ID number
int<lower=0,upper=P> fid[P]; // family ID number
int<lower=0,upper=M> mid[M]; // item model ID
int<lower=1,upper=P> parent[K]; //indexes items to families
int<lower=1,upper=M> imodel[P]; //indexes families to item models
int<lower=0,upper=1> X1[P];
int<lower=0,upper=1> X2[P];
}
transformed data{
real noval;
noval<-0;
}
parameters {
vector[J] theta; // abilities
vector[2] mum[M];
vector[2] mup[P];
real bxx1a;
real bxx1b;
real bxx2a;
real bxx2b;
vector<lower=0>[2] taup[P]; // vector for family-level alpha/beta residual sds
vector<lower=0>[2] taui; // vector for alpha/beta residual sds
cholesky_factor_corr[2] L_Omega; // cholesky factorization of residual correlation matrix
vector[2] xi[K]; // alpha/beta pair vectors
}
transformed parameters {
vector[2] mupm[P]; //parent means
for (p in 1:P){
mupm[p,1]<- mum[imodel[p],1] + if_else(X1[p], bxx1a, noval) + if_else(X2[p], bxx2a, noval);
mupm[p,2]<- mum[imodel[p],2] + if_else(X1[p], bxx1b, noval) + if_else(X2[p], bxx2b, noval);
}
}
model {
mum[1,1] ~ normal(0,10);
mum[2,1] ~ normal(0,10);
mum[3,1] ~ normal(0,10);
mum[1,2] ~ normal(0,10);
mum[2,2] ~ normal(0,10);
mum[3,2] ~ normal(0,10);
bxx1a ~ normal(0,10);
bxx1b ~ normal(0,10);
bxx2a ~ normal(0,10);
bxx2b ~ normal(0,10);
taup[1] ~ inv_gamma(.1,.1);
taup[2] ~ inv_gamma(.1,.1);
taui[1] ~ inv_gamma(.1,.1);
taui[2] ~ inv_gamma(.1,.1);
L_Omega ~ lkj_corr_cholesky(4);
theta ~ normal(0, 1);
for (p in 1:P){
mup[p,1] ~ normal(mupm[p,1], taup[p,1]);
mup[p,2] ~ normal(mupm[p,2], taup[p,2]);
}
for (k in 1:K){
xi[k] ~ multi_normal_cholesky(mup[parent[k]], diag_pre_multiply(taui,L_Omega));
}
for (n in 1:N){
score[n] ~ bernoulli_logit(xi[item[n],1] * (theta[peeps] - xi[item[n],2]));
}
}
generated quantities {
corr_matrix[2] Omega;
vector[K] alpha;
vector[K] beta;
vector[N] log_lik;
real log_lik0;
real dev;
Omega <- multiply_lower_tri_self_transpose(L_Omega);
dev <- 0;
for (n in 1:N){
log_lik[n] <- bernoulli_logit_log(score[n], xi[item[n],1] * (theta[peeps[n]] - xi[item[n],2]));
dev <- dev + (-2 * log_lik[n]);
}
log_lik0 <- sum(log_lik); // global log-likelihood
for (k in 1:K) {
alpha[k] <- exp(xi[k,1]);
beta[k] <- xi[k,2];
}
}
"