Hello all,
I have a series of survey responses that I wish to fit into a latent variable model of the following form (using python 3.10.5/PyStan v. 3.5.0):
- all
I
survey respondents were asked one common item (with responses on aQ
class Likert scale). I aim to model this component of my data using an ordinal logit model, with the latent classz
and logit cutpointsc_shared
. My features are labeled asalpha_*
(individual-level) andgamma
(group-level), and are fit using non-centered parametrization. - Respondents were additionally asked up to
N
additional items (with responses on anR
class Likert scale), with each items belonging to one ofM
“topics”, constitutingJ
item-response pairs. Each of theI
respondents was asked at least one survey item from each of theM
topics. I aim to model this component of my data using an ordinal IRT model, with responses to itemN = n
modeled using latent variablet_m
corresponding to topicm[n]
and item-specific cut-pointsc_n
. Features in this model are represented usingbeta_*
, and fit using non-centered parameterization. - Lastly, I wish to model individual covariance across latent vector
z
and theM
latent vectorst_m
. I aim to use non-centered parametrization for my covariance matrix, with Cholesky factor matrixL_Omega
and scale vectortau
.
My model is currently specified as follows:
data {
// Metadata
int<lower=0> I; // Number of Interviews
int<lower=0> J; // Number of Topic-Question Item-Response Pairs
int<lower=0> M; // Number of Item Classes/Latent Issue Dimensions
int<lower=0> N; // Number of Survey Items
int<lower=0> Q; // Number of Likert Response Punches for Core Question
int<lower=0> R; // Number of Likert Response Punches for Topic-Question Items
// Variable Level Declarations
int<lower=0> N_a;
int<lower=0> N_b;
int<lower=0> N_c;
int<lower=1> N_d;
// Individual-Level Predictors
array[I] int<lower=1,upper=N_a> a;
array[I] int<lower=1,upper=N_b> b;
array[I] int<lower=1,upper=N_c> c;
array[I] int<lower=1,upper=N_d> d;
// Group-Level Predictors
vector[N_d] u;
array[I] int<lower=1,upper=Q> y_shared; // Core Item Responses
// Topic-Level Items
/// Item-Level Lookup Metadata
array[N] int<lower=1,upper=M> m_n; // Additional Item Topic Lookup
array[J] int<lower=1,upper=I> i_j; // Response-Level Respondent ID
array[J] int<lower=1,upper=N> n_j; // Response-Level Item ID
array[J] int<lower=1,upper=R> y_n_j; // Additional Item Responses
}
parameters {
// Target Cutpoints
ordered[Q - 1] c_shared; // Core Item
array[N] ordered[R - 1] c_n; // Additional Items
// Core Item
// Hyperpriors
///Prior Uncertainties for Varying Intercepts
real<lower=0> sigma_alpha_a;
real<lower=0> sigma_alpha_b;
real<lower=0> sigma_alpha_c;
real<lower=0> sigma_alpha_d;
/// Prior Uncertainty for Group-Level Slope
real<lower=0> sigma_gamma;
// Modeled Effects
/// Varying Intercepts
vector[N_a] alpha_a_raw;
vector[N_b] alpha_b_raw;
vector[N_c] alpha_c_raw;
vector[N_d] alpha_d_raw;
/// Group Level Slope
real gamma_raw;
// Topic-Level Additional Items
// Hyperprior Uncertainties for Varying Intercepts
vector<lower=0>[M] sigma_beta_a;
vector<lower=0>[M] sigma_beta_b;
vector<lower=0>[M] sigma_beta_c;
vector<lower=0>[M] sigma_beta_d;
// Varying Intercepts
array[M] vector[N_a] beta_a_raw;
array[M] vector[N_b] beta_b_raw;
array[M] vector[N_c] beta_c_raw;
array[M] vector[N_d] beta_d_raw;
// Correlated Error Parameters
/// Hyperpriors
vector<lower=0>[3] tau; // prior scale
cholesky_factor_corr[3] L_Omega; // factored prior covariance
/// Raw Variable Vector of tau * L_Omega product for Non-Centered Parametrization
vector[3] tau_L_Omega_raw;
}
transformed parameters{
// Core Item
// Varying Intercepts
vector[N_a] alpha_a;
vector[N_b] alpha_b;
vector[N_c] alpha_c;
vector[N_d] alpha_d;
// Group Level Slopes
real gamma;
// Non-Centered Parametrization for Intercepts and Slopes
alpha_d = alpha_d_raw * sigma_alpha_d;
alpha_a = alpha_a_raw * sigma_alpha_a;
alpha_b = alpha_b_raw * sigma_alpha_b;
alpha_c = alpha_c_raw * sigma_alpha_c;
gamma = gamma_raw * sigma_gamma;
// Additional Items
// Varying Intercepts
array[M] vector[N_a] beta_a;
array[M] vector[N_b] beta_b;
array[M] vector[N_c] beta_c;
array[M] vector[N_d] beta_d;
// Non-Centered Parametrization for Varying Intercepts
/// Loop Over Latent Topics to vectorize Multilevel Intercepts
for (m in 1:M){
beta_a[m] = beta_a_raw[m] * sigma_beta_a[m];
beta_b[m] = beta_b_raw[m] * sigma_beta_b[m];
beta_c[m] = beta_c_raw[m] * sigma_beta_c[m];
beta_d[m] = beta_d_raw[m] * sigma_beta_d[m];
}
// Cholesky Factored Covariance Term
vector[3] tau_L_Omega;
tau_L_Omega = diag_pre_multiply(tau, L_Omega) * tau_L_Omega_raw;
}
model {
// Core Survey Item
/// Prior Uncertainties
sigma_alpha_a ~ std_normal();
sigma_alpha_b ~ std_normal();
sigma_alpha_c ~ std_normal();
sigma_alpha_d ~ std_normal();
sigma_gamma_support ~ std_normal();
/// Varying Intercepts
alpha_a_raw ~ std_normal();
alpha_b_raw ~ std_normal();
alpha_c_raw ~ std_normal();
alpha_d_raw ~ std_normal();
/// Group Level Effects
gamma_raw ~ std_normal();
// Additional Items
/// Prior Uncertainties
sigma_beta_a ~ std_normal();
sigma_beta_b ~ std_normal();
sigma_beta_c ~ std_normal();
sigma_beta_d ~ std_normal();
/// Varying Intercepts
//// Multilevel Intercepts
for (m in 1:M){
beta_a_raw[m] ~ std_normal();
beta_b_raw[m] ~ std_normal();
beta_c_raw[m] ~ std_normal();
beta_d_raw[m] ~ std_normal();
}
// Latent Variable Correlation
tau ~ cauchy(0,2);
L_Omega ~ lkj_corr_cholesky(2);
tau_L_Omega_raw ~ std_normal();
// Declare Local Scope for Latent Variable Vectorization
{
// Vectorized Group-Level Predictions for Individuals
vector[N_d] u_gamma;
u_gamma = u * gamma;
// Declare Core Question Latent Variable z, Latent Topic Variables t_m
vector[I] z;
array[M] vector[I] t_m_i;
vector[J] t_j;
// Vectorize Latent Variables for Core Item, Topics
for (i in 1:I){
z[i] = alpha_a[a[i]] + alpha_b[b[i]] + alpha_c[c[i]] + alpha_d[d[i]] + u_gamma[d[i]] + tau_L_Omega[1];
for (m in 1:M){
t_m_i[m,i] = beta_a[m,a[i]] + beta_b[m,b[i]] + beta_c[m,c[i]] + beta_state[m,d[i]] + tau_L_Omega[m + 1];
}
}
// Reindex Latent Topic Variables from individuals to item-reponse pairs
for (j in 1:J){
t_j[j] = t_m_i[m_n[n_j[j]],i_j[j]];
}
// Fit responses to Core Item
y_shared ~ ordered_logistic(z, c_shared);
// Fit responses to additional items
y_n_j ~ ordered_logistic(t_j, c_n[n_j]);
}
}
As a placeholder, I’ve currently hard-coded parameters associated with the individual-level covariance matrix in a manner that implies a 3 x 3 matrix (i.e M = 2
): I want to confirm that I’m able to make things work in the minimal case (two latent topics t_m
, plus core latent variable z
) before scaling any further.
Model output looks fine without the correlated individual covariance component (i.e. w/o the various tau
/L_Omega
terms). When I add the covariance terms, the output of my regression coefficients (alpha_*
, beta_*
, and gamma_*
and my cut-points (c_shared
and c_n
) remain reasonable. Moreover, all the tau
and L_Omega
terms appear to have converged (with stable trace-plots for these parameters, as well as R_hat
values around 1.0
).
However, when I try to reverse-engineer my covariance matrix by multiplying L_Omega
by its transpose, I recover a matrix that does not have unit entries on its diagonal:
[1. , 0. , 0. ],
[0. , 0.8443724 , 0. ],
[0. , 0. , 0.65253416]]
The above values are generated by calculating for each of my 4,000 non-discarded training samples and then averaged across the samples, as I first became suspicious when I used the averaged values for the entries L_Omega
and saw this counterintuitive output.
Notwithstanding the fact that the data is displaying no individual-level correlations across my three latent dimensions (if this is what the data has to tell me, so be it), the fact that the diagonal entries of my correlation matrix are not all equal to 1
suggests to me that something has gone wrong in my specification. I’m not sure whether I may have implemented the non-centered parametrization of the Cholesky-factored correlation matrix incorrectly here, or else whether something different might be throwing things off.
Appreciate any guidance on steps I might be able to take to rectify things here.