-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
v4 refactor for GP module #5055
Conversation
Codecov Report
@@ Coverage Diff @@
## main #5055 +/- ##
==========================================
+ Coverage 77.95% 79.01% +1.05%
==========================================
Files 88 88
Lines 14222 14242 +20
==========================================
+ Hits 11087 11253 +166
+ Misses 3135 2989 -146
|
…in Marginal, mark for deprecation
…kwargs (often default zero mean_func is used)
… at 1e-6. add deprecation warning for is_observed arg
- use model.logp instead of variable.logp - set req kwargs cov_func and mean_func - fix weirdly small scale on some input X, y - move predict calls into model block - the two kron models outstanding
Tests are passing now except for something windowsy. |
thank you @aloctavodia, made the changes. There are a few other DeprecationWarnings in here from a while back, like here. When should something like that be removed? |
That's seems to be very old. I think is OK to remove most deprecation/future warnings. Given that we are working on a major release it make sense to keep only those deprecation/future messages related to changes from 3.X to 4.0. |
Yes, agree, we should remove everything that has a deprecation/future warning now (that wasn't introduced in this release). |
So I think its ready for a look. There's a couple things I want to flag for the reviewers:
|
c = a * b | ||
|
||
(c_val,) = pm.gp.util.replace_with_values( | ||
[c], replacements={"a": 2, "b": 3, "x": 100}, model=model |
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.
Any reason to base this function on the variables names as opposed to the variables themselves?
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.
No particular reason, it just struck me as the most natural, but I'm not extremely familiar with Aesara and its usage patterns. I suspect I'm duplicating functionality that's elsewhere here though... Is there a reason that would be preferred? Happy to switch to that.
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.
In general I think we are trying to move away from our dependency on variable names but this is probably never going to happen so... I guess either way.
About duplicating code, if this is a once per sampling kind of thing, I think it is fine.
More importantly tests are passing and we should try to merge soon as it is quite a large PR. We can always come back later for final polishing
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.
If we're trying to move away from variable string names I'll go ahead and change it. Out of curiosity, what's the benefit for doing so?
Also, for gp.predict
, it's not even once per sample, it's just once. You'd make your GP model, take the MAP estimate say, and then do something like:
with model:
mu, cov = gp.predict(X, point=map_estimate)
# plot prediced mean
plt.plot(mu)
# +- 2 sigma uncertainty
plt.plot(mu + 2*np.sqrt(np.diag(cov)), 'b')
plt.plot(mu - 2*np.sqrt(np.diag(cov)), 'b')
Should replace_with_values
live in gp.util
? Or should it move to somewhere general, like aesaraf
? There isn't anything necessarily GP specific about it -- it could be of general use.
Sounds good, and yes sorry that it is a large PR! I'm happy to talk through anything too.
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.
If we're trying to move away from variable string names I'll go ahead and change it. Out of curiosity, what's the benefit for doing so?
It makes somethings easier like graph manipulations or variable inspection (e.g, you don't need access to the model object to know what you are dealing with). Also we don't need to worry about the transformed names which are another nuisance in a lot of the codebase.
Less important perhaps there is no real limitation at the Aesara level that variables must have unique names.
Also, for
gp.predict
, it's not even once per sample, it's just once. You'd make your GP model, take the MAP estimate say, and then do something like:with model: mu, cov = gp.predict(X, point=map_estimate) # plot prediced mean plt.plot(mu) # +- 2 sigma uncertainty plt.plot(mu + 2*np.sqrt(np.diag(cov)), 'b') plt.plot(mu - 2*np.sqrt(np.diag(cov)), 'b')Should
replace_with_values
live ingp.util
? Or should it move to somewhere general, likeaesaraf
? There isn't anything necessarily GP specific about it -- it could be of general use.
Is there a case were you might want to evaluate for more than a single point? Is it conceptually similar to posterior predictive sampling?
If so you may want to check if any of that code in sample_posterior_predictive may make more sense here.
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.
It makes somethings easier like graph manipulations or variable inspection (e.g, you don't need access to the model object to know what you are dealing with). Also we don't need to worry about the transformed names which are another nuisance in a lot of the codebase.
Gotcha, that makes a lot of sense, especially the transformed/untransformed distinction -- ideally that should stay under the hood.
Is there a case were you might want to evaluate for more than a single point? Is it conceptually similar to posterior predictive sampling?
Never say never, but I doubt it. gp.predict
is really just a convenience method for getting the conditional mean and variance given the MAP estimate. I actually did start with sample_posterior_predictive
to figure out how to make this function, since that's where I'd stolen draw_values
from originally! But it looked like draw_values had been refactored into component pieces.
Do you mind adding release notes with the important changes (kwargs only, new utils, etc)? |
RELEASE-NOTES.md
Outdated
@@ -45,8 +45,14 @@ All of the above apply to: | |||
- Changes to the BART implementation: | |||
- A BART variable can be combined with other random variables. The `inv_link` argument has been removed (see [4914](https://github.com/pymc-devs/pymc3/pull/4914)). | |||
- Moved BART to its own module (see [5058](https://github.com/pymc-devs/pymc3/pull/5058)). | |||
- ... | |||
|
|||
- Changes to the Gaussian Process (GP) submodule: |
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.
Add a link to the PR in this line?
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.
pushed
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.
LGTM. I am not very familiar with the GP module so I am trusting you and the tests as well :)
Alright, thank you for the review @ricardoV94! I'll leave it unmerged for a few days just in case someone wants to add anything |
pymc/tests/test_gp.py
Outdated
with pm.Model() as model: | ||
cov_func = pm.gp.cov.ExpQuad(3, [0.1, 0.2, 0.3]) | ||
mean_func = pm.gp.mean.Constant(0.5) | ||
gp = pm.gp.Marginal(mean_func, cov_func) | ||
gp = pm.gp.Marginal(mean_func=mean_func, cov_func=cov_func) | ||
f = gp.marginal_likelihood("f", X, y, noise=0.0, is_observed=False, observed=y) |
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.
If is_observed
is deprecated, should this be removed?
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.
yes, thank you for finding these! took them out
pymc/tests/test_gp.py
Outdated
npt.assert_allclose(mu1, mu2, atol=0, rtol=1e-3) | ||
npt.assert_allclose(var1, var2, atol=0, rtol=1e-3) | ||
|
||
def testPredictCov(self): | ||
with pm.Model() as model: | ||
cov_func = pm.gp.cov.ExpQuad(3, [0.1, 0.2, 0.3]) | ||
mean_func = pm.gp.mean.Constant(0.5) | ||
gp = pm.gp.MarginalSparse(mean_func, cov_func, approx="DTC") | ||
gp = pm.gp.MarginalSparse(mean_func=mean_func, cov_func=cov_func, approx="DTC") | ||
f = gp.marginal_likelihood("f", self.X, self.X, self.y, self.sigma, is_observed=False) |
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.
Another is_observed
Thanks @bwengals, awesome to have this ported! |
This PR has updates for getting the tests passing for v4. Ref issue #5035.
A few other minor changes included (will update this list):
is_observed
option fromgp.Marginal
. Should always be True.gp.Latent
, makemean_func
andcov_func
required kwargs.Related PRs in pymc/pymc-examples