Skip to content

Hide the interface so that all samplers can use HyperbandPruner#1168

Closed
HideakiImamura wants to merge 9 commits intooptuna:masterfrom
HideakiImamura:refactor/hyperband/sampler-logic2
Closed

Hide the interface so that all samplers can use HyperbandPruner#1168
HideakiImamura wants to merge 9 commits intooptuna:masterfrom
HideakiImamura:refactor/hyperband/sampler-logic2

Conversation

@HideakiImamura
Copy link
Copy Markdown
Member

@HideakiImamura HideakiImamura commented Apr 26, 2020

Motivation

The current Hyperband implementation in Optuna imposes users who want to implement a new sampler to implement the specific logic. This annoys us because it is exactly the same logic for all samplers. This PR aims to hide the interface so that all samplers can use HyperbandPruner without any additional efforts.

Description of the changes

  • Remove the specific logic for Hyperband in samplers/tpe/sampler.py.
  • Add sampler selection logic to study.py when the pruner is HyperbandPruner.
  • Add special sampler _HyperbandSampler to pruners/hyperband.py.

Note

This PR opposite PR #1196. If PR #1196 is merged, this PR should not be merged, and vice versa.

TODO

  • Implementation
  • Benchmark experiments to verify the no performance difference for RandomSampler and TPESampler.

Benchmark results

(CPU: Intel Core i7-7820HQ, python: 3.6.5)

  • Using kurobako
  • Compare the previous Hyperband and the new Hyperband
  • 2 types of samplers: RandomSampler and TPESampler
  • 5 types of benchmark dataset: HPO-bench-Naval, HPO-bench-Parkinson, HPO-bench-Protein, HPO-bench-Slice, and NASBench (A).

RandomSampler

hpo-bench-naval-ee2999bfd9f1fc303e0c5bdf4a76d7237e1884aafdf1d535316bbc1927817235
hpo-bench-parkinson-7f71d7f6c0b01f3bdcaa8ee5e8e6b9cea2667628dd9623a90fdb5af14754467f
hpo-bench-protein-8832ffb88f2ff6f835eadbd07398b19fce3042047382d8479c80eea0e02c8886
hpo-bench-slice-f512579d4e11ff63a39870d249d3e7aff31b11ef5a1805dc50b4b40b0f30571a
nasbench-a-9779307a379c2e5cf46d8fca53726d8b88d3c0d7752d16589401a6a20ca60850

TPESampler

hpo-bench-naval-ee2999bfd9f1fc303e0c5bdf4a76d7237e1884aafdf1d535316bbc1927817235
hpo-bench-parkinson-7f71d7f6c0b01f3bdcaa8ee5e8e6b9cea2667628dd9623a90fdb5af14754467f
hpo-bench-protein-8832ffb88f2ff6f835eadbd07398b19fce3042047382d8479c80eea0e02c8886
hpo-bench-slice-f512579d4e11ff63a39870d249d3e7aff31b11ef5a1805dc50b4b40b0f30571a
nasbench-a-9779307a379c2e5cf46d8fca53726d8b88d3c0d7752d16589401a6a20ca60850

@codecov-io
Copy link
Copy Markdown

codecov-io commented Apr 26, 2020

Codecov Report

Merging #1168 into master will decrease coverage by 0.01%.
The diff coverage is 85.71%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1168      +/-   ##
==========================================
- Coverage   91.02%   91.00%   -0.02%     
==========================================
  Files         142      142              
  Lines       12276    12300      +24     
