Restart hyperopt#1824
Conversation
|
Thanks @Cmurilochem , do you understand why the test is failing in ubuntu? |
Hi @RoyStegeman, I added a new test that compares the results of one restart and direct hyperopt runs. This then checks for files and depend on paths and from where you run Note: The test (as it is) is not expected to pass entirely since (among other asserts) it requires that the final json ['results'] dictionaries of both runs should match here. This relates to my comments above regarding the differences in the hyper losses for different folds. @goord gave me a nice idea on how to investigate this issue; further details to come. |
That's actually a good question. Since you added the |
|
Thanks a lot @Cmurilochem for these works! Regarding the issue you are facing now, are you sure that the other seeds (tr/vl, MC replicas) aren't also different? In any case, I think that there should be some (simple) ways to trick the random generators that it is starting from a |
RoyStegeman
left a comment
There was a problem hiding this comment.
Some comments but not a complete review
Thanks @Radonirinaunimi. This is something that I will need to check. Thanks for pointing this out. At least, the provisory change to |
There was a problem hiding this comment.
And these are my comments for now. Thanks for the work so far!
I suspect that this might be due to the fact that the seeds for the initial weights for each k-fold in difference runs are inherently different (see below).
I suspect so as well - I noticed you froze the seed for the folds but probably not the tensorflow/numpy/python seeds. If the disagreement is due to the effect of random seeds it's of course not a problem for a real run of the hyperoptimization, and for your tests you already freeze them by setting debug: true so that should also be fine.
Radonirinaunimi
left a comment
There was a problem hiding this comment.
Hi @Cmurilochem, here is a quick review. These are mainly formatting/styling and asking clarifications for various points.
In relation to the replica dependence. In the multireplica case I suspect that self._nn_seeds (= nnseeds argument in ModelTrainer) is already a list of seeds for each replica. I am not quite certain but, please, correct if I am wrong.
Yes, that is correct! The status of master right now is that it can have different seeds per replica for MCseed and NNseed during a multireplica fit, the only seed that is always the same is the TrVlseed
Hi @Radonirinaunimi. I have corrected for all @RoyStegeman's suggestions and now will proceed to yours. Thanks for you time in reviewing and for your excellent suggestions. |
Co-authored-by: Tanjona Rabemananjara <rrabeman@nikhef.nl>
Co-authored-by: Tanjona Rabemananjara <rrabeman@nikhef.nl>
Co-authored-by: Tanjona Rabemananjara <rrabeman@nikhef.nl>
Co-authored-by: Tanjona Rabemananjara <rrabeman@nikhef.nl>
Co-authored-by: Tanjona Rabemananjara <rrabeman@nikhef.nl>
…irectories of tmp_path
RoyStegeman
left a comment
There was a problem hiding this comment.
Thanks, looks good!
HYPEROPT_SEED is going to remain fixed for now?
Hi @RoyStegeman that is what we initially thought and implemented so far. But If you think it would be better, I could try to add a new entry into the runcard so that (like other seeds) the user could have control over it. Please, just let me know what do you think ? |
|
Don't worry I'm fine either way, was just making sure it wasn't something you had forgotten about as I had understood you planned to take it from a runcard seed. |
Great! Maybe we could add this feature in the near future as long as we feel the need to do so. I will keep this in mind. |
|
Thanks a lot @Cmurilochem! I guess the only minor thing missing in order to merge this PR is a small note in the documentation (at the end of https://docs.nnpdf.science/n3fit/hyperopt.html?highlight=hyperopt) describing how one can restart hyperopt. |
Hi @Radonirinaunimi. I could add a note after Changing the hyperoptimization target and let you know after the commit has been done. |
|
Ah good point! It needs to be documented of course. Completely forgot about that 😅 |
Thanks @Radonirinaunimi and @Radonirinaunimi. Documentation added! Please, feel free to suggest any possible changes and/or additions. |
Co-authored-by: Roy Stegeman <roystegeman@live.nl>
|
Hi @RoyStegeman, @Radonirinaunimi and @scarlehoff. Please, let me know whether I could merge this PR after the approval of Roy and Tanjona. Thanks you all for your very valuable suggestions. |
|
Yes, please merge this |
This PR addresses the issue of restarting an hyperoptimization with the Hyperopt library as discussed in #1800.
Comments on the initial changes made
1.
hyper_optimization/filetrials.pyFileTrialsclass I have added thefrom_pklandto_pklmethods. The last one is a@classmethodthat is useful to create instances of the class whentries.pklfile is available from a previous run.The
to_pklmethod saves the current state ofFileTrialsto a pickle file, although this is currently being indeed done for every trial inhyperopt.fmindirectly via thetrials_save_fileargument.self.pkl_filewhich will be responsible for generating atries.pklfile in the same directory as thetries.jsonself._rstateis also added that will store the lastnumpy.random.Generatorof the hyperopt algorithm and will be passed asrstatein thehyperopt.fminfunction so that we warrant thatby restarting we do so with the same history as if we were doing a direct calculation. The initial fixed seed in
trials.rstate = np.random.default_rng(42)here can still be relaxed and provided as input later.2.
hyper_optimization/hyper_scan.pyHyperScanner, namedself.restart_hyperoptwhich is settruein case of the--continueoption inn3fitcommand line (details to be discussed below).hyper_scan_wrapperto allow it to check ifhyperscanner.restart_hyperoptistrue. If so, it will generate an initialFileTrialinstance (trials) fromtries.pkl, which contains by built-in the history of the previous hyperopt and also thetrials.rstateattribute with the previous numpy random generator.3.
scripts/n3fit_exec.pyThis is perhaps the most fragile of the changes and where I would need help to adapt it properly.
N3FitAppI added a new parser--continuethat will be the keyword to hyperopt restarts.runmethod I add a newself.environment.restart = self.args["continue"]attribute.HyperScannerlater is to use it in connection withproduce_hyperscanner. If this istrue, I then updatehyperscan_configwithhyperscan_config.update({'restart': 'true'})and this will later be part of theHyperScanner'ssampling_dictargument.Questions and requested feedback
scripts/n3fit_exec.pyfile to allow for--continueare not optimal. Maybe a more experienced developer could suggest a more convenient way to do so.differences in the obtained final losses for different
kfolds. This might be due to the fact that the seeds for the initialweightsfor each k-fold in difference runs are inherently different (see below).For example, I have done a test in which I make a simple hyperoptimization with 2 trials, and then restart it to make another 2 trials (4 in total). Then I run another experiment and calculate (with the same runcard) 4 trials directly and compare the results.
By looking at the above results, we can see that Restart 2/3 have the same hyperparameters as Direct 2/3, with the 2 folds having different losses however. Maybe the 1st fold can still keep up with the losses but not the second fold.
With the help of @goord and @APJansen, I investigated this issue and have printed the generated random integers passed as
seedsto generate the PDF models for each fold inMoldelTrainer.hyperparametrizable(); see here. They are shown in the Table below:As foreseen, it is clear from the table that the
seeds are different for the second fold every time we run a new calculation, despite the fact that the runs start with the same hyperparameters. This clearly reflects in the different losses shown above. I suspect that if we want to make hyperoptruns completely reproducible we could think of alternatives to
to initialise the seeds.
Solution to the random integer issue described above
4.
model_trainer.pyTo ensure that these
seedsare generated in reproducible way, @RoyStegeman helped me to devise a new form that changes the way they are generated by defining:With all the above modifications, I have repeated my previous 4 trial experiment. The results are shown below for both restart and direct runs:
As seen, we are now able to ensure that both the hyperparameter space and the initial
weightsfor each k-fold are reproducible when restarting.Note
As can be seen from the above (last) table, because the seeds to generate the random integers for each k-fold are now derived from the fixed value
self._nn_seedshere, the generated random integers will always be the same in every trial; see #1824 (comment). This is an important aspect to keep in mind.