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
Allow get_config to return the same config more than once #487
Conversation
6bcad5d
to
6c9c4d3
Compare
@@ -122,6 +127,11 @@ def __init__( | |||
|
|||
self._hp_ranges = make_hyperparameter_ranges(self.config_space) | |||
self._excl_list = ExclusionList.empty_list(self._hp_ranges) | |||
self._allow_duplicates = allow_duplicates | |||
# Maps ``trial_id`` to configuration. This is used to blacklist |
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 a technicality:
- If not
allow_duplicates
, then_excl_list
collects all configs suggested, so none is suggested 2x - If
allow_duplicates
, then_excl_list
only collects configs for which the trial failed. In this case, we need to know the config for a trial, becauseevaluation_failed
only gets thetrial_id
.
The same code is used in other places, same comment applies.
def evaluation_failed(self, trial_id: str): | ||
if self._allow_duplicates and trial_id in self._config_for_trial_id: | ||
# Blacklist this configuration | ||
self._excl_list.add(self._config_for_trial_id[trial_id]) |
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.
Only get trial_id
here, but need to know the config. This is why _config_for_trial
is needed, only if allow_duplicates
.
def register_pending( | ||
self, | ||
trial_id: str, | ||
config: Optional[dict] = None, | ||
milestone: Optional[int] = None, | ||
): | ||
if self._allow_duplicates and trial_id not in self._config_for_trial_id: | ||
if config is not None: | ||
self._config_for_trial_id[trial_id] = config | ||
else: | ||
logger.warning( | ||
f"register_pending called for trial_id {trial_id} without passing config" | ||
) | ||
|
||
def evaluation_failed(self, trial_id: str): | ||
if self._allow_duplicates and trial_id in self._config_for_trial_id: | ||
# Blacklist this configuration | ||
self._excl_list.add(self._config_for_trial_id[trial_id]) |
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've seen these functions now a couple of times. Is it possible to move it up in the class hierarchy or use a utils function instead?
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, doing that
tst/schedulers/test_searchers.py
Outdated
(KDE, False), | ||
], | ||
) | ||
def test_allow_duplicates_or_not(scheduler_cls, multifid): |
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.
seems natural to move allow_duplicates
here
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.
What do you 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.
add another parametrize
line with allow_duplicates
before the function
Hi @wistuba , I am still working on this. I'd like to extend the unit test to the case where all trials fail, which should filter them out even if |
bc54f77
to
90864c8
Compare
OK, I am done with this now |
02445a3
to
4afa556
Compare
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 multi-inheritance is not a good choice here. What you could do instead is make it part of the base searcher. Searchers who don't support it can simply hard code the choice of allow_duplicates to False.
@@ -290,6 +290,9 @@ def _suggest(self, trial_id: int) -> Optional[TrialSuggestion]: | |||
config = self.searcher.get_config(trial_id=str(trial_id)) | |||
if config is not None: | |||
config = cast_config_values(config, self.config_space) | |||
self.searcher.register_pending( |
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.
are these changes related to the feature?
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, this is something that was missing and came to light because of the new test
@@ -450,3 +451,72 @@ def _restore_from_state(self, state: dict): | |||
|
|||
def set_random_state(self, random_state: np.random.RandomState): | |||
self.random_state = random_state | |||
|
|||
|
|||
class FilterDuplicatesMixin: |
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 mixin should not have functions overlapping with BaseSearcher
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, so this would basically be multiple inheritance then.
The problem with moving this into BaseSearcher:
- BaseSearcher is really supposed to be just an API. David would rightly reject putting code in there
- Some of the searchers do this
allow_duplicates
in a different way
Is multiple inheritance really so evil?
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, I think what I can do, is I can integrate this code into SearcherWithRandomSeed
. This way, the BaseSearcher
remains clean.
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.
Or better: make it a subclass of that one, this is best
29a9bc1
to
1b6b396
Compare
…duplicate filtering across all schedulers
1b6b396
to
d77523c
Compare
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.
There are few lines of code that might be suited in the newly added class.
if allow_duplicates is None: | ||
allow_duplicates = False |
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.
deal with this in parent class
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
if not self.allow_duplicates: | ||
self._excl_list.add(config) # Should not be suggested again |
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.
can't we delegate to parent class as well?
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'll try something
if allow_duplicates is None: | ||
allow_duplicates = False |
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.
same
Issue #, if available: 415
Description of changes:
Introduces flag
allow_duplicates
to searchers, which allows to return the same config more than once. Also contains a new test on searchers, whether they properly implementallow_duplicates=False
(the default).By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.