-
-
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
Refactor pm.Simulator (WIP) #4802
Conversation
pymc3/distributions/simulator.py
Outdated
if sum_stat != "identity": | ||
_log.info(f"Automatically setting sum_stat to identity as expected by {distance}") | ||
sum_stat = "identity" | ||
raise NotImplementedError("KL not refactored yet") |
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.
Would be great to link a Github issue right here.
rv_type = type(sim_op) | ||
|
||
@_logp.register(rv_type) | ||
def logp(op, sim_rv, rvs_to_values, *sim_params, **kwargs): |
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.
locally defined functions often cause problems with pickling.
Also it looks like this logp
uses variables from the __new__
scope. Doesn't this, in combination with the register lead to problems when having more than one Simulator
?
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 am not sure about pickling. Depends on the point at which the function is used (after obtaining the logp graph, the function is not needed anymore).
The registration part should be fine because I am creating a new type
which has a unique name. I also have a test for multiple Simulators with different methods and it seems to be fine.
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 can also try to add a layer of indirection when creating the simulator so that I can attach what are now the local variables to the tag
, in which case a single logp function / dispatcher would be enough.
@@ -144,7 +147,7 @@ def abs_diff(eps, obs_data, sim_data): | |||
) | |||
|
|||
with pm.Model() as self.SMABC_potential: | |||
a = pm.Normal("a", mu=0, sigma=1) | |||
a = pm.Normal("a", mu=0, sigma=1, initval=0.5) |
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.
Why was the initval added? Maybe this indicates an underlying problem?
@michaelosthege thanks for the pointers. This was not meant for implementation review yet, just proof of concept but some of the points you raise are already useful. |
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 really like where this is going!
I am not sure about separating the Simulator
from the Distance
, they both together define a pseudolikelihood. So it seems reasonable to have them as parts of the same object. For the same reason making the distance part of the sampling method is weird. Actually. I think the first version of SMC-ABC was like that. We can of course discuss the benefits of trying to automatically tune epsilon, nut I think having to define a single value of epsilon is actually a good feature in practice as allows to control the level of accuracy and the cost of the simulation with minimal hand tuning.
Codecov Report
@@ Coverage Diff @@
## main #4802 +/- ##
==========================================
+ Coverage 71.97% 72.97% +0.99%
==========================================
Files 85 85
Lines 13839 13838 -1
==========================================
+ Hits 9961 10098 +137
+ Misses 3878 3740 -138
|
@michaelosthege was totally right about the pickling issues. I didn't spot them in the first iteration because the default in
I temporarily set the ABC tests to all be singleprocess. |
@@ -156,6 +146,29 @@ def sample_smc( | |||
%282007%29133:7%28816%29>`__ | |||
""" | |||
|
|||
if kernel is not 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.
How are we going to select if the kernel is metropolis, independent Metropolis-Hasting, HMC, etc?
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.
Haven't thought about that yet. We might use the kernel argument for that, in which case I'll remove the DeprecationWarning. This is just temporarily.
Might be a good idea to add include the |
The pickling issue is proving non-trivial to overcome. It is caused by the dynamic RV class creation (as well as user I tried another approach that would require users to explicit subclass from the SimulatorRV instead of creating one dynamically, and these can be pickled / used in multiprocess. However as soon as the classes are defined inside a function (such as the The old Any suggestions? |
This is a proposal on how to
pm.Simulator
could be refactored intoV4
. It provides a helper method to create a SimulatorRV which tries to behave as a typical RV for prior and posterior predictive sampling, and whose logp isdistance(epsilon, sum_stat(value), sim_stat(sim_rv))
wheresim_rv
is just another reinstantiation of the originalSimulatorRV
.The advantages is that we don't need special logic for the
ABC
kernel insample_smc
as it works just like a normal variable. It can also be used in some conventional samplers (at least it seems to work with Metropolis MCMC). It does away with the limitation of having a singlepm.Simulator
and so on.Two other options would be to:
Separate the
Simulator
object from apm.Distance
likelihood term which may be more transparent / easier to think for the user. The mixing of the two doesn't make a lot of sense if we think ofSimulator
as a true RV (for example if we use the mean summary statistic, then the logp shape is completely different than the samples shape). It may also make it easier to manipulate parameters like theepsilon
by the sampler if we separate the two. Perhaps distance should even be completely the responsibility of the sampler kernel.Keep most of the unique logic from
v3
, which keeps (for better or worse)pm.Simulator
andABC
on their very specific corner in the library.CC: @aloctavodia @junpenglao