Uh oh!
There was an error while loading. Please reload this page.
Confirm the best fit of a stochastic fit by running the top parameter sets again (#659) - #675
Merged
Merged
Conversation
… sets again (#659) When a model is stochastic, running it twice with the same parameter values gives two different answers, so the objective value PyBNF computes is a noisy measurement rather than a fixed number. A fit picked its answer by taking the best value it ever saw, and every one of those came from a single simulation. A long fit scores tens of thousands of parameter sets, so the winner of that comparison was very often just the parameter set that happened to get a lucky simulation. Two things came out wrong and nothing said so. The reported objective value was the best of many noisy draws, so it was optimistic by a wide margin. The reported parameter values were not the best ones found, because a slightly worse parameter set with a lucky draw beats a better one with an average draw. A fit that uses at least one stochastic model now ends by running its top best_fit_candidates parameter sets best_fit_replicates more times each, ranking them by average objective value, and reporting that winner as the best fit. Ten and ten by default, so a hundred simulations against a run that may have done a hundred thousand, all submitted at once on processors that would otherwise sit idle at the end of a run. Results/best_fit_confirmation.txt gives each candidate's average, its standard error, and the single value the search had recorded for it. The winner is recorded on the trajectory, so everything the run writes afterwards describes the same parameter set. That includes the saved simulations, the best-fit model file, the information criteria, a refine's starting point, and a bootstrap replicate's answer. The stage is on under edition 2 and above. Under the legacy edition it is off, since an unchanged configuration file has to keep behaving as it always has, and a legacy fit with a stochastic model is told at startup which two keys turn it on. It is also skipped when the wall-time budget is already spent, and when every stochastic model in the fit pins its seed so replicates could not differ. Nothing happens for a fit with no stochastic model. No fitting method searches any differently. Running the NFsim receptor example for three iterations, the fit reported an objective of 54878 and the confirmation stage put the same parameter set at 72839 give or take 5522.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes#659.
The problem
When a model is stochastic, running it twice with the same parameter values gives two different answers. The objective value PyBNF computes is therefore a noisy measurement rather than a fixed number.
A fit picked its answer by taking the best objective value it ever saw, and every one of those values came from a single simulation. A long fit scores tens of thousands of parameter sets, so the winner of that comparison was very often just the parameter set that happened to get a lucky simulation.
Two things came out wrong and nothing in the output said so. The reported objective value was the best of many noisy draws, so it was optimistic by a wide margin. And the reported parameter values were not the best ones found, because a slightly worse parameter set with a lucky draw beats a better one with an average draw.
The change
A fit that uses at least one stochastic model now ends with one more stage. It takes the top
best_fit_candidatesparameter sets, runs each of thembest_fit_replicatesmore times, ranks them by their average objective value, and reports that winner as the best fit.Ten and ten by default. That is a hundred simulations against a run that may have done a hundred thousand, and all of them are submitted at once, which uses processors that would otherwise sit idle at the end of a run.
Results/best_fit_confirmation.txtrecords the result. Here is the real file from the run described below:The winner is recorded on the trajectory rather than passed around, so everything the run writes after this stage describes the same parameter set. That includes the saved simulations, the best-fit model file, the information criteria, a refine's starting point, and a bootstrap replicate's recorded answer. The record is dropped as soon as anything else is scored, which is what keeps a refine phase, which shares the fit's trajectory, from reporting the point it started from.
When it runs
The stage is on under
edition = 2and above. Under the legacy edition it is off, because an unchanged configuration file has to keep behaving exactly as it always has, and a legacy fit with a stochastic model is told at startup which two keys turn it on.It is also skipped in three cases. When no model is stochastic. When the
wall_time_fitbudget is already spent, because a budget is a promise about the whole run and this costs more simulations. And whenstochastic_seedis one of the_honorbnglmodes and every stochastic model pins its seed, because then every replicate would reproduce the same trajectory. Each of those says what it is skipping and why.No fitting method searches any differently. This covers the answer the fit reports and not the search that produced it, which is the larger piece of work the issue sets aside.
Two smaller pieces this needed
make_jobtakes areplicate_offset. Under the default seed policy a stochastic simulation's seed comes from the parameter values and the replicate index and nothing else, so running a parameter set again at the same index reproduces the same trajectory exactly. The new stage asks for indices past every one the fit itself used. Zero, the default, is the ordinary path and is unchanged.The scoring half of
add_to_trajectorymoved intoscore_result, so the new stage scores results it deliberately does not put in the trajectory through exactly the same path the fit used.Verification
The full test suite passes (4788 passed, 14 skipped). 42 new tests in
tests/test_best_fit_confirmation.pycover the arithmetic, the report text, the trajectory changes, the gates, and the stage itself driven by a fake dask client with a model whose objective depends on the replicate index. I checked that each of them fails when the corresponding piece of the change is removed.For a real check, I ran the NFsim receptor example (
examples/receptor_nf) for three iterations with three candidates at four replicates. The fit reported an objective of 54878. Running that same parameter set four more times gave an average of 72839 give or take 5522, so the reported value was about 18000 too good. That is the file shown above.Not in this change
Results/information_criteria.txtcomputes its log-likelihood by re-simulating the best fit once, so for a stochastic model with a likelihood objective it is still a single noisy draw, and it will now disagree with the average this stage reports. Averaging a likelihood over stochastic replicates is a question of its own, so I have not touched it here. Filed separately as #676.