Skip to content
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

Fix PBT issues with working dir and promotion of max fidelity trials #903

Merged
merged 99 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from 80 commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
b061b91
Use randint instead of choice in PBT.explore
bouthilx May 3, 2022
7729cef
Do not promote trials at last fidelity in PBT
bouthilx May 3, 2022
56ef0d4
Copy experiment when branching trial
bouthilx May 3, 2022
c999116
Fugly fix for trial working dir
bouthilx May 3, 2022
2c3ea09
isort
bouthilx May 3, 2022
de0fe44
Blackify
bouthilx May 3, 2022
8987e1d
Add prepare_trial arg to Runner and exp.workon
bouthilx Jun 22, 2022
f47850c
Adapt PBT to new way of trial working_dir copy
bouthilx Jun 22, 2022
1670090
Handle properly parent id in AlgoWrapper
bouthilx Jun 22, 2022
98b08a1
Compute remaining trials correctly in Runner
bouthilx Jun 22, 2022
04eb5e8
pylint
bouthilx Jun 22, 2022
0d06f22
Adjust functional tests for pbt and pb2
bouthilx Jun 22, 2022
c357931
Fix ExperimentClient.workon typing
bouthilx Jun 23, 2022
5c150f3
Add typing to Runner.__init__
bouthilx Jun 23, 2022
283b87b
Remove unnecessary `is not None`
bouthilx Jun 23, 2022
5113bb4
Add type hints to get_original_parent
bouthilx Jun 23, 2022
d6d3683
Add type hints to branching_rosenbrock
bouthilx Jun 23, 2022
5167330
Add type hints to test_branching_algos
bouthilx Jun 23, 2022
ca6a7cd
Remove unnecessary OrionState
bouthilx Jun 23, 2022
c34d3e8
Merge branch 'hotfix/pbt_cp_dir' of github.com:bouthilx/orion into ho…
bouthilx Jun 23, 2022
c05eb25
Add type hints to test_fidelity_upgrades
bouthilx Jun 23, 2022
b06ac03
Reenable PBT tests
bouthilx Jun 23, 2022
75dfb72
Merge branch 'hotfix/pbt_cp_dir' of github.com:bouthilx/orion into ho…
bouthilx Jun 23, 2022
8c29b3d
Remove useless shutil import
bouthilx Jun 23, 2022
a08e5ce
Add type hints to FakeClient.get_trial
bouthilx Jun 23, 2022
cd2ff72
Remove unnecessary tmp_path cleaning
bouthilx Jun 23, 2022
8b995b4
Merge branch 'hotfix/pbt_cp_dir' of github.com:bouthilx/orion into ho…
bouthilx Jun 23, 2022
0528bcf
Add type hints to new_runner
bouthilx Jun 23, 2022
f59808d
Add type hints to test_callback
bouthilx Jun 23, 2022
d913e82
Add type hints to test_prepare_trial_working_dir
bouthilx Jun 23, 2022
0d612bd
Rewrite test name
bouthilx Jun 23, 2022
82800ee
isort
bouthilx Jun 23, 2022
dbb9177
black
bouthilx Jun 23, 2022
15388bd
Merge branch 'hotfix/pbt_cp_dir' of github.com:bouthilx/orion into ho…
bouthilx Jun 23, 2022
f85d27e
Renable all tests for PBT
bouthilx Jun 23, 2022
90486b6
Remove Literal because only available in py3.8
bouthilx Jun 23, 2022
c63e3d3
isort
bouthilx Jun 23, 2022
dfb2b7b
Remove unrelevant tests
bouthilx Jun 27, 2022
2b45164
Add back Literal from another package
bouthilx Jun 27, 2022
5b7d217
isort
bouthilx Jun 27, 2022
42b549e
Reduce tests while debugging ConfigSpace version issue
bouthilx Jun 28, 2022
5b1c206
Merge branch 'develop' into hotfix/pbt_cp_dir
bouthilx Jul 4, 2022
a08e9e3
Add type-hints to build_params_hist
bouthilx Jul 4, 2022
697dd2a
Restore CI workflow
bouthilx Jul 4, 2022
780e25f
Merge branch 'hotfix/pbt_cp_dir' of github.com:bouthilx/orion into ho…
bouthilx Jul 4, 2022
cb4b35c
Restore all tests
bouthilx Jul 4, 2022
e76da18
Increase population size of PB2 to avoid early convergence
bouthilx Jul 4, 2022
f827cbb
Add ImportOptional and adjust algo imports
bouthilx Jul 5, 2022
5a18590
Fix wrong import_guard names
bouthilx Jul 5, 2022
fe14ac9
Remove useless else
bouthilx Jul 5, 2022
2dd5eeb
Make PB2 pass all the time
bouthilx Jul 5, 2022
b841b2a
Make test based on numpy indepedent of numpy version
bouthilx Jul 5, 2022
1ff694b
Skip PB2 in func tests if not installed
bouthilx Jul 5, 2022
aeacae0
Re-enable TPE fidelity functional test
bouthilx Jul 5, 2022
4432ac7
isort
bouthilx Jul 5, 2022
adae51e
Update doc about PBT
bouthilx Jul 5, 2022
1996834
Handle converging algos in func tests
bouthilx Jul 6, 2022
ece68bb
Fix Hyperband Typing and EVES state_dict
bouthilx Jul 13, 2022
26794fe
Fix PB2 repro
bouthilx Jul 13, 2022
aa29b86
Fix bug in test-suite
bouthilx Jul 13, 2022
198d65e
Use random objectives instead of incremental
bouthilx Jul 13, 2022
e54a1d9
Merge branch 'develop' into hotfix/pbt_cp_dir
bouthilx Jul 13, 2022
70de8be
Fix slips
bouthilx Jul 13, 2022
1edc6fc
Fix isort
bouthilx Jul 13, 2022
ea902b6
Please flake8
bouthilx Jul 14, 2022
70b6eab
Please flake8 once and for all :pray:
bouthilx Jul 14, 2022
f5959c5
Use conftest file for PBT fixtures
bouthilx Jul 14, 2022
c01b74a
Fix Ax tests now that objectives are random
bouthilx Jul 14, 2022
0681ab0
Fix EVES for cat data on random objectives
bouthilx Jul 14, 2022
1736ab0
Fix EVES sampling for close to convergence
bouthilx Jul 14, 2022
c87435f
Fix PB2 with random objectives
bouthilx Jul 14, 2022
224cd3f
Xfail properly EVES and PB2 on cat test
bouthilx Jul 15, 2022
55b2776
Adjust nevergrad tests based on random objectives
bouthilx Jul 15, 2022
99d59e1
Fix nevergrad config fixture
bouthilx Jul 15, 2022
f1fcd06
Fix handling of Fidelity in HEBO
bouthilx Jul 15, 2022
8dca072
Stop crashing PB2 when converging
bouthilx Jul 15, 2022
49ce21f
Relax RNG seed test
bouthilx Jul 15, 2022
1d970e8
Adjust tests of PBT because generate_offsprings does not raise anymore
bouthilx Jul 25, 2022
dab7dc1
Adjust HEBO fixture to new test-suite interface
bouthilx Jul 25, 2022
a593d7c
Adjust suggest_n for HEBO
bouthilx Jul 25, 2022
5b9b340
Swap parent classes of Hyperband
bouthilx Jul 26, 2022
9d5cf4e
Add type hint
bouthilx Jul 26, 2022
fbed0a3
Add type hints to prepare_trial_working_dir
bouthilx Jul 26, 2022
23bea94
Add type hints
bouthilx Jul 26, 2022
dbd4ac2
Remove deprecated note in docstring
bouthilx Jul 26, 2022
a4790a5
Add type hints
bouthilx Jul 26, 2022
0e702f0
Fix style
bouthilx Jul 26, 2022
a885b0d
Merge branch 'develop' into hotfix/pbt_cp_dir
bouthilx Jul 29, 2022
cf886dc
Remove test_trial_working_dir_is_created
bouthilx Jul 29, 2022
d3e4935
Fix control_randomness
bouthilx Jul 29, 2022
e7d5b92
Appease pylint for Protocol classes...
bouthilx Jul 29, 2022
a39c818
Rename Algo protocol to make it more generic
bouthilx Jul 29, 2022
4c94dd2
Using Protocol from typing_ext for py37
bouthilx Jul 29, 2022
7ef65b3
PB2 does not raise when converging, just like PBT
bouthilx Jul 30, 2022
0a67e66
Update src/orion/core/utils/random_state.py
bouthilx Aug 1, 2022
7230143
Remove useless asserts
bouthilx Aug 1, 2022
e3e27d6
Merge branch 'hotfix/pbt_cp_dir' of github.com:bouthilx/orion into ho…
bouthilx Aug 1, 2022
23bd733
Force pymoo dep to 0.5.0 for HEBO
bouthilx Aug 1, 2022
ec910bd
Make test robust to nb of trials
bouthilx Aug 1, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions docs/src/user/algorithms.rst
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,7 @@ Population Based Training (PBT)

.. warning::

PBT is broken in current version v0.2.4. We are working on a fix to be released in v0.2.5,
ETA July 2022.
PBT was broken in version v0.2.4. Make sure to use the latest release.

Population based training is an evolutionary algorithm that evolve trials
from low fidelity levels to high fidelity levels (ex: number of epochs), reusing
Expand Down Expand Up @@ -376,8 +375,7 @@ Population Based Bandits (PB2)

.. warning::

PBT is broken in current version v0.2.4. We are working on a fix to be released in v0.2.5,
ETA July 2022.
PB2 was broken in version v0.2.4. Make sure to use the latest release.

Population Based Bandits is a variant of Population Based Training using probabilistic model to
guide
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
],
"bohb": [
"hpbandster",
"ConfigSpace",
"ConfigSpace==0.5.0",
"sspace @ git+https://github.com/Epistimio/sample-space.git@master#egg=sspace",
],
"pb2": ["GPy"],
Expand Down
6 changes: 2 additions & 4 deletions src/orion/algo/asha.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import copy
import logging
from collections import defaultdict
from typing import Any, Sequence
from typing import Sequence

import numpy
import numpy as np
Expand Down Expand Up @@ -211,9 +211,7 @@ def sample(self, num: int) -> list[Trial]:
def suggest(self, num: int) -> list[Trial]:
return super().suggest(num)

def create_bracket(
self, i: Any, budgets: list[BudgetTuple], iteration: int
) -> ASHABracket:
def create_bracket(self, budgets: list[BudgetTuple], iteration: int) -> ASHABracket:
return ASHABracket(self, budgets, iteration)


Expand Down
35 changes: 21 additions & 14 deletions src/orion/algo/axoptimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@
import copy
from typing import List, Optional

try:
from ax.service.ax_client import AxClient
from ax.service.utils.instantiation import ObjectiveProperties

has_Ax = True
except ImportError:
AxClient = None
ObjectiveProperties = None
has_Ax = False

from orion.algo.base import BaseAlgorithm
from orion.algo.space import Space
from orion.algo.space import Fidelity, Space
from orion.core.utils import format_trials
from orion.core.utils.flatten import flatten
from orion.core.utils.module_import import ImportOptional
from orion.core.worker.transformer import TransformedDimension

with ImportOptional("Ax") as import_optional:
from ax.service.ax_client import AxClient
from ax.service.utils.instantiation import ObjectiveProperties

if import_optional.failed:
AxClient = None # noqa: F811
ObjectiveProperties = None # noqa: F811


class AxOptimizer(BaseAlgorithm):
Expand All @@ -35,7 +35,7 @@ class AxOptimizer(BaseAlgorithm):
generator and for BoTorch-powered models. For the latter models, the
trials generated from the same optimization setup with the same seed,
will be mostly similar, but the exact parameter values may still vary
and trials latter in the optimizations will diverge more and more. This
and trials latter in the optimizations will diverge more and more. This
is because a degree of randomness is essential for high performance of
the Bayesian optimization models and is not controlled by the seed.

Expand Down Expand Up @@ -75,6 +75,8 @@ def __init__(
extra_objectives: Optional[List[str]] = None,
constraints: Optional[List[str]] = None,
):
import_optional.ensure()

extra_objectives = set(extra_objectives if extra_objectives else [])
constraints = constraints if constraints else []

Expand Down Expand Up @@ -202,9 +204,14 @@ def suggest(self, num):
if self.fidelity_index is not None:
# Convert 0-dim arrays into python numbers so their type can
# be validated by Ax
parameters[self.fidelity_index] = float(
self.space[self.fidelity_index].high
assert isinstance(
lebrice marked this conversation as resolved.
Show resolved Hide resolved
self.space[self.fidelity_index], TransformedDimension
)
fidelity_dim = self.space[self.fidelity_index]
while isinstance(fidelity_dim, TransformedDimension):
fidelity_dim = fidelity_dim.original_dimension
assert isinstance(fidelity_dim, Fidelity)
parameters[self.fidelity_index] = float(fidelity_dim.high)

new_trial = format_trials.dict_to_trial(parameters, self.space)

Expand Down
21 changes: 11 additions & 10 deletions src/orion/algo/bohb.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@

import numpy as np

try:
from orion.algo.base import BaseAlgorithm
from orion.algo.parallel_strategy import strategy_factory
from orion.algo.space import Fidelity
from orion.core.utils.format_trials import dict_to_trial
from orion.core.utils.module_import import ImportOptional

with ImportOptional("BOHB") as import_optional:
from hpbandster.optimizers.config_generators.bohb import BOHB as CG_BOHB
from hpbandster.optimizers.iterations import SuccessiveHalving
from sspace.convert import convert_space, reverse, transform

has_HpBandSter = True
except ImportError:
CG_BOHB = None
SuccessiveHalving = None
has_HpBandSter = False
if import_optional.failed:
CG_BOHB = None # noqa: F811
SuccessiveHalving = None # noqa: F811

from orion.algo.base import BaseAlgorithm
from orion.algo.parallel_strategy import strategy_factory
from orion.algo.space import Fidelity
from orion.core.utils.format_trials import dict_to_trial

SPACE_ERROR = """
BOHB cannot be used if space does not contain a fidelity dimension.
Expand Down Expand Up @@ -110,6 +110,7 @@ def __init__(
min_bandwidth=1e-3,
parallel_strategy=None,
): # pylint: disable=too-many-arguments
import_optional.ensure()

if parallel_strategy is None:
parallel_strategy = {
Expand Down
12 changes: 6 additions & 6 deletions src/orion/algo/dehb/dehb.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,19 @@
from orion.algo.dehb.brackets import SHBracketManager
from orion.algo.space import Fidelity, Space
from orion.core.utils import format_trials
from orion.core.utils.module_import import ImportOptional
from orion.core.worker.trial import Trial

try:
with ImportOptional("DEHB") as import_optional:
from dehb.optimizers import DEHB as DEHBImpl
from sspace.convert import convert_space
from sspace.convert import transform as to_orion

IMPORT_ERROR = None
except ImportError as exc:
if import_optional.failed:

class DEHBImpl:
class DEHBImpl: # noqa: F811
pass

IMPORT_ERROR = exc


logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -265,6 +263,8 @@ def __init__(
min_clip: int | None = None,
max_clip: int | None = None,
):
import_optional.ensure()

# Sanity Check
if mutation_strategy not in MUTATION_STRATEGIES:
raise UnsupportedConfiguration(
Expand Down
21 changes: 14 additions & 7 deletions src/orion/algo/evolution_es.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import copy
import importlib
import logging
from typing import Callable, ClassVar, Sequence
from typing import Callable, ClassVar, Sequence, TypeVar

import numpy as np

Expand Down Expand Up @@ -89,7 +89,10 @@ def compute_budgets(
return budgets_eves


class EvolutionES(Hyperband):
BracketT = TypeVar("BracketT", bound="BracketEVES")


class EvolutionES(Hyperband[BracketT]):
"""EvolutionES formulates hyperparameter optimization as an evolution.

For more information on the algorithm,
Expand Down Expand Up @@ -137,14 +140,15 @@ def __init__(
mutate: str | dict | None = None,
max_retries: int = 1000,
):
self.mutate = mutate

super().__init__(space, seed=seed, repetitions=repetitions)
pair = nums_population // 2
mutate_ratio = 0.3
self.nums_population = nums_population
self.nums_comp_pairs = pair
self.max_retries = max_retries
self.mutate_ratio = mutate_ratio
self.mutate = mutate
self.nums_mutate_gene = (
int((len(self.space.values()) - 1) * mutate_ratio)
if int((len(self.space.values()) - 1) * mutate_ratio) > 0
Expand All @@ -168,11 +172,14 @@ def __init__(
pair,
)

self.brackets: list[BracketEVES] = [
BracketEVES(self, bracket_budgets, 1) for bracket_budgets in self.budgets
]
self.brackets: list[BracketT] = self.create_brackets()
self.seed_rng(seed)

def create_bracket(
self, bracket_budgets: list[BudgetTuple], iteration: int
) -> BracketT:
return BracketEVES(self, bracket_budgets, iteration)

@property
def state_dict(self) -> dict:
"""Return a state dict that can be used to reset the state of the algorithm."""
Expand All @@ -189,7 +196,7 @@ def set_state(self, state_dict: dict) -> None:
self.hurdles = state_dict["hurdles"]
super().set_state(state_dict)

def _get_bracket(self, trial: Trial) -> BracketEVES:
def _get_bracket(self, trial: Trial) -> BracketT:
"""Get the bracket of a trial during observe"""
return self.brackets[-1]

Expand Down
60 changes: 19 additions & 41 deletions src/orion/algo/hebo/hebo_algo.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"""
from __future__ import annotations

import contextlib
import copy
import typing
import warnings
Expand All @@ -19,26 +18,24 @@
from typing_extensions import Literal, TypedDict # type: ignore

from orion.algo.base import BaseAlgorithm
from orion.algo.hebo.random_state import RandomState
from orion.algo.space import Dimension, Fidelity, Space
from orion.core.utils.format_trials import dict_to_trial
from orion.core.utils.module_import import ImportOptional
from orion.core.utils.random_state import RandomState, control_randomness
from orion.core.worker.transformer import TransformedDimension
from orion.core.worker.trial import Trial

_HEBO_REQUIRED_ERROR = None
try:
with ImportOptional("HEBO") as import_optional:
import hebo
from hebo.acquisitions.acq import MACE, Acquisition
from hebo.design_space import DesignSpace
from hebo.design_space.param import Parameter
from torch.quasirandom import SobolEngine

except ImportError as err:
MACE = object
_HEBO_REQUIRED_ERROR = ImportError(
"The HEBO package is not installed. Install it with `pip install orion[hebo]`"
)
if import_optional.failed:
MACE = object # noqa: F811

if typing.TYPE_CHECKING and _HEBO_REQUIRED_ERROR:
if typing.TYPE_CHECKING and import_optional.failed:
Acquisition = object # noqa
DesignSpace = object # noqa
Parameter = object # noqa
Expand Down Expand Up @@ -137,8 +134,7 @@ def __init__(
seed: int | None = None,
parameters: Parameters | dict | None = None,
):
if _HEBO_REQUIRED_ERROR:
raise _HEBO_REQUIRED_ERROR
import_optional.ensure()

super().__init__(space)
if isinstance(parameters, dict):
Expand Down Expand Up @@ -167,7 +163,7 @@ def __init__(

self.hebo_space: DesignSpace = orion_space_to_hebo_space(self.space)

with self._control_randomness():
with control_randomness(self.random_state):
self.model = hebo.optimizers.hebo.HEBO(
space=self.hebo_space,
model_name=self.parameters.model_name,
Expand Down Expand Up @@ -238,7 +234,7 @@ def suggest(self, num: int) -> list[Trial]:
A list of trials representing values suggested by the algorithm.
"""
trials: list[Trial] = []
with self._control_randomness():
with control_randomness(self.random_state):
v: pd.DataFrame = self.model.suggest(n_suggestions=num)
point_dicts: dict[int, dict] = v.to_dict(orient="index") # type: ignore

Expand Down Expand Up @@ -280,7 +276,7 @@ def observe(self, trials: list[Trial]) -> None:

x_df = pd.DataFrame(new_xs)
y_array = np.array(new_ys).reshape([-1, 1])
with self._control_randomness():
with control_randomness(self.random_state):
self.model.observe(X=x_df, y=y_array)

def _hebo_params_to_orion_params(self, hebo_params: dict) -> dict:
Expand Down Expand Up @@ -330,12 +326,12 @@ def _orion_params_to_hebo_params(self, orion_params: dict) -> dict:
params = {}
for name, value in orion_params.items():
orion_dim: Dimension = self.space[name]
hebo_dim: Parameter = self.hebo_space.paras[name]

if orion_dim.type == "fidelity":
continue
from hebo.design_space.categorical_param import CategoricalPara

hebo_dim: Parameter = self.hebo_space.paras[name]

if isinstance(hebo_dim, CategoricalPara):
if (
value not in hebo_dim.categories
Expand All @@ -353,33 +349,15 @@ def _params_to_trial(self, orion_params: dict) -> Trial:
# Need to convert the {name: value} of point_dict into this format for Orion's Trial.
# Add the max value for the Fidelity dimensions, if any.
if self.fidelity_index is not None:
fidelity_dim: Fidelity = self.space[self.fidelity_index]
orion_params[self.fidelity_index] = fidelity_dim.high
fidelity_dim = self.space[self.fidelity_index]
assert isinstance(fidelity_dim, TransformedDimension)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here

while isinstance(fidelity_dim, TransformedDimension):
fidelity_dim = fidelity_dim.original_dimension
assert isinstance(fidelity_dim, Fidelity)
orion_params[self.fidelity_index] = float(fidelity_dim.high)
trial: Trial = dict_to_trial(orion_params, space=self.space)
return trial

@contextlib.contextmanager
def _control_randomness(self):
"""Seeds the randomness inside the indented block of code using `self.random_state`.

NOTE: This only has an effect if `seed_rng` was called previously, i.e. if
`self.random_state` is not None.
"""
if self.random_state is None:
yield
return

# Save the initial random state.
initial_rng_state = RandomState.current()
# Set the random state.
self.random_state.set()
yield
# Update the random state stored on `self`, so that the changes inside the block are
# reflected in the RandomState object.
self.random_state = RandomState.current()
# Reset the initial state.
initial_rng_state.set()


def orion_space_to_hebo_space(space: Space) -> DesignSpace:
"""Get the HEBO-equivalent space for the `Space` `space`.
Expand Down
Loading