Incorporate gempyor.distributions into gempyor.statistics#595
Incorporate gempyor.distributions into gempyor.statistics#595
gempyor.distributions into gempyor.statistics#595Conversation
|
3 questions going into review:
|
pearsonca
left a comment
There was a problem hiding this comment.
This is good start, but there are a few items to tack on.
Some of these can be easily replaced by distributions already (like poisson &
I would say keep them as xarrays for now... Under the hood xarray uses numpy for its array representation so if you design these functions to work on numpy arrays xarray is smart enough to properly apply that to slices of a dataset/dataarray. I think changing this would be better done when we change the output representation from the simulator which starts with the return of
Yeah, that's a fair point and as we've discussed before we do not want to be in the business of testing 3rd party packages. I would say just limit unit testing to where we implement some custom behavior. |
| def _likelihood(self, gt_data: npt.NDArray, model_data: npt.NDArray) -> npt.NDArray: | ||
| """Log-likelihood for the normal distribution.""" | ||
| return scipy.stats.norm.logpdf(x=gt_data, loc=model_data, scale=self.sigma) | ||
|
|
There was a problem hiding this comment.
It feels odd that you have to declare a mean to this distribution, but that doesn't get used. I'm pointing this out as an example, but I think pretty much all of these likelihoods suffer from this problem. For example, this is a truncated snippet from the simple_usa_statelevel.yml config:
inference:
method: emcee
iterations_per_slot: 100
do_inference: TRUE
gt_data_path: model_input/data/generated_gt_data.csv
statistics:
incidCase:
name: incidCase
sim_var: incidCase
data_var: incidCase
resample:
aggregator: sum
freq: W-SAT
skipna: True
zero_to_one: True
likelihood:
dist: poisthis now would become:
inference:
method: emcee
iterations_per_slot: 100
do_inference: TRUE
gt_data_path: model_input/data/generated_gt_data.csv
statistics:
incidCase:
name: incidCase
sim_var: incidCase
data_var: incidCase
resample:
aggregator: sum
freq: W-SAT
skipna: True
zero_to_one: True
likelihood:
distribution: pois
lam: 1You now have to add the lam and set it to a value so the pydantic model validation works, but it's totally unused by the likelihood function. I'm also not sure what the correct fix for this is though... thoughts @emprzy, @pearsonca?
There was a problem hiding this comment.
i've been thinking about some adjacent things.
what if we make the parameters optional?
we could write sample to take ** (at the abstract level, specific args in the implementations), which defaults to None. uses the arguments if available, optional field values if not (and raises if created with no values and asked to sample with no args)
this would work nicely with likelihoods that don't want mean to be specified
There was a problem hiding this comment.
So we make the parameters part of ** when you create an instance of a distribution? I like this, but let's take it to the logical extreme; what happens if people have a Distribution defined in their (janky) config with no parameters and then the .sample() method gets called, an error will come right? Are we ok with that possibility
I think RMSE would be ...like a multinormal likelihood? Anywho, I think these are more cases of alternative norms / distance functions, rather than something to try to capture with a likelihood based approach. We want to support other kinds of norms - I think the better answer here is to separate when we're doing a likelihood based norm vs something else, rather than trying to shoehorn these particular ones into a distribution framing.
agree |
Co-authored-by: Timothy Willard <9395586+TimothyWillard@users.noreply.github.com>
…ithub.com/HopkinsIDD/flepiMoP into incorporate-distributions-into-statistics
|
@pearsonca @TimothyWillard @MacdonaldJoshuaCaleb See here an example implementation of the approach we discussed in slack, where there are two different BaseModels for the distributions – one for Sampling and the other for calculating Loglikelihood. Let me know what you think re: our two options right now:
|
Now seeing approach (1) implemented I like this a bit more. I think it also gives us more flexibility to tweak and refine likelihoods independent of the rest of the configuration (thinking of #86 for example). Some minor comments before implementation & review in this PR:
|
Split distributions used for sampling and llik calculation into distinct modules.
pearsonca
left a comment
There was a problem hiding this comment.
Seems like progress, but some lingering bits from switch to split distribution vs likelihood. There are also some issues with the likelihood specifications, but where I think we can put off some implementations until they are actually needed.
| """ | ||
| Calculates the log-likelihood of observing data given the model's predictions. | ||
|
|
||
| Args: | ||
| gt_data: The observed ground truth data. | ||
| model_data: The data produced by flepiMoP. | ||
|
|
||
| Returns: | ||
| An array of log-likelihood values. | ||
| """ |
There was a problem hiding this comment.
whatever the proper docstring syntax here, let's have an details noting that:
gt_data,model_datamust be the same shape (leave aside for the moment any actual enforcement of this, however)- the caller is responsible for ensuring that gt_data and model_data align (i.e. this does nothing to deal with observation times versus model time)
There was a problem hiding this comment.
This is a good point, though do we think there would be failures earlier than this code if gt_data and model_data do not have matching dimensions? Users aren't going to interact with the ._likelihood() method directly .
|
|
||
| class TruncatedNormalLoglikelihood(LoglikelihoodABC): | ||
| """ | ||
| Represents a truncated normal distribution for calculating log-likelihood. |
There was a problem hiding this comment.
need to remind people that the interpretation of a, b here is different from what's going on w/ trunc normal sampling.
In sampling, a and b are the absolute sample limits. Here they should be the relative-offsets-to-mu sample limits.
There was a problem hiding this comment.
hmm, actually I think there's a lot more going on here with how we want to allow a truncated normal likelihood that's going to take some mathematical care.
for now, we shouldn't support this. we can revisit it if people request it in the future.
There was a problem hiding this comment.
i agree with this take, and feel the same way about uniform for llik. let me know if you disagree @TimothyWillard @pearsonca
| ) | ||
|
|
||
|
|
||
| class AbsoluteErrorLoglikelihood(LoglikelihoodABC): |
There was a problem hiding this comment.
Abs Error isn't a likelihood - we need an additional layer of distance or norm or target evaluation here - so like TargetABC => LikelihoodABC => [Distributions]ABC. But also TargetABC => AbsoluteError. Ibid for RMSE
There was a problem hiding this comment.
Definitely hear where you are coming from, though I think a better solution to this dilemma would not to create more parent ABCs, but to create a better name for the LogLikelihoodABC class that is more inclusive of these two "flavors" of error calculation. Like, just renaming LogLikelihoodABC to ErrorMetricABC, MetricABC, ObjectiveABC, or something like that? Keeps things simple and clean. @pearsonca
There was a problem hiding this comment.
ObjectiveFunctionABC ...? Any takers...?
flepimop/gempyor_pkg/tests/distributions/test_distributions_common.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Hmm - aren't we repeating the test-them-all error here?
There was a problem hiding this comment.
this is the same framework we used for testing gempyor.distributions (one test file per distribution). or is there something specific about this file that you think is repetitive..?
Change `gempyor.likelihoods` to be `gempyor.objective_functions`
|
In my most recent commit (32fc04c) I implement some of the minute suggestions from @pearsonca 's last review, and I also establish a new naming convention for what was previously called I'll update things like tests and docstring documentation once I get the green light on this! |
pearsonca
left a comment
There was a problem hiding this comment.
I need to do some closer look at some of the likelihood calculations, but perhaps something to work with ahead of that.
flepimop/gempyor_pkg/tests/distributions/test_distributions_common.py
Outdated
Show resolved
Hide resolved
Finish testing suite, update documentation,
|
Only thing left here is for @pearsonca to review whether or not scipy is doing what we want! |
Also, disable RMSE and Abs.Error as objective functions for now
|
Note that we have had to disable |
Describe your changes.
Incorporate the recently added distributions module into
gempyor.statistics, and add a new._likelihood()method to allDistributionsthat calculates the log likelihood of observed ground truth data, given the model data. It is worth noting that integrating this new log likelihood calculation method is not entirely seamless, as the previous implementation ingempyor.statistics(.llik()) used adist_mapto match distributions given in configs to their parameters. My changes leave us in a liminal state between these two, and provideif/elselogic to allow backwards compatibility withdist_map, but I think it would be cleaner to move away fromdist_mapentirely.Does this pull request make any user interface changes? If so please describe.
No user interface chagnes
What does your pull request address? Tag relevant issues.
This pull request addresses GH #583