-
-
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
Do not reference model variables in GP internals #6883
Comments
Can we refactor the code in the way it works correctly? |
It's not easy, we need to figure out how to represent GPs as PyTensor variables/Ops. Some very rough (and likely bad) ideas in https://github.com/pymc-devs/pymc-experimental/blob/2084e167486a42b313abcec77958d3a24e001ae3/pymc_experimental/gp/GPs%20from%20scratch.ipynb |
There's actually no GP object saved in the model, so no good way to raise an error. The problem is that GP objects hold references to model variables, so if you tried to do a gp.conditional after cloning a model (which happens whenever you used do/observe) you would be re-introducing the old model variables, instead of referring to cloned ones. import pymc as pm
import numpy as np
from pytensor.graph import ancestors
from pymc.model.fgraph import clone_model
with pm.Model() as model:
ell = pm.Gamma("ell", alpha=2, beta=1)
cov = 2 * pm.gp.cov.ExpQuad(1, ell)
gp = pm.gp.Latent(cov_func=cov)
f = gp.prior("f", X=np.arange(10)[:, None])
pm.Normal("y", f * 2)
assert ell in ancestors(f)
cloned_model = clone_model(model)
with cloned_model:
f_cloned = gp.prior("f_cloned", X=np.arange(10)[:, None])
pm.Normal("y_cloned", f_cloned * 2)
assert model["ell"] in ancestors(f_cloned) # bad
assert cloned_model["ell"] not in ancestors(f_cloned) # bad
cloned_model.logp() # ValueError: Random variables detected in the logp graph: {ell}. I see two ways of fixing this:
|
fgraph
Yes, the state space models have a list of required parameters, and it does name matching to model RVs when you "build" the model. The challenge with that approach is that the statespace model needs knows ahead of time what variables to look for. It ends up being really inflexible, because users have to conform to the demands of the model, rather than the (preferable) other way around. The core of the GP is just the conditioning operation. I think what could be interesting is to define something like |
Breaking the do-operator puts a bit more urgency on this idea now |
I'm wondering if rewriting the GP lib as pytensor ops is the only way forward here. It's probably easier to make
It wouldn't be hard to store GP objects in the model. It's not as slick, but could they be registered and then reproduced the same way random variables and deterministics are? I think designing a hierarchical normal model Op would be a simpler, univariate, version of what a GP Op would need to do:
I dunno what an Op-ified version of a GP would look like, but a hierarchical normal model that follows the current GP API would look like this: mu = pm.Normal("mu", ...)
sigma = pm.HalfNormal("sigma", ...)
hier = pm.Hierarchical("x", mu=mu, sigma=sigma) # like pm.gp.cov.Latent
x = hier.prior("x", dims="group") # (optionally) non-center under the hood, `gp.prior` does that and then for prediction xnew = hier.new_group("xnew") # like gp.conditional Not proposing we implement that like that, just to show the similarity for why I'm thinking a PyTensor version of a hierarchical normal model might be a good first step towards a PyTensor version of a GP. |
They could but that adds another layer of complexity for all Model methods and fgraph manipulation functions that now have to think about yet another kind of Model variable The is is the existing logic which is already pretty complex: Lines 116 to 277 in 01ddcb8
We also used to have special rules for Minibatch variables and it was quite cleaner to just represent those as vanilla PyTensor objects: #6523 As a first approach I would just to it with name approach like statespaces models does, so store the name of x/mu/sigma instead of the variable itself and retrieve it from the model context via Representing stuff as PyTensor objects shouldn't be that hard, but it definitely sounds like it will take a while longer to find the right representation and it would be nice to fix the incompatible behavior we have right now. |
@bwengals adding a model.gps to the model doesn't solve the problem. You still need to figure out how to replace any references of the old model variables (such as parameters of the covariance) with references of the new model. It still boils down to the same problem that gp methods are stateful, and not pure I/O functions of the model variables |
Description
The model fgraph functionality (used by
do
andobserve
) doesn't work with GP variables because those are not represented as pure PyTensor objects and make use of variables from the old model in the instance scope.The text was updated successfully, but these errors were encountered: