I am fitting a tree growth model in two stages, first by fitting a model that predicts potential/maximum growth, then by fitting a model that adds additional covariates to estimate the constraints on potential growth. I accomplish the first model by using quantile regression (using the AsymmetricLaplaceQuantile for likelihood). The posterior for the 3 coefficients of the potential growth model, b_potential
are well-described by a multivariate normal (MVN) distribution. I would like to incorporate this potential growth model into the potential*modifier model, but prevent MCMC or SVI from treating the parameters for the potential growth components as tunable. Is there a way to do this?
In contrast to the potential growth model, which I fit with an Asymmetric Laplace likelihood to model the 95th percentile of growth, I am fitting the potential*modifier model with a Laplace likelihood to predict the mean growth response.
If I simply include the location and covariance_matrix for the MVN describing the coefficients for estimating potential growth in the potential*modifier model as a latent MVN, when I fit the model using SVI or MCMC, it modifies the parameters of the potential growth MVN in way that changes them significantly from their values I initialize them with (which were retrieved by fitting the potential growth model).
I tried defining the mean and covariance matrix of the MVN as observed parameters so that they wouldn’t be modified or sampled during MCMC, and feed these into a latent MVN distribution, but every step of the MCMC is divergent. This is what I tried:
mvn_mu = numpyro.sample("mvn_mu", dist.Uniform(low=pot_loc, high=pot_loc), obs=pot_loc)
mvn_sigma = numpyro.sample("mvn_sigma", dist.Uniform(low=pot_cov, high=pot_cov), obs=pot_cov)
mvn = numpyro.sample("mvn", dist.MultivariateNormal(mvn_mu, mvn_sigma))
I’ve also tried using an MVN without registering it as a sample site (e.g., dist.MultivariateNormal(loc, cov).sample(key)
, but have been unable to figure out how to ensure the key changes each run. It seems like handler.seed
will not inject a seed into an object like this that is not a Pyro primitive, and if I need to instantiate the seed in the model, it’s not clear how to ensure that it changes in subsequent iterations of SVI or MCMC since it always gets pinned again to the original seed and split the same way each iteration. I tried something like this:
class fixed_mvn():
def __init__(self, seed, loc, cov):
self.loc = loc
self.cov = cov
self.key = random.PRNGKey(seed)
def sample(self, sample_shape=()):
self.key, sample_key = random.split(key)
return dist.MultivariateNormal(self.loc, self.cov).sample(sample_key, sample_shape=sample_shape)
and then in the model, use it like this:
def model(X, mvn_loc, mvn_cov, y=None):
mvn = fixed_mvn(42, mvn_loc, mvn_cov)
b_potential = mvn.sample()
potential = b_potential[0] + b_potential[1]*X[1] + b_potential[2]*X[1]**2
...
The seed is always reset to 42 and then split the same way each time the model is run through successive iterations of MCMC or SVI, so I don’t get any variability in the mvn
samples…
I think a generalization of this issue is defining known and irreducible noise in a model. I’m currently just pinning the values of b_potential
to constants (e.g., b_potential = [1.5, 2.5, -0.1]
), but would love to generate estimates for b_potential
in a stochastic way with an mvn whose loc
and covariance_matrix
parameters are not modified as the MCMC or SVI runs. Any ideas?