-
Notifications
You must be signed in to change notification settings - Fork 18
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
MESMER-X: refactor and test find first guess module #577
base: main
Are you sure you want to change the base?
MESMER-X: refactor and test find first guess module #577
Conversation
for more information, see https://pre-commit.ci
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #577 +/- ##
==========================================
+ Coverage 78.12% 80.10% +1.98%
==========================================
Files 49 49
Lines 3012 3066 +54
==========================================
+ Hits 2353 2456 +103
+ Misses 659 610 -49
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
if not globalfit_all.success: | ||
raise ValueError( | ||
"Global optimization for first guess failed, please check boundaries_coeff or ", | ||
"disable fg_with_global_opti in options_solver.", | ||
) |
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 not sure if this could also only be a warning. Before, if it failed self.coeffs
would just be None. @yquilcaille, could that become a problem at some point? I will also look into this a little more.
) | ||
dist2.find_fg() | ||
result2 = dist.fg_coeffs | ||
np.testing.assert_equal(result2, result) # No |
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.
Providing a first guess does not make the result any better in this case, but might in more difficult cases, or at least speed up the fitting, could also remove this test. Note that the user cannot provide a first guess in xr_train_distrib
. We could think about adding this.
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.
np.testing.assert_equal(result2, result) # No | |
# NOTE: leads to the same result as without first guess | |
np.testing.assert_equal(result2, result) # No |
# still finds a fg because we do not enforce the bounds on the fg | ||
# however the fg is significantly worse on the param with the wrong bounds | ||
# in contrast to the above this also runs step 6: fit on CDF or LL^n -> impications? |
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.
Not sure what to think about this... might not be a systematic problem....
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 have not looked at the tests yet.
ind_targ_low = np.where(smooth_targ < mean_minus_one_std)[0] | ||
ind_targ_high = np.where(smooth_targ > mean_plus_one_std)[0] |
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.
use _sm
and _lg
? (you see I like when words have the same length) but also ok to keep
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.
where? and what would it mean?
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.
sm(all)
and l(ar)g(e)
( 🤔 ) and instead of low
and high
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.
The fact that I didn't get that maybe speaks again doing that 😅. I see your point, we could also just go with small
and large
but it's quite long oc. I vote for keep as is.
# location might not be used (beta distribution) or set in the expression | ||
if len(self.fg_ind_loc) > 0: | ||
localfit_loc = self._minimize( | ||
func=self._fg_fun_loc, |
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.
This is to enable finding a first guess also for expr = Expression("loc=0, scale=c1", expr_name="name"
. This is a rather major change and it also led me to add two tests to test_mesmer_x_expression
and found a bug (#525 (comment)), so this might deserve its own PR?
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 may be easier to see what is happening if done in a separate PR
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.
Looking good - thanks. But it's such a large beast so super difficult to have to overview. And also difficult to know what is a limitation of the data and distribution and what one of the code...
@@ -261,6 +257,16 @@ def np_train_distrib( | |||
return dfit.coefficients_fit, dfit.quality_fit | |||
|
|||
|
|||
def _smooth_data(data, nn=10): |
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.
def _smooth_data(data, nn=10): | |
def _smooth_data(data, length=10): |
(or size
or something similar)
# location might not be used (beta distribution) or set in the expression | ||
if len(self.fg_ind_loc) > 0: | ||
localfit_loc = self._minimize( | ||
func=self._fg_fun_loc, |
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 may be easier to see what is happening if done in a separate PR
x0=self.fg_coeffs[self.fg_ind_loc], | ||
fact_maxfev_iter=len(self.fg_ind_loc) / self.n_coeffs, | ||
option_NelderMead="best_run", | ||
[self.expr_fit.coefficients_list.index(c) for c in loc_coeffs] |
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.
ok to keep but that would better be done in Expression I think
# compared to all 0, better for ref level but worse for trend | ||
x0 = np.full(len(scale), fill_value=np.std(self.data_targ)) | ||
# scale might not be used (beta distribution) or set in the expression | ||
if len(self.fg_ind_sca) > 0: |
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 len(self.fg_ind_sca) > 0: | |
if len(self.fg_ind_sca) > 0: | |
pred = np.ones(n) | ||
targ = rng.normal(loc=0, scale=1, size=n) | ||
|
||
expression = Expression("norm(loc=c1, scale=c3)", expr_name="exp1") |
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.
expression = Expression("norm(loc=c1, scale=c3)", expr_name="exp1") | |
expression = Expression("norm(loc=c1, scale=c2)", expr_name="exp1") |
not that is matters...
expected = [loc, scale] | ||
|
||
np.testing.assert_allclose(result, expected, rtol=0.1) | ||
|
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 test 1-2 discrete distributions?
expected = np.array([-0.005093813, 1.015267311]) | ||
np.testing.assert_allclose(result, expected, rtol=1e-5) | ||
|
||
# test with wrong bounds |
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.
# test with wrong bounds | |
# test with bounds outside true value |
) | ||
dist.find_fg() | ||
result = dist.fg_coeffs | ||
expected = np.array([-0.005093817, 1.015267298]) |
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.
So here we get an estimate outside the bounds, correct?
expression = Expression("norm(loc=c1*__tas__, scale=c2)", expr_name="exp1") | ||
dist = distrib_cov(targ, {"tas": pred}, expression) | ||
|
||
smooth_targ = dist._smooth_data(targ) |
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.
didn't you turn that into a function?
dist.fg_ind_loc = np.array([0]) | ||
|
||
# test local minima at true coefficients | ||
loss_at_toolow = dist._fg_fun_loc(c1 - 1, targ) |
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.
Choose a smaller
I test the first guess functionality of
distrib_cov
and along this started refactoring the functions as well.The refactoring includes:
self.var = ...
) to localsfg_fun_*
instead of the summed squared error for easier testingI write tests for
find_fg()
testing several distributions, a few expressions and a few starting first guesses. Note that the tests generally test the simplest setup possible, like the standard normal or just the mean varying with the predictor. This is to test if we manage to find the right solution for the simplest use cases. As we know, it is impossible to test all expressions and distributions.CHANGELOG.rst