==========================================
+ Hits        11174    11194      +20     
- Misses       1102     1106       +4     
Impacted Files Coverage Δ
optuna/samplers/tpe/sampler.py 88.11% <ø> (-0.20%) ⬇️
optuna/study.py 92.63% <66.66%> (-0.28%) ⬇️
optuna/pruners/hyperband.py 87.23% <88.00%> (+0.27%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3573c8d...4d09112. Read the comment docs.

@hvy hvy added the feature Change that does not break compatibility, but affects the public interfaces. label Apr 27, 2020
@HideakiImamura HideakiImamura changed the title [WIP] Hide the interface so that all samplers can use HyperbandPruner Hide the interface so that all samplers can use HyperbandPruner Apr 27, 2020
@hvy hvy self-assigned this Apr 27, 2020
optuna/study.py Outdated
self.sampler = sampler or samplers.TPESampler()
self.pruner = pruner or pruners.MedianPruner()
if isinstance(self.pruner, pruners.HyperbandPruner):
self.sampler = pruners.hyperband._HyperbandSampler(self.sampler, self.pruner)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about using custom getters and setters for self.sampler (and internally store self._sampler = _HyperbandSampler)? Study.sampler is a part of the public interface after all.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we test this in e.g. test_hyperband.py?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented the custom getter and setter of _HyperbandSampler in hyperband.py. I think it addresses the suggestion as pointed out.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I'm not so sure about that. study.sampler here return an instance of _HyperbandSampler. I was imagining having study._sampler being the _HyperbandSampler while study.sampler being a custom, in this case, accessor that return study._sampler.sampler which is the original sampler object given by the user. Similarly for the setter.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that we must also update Trial._suggest and Trial._init_relative_params.

Copy link
Copy Markdown
Member

@hvy hvy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, I left some early comments if you could take a look.

optuna/study.py Outdated
self.sampler = sampler or samplers.TPESampler()
self.pruner = pruner or pruners.MedianPruner()
if isinstance(self.pruner, pruners.HyperbandPruner):
self.sampler = pruners.hyperband._HyperbandSampler(self.sampler, self.pruner)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we test this in e.g. test_hyperband.py?


@sampler.setter
def sampler(self, new_sampler: BaseSampler) -> None:
self._sampler = new_sampler
Copy link
Copy Markdown
Member

@hvy hvy Apr 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we don't need custom getters/setters here as there's no logic involved.

optuna/study.py Outdated
self.sampler = sampler or samplers.TPESampler()
self.pruner = pruner or pruners.MedianPruner()
if isinstance(self.pruner, pruners.HyperbandPruner):
self.sampler = pruners.hyperband._HyperbandSampler(self.sampler, self.pruner)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I'm not so sure about that. study.sampler here return an instance of _HyperbandSampler. I was imagining having study._sampler being the _HyperbandSampler while study.sampler being a custom, in this case, accessor that return study._sampler.sampler which is the original sampler object given by the user. Similarly for the setter.

return _BracketStudy(study, bracket_index)


class _HyperbandSampler(BaseSampler):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about _BracketSampler? I think it better describes its logic/better aligned with _BracketStudy.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the name of _BracketSampler is not good because the _HyperbandSampler depends on the underlying HyperbandPruner but _BracketStudy depends on each bracket of HyperbandPruner. Therefore, I think BracketStudy and _HyperbandSampler are appropriate as they are now.

@hvy hvy added the optuna.pruners Related to the `optuna.pruners` submodule. This is automatically labeled by github-actions. label Apr 28, 2020
@HideakiImamura
Copy link
Copy Markdown
Member Author

HideakiImamura commented Apr 28, 2020

@hvy Thank you for your insightful comments. I would like to talk about how study.sampler looks like from users.

If we implement custom getter/setter for study.sampler, then users need not know about _HyperbandSampler not at all. It is a good news for users. However, in this case, the developers of optuna must know about the difference between study.sampler and study._sampler. We have to replace all access to study.sampler in the current code base with study._sampler. All developers have to remember the difference over the future. I think it is not good news for developers (ours).

On the other hand, if we do not implement a custom getter, then users need know about _HyperbandSampler but any other code base never change. The changes does not effect outside the functionality of Hyperband and its users.

Which do you prefer in light of your experience?

@HideakiImamura
Copy link
Copy Markdown
Member Author

I decided to use the alternative implementation (#1196 ) to achieve this functionality, so I close this PR.

@HideakiImamura HideakiImamura deleted the refactor/hyperband/sampler-logic2 branch May 18, 2021 04:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Change that does not break compatibility, but affects the public interfaces. optuna.pruners Related to the `optuna.pruners` submodule. This is automatically labeled by github-actions.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants