-
-
Notifications
You must be signed in to change notification settings - Fork 132
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add alternative default priors #360
Conversation
Codecov Report
@@ Coverage Diff @@
## master #360 +/- ##
==========================================
+ Coverage 88.87% 90.20% +1.33%
==========================================
Files 16 17 +1
Lines 1411 1613 +202
==========================================
+ Hits 1254 1455 +201
- Misses 157 158 +1
Continue to review full report at Codecov.
|
…with machinery to use correlated priors
…essions in group-specific terms
I think this is ready for a review @aloctavodia, @canyon289, @twiecki. I know there's a lot going on in this PR and many things may not be that clear. Please ask as many questions as you want. The TL;DR of this PR would be
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few small comments, overall seems good. Many small details that improve readability also some changes that I need to read with more care to understand them. I will try to keep reading tomorrow.
sigma = pm.HalfNormal.dist(sigma=sigma, shape=rows) | ||
|
||
# Obtain Cholesky factor for the covariance | ||
lkj_decomp, corr, sigma = pm.LKJCholeskyCov( # pylint: disable=unused-variable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are not using corr, right? then do :
lkj_decomp, _, sigma
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also can we use a different name for the returned sigma and the input sigma?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to use corr in the nearby future. I've been thinking we should report it even when independent priors are used. That's why it's there.
But in the meantime, I have no problem if you think the underscore is more appropriate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And sigma... Well, they represent the same random variable in the model. The problem is the first one is a .dist, so I have to recover the one returned by lkjcholeskycov and add it to the trace.
I don't know if there are plans in pymc3 to allow a random variable in lkjcholeskycov, that would be the best solution I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is done in this PR
bambi/models.py
Outdated
automatic_priors: str | ||
An optional specification to compute/scale automatic priors. ``"default"`` means to use | ||
Bambi's default method. ``"rstanarm"`` means to use default priors from the R rstanarm | ||
library. The latter are available in more scenarios because they don't depend on MLE. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we detect when default Bambi priors fail and switch to rstanarm's priors?
What is the advantage of keep using Bambi defaults if they are more restricted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to change the defaults for the rstanarm inspired priors. This will make it simpler to implement t and beta families.
|
||
|
||
def add_lkj(terms, eta=1): | ||
# Parameters |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would make this a full comment in numpy style, that way it shows up under add_lkj.__doc__
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense!
if self.model.family.name == "gaussian": | ||
sigma = np.std(self.model.response.data) | ||
self.model.response.prior.update(sigma=Prior("HalfStudentT", nu=4, sigma=sigma)) | ||
# Add cases for other families |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whats this comment for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, the Gamma family has an auxiliary parameter alpha
whose prior is HalfCauchy(beta=1)
. I left the comment to remember that it might be good to consider updating beta
to another value based on the data, as with the sigma
in the HalfStudentT prior above.
bambi/priors/scaler_mle.py
Outdated
# Convert scale names to floats | ||
if isinstance(term.prior.scale, str): | ||
term.prior.scale = self.names[term.prior.scale] | ||
|
||
if self.mle is None: | ||
self.fit_mle() | ||
|
||
# Scale it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expand it to full name, makes comment easier to read in isolation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean with full name? Something like self.fit_maximum_likeliihood_estimator
? Or is it about the comment below?
elif isinstance(family, Family): | ||
# Only work if there are nuisance parameters in the family, and if any of these nuisance | ||
# parameters is present in 'priors' dictionary. | ||
nuisance_params = [k for k in family.prior.args if k not in ["observed", family.parent]] | ||
if set(nuisance_params).intersection(set(priors)): | ||
return {k: priors[k] for k in nuisance_params if k in priors} | ||
return {k: priors.pop(k) for k in nuisance_params if k in priors} | ||
return None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if none is returned to downstream call?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing. When None is returned it means the user didn't pass any prior for any parameter in the response distribution and there's no need to update them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Short review. Will do more indepth review in next 24 hours
Im sorry for missing this @mention. I get so many github emails things get lost. If Im not responding to a PR fast enough message me on slack, or even proactively message. That reduces chances I'll miss PRs tremendously |
Thanks for the feedback Ravin. I'll message on Slack next time then! :D |
I also missed more in depth review but will get it in this week :( |
I've just realized this PR will close #320 since we're changing default priors |
This PR aims to add alternative default priors that will be the default priors for those models where we lack statsmodels support. This new prior does not replace existing defaults (until we've evidence they're equivalent). I will be updating the following list of changes as I commit to this PR.
Changes
Model._match_derived_terms()
. This was unused because categorical group-specific terms are indeed one term in the model and not several terms (as many as dummies in the encoding of the categorical variable) as it used to be before.PriorFactory._get_prior()
into several methods with clearer names and goals. Also modifiedconfig.json
as proposed in Use objects instead of arrays in the config.json of the priors #361.Prior._auto_scale
is nowPrior.auto_scale
. It's bothering to add pylint exceptions all the time.PriorScaler2
.Model.build()
calls.Model
has a new argument,priors_cor
. It accepts dictionaries where keys are the names of the groups, and values are the eta parameter in the LKJ distribution for correlation matrices. If such a dictionary is present, priors for group-specific terms are a multivariate normal distribution with a non-zero correlation.