From 7f8a43bab61225415b7ed215d2525df76acf4e1d Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Wed, 22 May 2024 20:30:17 +0000 Subject: [PATCH 01/21] minimal implementation of mutli-fidelity --- .vscode/settings.json | 6 +- .../optimizers/mlos_core_optimizer.py | 2 +- .../bayesian_optimizers/smac_optimizer.py | 348 ++++++++++--- .../mlos_core/optimizers/flaml_optimizer.py | 8 +- mlos_core/mlos_core/optimizers/optimizer.py | 37 +- .../mlos_core/optimizers/random_optimizer.py | 12 +- .../optimizers/bayesian_optimizers_test.py | 5 +- .../optimizers/optimizer_multiobj_test.py | 6 +- .../tests/optimizers/optimizer_test.py | 26 +- .../notebooks/BayesianOptimization.ipynb | 489 ++++++++---------- 10 files changed, 568 insertions(+), 371 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2c8098f9d9..6d2ceb53f5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,7 +17,6 @@ // See Also: // - https://github.com/microsoft/vscode/issues/2809#issuecomment-1544387883 // - mlos_bench/config/schemas/README.md - { "fileMatch": [ "mlos_bench/mlos_bench/tests/config/schemas/environments/test-cases/**/*.jsonc", @@ -139,5 +138,8 @@ "--log-level=DEBUG", "." ], - "python.testing.unittestEnabled": false + "python.testing.unittestEnabled": false, + "cSpell.words": [ + "SOBOL" + ] } diff --git a/mlos_bench/mlos_bench/optimizers/mlos_core_optimizer.py b/mlos_bench/mlos_bench/optimizers/mlos_core_optimizer.py index 7747035c13..d126df4f48 100644 --- a/mlos_bench/mlos_bench/optimizers/mlos_core_optimizer.py +++ b/mlos_bench/mlos_bench/optimizers/mlos_core_optimizer.py @@ -180,7 +180,7 @@ def suggest(self) -> TunableGroups: tunables = super().suggest() if self._start_with_defaults: _LOG.info("Use default values for the first trial") - df_config = self._opt.suggest(defaults=self._start_with_defaults) + df_config, _ = self._opt.suggest(defaults=self._start_with_defaults) self._start_with_defaults = False _LOG.info("Iteration %d :: Suggest:\n%s", self._iter, df_config) return tunables.assign( diff --git a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py index 2e58a8e057..e53dc67f4f 100644 --- a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py +++ b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py @@ -7,16 +7,27 @@ See Also: """ +import inspect +import threading from logging import warning from pathlib import Path -from typing import Dict, List, Optional, Union, TYPE_CHECKING from tempfile import TemporaryDirectory +from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union from warnings import warn import ConfigSpace +import numpy as np import numpy.typing as npt import pandas as pd +from smac import HyperparameterOptimizationFacade as Optimizer_Smac +from smac import Scenario +from smac.facade import AbstractFacade +from smac.initial_design import AbstractInitialDesign, SobolInitialDesign +from smac.intensifier.abstract_intensifier import AbstractIntensifier +from smac.main.config_selector import ConfigSelector +from smac.random_design.probability_design import ProbabilityRandomDesign +from smac.runhistory import StatusType, TrialInfo, TrialValue from mlos_core.optimizers.bayesian_optimizers.bayesian_optimizer import BaseBayesianOptimizer from mlos_core.spaces.adapters.adapter import BaseSpaceAdapter from mlos_core.spaces.adapters.identity_adapter import IdentityAdapter @@ -27,18 +38,25 @@ class SmacOptimizer(BaseBayesianOptimizer): Wrapper class for SMAC based Bayesian optimization. """ - def __init__(self, *, # pylint: disable=too-many-locals,too-many-arguments - parameter_space: ConfigSpace.ConfigurationSpace, - optimization_targets: List[str], - space_adapter: Optional[BaseSpaceAdapter] = None, - seed: Optional[int] = 0, - run_name: Optional[str] = None, - output_directory: Optional[str] = None, - max_trials: int = 100, - n_random_init: Optional[int] = None, - max_ratio: Optional[float] = None, - use_default_config: bool = False, - n_random_probability: float = 0.1): + def __init__( + self, # pylint: disable=too-many-locals + *, # pylint: disable=too-many-locals + parameter_space: ConfigSpace.ConfigurationSpace, + optimization_targets: str | List[str] | None = None, + space_adapter: Optional[BaseSpaceAdapter] = None, + seed: Optional[int] = 0, + run_name: Optional[str] = None, + output_directory: Optional[str] = None, + max_trials: int = 100, + n_random_init: Optional[int] = None, + max_ratio: Optional[float] = None, + use_default_config: bool = False, + n_random_probability: float = 0.1, + facade: Type[AbstractFacade] = Optimizer_Smac, + intensifier: Optional[Type[AbstractIntensifier]] = None, + initial_design_class: Type[AbstractInitialDesign] = SobolInitialDesign, + **kwargs: Any, + ): """ Instantiate a new SMAC optimizer wrapper. @@ -87,6 +105,22 @@ def __init__(self, *, # pylint: disable=too-many-locals,too-many-arguments n_random_probability: float Probability of choosing to evaluate a random configuration during optimization. Defaults to `0.1`. Setting this to a higher value favors exploration over exploitation. + + facade: AbstractFacade + sets the facade to use for SMAC + + intensifier: Optional[Type[AbstractIntensifier]] + Sets the intensifier type to use in the optimizer. If not set, the + default intensifier + from the facade will be used + + initial_design_class: AbstractInitialDesign + Sets the initial design class to be used in the optimizer. + Defaults to SobolInitialDesign + + **kwargs: + Additional arguments to be passed to the + scenerio, and intensifier """ super().__init__( parameter_space=parameter_space, @@ -97,17 +131,10 @@ def __init__(self, *, # pylint: disable=too-many-locals,too-many-arguments # Declare at the top because we need it in __del__/cleanup() self._temp_output_directory: Optional[TemporaryDirectory] = None - # pylint: disable=import-outside-toplevel - from smac import HyperparameterOptimizationFacade as Optimizer_Smac - from smac import Scenario - from smac.intensifier.abstract_intensifier import AbstractIntensifier - from smac.main.config_selector import ConfigSelector - from smac.random_design.probability_design import ProbabilityRandomDesign - from smac.runhistory import TrialInfo - # Store for TrialInfo instances returned by .ask() - self.trial_info_map: Dict[ConfigSpace.Configuration, TrialInfo] = {} - + self.trial_info_df: pd.DataFrame = pd.DataFrame( + columns=["Configuration", "Context", "TrialInfo", "TrialValue"] + ) # The default when not specified is to use a known seed (0) to keep results reproducible. # However, if a `None` seed is explicitly provided, we let a random seed be produced by SMAC. # https://automl.github.io/SMAC3/main/api/smac.scenario.html#smac.scenario.Scenario @@ -138,9 +165,19 @@ def __init__(self, *, # pylint: disable=too-many-locals,too-many-arguments n_trials=max_trials, seed=seed or -1, # if -1, SMAC will generate a random seed internally n_workers=1, # Use a single thread for evaluating trials + **SmacOptimizer._filter_kwargs(Scenario, **kwargs), + ) + + config_selector: ConfigSelector = facade.get_config_selector( + scenario, retrain_after=1 ) - intensifier: AbstractIntensifier = Optimizer_Smac.get_intensifier(scenario, max_config_calls=1) - config_selector: ConfigSelector = Optimizer_Smac.get_config_selector(scenario, retrain_after=1) + + if intensifier is None: + intensifier_instance = facade.get_intensifier(scenario) + else: + intensifier_instance = intensifier( + scenario, **SmacOptimizer._filter_kwargs(intensifier, **kwargs) + ) # TODO: When bulk registering prior configs to rewarm the optimizer, # there is a way to inform SMAC's initial design that we have @@ -174,10 +211,9 @@ def __init__(self, *, # pylint: disable=too-many-locals,too-many-arguments initial_design_args['max_ratio'] = max_ratio # Use the default InitialDesign from SMAC. - # (currently SBOL instead of LatinHypercube due to better uniformity + # (currently SOBOL instead of LatinHypercube due to better uniformity # for initial sampling which results in lower overall samples required) - initial_design = Optimizer_Smac.get_initial_design(**initial_design_args) # type: ignore[arg-type] - # initial_design = LatinHypercubeInitialDesign(**initial_design_args) # type: ignore[arg-type] + initial_design = initial_design_class(**initial_design_args) # Workaround a bug in SMAC that doesn't pass the seed to the random # design when generated a random_design for itself via the @@ -185,21 +221,20 @@ def __init__(self, *, # pylint: disable=too-many-locals,too-many-arguments assert isinstance(n_random_probability, float) and n_random_probability >= 0 random_design = ProbabilityRandomDesign(probability=n_random_probability, seed=scenario.seed) - self.base_optimizer = Optimizer_Smac( + self.base_optimizer = facade( scenario, SmacOptimizer._dummy_target_func, initial_design=initial_design, - intensifier=intensifier, + intensifier=intensifier_instance, random_design=random_design, config_selector=config_selector, - multi_objective_algorithm=Optimizer_Smac.get_multi_objective_algorithm( - scenario, - # objective_weights=[1, 2], # TODO: pass weights as constructor args - ), overwrite=True, logging_level=False, # Use the existing logger + **SmacOptimizer._filter_kwargs(facade, **kwargs), ) + self.lock = threading.Lock() + def __del__(self) -> None: # Best-effort attempt to clean up, in case the user forgets to call .cleanup() self.cleanup() @@ -221,7 +256,41 @@ def n_random_init(self) -> int: return self.base_optimizer._initial_design._n_configs @staticmethod - def _dummy_target_func(config: ConfigSpace.Configuration, seed: int = 0) -> None: + def _filter_kwargs(function: Callable, **kwargs: Any) -> Dict[str, Any]: + """ + Filters arguments provided in the kwargs dictionary to be restricted to the arguments legal for + the called function. + + Parameters + ---------- + function : Callable + function over which we filter kwargs for. + kwargs: + kwargs that we are filtering for the target function + + Returns + ------- + dict + kwargs with the non-legal argument filtered out + """ + sig = inspect.signature(function) + filter_keys = [ + param.name + for param in sig.parameters.values() + if param.kind == param.POSITIONAL_OR_KEYWORD + ] + filtered_dict = { + filter_key: kwargs[filter_key] for filter_key in filter_keys & kwargs.keys() + } + return filtered_dict + + @staticmethod + def _dummy_target_func( + config: ConfigSpace.Configuration, + seed: int = 0, + budget: float = 1, + instance: object = None, + ) -> None: """Dummy target function for SMAC optimizer. Since we only use the ask-and-tell interface, this is never called. @@ -233,6 +302,12 @@ def _dummy_target_func(config: ConfigSpace.Configuration, seed: int = 0) -> None seed : int Random seed to use for the target function. Not actually used. + + budget : int + The budget that was used for evaluating the configuration. + + instance : object + The instance that the configuration was evaluated on. """ # NOTE: Providing a target function when using the ask-and-tell interface is an imperfection of the API # -- this planned to be fixed in some future release: https://github.com/automl/SMAC3/issues/946 @@ -251,25 +326,70 @@ def _register(self, configurations: pd.DataFrame, Scores from running the configurations. The index is the same as the index of the configurations. context : pd.DataFrame - Not Yet Implemented. + Context of the request that is being registered. """ - from smac.runhistory import StatusType, TrialInfo, TrialValue # pylint: disable=import-outside-toplevel - - if context is not None: - warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) - - # Register each trial (one-by-one) - for (config, (_i, score)) in zip(self._to_configspace_configs(configurations), scores.iterrows()): - # Retrieve previously generated TrialInfo (returned by .ask()) or create new TrialInfo instance - info: TrialInfo = self.trial_info_map.get( - config, TrialInfo(config=config, seed=self.base_optimizer.scenario.seed)) - value = TrialValue(cost=list(score.astype(float)), time=0.0, status=StatusType.SUCCESS) - self.base_optimizer.tell(info, value, save=False) - - # Save optimizer once we register all configs - self.base_optimizer.optimizer.save() + with self.lock: + # Register each trial (one-by-one) + contexts: Union[List[pd.Series], List[None]] = _to_context(context) or [ + None for _ in scores # type: ignore[misc] + ] + for config, score, ctx in zip( + self._to_configspace_configs(configurations), + scores.values.tolist(), + contexts, + ): + value: TrialValue = TrialValue( + cost=score, time=0.0, status=StatusType.SUCCESS + ) - def _suggest(self, context: Optional[pd.DataFrame] = None) -> pd.DataFrame: + matching: pd.Series[bool] + if ctx is None: + matching = self.trial_info_df["Configuration"] == config + else: + matching = ( + self.trial_info_df["Configuration"] == config + ) & pd.Series( + [df_ctx.equals(ctx) for df_ctx in self.trial_info_df["Context"]] + ) + + # make a new entry + if sum(matching) > 0: + info = self.trial_info_df[matching]["TrialInfo"].iloc[-1] + self.trial_info_df.at[list(matching).index(True), "TrialValue"] = ( + value + ) + else: + if ctx is None or "budget" not in ctx or "instance" not in ctx: + info = TrialInfo( + config=config, seed=self.base_optimizer.scenario.seed + ) + self.trial_info_df.loc[len(self.trial_info_df.index)] = [ + config, + info, + info, + value, + ] + else: + info = TrialInfo( + config=config, + seed=self.base_optimizer.scenario.seed, + budget=ctx["budget"], + instance=ctx["instance"], + ) + self.trial_info_df.loc[len(self.trial_info_df.index)] = [ + config, + ctx, + info, + value, + ] + self.base_optimizer.tell(info, value, save=False) + + # Save optimizer once we register all configs + self.base_optimizer.optimizer.save() + + def _suggest( + self, context: Optional[pd.DataFrame] = None + ) -> Tuple[pd.DataFrame, Optional[pd.DataFrame]]: """Suggests a new configuration. Parameters @@ -281,20 +401,34 @@ def _suggest(self, context: Optional[pd.DataFrame] = None) -> pd.DataFrame: ------- configuration : pd.DataFrame Pandas dataframe with a single row. Column names are the parameter names. + + context : pd.DataFrame + Pandas dataframe with a single row containing the context. + Column names are the budget, seed, and instance of the evaluation, if valid. """ - if TYPE_CHECKING: - from smac.runhistory import TrialInfo # pylint: disable=import-outside-toplevel,unused-import + with self.lock: + if context is not None: + warn( + f"Not Implemented: Ignoring context {list(context.columns)}", + UserWarning, + ) - if context is not None: - warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) + trial: TrialInfo = self.base_optimizer.ask() + trial.config.is_valid_configuration() + self.optimizer_parameter_space.check_configuration(trial.config) + assert trial.config.config_space == self.optimizer_parameter_space + + config_df = self._extract_config(trial) + context_df = SmacOptimizer._extract_context(trial) + + self.trial_info_df.loc[len(self.trial_info_df.index)] = [ + trial.config, + context_df.iloc[0], + trial, + None, + ] - trial: TrialInfo = self.base_optimizer.ask() - trial.config.is_valid_configuration() - self.optimizer_parameter_space.check_configuration(trial.config) - assert trial.config.config_space == self.optimizer_parameter_space - self.trial_info_map[trial.config] = trial - config_df = pd.DataFrame([trial.config], columns=list(self.optimizer_parameter_space.keys())) - return config_df + return config_df, context_df def register_pending(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> None: raise NotImplementedError() @@ -333,9 +467,12 @@ def acquisition_function(self, configurations: pd.DataFrame, context: Optional[p return self.base_optimizer._config_selector._acquisition_function(configs).reshape(-1,) def cleanup(self) -> None: - if self._temp_output_directory is not None: - self._temp_output_directory.cleanup() - self._temp_output_directory = None + try: + if self._temp_output_directory is not None: + self._temp_output_directory.cleanup() + self._temp_output_directory = None + except AttributeError: + warning("_temp_output_directory does not exist.") def _to_configspace_configs(self, configurations: pd.DataFrame) -> List[ConfigSpace.Configuration]: """Convert a dataframe of configurations to a list of ConfigSpace configurations. @@ -354,3 +491,82 @@ def _to_configspace_configs(self, configurations: pd.DataFrame) -> List[ConfigSp ConfigSpace.Configuration(self.optimizer_parameter_space, values=config.to_dict()) for (_, config) in configurations.astype('O').iterrows() ] + + @staticmethod + def _extract_context(trial: TrialInfo) -> pd.DataFrame: + """Convert TrialInfo to a DataFrame. + + Parameters + ---------- + trial : TrialInfo + The trial to extract. + + Returns + ------- + context : pd.DataFrame + Pandas dataframe with a single row containing the context. + Column names are the budget and instance of the evaluation, if valid. + """ + return pd.DataFrame( + [[trial.instance, trial.seed, trial.budget]], + columns=["instance", "seed", "budget"], + ) + + def _extract_config(self, trial: TrialInfo) -> pd.DataFrame: + return pd.DataFrame( + [trial.config], columns=list(self.optimizer_parameter_space.keys()) + ) + + def get_observations_full(self) -> pd.DataFrame: + """Returns the observations as a dataframe with additional info. + + Returns + ------- + observations : pd.DataFrame + Dataframe of observations. The columns are parameter names and "score" for the score, each row is an observation. + """ + if len(self.trial_info_df) == 0: + raise ValueError("No observations registered yet.") + + return self.trial_info_df + + def get_best_observation(self) -> pd.DataFrame: + """Returns the best observation so far as a dataframe. + + Returns + ------- + best_observation : pd.DataFrame + Dataframe with a single row containing the best observation. The columns are parameter names and "score" for the score. + """ + if len(self._observations) == 0: + raise ValueError("No observations registered yet.") + + observations = self._observations + + max_budget = np.nan + budgets = [ + context["budget"].max() + for _, _, context in self._observations + if context is not None + ] + if len(budgets) > 0: + max_budget = max(budgets) + + if max_budget is not np.nan: + observations = [ + (config, score, context) + for config, score, context in self._observations + if context is not None and context["budget"].max() == max_budget + ] + + configs = pd.concat([config for config, _, _ in observations]) + scores = pd.concat([score for _, score, _ in observations]) + configs["score"] = scores + + return configs.nsmallest(1, columns="score") + + +def _to_context(contexts: Optional[pd.DataFrame]) -> Optional[List[pd.Series]]: + if contexts is None: + return None + return [idx_series[1] for idx_series in contexts.iterrows()] diff --git a/mlos_core/mlos_core/optimizers/flaml_optimizer.py b/mlos_core/mlos_core/optimizers/flaml_optimizer.py index 14b4433f6c..3f7dbe763a 100644 --- a/mlos_core/mlos_core/optimizers/flaml_optimizer.py +++ b/mlos_core/mlos_core/optimizers/flaml_optimizer.py @@ -6,7 +6,7 @@ Contains the FlamlOptimizer class. """ -from typing import Dict, List, NamedTuple, Optional, Union +from typing import Dict, List, NamedTuple, Optional, Tuple, Union from warnings import warn import ConfigSpace @@ -108,7 +108,9 @@ def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, self.evaluated_samples[cs_config] = EvaluatedSample(config=config.to_dict(), score=score) - def _suggest(self, context: Optional[pd.DataFrame] = None) -> pd.DataFrame: + def _suggest( + self, context: Optional[pd.DataFrame] = None + ) -> Tuple[pd.DataFrame, Optional[pd.DataFrame]]: """Suggests a new configuration. Sampled at random using ConfigSpace. @@ -126,7 +128,7 @@ def _suggest(self, context: Optional[pd.DataFrame] = None) -> pd.DataFrame: if context is not None: warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) config: dict = self._get_next_config() - return pd.DataFrame(config, index=[0]) + return pd.DataFrame(config, index=[0]), None def register_pending(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> None: diff --git a/mlos_core/mlos_core/optimizers/optimizer.py b/mlos_core/mlos_core/optimizers/optimizer.py index bd26619754..71875bdfb1 100644 --- a/mlos_core/mlos_core/optimizers/optimizer.py +++ b/mlos_core/mlos_core/optimizers/optimizer.py @@ -26,7 +26,7 @@ class BaseOptimizer(metaclass=ABCMeta): def __init__(self, *, parameter_space: ConfigSpace.ConfigurationSpace, - optimization_targets: List[str], + optimization_targets: str | List[str] | None = None, space_adapter: Optional[BaseSpaceAdapter] = None): """ Create a new instance of the base optimizer. @@ -52,6 +52,8 @@ def __init__(self, *, self._observations: List[Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame]]] = [] self._has_context: Optional[bool] = None self._pending_observations: List[Tuple[pd.DataFrame, Optional[pd.DataFrame]]] = [] + self.delayed_config: Optional[pd.DataFrame] = None + self.delayed_context: Optional[pd.DataFrame] = None def __repr__(self) -> str: return f"{self.__class__.__name__}(space_adapter={self.space_adapter})" @@ -76,8 +78,10 @@ def register(self, configurations: pd.DataFrame, scores: pd.DataFrame, Not Yet Implemented. """ # Do some input validation. - assert set(scores.columns) == set(self._optimization_targets), \ - "Mismatched optimization targets." + if type(self._optimization_targets) is str: + assert self._optimization_targets in scores.columns, "Mismatched optimization targets." + if type(self._optimization_targets) is list: + assert set(scores.columns) >= set(self._optimization_targets), "Mismatched optimization targets." assert self._has_context is None or self._has_context ^ (context is None), \ "Context must always be added or never be added." assert len(configurations) == len(scores), \ @@ -113,7 +117,9 @@ def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, """ pass # pylint: disable=unnecessary-pass # pragma: no cover - def suggest(self, context: Optional[pd.DataFrame] = None, defaults: bool = False) -> pd.DataFrame: + def suggest( + self, context: Optional[pd.DataFrame] = None, defaults: bool = False + ) -> Tuple[pd.DataFrame, Optional[pd.DataFrame]]: """ Wrapper method, which employs the space adapter (if any), after suggesting a new configuration. @@ -129,13 +135,26 @@ def suggest(self, context: Optional[pd.DataFrame] = None, defaults: bool = False ------- configuration : pd.DataFrame Pandas dataframe with a single row. Column names are the parameter names. + + context : pd.DataFrame + Pandas dataframe with a single row containing the context. + Column names are the budget, seed, and instance of the evaluation, if valid. """ if defaults: - configuration = config_to_dataframe(self.parameter_space.get_default_configuration()) + self.delayed_config, self.delayed_context = self._suggest(context) + + configuration: pd.DataFrame = config_to_dataframe( + self.parameter_space.get_default_configuration() + ) + context = self.delayed_context if self.space_adapter is not None: configuration = self.space_adapter.inverse_transform(configuration) else: - configuration = self._suggest(context) + if self.delayed_config is None: + configuration, context = self._suggest(context) + else: + configuration, context = self.delayed_config, self.delayed_context + self.delayed_config, self.delayed_context = None, None assert len(configuration) == 1, \ "Suggest must return a single configuration." assert set(configuration.columns).issubset(set(self.optimizer_parameter_space)), \ @@ -144,10 +163,12 @@ def suggest(self, context: Optional[pd.DataFrame] = None, defaults: bool = False configuration = self._space_adapter.transform(configuration) assert set(configuration.columns).issubset(set(self.parameter_space)), \ "Space adapter produced a configuration that does not match the expected parameter space." - return configuration + return configuration, context @abstractmethod - def _suggest(self, context: Optional[pd.DataFrame] = None) -> pd.DataFrame: + def _suggest( + self, context: Optional[pd.DataFrame] = None + ) -> Tuple[pd.DataFrame, Optional[pd.DataFrame]]: """Suggests a new configuration. Parameters diff --git a/mlos_core/mlos_core/optimizers/random_optimizer.py b/mlos_core/mlos_core/optimizers/random_optimizer.py index f81092a65d..8129f65b2e 100644 --- a/mlos_core/mlos_core/optimizers/random_optimizer.py +++ b/mlos_core/mlos_core/optimizers/random_optimizer.py @@ -6,7 +6,7 @@ Contains the RandomOptimizer class. """ -from typing import Optional +from typing import Optional, Tuple from warnings import warn import pandas as pd @@ -45,7 +45,9 @@ def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) # should we pop them from self.pending_observations? - def _suggest(self, context: Optional[pd.DataFrame] = None) -> pd.DataFrame: + def _suggest( + self, context: Optional[pd.DataFrame] = None + ) -> Tuple[pd.DataFrame, Optional[pd.DataFrame]]: """Suggests a new configuration. Sampled at random using ConfigSpace. @@ -59,11 +61,15 @@ def _suggest(self, context: Optional[pd.DataFrame] = None) -> pd.DataFrame: ------- configuration : pd.DataFrame Pandas dataframe with a single row. Column names are the parameter names. + + context : pd.DataFrame + Pandas dataframe with a single row containing the context. + Column names are the budget, seed, and instance of the evaluation, if valid. """ if context is not None: # not sure how that works here? warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) - return pd.DataFrame(dict(self.optimizer_parameter_space.sample_configuration()), index=[0]) + return pd.DataFrame(dict(self.optimizer_parameter_space.sample_configuration()), index=[0]), None def register_pending(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> None: diff --git a/mlos_core/mlos_core/tests/optimizers/bayesian_optimizers_test.py b/mlos_core/mlos_core/tests/optimizers/bayesian_optimizers_test.py index 69ce4f8dff..4e91ba89cc 100644 --- a/mlos_core/mlos_core/tests/optimizers/bayesian_optimizers_test.py +++ b/mlos_core/mlos_core/tests/optimizers/bayesian_optimizers_test.py @@ -34,13 +34,10 @@ def test_context_not_implemented_warning(configuration_space: CS.ConfigurationSp optimization_targets=['score'], **kwargs ) - suggestion = optimizer.suggest() + suggestion, _ = optimizer.suggest() scores = pd.DataFrame({'score': [1]}) context = pd.DataFrame([["something"]]) - with pytest.raises(UserWarning): - optimizer.register(suggestion, scores, context=context) - with pytest.raises(UserWarning): optimizer.suggest(context=context) diff --git a/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py b/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py index 888d07ff54..159997d1ad 100644 --- a/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py +++ b/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py @@ -61,7 +61,7 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: optimizer.get_observations() for _ in range(max_iterations): - suggestion = optimizer.suggest() + suggestion, context = optimizer.suggest() assert isinstance(suggestion, pd.DataFrame) assert set(suggestion.columns) == {'x', 'y'} # Check suggestion values are the expected dtype @@ -76,12 +76,11 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: observation = objective(suggestion) assert isinstance(observation, pd.DataFrame) assert set(observation.columns) == {'score', 'other_score'} - optimizer.register(suggestion, observation) + optimizer.register(suggestion, observation, context) (best_config, best_score, best_context) = optimizer.get_best_observations() assert isinstance(best_config, pd.DataFrame) assert isinstance(best_score, pd.DataFrame) - assert best_context is None assert set(best_config.columns) == {'x', 'y'} assert set(best_score.columns) == {'score', 'other_score'} assert best_config.shape == (1, 2) @@ -90,7 +89,6 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: (all_configs, all_scores, all_contexts) = optimizer.get_observations() assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) - assert all_contexts is None assert set(all_configs.columns) == {'x', 'y'} assert set(all_scores.columns) == {'score', 'other_score'} assert all_configs.shape == (max_iterations, 2) diff --git a/mlos_core/mlos_core/tests/optimizers/optimizer_test.py b/mlos_core/mlos_core/tests/optimizers/optimizer_test.py index 67c7eddf3b..4d3aea7d77 100644 --- a/mlos_core/mlos_core/tests/optimizers/optimizer_test.py +++ b/mlos_core/mlos_core/tests/optimizers/optimizer_test.py @@ -48,7 +48,7 @@ def test_create_optimizer_and_suggest(configuration_space: CS.ConfigurationSpace assert optimizer.parameter_space is not None - suggestion = optimizer.suggest() + suggestion, context = optimizer.suggest() assert suggestion is not None myrepr = repr(optimizer) @@ -94,7 +94,7 @@ def objective(x: pd.Series) -> pd.DataFrame: optimizer.get_observations() for _ in range(max_iterations): - suggestion = optimizer.suggest() + suggestion, context = optimizer.suggest() assert isinstance(suggestion, pd.DataFrame) assert set(suggestion.columns) == {'x', 'y', 'z'} # check that suggestion is in the space @@ -103,12 +103,11 @@ def objective(x: pd.Series) -> pd.DataFrame: configuration.is_valid_configuration() observation = objective(suggestion['x']) assert isinstance(observation, pd.DataFrame) - optimizer.register(suggestion, observation) + optimizer.register(suggestion, observation, context) (best_config, best_score, best_context) = optimizer.get_best_observations() assert isinstance(best_config, pd.DataFrame) assert isinstance(best_score, pd.DataFrame) - assert best_context is None assert set(best_config.columns) == {'x', 'y', 'z'} assert set(best_score.columns) == {'score'} assert best_config.shape == (1, 3) @@ -118,7 +117,6 @@ def objective(x: pd.Series) -> pd.DataFrame: (all_configs, all_scores, all_contexts) = optimizer.get_observations() assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) - assert all_contexts is None assert set(all_configs.columns) == {'x', 'y', 'z'} assert set(all_scores.columns) == {'score'} assert all_configs.shape == (20, 3) @@ -176,7 +174,7 @@ def test_create_optimizer_with_factory_method(configuration_space: CS.Configurat assert optimizer.parameter_space is not None - suggestion = optimizer.suggest() + suggestion, _ = optimizer.suggest() assert suggestion is not None if optimizer_type is not None: @@ -268,16 +266,16 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: _LOG.debug("Optimizer is done with random init.") # loop for optimizer - suggestion = optimizer.suggest() + suggestion, context = optimizer.suggest() observation = objective(suggestion) - optimizer.register(suggestion, observation) + optimizer.register(suggestion, observation, context) # loop for llamatune-optimizer - suggestion = llamatune_optimizer.suggest() + suggestion, context = llamatune_optimizer.suggest() _x, _y = suggestion['x'].iloc[0], suggestion['y'].iloc[0] assert _x == pytest.approx(_y, rel=1e-3) or _x + _y == pytest.approx(3., rel=1e-3) # optimizer explores 1-dimensional space observation = objective(suggestion) - llamatune_optimizer.register(suggestion, observation) + llamatune_optimizer.register(suggestion, observation, context) # Retrieve best observations best_observation = optimizer.get_best_observations() @@ -286,7 +284,6 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: for (best_config, best_score, best_context) in (best_observation, llamatune_best_observation): assert isinstance(best_config, pd.DataFrame) assert isinstance(best_score, pd.DataFrame) - assert best_context is None assert set(best_config.columns) == {'x', 'y'} assert set(best_score.columns) == {'score'} @@ -302,7 +299,6 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: optimizer.get_observations(), llamatune_optimizer.get_observations()): assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) - assert all_contexts is None assert set(all_configs.columns) == {'x', 'y'} assert set(all_scores.columns) == {'score'} assert len(all_configs) == num_iters @@ -375,7 +371,7 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: optimizer.get_observations() for _ in range(max_iterations): - suggestion = optimizer.suggest() + suggestion, context = optimizer.suggest() assert isinstance(suggestion, pd.DataFrame) assert (suggestion.columns == ['x', 'y']).all() # Check suggestion values are the expected dtype @@ -388,14 +384,12 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: # Test registering the suggested configuration with a score. observation = objective(suggestion) assert isinstance(observation, pd.DataFrame) - optimizer.register(suggestion, observation) + optimizer.register(suggestion, observation, context) (best_config, best_score, best_context) = optimizer.get_best_observations() assert isinstance(best_config, pd.DataFrame) assert isinstance(best_score, pd.DataFrame) - assert best_context is None (all_configs, all_scores, all_contexts) = optimizer.get_observations() assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) - assert all_contexts is None diff --git a/mlos_core/notebooks/BayesianOptimization.ipynb b/mlos_core/notebooks/BayesianOptimization.ipynb index adf38869ff..5ef3615aa7 100644 --- a/mlos_core/notebooks/BayesianOptimization.ipynb +++ b/mlos_core/notebooks/BayesianOptimization.ipynb @@ -10,7 +10,7 @@ }, { "cell_type": "code", - "execution_count": 165, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -21,7 +21,7 @@ }, { "cell_type": "code", - "execution_count": 166, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -35,7 +35,7 @@ }, { "cell_type": "code", - "execution_count": 167, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -44,13 +44,13 @@ "Text(0, 0.5, 'Objective (i.e. performance)')" ] }, - "execution_count": 167, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -72,7 +72,7 @@ }, { "cell_type": "code", - "execution_count": 168, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -81,7 +81,7 @@ "x, Type: UniformFloat, Range: [0.0, 1.0], Default: 0.5" ] }, - "execution_count": 168, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -98,7 +98,7 @@ }, { "cell_type": "code", - "execution_count": 169, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -107,15 +107,15 @@ }, { "cell_type": "code", - "execution_count": 170, + "execution_count": 6, "metadata": {}, "outputs": [ { - "name": "stdout", + "name": "stderr", "output_type": "stream", "text": [ - "[INFO][abstract_initial_design.py:82] Using `n_configs` and ignoring `n_configs_per_hyperparameter`.\n", - "[INFO][abstract_initial_design.py:147] Using 10 initial design configurations and 0 additional configurations.\n" + "The argument budget is not set by SMAC: Consider removing it from the target function.\n", + "The argument instance is not set by SMAC: Consider removing it from the target function.\n" ] } ], @@ -131,19 +131,16 @@ }, { "cell_type": "code", - "execution_count": 171, + "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "SmacOptimizer(parameter_space=Configuration space object:\n", - " Hyperparameters:\n", - " x, Type: UniformFloat, Range: [0.0, 1.0], Default: 0.5\n", - ")" + "SmacOptimizer(space_adapter=None)" ] }, - "execution_count": 171, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -163,99 +160,85 @@ }, { "cell_type": "code", - "execution_count": 172, + "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[INFO][abstract_intensifier.py:305] Using only one seed for deterministic scenario.\n", " x\n", - "0 0.529394 \n", - " 0 0.981903\n", + "0 0.885216 \n", + " 0 3.650483\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:515] Added config 046b02 as new incumbent because there are no incumbents yet.\n", - " x\n", - "0 0.412449 \n", - " 0 0.183208\n", + " x\n", + "0 0.46549 \n", + " 0 0.628686\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 6c9ddf and rejected config 046b02 as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.5293941991008323 -> 0.4124485902231257\n", " x\n", - "0 0.137717 \n", - " 0 -0.982619\n", + "0 0.083535 \n", + " 0 -0.322383\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 6c38e4 and rejected config 6c9ddf as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.4124485902231257 -> 0.13771684308449206\n", " x\n", - "0 0.965271 \n", - " 0 13.852784\n", + "0 0.503256 \n", + " 0 0.927554\n", "Name: x, dtype: float64\n", " x\n", - "0 0.072383 \n", - " 0 -0.024966\n", + "0 0.672617 \n", + " 0 -3.321526\n", "Name: x, dtype: float64\n", " x\n", - "0 0.220613 \n", - " 0 -0.446572\n", + "0 0.224721 \n", + " 0 -0.409583\n", "Name: x, dtype: float64\n", " x\n", - "0 0.384809 \n", - " 0 0.055248\n", + "0 0.358637 \n", + " 0 0.006892\n", "Name: x, dtype: float64\n", " x\n", - "0 0.724267 \n", - " 0 -5.500623\n", + "0 0.806523 \n", + " 0 -4.584014\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 9bb6f1 and rejected config 6c38e4 as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.13771684308449206 -> 0.7242672939130698\n", " x\n", - "0 0.650459 \n", - " 0 -2.230947\n", + "0 0.848845 \n", + " 0 -0.926933\n", "Name: x, dtype: float64\n", " x\n", - "0 0.81174 \n", - " 0 -4.252442\n", + "0 0.30069 \n", + " 0 -0.014645\n", "Name: x, dtype: float64\n", " x\n", - "0 0.728255 \n", - " 0 -5.612675\n", + "0 0.062298 \n", + " 0 0.292515\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 5cac60 and rejected config 9bb6f1 as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7242672939130698 -> 0.7282549967676114\n", " x\n", - "0 0.728117 \n", - " 0 -5.609006\n", + "0 0.067593 \n", + " 0 0.120172\n", "Name: x, dtype: float64\n", " x\n", - "0 0.747548 \n", - " 0 -5.971926\n", - "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 2c10de and rejected config 5cac60 as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7282549967676114 -> 0.747547764106232\n", - " x\n", - "0 0.74397 \n", - " 0 -5.930323\n", + "0 0.052183 \n", + " 0 0.654852\n", "Name: x, dtype: float64\n", - " x\n", - "0 0.74826 \n", - " 0 -5.978736\n", + " x\n", + "0 0.801283 \n", + " 0 -4.881645\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 600943 and rejected config 2c10de as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.747547764106232 -> 0.7482599645068192\n" + " x\n", + "0 0.792032 \n", + " 0 -5.32058\n", + "Name: x, dtype: float64\n" ] } ], "source": [ "def run_optimization(optimizer: mlos_core.optimizers.BaseOptimizer):\n", " # get a new config value suggestion to try from the optimizer.\n", - " suggested_value = optimizer.suggest()\n", + " suggested_value, context = optimizer.suggest()\n", " # suggested value are dictionary-like, keys are input space parameter names\n", " # evaluate target function\n", " target_value = f(suggested_value['x'])\n", " print(suggested_value, \"\\n\", target_value)\n", - " optimizer.register(suggested_value, target_value)\n", + " optimizer.register(suggested_value, target_value, context)\n", "\n", "# run for some iterations\n", "n_iterations = 15\n", @@ -273,7 +256,7 @@ }, { "cell_type": "code", - "execution_count": 173, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -304,103 +287,103 @@ " \n", " \n", " 0\n", - " 0.529394\n", - " 0.981903\n", + " 0.885216\n", + " 3.650483\n", " \n", " \n", " 0\n", - " 0.412449\n", - " 0.183208\n", + " 0.465490\n", + " 0.628686\n", " \n", " \n", " 0\n", - " 0.137717\n", - " -0.982619\n", + " 0.083535\n", + " -0.322383\n", " \n", " \n", " 0\n", - " 0.965271\n", - " 13.852784\n", + " 0.503256\n", + " 0.927554\n", " \n", " \n", " 0\n", - " 0.072383\n", - " -0.024966\n", + " 0.672617\n", + " -3.321526\n", " \n", " \n", " 0\n", - " 0.220613\n", - " -0.446572\n", + " 0.224721\n", + " -0.409583\n", " \n", " \n", " 0\n", - " 0.384809\n", - " 0.055248\n", + " 0.358637\n", + " 0.006892\n", " \n", " \n", " 0\n", - " 0.724267\n", - " -5.500623\n", + " 0.806523\n", + " -4.584014\n", " \n", " \n", " 0\n", - " 0.650459\n", - " -2.230947\n", + " 0.848845\n", + " -0.926933\n", " \n", " \n", " 0\n", - " 0.811740\n", - " -4.252442\n", + " 0.300690\n", + " -0.014645\n", " \n", " \n", " 0\n", - " 0.728255\n", - " -5.612675\n", + " 0.062298\n", + " 0.292515\n", " \n", " \n", " 0\n", - " 0.728117\n", - " -5.609006\n", + " 0.067593\n", + " 0.120172\n", " \n", " \n", " 0\n", - " 0.747548\n", - " -5.971926\n", + " 0.052183\n", + " 0.654852\n", " \n", " \n", " 0\n", - " 0.743970\n", - " -5.930323\n", + " 0.801283\n", + " -4.881645\n", " \n", " \n", " 0\n", - " 0.748260\n", - " -5.978736\n", + " 0.792032\n", + " -5.320580\n", " \n", " \n", "\n", "" ], "text/plain": [ - " x score\n", - "0 0.529394 0.981903\n", - "0 0.412449 0.183208\n", - "0 0.137717 -0.982619\n", - "0 0.965271 13.852784\n", - "0 0.072383 -0.024966\n", - "0 0.220613 -0.446572\n", - "0 0.384809 0.055248\n", - "0 0.724267 -5.500623\n", - "0 0.650459 -2.230947\n", - "0 0.811740 -4.252442\n", - "0 0.728255 -5.612675\n", - "0 0.728117 -5.609006\n", - "0 0.747548 -5.971926\n", - "0 0.743970 -5.930323\n", - "0 0.748260 -5.978736" + " x score\n", + "0 0.885216 3.650483\n", + "0 0.465490 0.628686\n", + "0 0.083535 -0.322383\n", + "0 0.503256 0.927554\n", + "0 0.672617 -3.321526\n", + "0 0.224721 -0.409583\n", + "0 0.358637 0.006892\n", + "0 0.806523 -4.584014\n", + "0 0.848845 -0.926933\n", + "0 0.300690 -0.014645\n", + "0 0.062298 0.292515\n", + "0 0.067593 0.120172\n", + "0 0.052183 0.654852\n", + "0 0.801283 -4.881645\n", + "0 0.792032 -5.320580" ] }, - "execution_count": 173, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -411,12 +394,12 @@ }, { "cell_type": "code", - "execution_count": 174, + "execution_count": 10, "metadata": {}, "outputs": [ { "data": { - "image/png": "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hI0hSY0QQghRinWp40/zamVzbffzdGLeoIYlqk6NFN8TQgghSrH0LDUnriUCMPGZUHzcHPB113Y5lZQWmhyS1AghhBCl2LYz8SRnZBPg6cRLzatgU8ISmUdJ95MQQghRiv129AYA3esHlOiEBiSpEcJkhg4dSq9evXSv27Zty9tvv/1E5zTFOYQQIj+JaVlsOR0PQK/6FSwczZOT7idR7FSpUoW33367xN/MV69ebfAintu2baNdu3bcvXsXLy+vQp1DCCGMtfv8bTLVGqqVc6Wmn7ulw3liktQIPZmZmTg4OOhtU6vVqFQqbGysv2Evr+9fWAWtmF6U5xBCiPzsPn8bgFbVy6FSleyuJ5DupxJv5cqV1K1bF2dnZ8qWLUuHDh1ISUkB8u666NWrl94K1VWqVGHatGkMHToUT09PXnvtNRYtWoSXlxd//fUXoaGhODo6cvnyZe7evcvgwYMpU6YMLi4udO3alXPnzumdf8GCBQQGBuLi4sKzzz7L7Nmz9VoeLly4QM+ePSlfvjxubm40btyYf/75R/d+27ZtuXz5MmPGjEGlUun9JduzZw+tW7fG2dmZwMBARo0apfuueZk8eTL169fn22+/1cX0/PPPc+/ePd0+OV1GM2bMICAggJCQEACuX79O3759KVOmDGXLlqVnz55cunRJd5xarWbs2LF4eXlRtmxZxo8fz7/Xhv339c/IyGD8+PEEBgbi6OhI9erV+eGHH7h06RLt2rUDoEyZMqhUKt3v6N/neNzvIOd3t2HDBmrVqoWbmxtdunQhNvbhKrvbtm2jSZMmuLq64uXlRYsWLbh8+XK+11EIYb32XrgDQItgHwtHYhqS1ORHUSAzxTIPAxdOj42NpX///rz88stER0ezbds2evfunevm+jifffYZderU4fDhw0ycOBGA1NRUZsyYwffff8+pU6fw9fVl6NChHDp0iD/++IO9e/eiKArdunUjKysLgN27dzNs2DBGjx5NZGQkHTt25OOPP9b7rPv379OtWzf++ecfjh49SufOnenevTtXrlwBtN0tFStWZOrUqcTGxupuxidOnKBz58707t2b48ePs2zZMnbt2sXIkSML/G7nz59n+fLl/Pnnn6xfv57IyEjefPNNvX02b95MdHQ0mzZt4q+//iI1NZV27drh5ubGjh072LVrly45yMzMBGDWrFn8+OOP/PDDD+zatYuEhATWrFlTYCyDBw9m6dKlzJkzh+joaObPn4+bmxuBgYGsWrUKgDNnzhAbG8uXX36Z5zke9zvI+d3997//5X//+x87duzgypUrjBs3DoDs7Gx69epFmzZtOH78OHv37uX111+3iv+hCSGME5uYxsXbKdioKDEVgx9Hup/yk5UK0wMs89n/dwMcXB+7W2xsLNnZ2fTu3ZvKlSsDULduXaM/rn379rqbHsCuXbvIyspi7ty5hIWFAXDu3Dn++OMPdu/eTfPmzQFYsmQJgYGB/Pbbbzz//PN89dVXdO3aVXeukJAQ9uzZw19//aU7d1hYmO6cANOmTWPNmjX88ccfjBw5Em9vb2xtbXF3d8fPz0+332effcaAAQN0rRbVq1dnzpw5tGnThnnz5uHklHeZ7/T0dBYvXkzFihUB+Oqrr3j66aeZNWuW7vyurq58//33um6nH3/8ERsbG77//nvdzX7hwoV4eXmxbds2OnXqxBdffMGECRPo06cPAPPnz2fDhg35XuOzZ8+yfPlyNm3aRIcOHQCoWrWq7v2cbiZfX1+9lq1HGfI7AMjKymL+/PlUq1YNgJEjRzJ16lQAkpKSSExM5JlnntG9X6tWrXzjFkJYrz3nta00dSt64elsHWP3pKWmBAsLC+Opp56ibt26PP/88yxYsIC7d+8afZ7w8PBc2xwcHKhXr57udXR0NHZ2djRt2lS3rWzZstSoUYPo6GhA28rQpEkTvfP8+3VKSgrjx48nNDQULy8v3NzcOH36tK6lJj+HDx9m0aJFuLm56R6dO3dGo9EQExOT73GVKlXSJTQAERERaDQazpw5o9tWt25dvXE0hw8f5vz587i7u+s+y9vbm/T0dC5cuEBiYiKxsbFERETojrGzs8vzOuaIjIzE1taWNm3aFPg9C2LI7wDAxcVFl7AA+Pv7Ex+vnd3g7e3N0KFDdS1kX375pV7XlBCi9Nh9QTuepkUe1YRLKmmpyY+9i7bFxFKfbQBbW1s2bdrEnj172LhxI1999RXvv/8++/fvJygoCBsbm1xdUY92U+Rwdc3dKuTs7KzXJZFfl5aiKLr9Hn2e33H/+c9/2LBhA//9738JDg7G2dmZ5557Ttetkx+NRsMbb7zBqFGjcr1XqVKlAo99VE58j8b57++v0Who1KgRS5YsyXV8uXLlDP6sRzk7OxfquEcZ8jsAcs2WUqlUescuXLiQUaNGsX79epYtW8YHH3zApk2baNas2RPHKIQoGRRF0bXUNK9mHeNpQFpq8qdSabuALPEwYnyDSqWiRYsWTJkyhaNHj+Lg4KAb21GuXDm9/4Wr1WpOnjxZqMsRGhpKdnY2+/fv1227c+cOZ8+e1XVf1KxZkwMHDugdd+jQIb3XO3fuZOjQoTz77LPUrVsXPz8/vQG4oG0lUqvVetsaNmzIqVOnCA4OzvUoaLbSlStXuHHjYXK6d+9ebGxsdAOC89KwYUPOnTuHr69vrs/y9PTE09MTf39/9u3bpzsmOzubw4cP53vOunXrotFo2L59e57v53yHf3/vRxnyOzBUgwYNmDBhAnv27KFOnTr88ssvRh0vhCjZYm6nEJeUjoOtDeFVylg6HJORpKYE279/P9OnT+fQoUNcuXKF1atXc+vWLd0Nrn379qxdu5a1a9dy+vRpRowYoTfzxxjVq1enZ8+evPbaa+zatYtjx44xaNAgKlSoQM+ePQF46623WLduHbNnz+bcuXN8++23/P3333qtCMHBwaxevZrIyEiOHTvGgAED0Gg0ep9VpUoVduzYwfXr17l9W9s8+u6777J3717efPNNIiMjdeNL3nrrrQLjdnJyYsiQIRw7doydO3cyatQoXnjhBb3xOv82cOBAfHx86NmzJzt37iQmJobt27czevRorl27BsDo0aP55JNPWLNmjUHXtkqVKgwZMoSXX36Z3377jZiYGLZt28by5csBqFy5MiqVir/++otbt25x//79Qv0OHicmJoYJEyawd+9eLl++zMaNGwuVFAkhSrbdD2Y9NazshZO9rYWjMR1JakowDw8PduzYQbdu3QgJCeGDDz5g1qxZdO3aFYCXX36ZIUOGMHjwYNq0aUNQUJBu6nBhLFy4kEaNGvHMM88QERGBoiisW7dO193RokUL5s+fz+zZswkLC2P9+vWMGTNGbxDv559/TpkyZWjevDndu3enc+fONGzYUO9zpk6dyqVLl6hWrZquu6devXps376dc+fO0apVKxo0aMDEiRPx9y949djg4GB69+5Nt27d6NSpE3Xq1GHu3LkFHuPi4sKOHTuoVKkSvXv3platWrz88sukpaXh4eEBwDvvvMPgwYMZOnQoERERuLu78+yzzxZ43nnz5vHcc88xYsQIatasyWuvvaabkl6h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", + "image/png": "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", "text/plain": [ "
" ] @@ -465,7 +448,7 @@ }, { "cell_type": "code", - "execution_count": 175, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -496,19 +479,19 @@ " \n", " \n", " 0\n", - " 0.74826\n", - " -5.978736\n", + " 0.792032\n", + " -5.32058\n", " \n", " \n", "\n", "" ], "text/plain": [ - " x score\n", - "0 0.74826 -5.978736" + " x score\n", + "0 0.792032 -5.32058" ] }, - "execution_count": 175, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -527,7 +510,7 @@ }, { "cell_type": "code", - "execution_count": 176, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -535,226 +518,204 @@ "output_type": "stream", "text": [ " x\n", - "0 0.760236 \n", - " 0 -6.015935\n", - "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 314671 and rejected config 600943 as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7482599645068192 -> 0.7602360169805238\n", - " x\n", - "0 0.767468 \n", - " 0 -5.963419\n", - "Name: x, dtype: float64\n", - " x\n", - "0 0.760029 \n", - " 0 -6.016581\n", - "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 225bde and rejected config 314671 as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7602360169805238 -> 0.7600285216804508\n", - " x\n", - "0 0.759498 \n", - " 0 -6.01802\n", + "0 0.073487 \n", + " 0 -0.056953\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 5ad459 and rejected config 225bde as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7600285216804508 -> 0.7594984647988082\n", " x\n", - "0 0.758829 \n", - " 0 -6.019401\n", + "0 0.778805 \n", + " 0 -5.758773\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config c3822f and rejected config 5ad459 as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7594984647988082 -> 0.7588287840653773\n", " x\n", - "0 0.756974 \n", - " 0 -6.0207\n", + "0 0.772863 \n", + " 0 -5.885139\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config d7d064 and rejected config c3822f as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7588287840653773 -> 0.7569743298244708\n", " x\n", - "0 0.756988 \n", - " 0 -6.020704\n", + "0 0.761347 \n", + " 0 -6.011669\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 0c202f and rejected config d7d064 as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7569743298244708 -> 0.7569876149262672\n", " x\n", - "0 0.619115 \n", - " 0 -0.834508\n", + "0 0.746361 \n", + " 0 -5.959482\n", "Name: x, dtype: float64\n", " x\n", - "0 0.822689 \n", - " 0 -3.443583\n", + "0 0.761452 \n", + " 0 -6.011193\n", "Name: x, dtype: float64\n", - " x\n", - "0 0.645017 \n", - " 0 -1.970694\n", + " x\n", + "0 0.75968 \n", + " 0 -6.017561\n", "Name: x, dtype: float64\n", " x\n", - "0 0.75556 \n", - " 0 -6.019224\n", + "0 0.75746 \n", + " 0 -6.020716\n", "Name: x, dtype: float64\n", " x\n", - "0 0.75774 \n", - " 0 -6.020611\n", + "0 0.74897 \n", + " 0 -5.985032\n", "Name: x, dtype: float64\n", " x\n", - "0 0.756752 \n", - " 0 -6.020609\n", + "0 0.753181 \n", + " 0 -6.012009\n", "Name: x, dtype: float64\n", " x\n", - "0 0.75701 \n", - " 0 -6.02071\n", + "0 0.75743 \n", + " 0 -6.020722\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config d5b33f and rejected config 0c202f as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7569876149262672 -> 0.7570100730780202\n", " x\n", - "0 0.757309 \n", - " 0 -6.020738\n", + "0 0.010426 \n", + " 0 2.512407\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 578200 and rejected config d5b33f as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7570100730780202 -> 0.7573094718822252\n", " x\n", - "0 0.757339 \n", - " 0 -6.020736\n", + "0 0.756996 \n", + " 0 -6.020706\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757401 \n", - " 0 -6.020728\n", + "0 0.000775 \n", + " 0 2.988794\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757106 \n", - " 0 -6.020729\n", + "0 0.757901 \n", + " 0 -6.020512\n", + "Name: x, dtype: float64\n", + " x\n", + "0 0.75639 \n", + " 0 -6.020348\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757475 \n", - " 0 -6.020713\n", + "0 0.059265 \n", + " 0 0.396695\n", "Name: x, dtype: float64\n", " x\n", - "0 0.623596 \n", - " 0 -1.015968\n", + "0 0.757405 \n", + " 0 -6.020727\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757238 \n", - " 0 -6.02074\n", + "0 0.757312 \n", + " 0 -6.020738\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 671e3a and rejected config 578200 as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7573094718822252 -> 0.7572379148000737\n", " x\n", - "0 0.757328 \n", - " 0 -6.020737\n", + "0 0.757304 \n", + " 0 -6.020738\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757239 \n", - " 0 -6.02074\n", + "0 0.853521 \n", + " 0 -0.398606\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config 577e26 and rejected config 671e3a as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7572379148000737 -> 0.7572392478479336\n", " x\n", - "0 0.13462 \n", - " 0 -0.976262\n", + "0 0.75711 \n", + " 0 -6.02073\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757229 \n", + "0 0.757242 \n", " 0 -6.02074\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757282 \n", - " 0 -6.020739\n", - "Name: x, dtype: float64\n", - " x\n", - "0 0.75712 \n", - " 0 -6.020731\n", + "0 0.757276 \n", + " 0 -6.02074\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757272 \n", + "0 0.757244 \n", " 0 -6.02074\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757212 \n", - " 0 -6.020739\n", + "0 0.756502 \n", + " 0 -6.020443\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757221 \n", + "0 0.757258 \n", " 0 -6.02074\n", "Name: x, dtype: float64\n", - " x\n", - "0 0.75725 \n", - " 0 -6.02074\n", + " x\n", + "0 0.757164 \n", + " 0 -6.020736\n", "Name: x, dtype: float64\n", - "[INFO][abstract_intensifier.py:590] Added config e78bc3 and rejected config 577e26 as incumbent because it is not better than the incumbents on 1 instances:\n", - "[INFO][configspace.py:175] --- x: 0.7572392478479336 -> 0.7572497477046122\n", " x\n", - "0 0.757229 \n", + "0 0.757222 \n", " 0 -6.02074\n", "Name: x, dtype: float64\n", - " x\n", - "0 0.757242 \n", + " x\n", + "0 0.75723 \n", " 0 -6.02074\n", "Name: x, dtype: float64\n", " x\n", - "0 0.516326 \n", - " 0 0.97754\n", + "0 0.758168 \n", + " 0 -6.020288\n", "Name: x, dtype: float64\n", " x\n", - "0 0.803298 \n", - " 0 -4.771415\n", + "0 0.729207 \n", + " 0 -5.637674\n", "Name: x, dtype: float64\n", " x\n", - "0 0.992187 \n", - " 0 15.605854\n", + "0 0.933398 \n", + " 0 10.294283\n", + "Name: x, dtype: float64\n", + " x\n", + "0 0.75717 \n", + " 0 -6.020737\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757265 \n", - " 0 -6.02074\n", + "0 0.522945 \n", + " 0 0.986131\n", "Name: x, dtype: float64\n", " x\n", - "0 0.632956 \n", - " 0 -1.417024\n", + "0 0.243288 \n", + " 0 -0.257511\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757921 \n", - " 0 -6.020498\n", + "0 0.757385 \n", + " 0 -6.02073\n", "Name: x, dtype: float64\n", - " x\n", - "0 0.64828 \n", - " 0 -2.126137\n", + " x\n", + "0 0.198573 \n", + " 0 -0.653071\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757166 \n", - " 0 -6.020736\n", + "0 0.757235 \n", + " 0 -6.02074\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757244 \n", + "0 0.757261 \n", " 0 -6.02074\n", "Name: x, dtype: float64\n", " x\n", - "0 0.567433 \n", - " 0 0.643774\n", + "0 0.757364 \n", + " 0 -6.020733\n", "Name: x, dtype: float64\n", " x\n", - "0 0.087024 \n", - " 0 -0.403632\n", + "0 0.176412 \n", + " 0 -0.843609\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757232 \n", - " 0 -6.02074\n", + "0 0.757606 \n", + " 0 -6.020672\n", "Name: x, dtype: float64\n", " x\n", - "0 0.756384 \n", - " 0 -6.020342\n", + "0 0.757209 \n", + " 0 -6.020739\n", + "Name: x, dtype: float64\n", + " x\n", + "0 0.141766 \n", + " 0 -0.986222\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757538 \n", - " 0 -6.020695\n", + "0 0.963665 \n", + " 0 13.706197\n", "Name: x, dtype: float64\n", " x\n", - "0 0.361307 \n", - " 0 0.00928\n", + "0 0.150213 \n", + " 0 -0.977822\n", "Name: x, dtype: float64\n", " x\n", - "0 0.75726 \n", - " 0 -6.02074\n", + "0 0.52569 \n", + " 0 0.985926\n", "Name: x, dtype: float64\n", " x\n", - "0 0.757206 \n", - " 0 -6.020739\n", + "0 0.020198 \n", + " 0 2.039602\n", + "Name: x, dtype: float64\n", + " x\n", + "0 0.831894 \n", + " 0 -2.648306\n", "Name: x, dtype: float64\n" ] } @@ -776,7 +737,7 @@ }, { "cell_type": "code", - "execution_count": 177, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -807,7 +768,7 @@ " \n", " \n", " 0\n", - " 0.75725\n", + " 0.757244\n", " -6.02074\n", " \n", " \n", @@ -815,11 +776,11 @@ "" ], "text/plain": [ - " x score\n", - "0 0.75725 -6.02074" + " x score\n", + "0 0.757244 -6.02074" ] }, - "execution_count": 177, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -839,22 +800,22 @@ }, { "cell_type": "code", - "execution_count": 178, + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 178, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -914,7 +875,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.11" + "version": "3.11.9" } }, "nbformat": 4, From 8afb5f0ebd08d697d89db85494c5868a409df8f5 Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Thu, 23 May 2024 23:00:56 +0000 Subject: [PATCH 02/21] revert changes --- .cspell.json | 1 + .vscode/settings.json | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.cspell.json b/.cspell.json index 2cd9280fc8..d89a58f175 100644 --- a/.cspell.json +++ b/.cspell.json @@ -72,6 +72,7 @@ "sklearn", "skopt", "smac", + "SOBOL", "sqlalchemy", "srcpaths", "subcmd", diff --git a/.vscode/settings.json b/.vscode/settings.json index 6d2ceb53f5..971d193928 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -135,11 +135,7 @@ // See Also .vscode/launch.json for environment variable args to pytest during debug sessions. // For the rest, see setup.cfg "python.testing.pytestArgs": [ - "--log-level=DEBUG", "." ], - "python.testing.unittestEnabled": false, - "cSpell.words": [ - "SOBOL" - ] + "python.testing.unittestEnabled": false } From 08575afa05264d3121f70a94010dd6084cb57ee0 Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Thu, 23 May 2024 23:04:22 +0000 Subject: [PATCH 03/21] revert --- .vscode/settings.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 971d193928..2c8098f9d9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,6 +17,7 @@ // See Also: // - https://github.com/microsoft/vscode/issues/2809#issuecomment-1544387883 // - mlos_bench/config/schemas/README.md + { "fileMatch": [ "mlos_bench/mlos_bench/tests/config/schemas/environments/test-cases/**/*.jsonc", @@ -135,6 +136,7 @@ // See Also .vscode/launch.json for environment variable args to pytest during debug sessions. // For the rest, see setup.cfg "python.testing.pytestArgs": [ + "--log-level=DEBUG", "." ], "python.testing.unittestEnabled": false From fcfca5352de36d4453030959d4a3d07495f9cfbb Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Sat, 1 Jun 2024 22:55:14 +0000 Subject: [PATCH 04/21] fix minor bug with logging --- .../bayesian_optimizers/smac_optimizer.py | 120 ++++++++++++------ 1 file changed, 80 insertions(+), 40 deletions(-) diff --git a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py index e53dc67f4f..017a0947fb 100644 --- a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py +++ b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py @@ -19,7 +19,11 @@ import numpy as np import numpy.typing as npt import pandas as pd - +from mlos_core.optimizers.bayesian_optimizers.bayesian_optimizer import ( + BaseBayesianOptimizer, +) +from mlos_core.spaces.adapters.adapter import BaseSpaceAdapter +from mlos_core.spaces.adapters.identity_adapter import IdentityAdapter from smac import HyperparameterOptimizationFacade as Optimizer_Smac from smac import Scenario from smac.facade import AbstractFacade @@ -28,9 +32,6 @@ from smac.main.config_selector import ConfigSelector from smac.random_design.probability_design import ProbabilityRandomDesign from smac.runhistory import StatusType, TrialInfo, TrialValue -from mlos_core.optimizers.bayesian_optimizers.bayesian_optimizer import BaseBayesianOptimizer -from mlos_core.spaces.adapters.adapter import BaseSpaceAdapter -from mlos_core.spaces.adapters.identity_adapter import IdentityAdapter class SmacOptimizer(BaseBayesianOptimizer): @@ -144,7 +145,9 @@ def __init__( if output_directory is None: # pylint: disable=consider-using-with try: - self._temp_output_directory = TemporaryDirectory(ignore_cleanup_errors=True) # Argument added in Python 3.10 + self._temp_output_directory = TemporaryDirectory( + ignore_cleanup_errors=True + ) # Argument added in Python 3.10 except TypeError: self._temp_output_directory = TemporaryDirectory() output_directory = self._temp_output_directory.name @@ -188,27 +191,27 @@ def __init__( # See Also: #488 initial_design_args: Dict[str, Union[list, int, float, Scenario]] = { - 'scenario': scenario, + "scenario": scenario, # Workaround a bug in SMAC that sets a default arg to a mutable # value that can cause issues when multiple optimizers are # instantiated with the use_default_config option within the same # process that use different ConfigSpaces so that the second # receives the default config from both as an additional config. - 'additional_configs': [] + "additional_configs": [], } if n_random_init is not None: - initial_design_args['n_configs'] = n_random_init + initial_design_args["n_configs"] = n_random_init if n_random_init > 0.25 * max_trials and max_ratio is None: warning( - 'Number of random initial configurations (%d) is ' + - 'greater than 25%% of max_trials (%d). ' + - 'Consider setting max_ratio to avoid SMAC overriding n_random_init.', + "Number of random initial configurations (%d) is " + + "greater than 25%% of max_trials (%d). " + + "Consider setting max_ratio to avoid SMAC overriding n_random_init.", n_random_init, max_trials, ) if max_ratio is not None: assert isinstance(max_ratio, float) and 0.0 <= max_ratio <= 1.0 - initial_design_args['max_ratio'] = max_ratio + initial_design_args["max_ratio"] = max_ratio # Use the default InitialDesign from SMAC. # (currently SOBOL instead of LatinHypercube due to better uniformity @@ -219,7 +222,9 @@ def __init__( # design when generated a random_design for itself via the # get_random_design static method when random_design is None. assert isinstance(n_random_probability, float) and n_random_probability >= 0 - random_design = ProbabilityRandomDesign(probability=n_random_probability, seed=scenario.seed) + random_design = ProbabilityRandomDesign( + probability=n_random_probability, seed=scenario.seed + ) self.base_optimizer = facade( scenario, @@ -311,10 +316,14 @@ def _dummy_target_func( """ # NOTE: Providing a target function when using the ask-and-tell interface is an imperfection of the API # -- this planned to be fixed in some future release: https://github.com/automl/SMAC3/issues/946 - raise RuntimeError('This function should never be called.') + raise RuntimeError("This function should never be called.") - def _register(self, configurations: pd.DataFrame, - scores: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> None: + def _register( + self, + configurations: pd.DataFrame, + scores: pd.DataFrame, + context: Optional[pd.DataFrame] = None, + ) -> None: """Registers the given configurations and scores. Parameters @@ -328,10 +337,11 @@ def _register(self, configurations: pd.DataFrame, context : pd.DataFrame Context of the request that is being registered. """ + with self.lock: # Register each trial (one-by-one) contexts: Union[List[pd.Series], List[None]] = _to_context(context) or [ - None for _ in scores # type: ignore[misc] + None for _ in scores # type: ignore[misc] ] for config, score, ctx in zip( self._to_configspace_configs(configurations), @@ -354,10 +364,9 @@ def _register(self, configurations: pd.DataFrame, # make a new entry if sum(matching) > 0: - info = self.trial_info_df[matching]["TrialInfo"].iloc[-1] - self.trial_info_df.at[list(matching).index(True), "TrialValue"] = ( - value - ) + self.trial_info_df.loc[ + np.where(np.array(matching) == True)[0][-1], "TrialValue" + ] = value else: if ctx is None or "budget" not in ctx or "instance" not in ctx: info = TrialInfo( @@ -365,7 +374,7 @@ def _register(self, configurations: pd.DataFrame, ) self.trial_info_df.loc[len(self.trial_info_df.index)] = [ config, - info, + ctx, info, value, ] @@ -430,41 +439,68 @@ def _suggest( return config_df, context_df - def register_pending(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> None: + def register_pending( + self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None + ) -> None: raise NotImplementedError() - def surrogate_predict(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> npt.NDArray: - from smac.utils.configspace import convert_configurations_to_array # pylint: disable=import-outside-toplevel + def surrogate_predict( + self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None + ) -> npt.NDArray: + from smac.utils.configspace import ( + convert_configurations_to_array, # pylint: disable=import-outside-toplevel + ) if context is not None: - warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) + warn( + f"Not Implemented: Ignoring context {list(context.columns)}", + UserWarning, + ) if self._space_adapter and not isinstance(self._space_adapter, IdentityAdapter): - raise NotImplementedError("Space adapter not supported for surrogate_predict.") + raise NotImplementedError( + "Space adapter not supported for surrogate_predict." + ) # pylint: disable=protected-access if len(self._observations) <= self.base_optimizer._initial_design._n_configs: raise RuntimeError( - 'Surrogate model can make predictions *only* after all initial points have been evaluated ' + - f'{len(self._observations)} <= {self.base_optimizer._initial_design._n_configs}') + "Surrogate model can make predictions *only* after all initial points have been evaluated " + + f"{len(self._observations)} <= {self.base_optimizer._initial_design._n_configs}" + ) if self.base_optimizer._config_selector._model is None: - raise RuntimeError('Surrogate model is not yet trained') + raise RuntimeError("Surrogate model is not yet trained") - configs: npt.NDArray = convert_configurations_to_array(self._to_configspace_configs(configurations)) - mean_predictions, _ = self.base_optimizer._config_selector._model.predict(configs) - return mean_predictions.reshape(-1,) + configs: npt.NDArray = convert_configurations_to_array( + self._to_configspace_configs(configurations) + ) + mean_predictions, _ = self.base_optimizer._config_selector._model.predict( + configs + ) + return mean_predictions.reshape( + -1, + ) - def acquisition_function(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> npt.NDArray: + def acquisition_function( + self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None + ) -> npt.NDArray: if context is not None: - warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) + warn( + f"Not Implemented: Ignoring context {list(context.columns)}", + UserWarning, + ) if self._space_adapter: raise NotImplementedError() # pylint: disable=protected-access if self.base_optimizer._config_selector._acquisition_function is None: - raise RuntimeError('Acquisition function is not yet initialized') + raise RuntimeError("Acquisition function is not yet initialized") configs: list = self._to_configspace_configs(configurations) - return self.base_optimizer._config_selector._acquisition_function(configs).reshape(-1,) + return self.base_optimizer._config_selector._acquisition_function( + configs + ).reshape( + -1, + ) def cleanup(self) -> None: try: @@ -474,7 +510,9 @@ def cleanup(self) -> None: except AttributeError: warning("_temp_output_directory does not exist.") - def _to_configspace_configs(self, configurations: pd.DataFrame) -> List[ConfigSpace.Configuration]: + def _to_configspace_configs( + self, configurations: pd.DataFrame + ) -> List[ConfigSpace.Configuration]: """Convert a dataframe of configurations to a list of ConfigSpace configurations. Parameters @@ -488,8 +526,10 @@ def _to_configspace_configs(self, configurations: pd.DataFrame) -> List[ConfigSp List of ConfigSpace configurations. """ return [ - ConfigSpace.Configuration(self.optimizer_parameter_space, values=config.to_dict()) - for (_, config) in configurations.astype('O').iterrows() + ConfigSpace.Configuration( + self.optimizer_parameter_space, values=config.to_dict() + ) + for (_, config) in configurations.astype("O").iterrows() ] @staticmethod From 838c1dbc94d1b0aded2e548b0c958c4e2cd3df32 Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Sat, 1 Jun 2024 23:01:43 +0000 Subject: [PATCH 05/21] undo formatting --- .../bayesian_optimizers/smac_optimizer.py | 115 ++++++------------ 1 file changed, 38 insertions(+), 77 deletions(-) diff --git a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py index 017a0947fb..0e7dfc649d 100644 --- a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py +++ b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py @@ -19,9 +19,6 @@ import numpy as np import numpy.typing as npt import pandas as pd -from mlos_core.optimizers.bayesian_optimizers.bayesian_optimizer import ( - BaseBayesianOptimizer, -) from mlos_core.spaces.adapters.adapter import BaseSpaceAdapter from mlos_core.spaces.adapters.identity_adapter import IdentityAdapter from smac import HyperparameterOptimizationFacade as Optimizer_Smac @@ -32,6 +29,9 @@ from smac.main.config_selector import ConfigSelector from smac.random_design.probability_design import ProbabilityRandomDesign from smac.runhistory import StatusType, TrialInfo, TrialValue +from mlos_core.optimizers.bayesian_optimizers.bayesian_optimizer import BaseBayesianOptimizer +from mlos_core.spaces.adapters.adapter import BaseSpaceAdapter +from mlos_core.spaces.adapters.identity_adapter import IdentityAdapter class SmacOptimizer(BaseBayesianOptimizer): @@ -145,9 +145,7 @@ def __init__( if output_directory is None: # pylint: disable=consider-using-with try: - self._temp_output_directory = TemporaryDirectory( - ignore_cleanup_errors=True - ) # Argument added in Python 3.10 + self._temp_output_directory = TemporaryDirectory(ignore_cleanup_errors=True) # Argument added in Python 3.10 except TypeError: self._temp_output_directory = TemporaryDirectory() output_directory = self._temp_output_directory.name @@ -191,27 +189,27 @@ def __init__( # See Also: #488 initial_design_args: Dict[str, Union[list, int, float, Scenario]] = { - "scenario": scenario, + 'scenario': scenario, # Workaround a bug in SMAC that sets a default arg to a mutable # value that can cause issues when multiple optimizers are # instantiated with the use_default_config option within the same # process that use different ConfigSpaces so that the second # receives the default config from both as an additional config. - "additional_configs": [], + 'additional_configs': [], } if n_random_init is not None: - initial_design_args["n_configs"] = n_random_init + initial_design_args['n_configs'] = n_random_init if n_random_init > 0.25 * max_trials and max_ratio is None: warning( - "Number of random initial configurations (%d) is " - + "greater than 25%% of max_trials (%d). " - + "Consider setting max_ratio to avoid SMAC overriding n_random_init.", + 'Number of random initial configurations (%d) is ' + + 'greater than 25%% of max_trials (%d). ' + + 'Consider setting max_ratio to avoid SMAC overriding n_random_init.', n_random_init, max_trials, ) if max_ratio is not None: assert isinstance(max_ratio, float) and 0.0 <= max_ratio <= 1.0 - initial_design_args["max_ratio"] = max_ratio + initial_design_args['max_ratio'] = max_ratio # Use the default InitialDesign from SMAC. # (currently SOBOL instead of LatinHypercube due to better uniformity @@ -222,9 +220,7 @@ def __init__( # design when generated a random_design for itself via the # get_random_design static method when random_design is None. assert isinstance(n_random_probability, float) and n_random_probability >= 0 - random_design = ProbabilityRandomDesign( - probability=n_random_probability, seed=scenario.seed - ) + random_design = ProbabilityRandomDesign(probability=n_random_probability, seed=scenario.seed) self.base_optimizer = facade( scenario, @@ -316,14 +312,10 @@ def _dummy_target_func( """ # NOTE: Providing a target function when using the ask-and-tell interface is an imperfection of the API # -- this planned to be fixed in some future release: https://github.com/automl/SMAC3/issues/946 - raise RuntimeError("This function should never be called.") + raise RuntimeError('This function should never be called.') - def _register( - self, - configurations: pd.DataFrame, - scores: pd.DataFrame, - context: Optional[pd.DataFrame] = None, - ) -> None: + def _register(self, configurations: pd.DataFrame, + scores: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> None: """Registers the given configurations and scores. Parameters @@ -337,11 +329,10 @@ def _register( context : pd.DataFrame Context of the request that is being registered. """ - with self.lock: # Register each trial (one-by-one) contexts: Union[List[pd.Series], List[None]] = _to_context(context) or [ - None for _ in scores # type: ignore[misc] + None for _ in scores # type: ignore[misc] ] for config, score, ctx in zip( self._to_configspace_configs(configurations), @@ -364,9 +355,10 @@ def _register( # make a new entry if sum(matching) > 0: - self.trial_info_df.loc[ - np.where(np.array(matching) == True)[0][-1], "TrialValue" - ] = value + info = self.trial_info_df[matching]["TrialInfo"].iloc[-1] + self.trial_info_df.at[list(matching).index(True), "TrialValue"] = ( + value + ) else: if ctx is None or "budget" not in ctx or "instance" not in ctx: info = TrialInfo( @@ -439,68 +431,41 @@ def _suggest( return config_df, context_df - def register_pending( - self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None - ) -> None: + def register_pending(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> None: raise NotImplementedError() - def surrogate_predict( - self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None - ) -> npt.NDArray: - from smac.utils.configspace import ( - convert_configurations_to_array, # pylint: disable=import-outside-toplevel - ) + def surrogate_predict(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> npt.NDArray: + from smac.utils.configspace import convert_configurations_to_array # pylint: disable=import-outside-toplevel if context is not None: - warn( - f"Not Implemented: Ignoring context {list(context.columns)}", - UserWarning, - ) + warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) if self._space_adapter and not isinstance(self._space_adapter, IdentityAdapter): - raise NotImplementedError( - "Space adapter not supported for surrogate_predict." - ) + raise NotImplementedError("Space adapter not supported for surrogate_predict.") # pylint: disable=protected-access if len(self._observations) <= self.base_optimizer._initial_design._n_configs: raise RuntimeError( - "Surrogate model can make predictions *only* after all initial points have been evaluated " - + f"{len(self._observations)} <= {self.base_optimizer._initial_design._n_configs}" - ) + 'Surrogate model can make predictions *only* after all initial points have been evaluated ' + + f'{len(self._observations)} <= {self.base_optimizer._initial_design._n_configs}') if self.base_optimizer._config_selector._model is None: - raise RuntimeError("Surrogate model is not yet trained") + raise RuntimeError('Surrogate model is not yet trained') - configs: npt.NDArray = convert_configurations_to_array( - self._to_configspace_configs(configurations) - ) - mean_predictions, _ = self.base_optimizer._config_selector._model.predict( - configs - ) - return mean_predictions.reshape( - -1, - ) + configs: npt.NDArray = convert_configurations_to_array(self._to_configspace_configs(configurations)) + mean_predictions, _ = self.base_optimizer._config_selector._model.predict(configs) + return mean_predictions.reshape(-1,) - def acquisition_function( - self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None - ) -> npt.NDArray: + def acquisition_function(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> npt.NDArray: if context is not None: - warn( - f"Not Implemented: Ignoring context {list(context.columns)}", - UserWarning, - ) + warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) if self._space_adapter: raise NotImplementedError() # pylint: disable=protected-access if self.base_optimizer._config_selector._acquisition_function is None: - raise RuntimeError("Acquisition function is not yet initialized") + raise RuntimeError('Acquisition function is not yet initialized') configs: list = self._to_configspace_configs(configurations) - return self.base_optimizer._config_selector._acquisition_function( - configs - ).reshape( - -1, - ) + return self.base_optimizer._config_selector._acquisition_function(configs).reshape(-1,) def cleanup(self) -> None: try: @@ -510,9 +475,7 @@ def cleanup(self) -> None: except AttributeError: warning("_temp_output_directory does not exist.") - def _to_configspace_configs( - self, configurations: pd.DataFrame - ) -> List[ConfigSpace.Configuration]: + def _to_configspace_configs(self, configurations: pd.DataFrame) -> List[ConfigSpace.Configuration]: """Convert a dataframe of configurations to a list of ConfigSpace configurations. Parameters @@ -526,10 +489,8 @@ def _to_configspace_configs( List of ConfigSpace configurations. """ return [ - ConfigSpace.Configuration( - self.optimizer_parameter_space, values=config.to_dict() - ) - for (_, config) in configurations.astype("O").iterrows() + ConfigSpace.Configuration(self.optimizer_parameter_space, values=config.to_dict()) + for (_, config) in configurations.astype('O').iterrows() ] @staticmethod From 7533b4e35c52225afe7466846499089db38a261f Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Mon, 3 Jun 2024 17:58:30 -0500 Subject: [PATCH 06/21] Update mlos_core/mlos_core/optimizers/optimizer.py Co-authored-by: Brian Kroth --- mlos_core/mlos_core/optimizers/optimizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlos_core/mlos_core/optimizers/optimizer.py b/mlos_core/mlos_core/optimizers/optimizer.py index 71875bdfb1..9b7e724941 100644 --- a/mlos_core/mlos_core/optimizers/optimizer.py +++ b/mlos_core/mlos_core/optimizers/optimizer.py @@ -26,7 +26,7 @@ class BaseOptimizer(metaclass=ABCMeta): def __init__(self, *, parameter_space: ConfigSpace.ConfigurationSpace, - optimization_targets: str | List[str] | None = None, + optimization_targets: Optional[Union[str, List[str]] = None, space_adapter: Optional[BaseSpaceAdapter] = None): """ Create a new instance of the base optimizer. From 7278994facd97822982cb094f4c4a894d264107b Mon Sep 17 00:00:00 2001 From: jsfreischuetz Date: Mon, 3 Jun 2024 22:36:04 -0500 Subject: [PATCH 07/21] add checks back to optimizer --- .vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 776ba120ac..2c8098f9d9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,6 +17,7 @@ // See Also: // - https://github.com/microsoft/vscode/issues/2809#issuecomment-1544387883 // - mlos_bench/config/schemas/README.md + { "fileMatch": [ "mlos_bench/mlos_bench/tests/config/schemas/environments/test-cases/**/*.jsonc", @@ -135,7 +136,8 @@ // See Also .vscode/launch.json for environment variable args to pytest during debug sessions. // For the rest, see setup.cfg "python.testing.pytestArgs": [ + "--log-level=DEBUG", "." ], "python.testing.unittestEnabled": false -} \ No newline at end of file +} From c79294ad30aae7b38c4ef4410859136e906edaab Mon Sep 17 00:00:00 2001 From: jsfreischuetz Date: Mon, 3 Jun 2024 22:40:13 -0500 Subject: [PATCH 08/21] add checks back --- mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py | 1 + mlos_core/mlos_core/tests/optimizers/optimizer_test.py | 1 + 2 files changed, 2 insertions(+) diff --git a/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py b/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py index 510ca119fa..333534378f 100644 --- a/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py +++ b/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py @@ -112,6 +112,7 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: (all_configs, all_scores, all_contexts) = optimizer.get_observations() assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) + assert isinstance(all_contexts, pd.DataFrame) or all_contexts is None assert set(all_configs.columns) == {'x', 'y'} assert set(all_scores.columns) == {'main_score', 'other_score'} assert all_configs.shape == (max_iterations, 2) diff --git a/mlos_core/mlos_core/tests/optimizers/optimizer_test.py b/mlos_core/mlos_core/tests/optimizers/optimizer_test.py index 4d3aea7d77..14dec95f5b 100644 --- a/mlos_core/mlos_core/tests/optimizers/optimizer_test.py +++ b/mlos_core/mlos_core/tests/optimizers/optimizer_test.py @@ -393,3 +393,4 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: (all_configs, all_scores, all_contexts) = optimizer.get_observations() assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) + assert isinstance(all_contexts, pd.DataFrame) or all_contexts is None From 048269c5d4a0838d3ae8bc31fb3561939a8bda81 Mon Sep 17 00:00:00 2001 From: jsfreischuetz Date: Mon, 3 Jun 2024 22:42:29 -0500 Subject: [PATCH 09/21] add checks back --- .../mlos_core/tests/optimizers/optimizer_multiobj_test.py | 5 ++++- mlos_core/mlos_core/tests/optimizers/optimizer_test.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py b/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py index 333534378f..4c8698c90d 100644 --- a/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py +++ b/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py @@ -112,7 +112,10 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: (all_configs, all_scores, all_contexts) = optimizer.get_observations() assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) - assert isinstance(all_contexts, pd.DataFrame) or all_contexts is None + if optimizer_class is OptimizerType.SMAC: + assert isinstance(all_contexts, pd.DataFrame) or all_contexts is None + else: + assert all_contexts is None assert set(all_configs.columns) == {'x', 'y'} assert set(all_scores.columns) == {'main_score', 'other_score'} assert all_configs.shape == (max_iterations, 2) diff --git a/mlos_core/mlos_core/tests/optimizers/optimizer_test.py b/mlos_core/mlos_core/tests/optimizers/optimizer_test.py index 14dec95f5b..8e082cc88b 100644 --- a/mlos_core/mlos_core/tests/optimizers/optimizer_test.py +++ b/mlos_core/mlos_core/tests/optimizers/optimizer_test.py @@ -393,4 +393,7 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: (all_configs, all_scores, all_contexts) = optimizer.get_observations() assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) - assert isinstance(all_contexts, pd.DataFrame) or all_contexts is None + if optimizer_type is OptimizerType.SMAC: + assert isinstance(all_contexts, pd.DataFrame) or all_contexts is None + else: + assert all_contexts is None From 019192a9154eacc7748287859475ad6f2dabb861 Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Wed, 5 Jun 2024 15:26:52 -0500 Subject: [PATCH 10/21] update name of context to metadata, and add readme --- mlos_core/mlos_core/optimizers/README.md | 18 +++++ .../bayesian_optimizers/smac_optimizer.py | 48 ++++++------ .../mlos_core/optimizers/flaml_optimizer.py | 10 +-- mlos_core/mlos_core/optimizers/optimizer.py | 76 +++++++++---------- .../mlos_core/optimizers/random_optimizer.py | 17 ++--- 5 files changed, 93 insertions(+), 76 deletions(-) create mode 100644 mlos_core/mlos_core/optimizers/README.md diff --git a/mlos_core/mlos_core/optimizers/README.md b/mlos_core/mlos_core/optimizers/README.md new file mode 100644 index 0000000000..d00814108f --- /dev/null +++ b/mlos_core/mlos_core/optimizers/README.md @@ -0,0 +1,18 @@ +This is a directory that contains wrappers for different optimizers to integrate into MLOS. +This is implemented though child classes for the `BaseOptimizer` class defined in `optimizer.py`. + +The main goal of these optimizers is to take a suggest configurations based on prior samples to find an optimum based on some objective. This process is interacted with through and ask and tell interface. + +The following defintions are useful for understanding the implementation +- `configuration`: a vector representation of a configuration of a system to be evaluated. +- `score`: the objective(s) associated with a configuration +- `metadata`: additional information about the evaluation, such as the runtime budget used during evaluation. +- `context`: additional information about the evaluation used to extend the internal model used for suggesting samples. This is not yet implemented. + +The interface for these classes can be described as follows: + +- `register`: this is a function that takes a configuration, a score, and, optionally, metadata about the evaluation to update the model for future evaluations. +- `suggest`: this function returns a new confiugration for evaluation. Some optimizers will return additional metadata for evaluation, that should be used durin the register phase. This function can also optionally take context (not yet implemented), and an argument to force the function to return the default configuration. +- `register_pending`: registers a configuration and metadata pair as pending to the optimizer. +- `get_observations`: returns all observations reproted to the optimizer as a triplet of DataFrames (config, score, metadata). +- `get_best_observations`: returns the best observation as A triplet of best (config, score, metadata) DataFrames. \ No newline at end of file diff --git a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py index e276476b6a..0bd7b982f6 100644 --- a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py +++ b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py @@ -139,7 +139,7 @@ def __init__( # Store for TrialInfo instances returned by .ask() self.trial_info_df: pd.DataFrame = pd.DataFrame( - columns=["Configuration", "Context", "TrialInfo", "TrialValue"] + columns=["Configuration", "Metadata", "TrialInfo", "TrialValue"] ) # The default when not specified is to use a known seed (0) to keep results reproducible. # However, if a `None` seed is explicitly provided, we let a random seed be produced by SMAC. @@ -322,7 +322,7 @@ def _dummy_target_func( raise RuntimeError('This function should never be called.') def _register(self, configurations: pd.DataFrame, - scores: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> None: + scores: pd.DataFrame, metadata: Optional[pd.DataFrame] = None) -> None: """Registers the given configurations and scores. Parameters @@ -333,18 +333,18 @@ def _register(self, configurations: pd.DataFrame, scores : pd.DataFrame Scores from running the configurations. The index is the same as the index of the configurations. - context : pd.DataFrame - Context of the request that is being registered. + metadata : pd.DataFrame + Metadata of the request that is being registered. """ with self.lock: # Register each trial (one-by-one) - contexts: Union[List[pd.Series], List[None]] = _to_context(context) or [ + metadatas: Union[List[pd.Series], List[None]] = _to_metadata(metadata) or [ None for _ in scores # type: ignore[misc] ] for config, score, ctx in zip( self._to_configspace_configs(configurations), scores.values.tolist(), - contexts, + metadatas, ): value: TrialValue = TrialValue( cost=score, time=0.0, status=StatusType.SUCCESS @@ -357,7 +357,7 @@ def _register(self, configurations: pd.DataFrame, matching = ( self.trial_info_df["Configuration"] == config ) & pd.Series( - [df_ctx.equals(ctx) for df_ctx in self.trial_info_df["Context"]] + [df_ctx.equals(ctx) for df_ctx in self.trial_info_df["Metadata"]] ) # make a new entry @@ -410,8 +410,8 @@ def _suggest( configuration : pd.DataFrame Pandas dataframe with a single row. Column names are the parameter names. - context : pd.DataFrame - Pandas dataframe with a single row containing the context. + metadata : pd.DataFrame + Pandas dataframe with a single row containing the metadata. Column names are the budget, seed, and instance of the evaluation, if valid. """ with self.lock: @@ -427,16 +427,16 @@ def _suggest( assert trial.config.config_space == self.optimizer_parameter_space config_df = self._extract_config(trial) - context_df = SmacOptimizer._extract_context(trial) + metadata_df = SmacOptimizer._extract_metadata(trial) self.trial_info_df.loc[len(self.trial_info_df.index)] = [ trial.config, - context_df.iloc[0], + metadata_df.iloc[0], trial, None, ] - return config_df, context_df + return config_df, metadata_df def register_pending(self, configurations: pd.DataFrame, context: Optional[pd.DataFrame] = None) -> None: raise NotImplementedError() @@ -501,7 +501,7 @@ def _to_configspace_configs(self, configurations: pd.DataFrame) -> List[ConfigSp ] @staticmethod - def _extract_context(trial: TrialInfo) -> pd.DataFrame: + def _extract_metadata(trial: TrialInfo) -> pd.DataFrame: """Convert TrialInfo to a DataFrame. Parameters @@ -511,8 +511,8 @@ def _extract_context(trial: TrialInfo) -> pd.DataFrame: Returns ------- - context : pd.DataFrame - Pandas dataframe with a single row containing the context. + metadata : pd.DataFrame + Pandas dataframe with a single row containing the metadata. Column names are the budget and instance of the evaluation, if valid. """ return pd.DataFrame( @@ -553,18 +553,18 @@ def get_best_observation(self) -> pd.DataFrame: max_budget = np.nan budgets = [ - context["budget"].max() - for _, _, context in self._observations - if context is not None + metadata["budget"].max() + for _, _, metadata in self._observations + if metadata is not None ] if len(budgets) > 0: max_budget = max(budgets) if max_budget is not np.nan: observations = [ - (config, score, context) - for config, score, context in self._observations - if context is not None and context["budget"].max() == max_budget + (config, score, metadata) + for config, score, metadata in self._observations + if metadata is not None and metadata["budget"].max() == max_budget ] configs = pd.concat([config for config, _, _ in observations]) @@ -574,7 +574,7 @@ def get_best_observation(self) -> pd.DataFrame: return configs.nsmallest(1, columns="score") -def _to_context(contexts: Optional[pd.DataFrame]) -> Optional[List[pd.Series]]: - if contexts is None: +def _to_metadata(metadata: Optional[pd.DataFrame]) -> Optional[List[pd.Series]]: + if metadata is None: return None - return [idx_series[1] for idx_series in contexts.iterrows()] + return [idx_series[1] for idx_series in metadata.iterrows()] diff --git a/mlos_core/mlos_core/optimizers/flaml_optimizer.py b/mlos_core/mlos_core/optimizers/flaml_optimizer.py index 253cf98561..f444392cf3 100644 --- a/mlos_core/mlos_core/optimizers/flaml_optimizer.py +++ b/mlos_core/mlos_core/optimizers/flaml_optimizer.py @@ -86,7 +86,7 @@ def __init__(self, *, # pylint: disable=too-many-arguments self._suggested_config: Optional[dict] def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, - context: Optional[pd.DataFrame] = None) -> None: + metadata: Optional[pd.DataFrame] = None) -> None: """Registers the given configurations and scores. Parameters @@ -97,11 +97,11 @@ def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, scores : pd.DataFrame Scores from running the configurations. The index is the same as the index of the configurations. - context : None + metadata : None Not Yet Implemented. """ - if context is not None: - warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) + if metadata is not None: + warn(f"Not Implemented: Ignoring context {list(metadata.columns)}", UserWarning) for (_, config), (_, score) in zip(configurations.astype('O').iterrows(), scores.iterrows()): cs_config: ConfigSpace.Configuration = ConfigSpace.Configuration( self.optimizer_parameter_space, values=config.to_dict()) @@ -135,7 +135,7 @@ def _suggest( return pd.DataFrame(config, index=[0]), None def register_pending(self, configurations: pd.DataFrame, - context: Optional[pd.DataFrame] = None) -> None: + metadata: Optional[pd.DataFrame] = None) -> None: raise NotImplementedError() def _target_function(self, config: dict) -> Union[dict, None]: diff --git a/mlos_core/mlos_core/optimizers/optimizer.py b/mlos_core/mlos_core/optimizers/optimizer.py index aa3dc5b377..548e704f16 100644 --- a/mlos_core/mlos_core/optimizers/optimizer.py +++ b/mlos_core/mlos_core/optimizers/optimizer.py @@ -57,10 +57,10 @@ def __init__(self, *, self._space_adapter: Optional[BaseSpaceAdapter] = space_adapter self._observations: List[Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame]]] = [] - self._has_context: Optional[bool] = None + self._has_metadata: Optional[bool] = None self._pending_observations: List[Tuple[pd.DataFrame, Optional[pd.DataFrame]]] = [] self.delayed_config: Optional[pd.DataFrame] = None - self.delayed_context: Optional[pd.DataFrame] = None + self.delayed_metadata: Optional[pd.DataFrame] = None def __repr__(self) -> str: return f"{self.__class__.__name__}(space_adapter={self.space_adapter})" @@ -71,7 +71,7 @@ def space_adapter(self) -> Optional[BaseSpaceAdapter]: return self._space_adapter def register(self, configurations: pd.DataFrame, scores: pd.DataFrame, - context: Optional[pd.DataFrame] = None) -> None: + metadata: Optional[pd.DataFrame] = None) -> None: """Wrapper method, which employs the space adapter (if any), before registering the configurations and scores. Parameters @@ -80,36 +80,35 @@ def register(self, configurations: pd.DataFrame, scores: pd.DataFrame, Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations. scores : pd.DataFrame Scores from running the configurations. The index is the same as the index of the configurations. - - context : pd.DataFrame - Not Yet Implemented. + metadata : pd.DataFrame + Implementaton depends on instance. """ # Do some input validation. if type(self._optimization_targets) is str: assert self._optimization_targets in scores.columns, "Mismatched optimization targets." if type(self._optimization_targets) is list: assert set(scores.columns) >= set(self._optimization_targets), "Mismatched optimization targets." - assert self._has_context is None or self._has_context ^ (context is None), \ - "Context must always be added or never be added." + assert self._has_metadata is None or self._has_metadata ^ (metadata is None), \ + "Metadata must always be added or never be added." assert len(configurations) == len(scores), \ "Mismatched number of configurations and scores." - if context is not None: - assert len(configurations) == len(context), \ - "Mismatched number of configurations and context." + if metadata is not None: + assert len(configurations) == len(metadata), \ + "Mismatched number of configurations and metadata." assert configurations.shape[1] == len(self.parameter_space.values()), \ "Mismatched configuration shape." - self._observations.append((configurations, scores, context)) - self._has_context = context is not None + self._observations.append((configurations, scores, metadata)) + self._has_metadata = metadata is not None if self._space_adapter: configurations = self._space_adapter.inverse_transform(configurations) assert configurations.shape[1] == len(self.optimizer_parameter_space.values()), \ "Mismatched configuration shape after inverse transform." - return self._register(configurations, scores, context) + return self._register(configurations, scores, metadata) @abstractmethod def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, - context: Optional[pd.DataFrame] = None) -> None: + metadata: Optional[pd.DataFrame] = None) -> None: """Registers the given configurations and scores. Parameters @@ -119,8 +118,8 @@ def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, scores : pd.DataFrame Scores from running the configurations. The index is the same as the index of the configurations. - context : pd.DataFrame - Not Yet Implemented. + metadata : pd.DataFrame + Implementaton depends on instance. """ pass # pylint: disable=unnecessary-pass # pragma: no cover @@ -142,26 +141,25 @@ def suggest( ------- configuration : pd.DataFrame Pandas dataframe with a single row. Column names are the parameter names. - - context : pd.DataFrame - Pandas dataframe with a single row containing the context. + metadata : pd.DataFrame + Pandas dataframe with a single row containing the metadata. Column names are the budget, seed, and instance of the evaluation, if valid. """ if defaults: - self.delayed_config, self.delayed_context = self._suggest(context) + self.delayed_config, self.delayed_metadata = self._suggest(context) configuration: pd.DataFrame = config_to_dataframe( self.parameter_space.get_default_configuration() ) - context = self.delayed_context + metadata = self.delayed_metadata if self.space_adapter is not None: configuration = self.space_adapter.inverse_transform(configuration) else: if self.delayed_config is None: - configuration, context = self._suggest(context) + configuration, metadata = self._suggest(metadata) else: - configuration, context = self.delayed_config, self.delayed_context - self.delayed_config, self.delayed_context = None, None + configuration, metadata = self.delayed_config, self.delayed_metadata + self.delayed_config, self.delayed_metadata = None, None assert len(configuration) == 1, \ "Suggest must return a single configuration." assert set(configuration.columns).issubset(set(self.optimizer_parameter_space)), \ @@ -170,7 +168,7 @@ def suggest( configuration = self._space_adapter.transform(configuration) assert set(configuration.columns).issubset(set(self.parameter_space)), \ "Space adapter produced a configuration that does not match the expected parameter space." - return configuration, context + return configuration, metadata @abstractmethod def _suggest( @@ -187,12 +185,16 @@ def _suggest( ------- configuration : pd.DataFrame Pandas dataframe with a single row. Column names are the parameter names. + + metadata : pd.DataFrame + Pandas dataframe with a single row containing the metadata. + Column names are the budget, seed, and instance of the evaluation, if valid. """ pass # pylint: disable=unnecessary-pass # pragma: no cover @abstractmethod def register_pending(self, configurations: pd.DataFrame, - context: Optional[pd.DataFrame] = None) -> None: + metadata: Optional[pd.DataFrame] = None) -> None: """Registers the given configurations as "pending". That is it say, it has been suggested by the optimizer, and an experiment trial has been started. This can be useful for executing multiple trials in parallel, retry logic, etc. @@ -201,31 +203,29 @@ def register_pending(self, configurations: pd.DataFrame, ---------- configurations : pd.DataFrame Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations. - context : pd.DataFrame - Not Yet Implemented. """ pass # pylint: disable=unnecessary-pass # pragma: no cover def get_observations(self) -> Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame]]: """ - Returns the observations as a triplet of DataFrames (config, score, context). + Returns the observations as a triplet of DataFrames (config, score, metadata). Returns ------- observations : Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame]] - A triplet of (config, score, context) DataFrames of observations. + A triplet of (config, score, metadata) DataFrames of observations. """ if len(self._observations) == 0: raise ValueError("No observations registered yet.") configs = pd.concat([config for config, _, _ in self._observations]).reset_index(drop=True) scores = pd.concat([score for _, score, _ in self._observations]).reset_index(drop=True) - contexts = pd.concat([pd.DataFrame() if context is None else context - for _, _, context in self._observations]).reset_index(drop=True) - return (configs, scores, contexts if len(contexts.columns) > 0 else None) + metadatas = pd.concat([pd.DataFrame() if metadata is None else metadata + for _, _, metadata in self._observations]).reset_index(drop=True) + return (configs, scores, metadatas if len(metadatas.columns) > 0 else None) def get_best_observations(self, n_max: int = 1) -> Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame]]: """ - Get the N best observations so far as a triplet of DataFrames (config, score, context). + Get the N best observations so far as a triplet of DataFrames (config, score, metadata). Default is N=1. The columns are ordered in ASCENDING order of the optimization targets. The function uses `pandas.DataFrame.nsmallest(..., keep="first")` method under the hood. @@ -237,14 +237,14 @@ def get_best_observations(self, n_max: int = 1) -> Tuple[pd.DataFrame, pd.DataFr Returns ------- observations : Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame]] - A triplet of best (config, score, context) DataFrames of best observations. + A triplet of best (config, score, metadata) DataFrames of best observations. """ if len(self._observations) == 0: raise ValueError("No observations registered yet.") - (configs, scores, contexts) = self.get_observations() + (configs, scores, metadatas) = self.get_observations() idx = scores.nsmallest(n_max, columns=self._optimization_targets, keep="first").index return (configs.loc[idx], scores.loc[idx], - None if contexts is None else contexts.loc[idx]) + None if metadatas is None else metadatas.loc[idx]) def cleanup(self) -> None: """ diff --git a/mlos_core/mlos_core/optimizers/random_optimizer.py b/mlos_core/mlos_core/optimizers/random_optimizer.py index 8129f65b2e..e9bbe12559 100644 --- a/mlos_core/mlos_core/optimizers/random_optimizer.py +++ b/mlos_core/mlos_core/optimizers/random_optimizer.py @@ -25,7 +25,7 @@ class RandomOptimizer(BaseOptimizer): """ def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, - context: Optional[pd.DataFrame] = None) -> None: + metadata: Optional[pd.DataFrame] = None) -> None: """Registers the given configurations and scores. Doesn't do anything on the RandomOptimizer except storing configurations for logging. @@ -38,11 +38,10 @@ def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, scores : pd.DataFrame Scores from running the configurations. The index is the same as the index of the configurations. - context : None - Not Yet Implemented. + metadata : None + Metadata is ignored for random_optimizer. """ - if context is not None: - warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) + pass # should we pop them from self.pending_observations? def _suggest( @@ -62,8 +61,8 @@ def _suggest( configuration : pd.DataFrame Pandas dataframe with a single row. Column names are the parameter names. - context : pd.DataFrame - Pandas dataframe with a single row containing the context. + metadata : pd.DataFrame + Pandas dataframe with a single row containing the metadata. Column names are the budget, seed, and instance of the evaluation, if valid. """ if context is not None: @@ -72,6 +71,6 @@ def _suggest( return pd.DataFrame(dict(self.optimizer_parameter_space.sample_configuration()), index=[0]), None def register_pending(self, configurations: pd.DataFrame, - context: Optional[pd.DataFrame] = None) -> None: + metadata: Optional[pd.DataFrame] = None) -> None: raise NotImplementedError() - # self._pending_observations.append((configurations, context)) + # self._pending_observations.append((configurations, metadata)) From 88d63c1cef151b8a2f51ccc623848a20768bfd36 Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Wed, 5 Jun 2024 15:34:10 -0500 Subject: [PATCH 11/21] update tests to also use correct terminology --- .../optimizers/optimizer_multiobj_test.py | 16 +++++++----- .../tests/optimizers/optimizer_test.py | 26 +++++++++---------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py b/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py index 4c8698c90d..60f51c5c87 100644 --- a/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py +++ b/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py @@ -84,7 +84,7 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: optimizer.get_observations() for _ in range(max_iterations): - suggestion, context = optimizer.suggest() + suggestion, metadata = optimizer.suggest() assert isinstance(suggestion, pd.DataFrame) assert set(suggestion.columns) == {'x', 'y'} # Check suggestion values are the expected dtype @@ -99,23 +99,27 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: observation = objective(suggestion) assert isinstance(observation, pd.DataFrame) assert set(observation.columns) == {'main_score', 'other_score'} - optimizer.register(suggestion, observation, context) + optimizer.register(suggestion, observation, metadata) - (best_config, best_score, best_context) = optimizer.get_best_observations() + (best_config, best_score, best_metadata) = optimizer.get_best_observations() assert isinstance(best_config, pd.DataFrame) assert isinstance(best_score, pd.DataFrame) + if optimizer_class is OptimizerType.SMAC: + assert isinstance(best_metadata, pd.DataFrame) or best_metadata is None + else: + assert best_metadata is None assert set(best_config.columns) == {'x', 'y'} assert set(best_score.columns) == {'main_score', 'other_score'} assert best_config.shape == (1, 2) assert best_score.shape == (1, 2) - (all_configs, all_scores, all_contexts) = optimizer.get_observations() + (all_configs, all_scores, all_metadata) = optimizer.get_observations() assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) if optimizer_class is OptimizerType.SMAC: - assert isinstance(all_contexts, pd.DataFrame) or all_contexts is None + assert isinstance(all_metadata, pd.DataFrame) or all_metadata is None else: - assert all_contexts is None + assert all_metadata is None assert set(all_configs.columns) == {'x', 'y'} assert set(all_scores.columns) == {'main_score', 'other_score'} assert all_configs.shape == (max_iterations, 2) diff --git a/mlos_core/mlos_core/tests/optimizers/optimizer_test.py b/mlos_core/mlos_core/tests/optimizers/optimizer_test.py index 8e082cc88b..3c29b832fd 100644 --- a/mlos_core/mlos_core/tests/optimizers/optimizer_test.py +++ b/mlos_core/mlos_core/tests/optimizers/optimizer_test.py @@ -48,7 +48,7 @@ def test_create_optimizer_and_suggest(configuration_space: CS.ConfigurationSpace assert optimizer.parameter_space is not None - suggestion, context = optimizer.suggest() + suggestion, _ = optimizer.suggest() assert suggestion is not None myrepr = repr(optimizer) @@ -94,7 +94,7 @@ def objective(x: pd.Series) -> pd.DataFrame: optimizer.get_observations() for _ in range(max_iterations): - suggestion, context = optimizer.suggest() + suggestion, metadata = optimizer.suggest() assert isinstance(suggestion, pd.DataFrame) assert set(suggestion.columns) == {'x', 'y', 'z'} # check that suggestion is in the space @@ -103,9 +103,9 @@ def objective(x: pd.Series) -> pd.DataFrame: configuration.is_valid_configuration() observation = objective(suggestion['x']) assert isinstance(observation, pd.DataFrame) - optimizer.register(suggestion, observation, context) + optimizer.register(suggestion, observation, metadata) - (best_config, best_score, best_context) = optimizer.get_best_observations() + (best_config, best_score, _) = optimizer.get_best_observations() assert isinstance(best_config, pd.DataFrame) assert isinstance(best_score, pd.DataFrame) assert set(best_config.columns) == {'x', 'y', 'z'} @@ -114,7 +114,7 @@ def objective(x: pd.Series) -> pd.DataFrame: assert best_score.shape == (1, 1) assert best_score.score.iloc[0] < -5 - (all_configs, all_scores, all_contexts) = optimizer.get_observations() + (all_configs, all_scores, _) = optimizer.get_observations() assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) assert set(all_configs.columns) == {'x', 'y', 'z'} @@ -266,36 +266,36 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: _LOG.debug("Optimizer is done with random init.") # loop for optimizer - suggestion, context = optimizer.suggest() + suggestion, metadata = optimizer.suggest() observation = objective(suggestion) - optimizer.register(suggestion, observation, context) + optimizer.register(suggestion, observation, metadata) # loop for llamatune-optimizer - suggestion, context = llamatune_optimizer.suggest() + suggestion, metadata = llamatune_optimizer.suggest() _x, _y = suggestion['x'].iloc[0], suggestion['y'].iloc[0] assert _x == pytest.approx(_y, rel=1e-3) or _x + _y == pytest.approx(3., rel=1e-3) # optimizer explores 1-dimensional space observation = objective(suggestion) - llamatune_optimizer.register(suggestion, observation, context) + llamatune_optimizer.register(suggestion, observation, metadata) # Retrieve best observations best_observation = optimizer.get_best_observations() llamatune_best_observation = llamatune_optimizer.get_best_observations() - for (best_config, best_score, best_context) in (best_observation, llamatune_best_observation): + for (best_config, best_score, best_metadata) in (best_observation, llamatune_best_observation): assert isinstance(best_config, pd.DataFrame) assert isinstance(best_score, pd.DataFrame) assert set(best_config.columns) == {'x', 'y'} assert set(best_score.columns) == {'score'} - (best_config, best_score, _context) = best_observation - (llamatune_best_config, llamatune_best_score, _context) = llamatune_best_observation + (best_config, best_score, _metadata) = best_observation + (llamatune_best_config, llamatune_best_score, _metadata) = llamatune_best_observation # LlamaTune's optimizer score should better (i.e., lower) than plain optimizer's one, or close to that assert best_score.score.iloc[0] > llamatune_best_score.score.iloc[0] or \ best_score.score.iloc[0] + 1e-3 > llamatune_best_score.score.iloc[0] # Retrieve and check all observations - for (all_configs, all_scores, all_contexts) in ( + for (all_configs, all_scores, all_metadata) in ( optimizer.get_observations(), llamatune_optimizer.get_observations()): assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) From 4e36f28292302ee8023336701a886d9081b8849a Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Thu, 6 Jun 2024 14:08:27 -0500 Subject: [PATCH 12/21] Update mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py Co-authored-by: Brian Kroth --- .../mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py index 0bd7b982f6..650488acb0 100644 --- a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py +++ b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py @@ -125,7 +125,7 @@ def __init__( **kwargs: Additional arguments to be passed to the - scenerio, and intensifier + facade, scenario, and intensifier """ super().__init__( parameter_space=parameter_space, From 3326ac95128d16e9927015da5b762befd7bbafaf Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Thu, 6 Jun 2024 14:09:40 -0500 Subject: [PATCH 13/21] Update mlos_core/mlos_core/optimizers/README.md Co-authored-by: Brian Kroth --- mlos_core/mlos_core/optimizers/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlos_core/mlos_core/optimizers/README.md b/mlos_core/mlos_core/optimizers/README.md index d00814108f..002c2c2ff8 100644 --- a/mlos_core/mlos_core/optimizers/README.md +++ b/mlos_core/mlos_core/optimizers/README.md @@ -14,5 +14,5 @@ The interface for these classes can be described as follows: - `register`: this is a function that takes a configuration, a score, and, optionally, metadata about the evaluation to update the model for future evaluations. - `suggest`: this function returns a new confiugration for evaluation. Some optimizers will return additional metadata for evaluation, that should be used durin the register phase. This function can also optionally take context (not yet implemented), and an argument to force the function to return the default configuration. - `register_pending`: registers a configuration and metadata pair as pending to the optimizer. -- `get_observations`: returns all observations reproted to the optimizer as a triplet of DataFrames (config, score, metadata). +- `get_observations`: returns all observations reproted to the optimizer as a triplet of DataFrames (config, score, context, metadata). - `get_best_observations`: returns the best observation as A triplet of best (config, score, metadata) DataFrames. \ No newline at end of file From 2399d3e146fcc3cf98477e0471fc95ed1210e698 Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Thu, 6 Jun 2024 14:11:06 -0500 Subject: [PATCH 14/21] Update mlos_core/mlos_core/optimizers/README.md Co-authored-by: Brian Kroth --- mlos_core/mlos_core/optimizers/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mlos_core/mlos_core/optimizers/README.md b/mlos_core/mlos_core/optimizers/README.md index 002c2c2ff8..509c7c8e6f 100644 --- a/mlos_core/mlos_core/optimizers/README.md +++ b/mlos_core/mlos_core/optimizers/README.md @@ -1,3 +1,5 @@ +# Optimizers + This is a directory that contains wrappers for different optimizers to integrate into MLOS. This is implemented though child classes for the `BaseOptimizer` class defined in `optimizer.py`. From 1f210b54637e5cb7adb25a3375b4c0ad335e54d3 Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Thu, 6 Jun 2024 14:36:54 -0500 Subject: [PATCH 15/21] Add context back to the register interface This also involves modifying the test cases that interact with this interfaces --- .../bayesian_optimizers/smac_optimizer.py | 114 ++++++++---------- .../mlos_core/optimizers/flaml_optimizer.py | 9 +- mlos_core/mlos_core/optimizers/optimizer.py | 54 +++++---- .../mlos_core/optimizers/random_optimizer.py | 6 +- mlos_core/mlos_core/optimizers/utils.py | 36 ++++++ .../optimizers/optimizer_multiobj_test.py | 8 +- .../tests/optimizers/optimizer_test.py | 16 +-- 7 files changed, 142 insertions(+), 101 deletions(-) create mode 100644 mlos_core/mlos_core/optimizers/utils.py diff --git a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py index 650488acb0..2a557cfce2 100644 --- a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py +++ b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py @@ -19,6 +19,7 @@ import numpy as np import numpy.typing as npt import pandas as pd +from mlos_core.mlos_core.optimizers.utils import filter_kwargs, to_metadata from mlos_core.spaces.adapters.adapter import BaseSpaceAdapter from mlos_core.spaces.adapters.identity_adapter import IdentityAdapter from smac import HyperparameterOptimizationFacade as Optimizer_Smac @@ -171,7 +172,7 @@ def __init__( n_trials=max_trials, seed=seed or -1, # if -1, SMAC will generate a random seed internally n_workers=1, # Use a single thread for evaluating trials - **SmacOptimizer._filter_kwargs(Scenario, **kwargs), + **filter_kwargs(Scenario, **kwargs), ) config_selector: ConfigSelector = facade.get_config_selector( @@ -182,7 +183,7 @@ def __init__( intensifier_instance = facade.get_intensifier(scenario) else: intensifier_instance = intensifier( - scenario, **SmacOptimizer._filter_kwargs(intensifier, **kwargs) + scenario, **filter_kwargs(intensifier, **kwargs) ) # TODO: When bulk registering prior configs to rewarm the optimizer, @@ -238,7 +239,7 @@ def __init__( scenario, objective_weights=self._objective_weights), overwrite=True, logging_level=False, # Use the existing logger - **SmacOptimizer._filter_kwargs(facade, **kwargs), + **filter_kwargs(facade, **kwargs), ) self.lock = threading.Lock() @@ -264,33 +265,7 @@ def n_random_init(self) -> int: return self.base_optimizer._initial_design._n_configs @staticmethod - def _filter_kwargs(function: Callable, **kwargs: Any) -> Dict[str, Any]: - """ - Filters arguments provided in the kwargs dictionary to be restricted to the arguments legal for - the called function. - - Parameters - ---------- - function : Callable - function over which we filter kwargs for. - kwargs: - kwargs that we are filtering for the target function - - Returns - ------- - dict - kwargs with the non-legal argument filtered out - """ - sig = inspect.signature(function) - filter_keys = [ - param.name - for param in sig.parameters.values() - if param.kind == param.POSITIONAL_OR_KEYWORD - ] - filtered_dict = { - filter_key: kwargs[filter_key] for filter_key in filter_keys & kwargs.keys() - } - return filtered_dict + @staticmethod def _dummy_target_func( @@ -322,7 +297,8 @@ def _dummy_target_func( raise RuntimeError('This function should never be called.') def _register(self, configurations: pd.DataFrame, - scores: pd.DataFrame, metadata: Optional[pd.DataFrame] = None) -> None: + scores: pd.DataFrame, context: Optional[pd.DataFrame] = None, + metadata: Optional[pd.DataFrame] = None) -> None: """Registers the given configurations and scores. Parameters @@ -335,10 +311,16 @@ def _register(self, configurations: pd.DataFrame, metadata : pd.DataFrame Metadata of the request that is being registered. + + context : pd.DataFrame + Not Yet Implemented. """ + if context is not None: + warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) + with self.lock: # Register each trial (one-by-one) - metadatas: Union[List[pd.Series], List[None]] = _to_metadata(metadata) or [ + metadatas: Union[List[pd.Series], List[None]] = to_metadata(metadata) or [ None for _ in scores # type: ignore[misc] ] for config, score, ctx in zip( @@ -426,8 +408,8 @@ def _suggest( self.optimizer_parameter_space.check_configuration(trial.config) assert trial.config.config_space == self.optimizer_parameter_space - config_df = self._extract_config(trial) - metadata_df = SmacOptimizer._extract_metadata(trial) + config_df = _extract_config(trial) + metadata_df = _extract_metadata(trial) self.trial_info_df.loc[len(self.trial_info_df.index)] = [ trial.config, @@ -500,31 +482,6 @@ def _to_configspace_configs(self, configurations: pd.DataFrame) -> List[ConfigSp for (_, config) in configurations.astype('O').iterrows() ] - @staticmethod - def _extract_metadata(trial: TrialInfo) -> pd.DataFrame: - """Convert TrialInfo to a DataFrame. - - Parameters - ---------- - trial : TrialInfo - The trial to extract. - - Returns - ------- - metadata : pd.DataFrame - Pandas dataframe with a single row containing the metadata. - Column names are the budget and instance of the evaluation, if valid. - """ - return pd.DataFrame( - [[trial.instance, trial.seed, trial.budget]], - columns=["instance", "seed", "budget"], - ) - - def _extract_config(self, trial: TrialInfo) -> pd.DataFrame: - return pd.DataFrame( - [trial.config], columns=list(self.optimizer_parameter_space.keys()) - ) - def get_observations_full(self) -> pd.DataFrame: """Returns the observations as a dataframe with additional info. @@ -573,8 +530,39 @@ def get_best_observation(self) -> pd.DataFrame: return configs.nsmallest(1, columns="score") +def _extract_metadata(trial: TrialInfo) -> pd.DataFrame: + """Convert TrialInfo to a metadata DataFrame. -def _to_metadata(metadata: Optional[pd.DataFrame]) -> Optional[List[pd.Series]]: - if metadata is None: - return None - return [idx_series[1] for idx_series in metadata.iterrows()] + Parameters + ---------- + trial : TrialInfo + The trial to extract. + + Returns + ------- + metadata : pd.DataFrame + Pandas dataframe with a single row containing the metadata. + Column names are the budget and instance of the evaluation, if valid. + """ + return pd.DataFrame( + [[trial.instance, trial.seed, trial.budget]], + columns=["instance", "seed", "budget"], + ) + +def _extract_config(self, trial: TrialInfo) -> pd.DataFrame: + """Convert TrialInfo to a config DataFrame. + + Parameters + ---------- + trial : TrialInfo + The trial to extract. + + Returns + ------- + config : pd.DataFrame + Pandas dataframe with a single row containing the config. + Column names are config parameters + """ + return pd.DataFrame( + [trial.config], columns=list(self.optimizer_parameter_space.keys()) + ) \ No newline at end of file diff --git a/mlos_core/mlos_core/optimizers/flaml_optimizer.py b/mlos_core/mlos_core/optimizers/flaml_optimizer.py index f444392cf3..d07622dd0c 100644 --- a/mlos_core/mlos_core/optimizers/flaml_optimizer.py +++ b/mlos_core/mlos_core/optimizers/flaml_optimizer.py @@ -86,7 +86,7 @@ def __init__(self, *, # pylint: disable=too-many-arguments self._suggested_config: Optional[dict] def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, - metadata: Optional[pd.DataFrame] = None) -> None: + context: Optional[pd.DataFrame] = None, metadata: Optional[pd.DataFrame] = None) -> None: """Registers the given configurations and scores. Parameters @@ -96,12 +96,15 @@ def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, scores : pd.DataFrame Scores from running the configurations. The index is the same as the index of the configurations. - + context : None + Not Yet Implemented. metadata : None Not Yet Implemented. """ + if context is not None: + warn(f"Not Implemented: Ignoring context {list(context.columns)}", UserWarning) if metadata is not None: - warn(f"Not Implemented: Ignoring context {list(metadata.columns)}", UserWarning) + warn(f"Not Implemented: Ignoring metadata {list(metadata.columns)}", UserWarning) for (_, config), (_, score) in zip(configurations.astype('O').iterrows(), scores.iterrows()): cs_config: ConfigSpace.Configuration = ConfigSpace.Configuration( self.optimizer_parameter_space, values=config.to_dict()) diff --git a/mlos_core/mlos_core/optimizers/optimizer.py b/mlos_core/mlos_core/optimizers/optimizer.py index 548e704f16..30ef49e4d2 100644 --- a/mlos_core/mlos_core/optimizers/optimizer.py +++ b/mlos_core/mlos_core/optimizers/optimizer.py @@ -56,9 +56,9 @@ def __init__(self, *, raise ValueError("Number of weights must match the number of optimization targets") self._space_adapter: Optional[BaseSpaceAdapter] = space_adapter - self._observations: List[Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame]]] = [] - self._has_metadata: Optional[bool] = None - self._pending_observations: List[Tuple[pd.DataFrame, Optional[pd.DataFrame]]] = [] + self._observations: List[Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame], Optional[pd.DataFrame]]] = [] + self._has_context: Optional[bool] = None + self._pending_observations: List[Tuple[pd.DataFrame, Optional[pd.DataFrame], Optional[pd.DataFrame]]] = [] self.delayed_config: Optional[pd.DataFrame] = None self.delayed_metadata: Optional[pd.DataFrame] = None @@ -71,7 +71,7 @@ def space_adapter(self) -> Optional[BaseSpaceAdapter]: return self._space_adapter def register(self, configurations: pd.DataFrame, scores: pd.DataFrame, - metadata: Optional[pd.DataFrame] = None) -> None: + context: Optional[pd.DataFrame] = None, metadata: Optional[pd.DataFrame] = None) -> None: """Wrapper method, which employs the space adapter (if any), before registering the configurations and scores. Parameters @@ -80,6 +80,8 @@ def register(self, configurations: pd.DataFrame, scores: pd.DataFrame, Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations. scores : pd.DataFrame Scores from running the configurations. The index is the same as the index of the configurations. + context : pd.DataFrame + Not implemented yet. metadata : pd.DataFrame Implementaton depends on instance. """ @@ -88,27 +90,30 @@ def register(self, configurations: pd.DataFrame, scores: pd.DataFrame, assert self._optimization_targets in scores.columns, "Mismatched optimization targets." if type(self._optimization_targets) is list: assert set(scores.columns) >= set(self._optimization_targets), "Mismatched optimization targets." - assert self._has_metadata is None or self._has_metadata ^ (metadata is None), \ + assert self._has_context is None or self._has_context ^ (context is None), \ "Metadata must always be added or never be added." assert len(configurations) == len(scores), \ "Mismatched number of configurations and scores." + if context is not None: + assert len(configurations) == len(context), \ + "Mismatched number of configurations and context." if metadata is not None: assert len(configurations) == len(metadata), \ "Mismatched number of configurations and metadata." assert configurations.shape[1] == len(self.parameter_space.values()), \ "Mismatched configuration shape." - self._observations.append((configurations, scores, metadata)) - self._has_metadata = metadata is not None + self._observations.append((configurations, scores, metadata, context)) + self._has_context = context is not None if self._space_adapter: configurations = self._space_adapter.inverse_transform(configurations) assert configurations.shape[1] == len(self.optimizer_parameter_space.values()), \ "Mismatched configuration shape after inverse transform." - return self._register(configurations, scores, metadata) + return self._register(configurations, scores, metadata, context) @abstractmethod def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, - metadata: Optional[pd.DataFrame] = None) -> None: + context: Optional[pd.DataFrame] = None, metadata: Optional[pd.DataFrame] = None) -> None: """Registers the given configurations and scores. Parameters @@ -117,7 +122,8 @@ def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations. scores : pd.DataFrame Scores from running the configurations. The index is the same as the index of the configurations. - + context : pd.DataFrame + Not implemented yet. metadata : pd.DataFrame Implementaton depends on instance. """ @@ -194,7 +200,7 @@ def _suggest( @abstractmethod def register_pending(self, configurations: pd.DataFrame, - metadata: Optional[pd.DataFrame] = None) -> None: + context: Optional[pd.DataFrame] = None, metadata: Optional[pd.DataFrame] = None) -> None: """Registers the given configurations as "pending". That is it say, it has been suggested by the optimizer, and an experiment trial has been started. This can be useful for executing multiple trials in parallel, retry logic, etc. @@ -203,10 +209,14 @@ def register_pending(self, configurations: pd.DataFrame, ---------- configurations : pd.DataFrame Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations. + context : pd.DataFrame + Not implemented yet. + metadata : pd.DataFrame + Implementaton depends on instance. """ pass # pylint: disable=unnecessary-pass # pragma: no cover - def get_observations(self) -> Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame]]: + def get_observations(self) -> Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame], Optional[pd.DataFrame]]: """ Returns the observations as a triplet of DataFrames (config, score, metadata). @@ -217,13 +227,15 @@ def get_observations(self) -> Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.Data """ if len(self._observations) == 0: raise ValueError("No observations registered yet.") - configs = pd.concat([config for config, _, _ in self._observations]).reset_index(drop=True) - scores = pd.concat([score for _, score, _ in self._observations]).reset_index(drop=True) + configs = pd.concat([config for config, _, _, _ in self._observations]).reset_index(drop=True) + scores = pd.concat([score for _, score, _, _ in self._observations]).reset_index(drop=True) + contexts = pd.concat([pd.DataFrame() if context is None else context + for _, _, context, _ in self._observations]).reset_index(drop=True) metadatas = pd.concat([pd.DataFrame() if metadata is None else metadata - for _, _, metadata in self._observations]).reset_index(drop=True) - return (configs, scores, metadatas if len(metadatas.columns) > 0 else None) + for _, _, _, metadata in self._observations]).reset_index(drop=True) + return (configs, scores, contexts, metadatas if len(metadatas.columns) > 0 else None) - def get_best_observations(self, n_max: int = 1) -> Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame]]: + def get_best_observations(self, n_max: int = 1) -> Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame], Optional[pd.DataFrame]]: """ Get the N best observations so far as a triplet of DataFrames (config, score, metadata). Default is N=1. The columns are ordered in ASCENDING order of the optimization targets. @@ -236,15 +248,15 @@ def get_best_observations(self, n_max: int = 1) -> Tuple[pd.DataFrame, pd.DataFr Returns ------- - observations : Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame]] - A triplet of best (config, score, metadata) DataFrames of best observations. + observations : Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame], Optional[pd.DataFrame]] + A triplet of best (config, score, context, metadata) DataFrames of best observations. """ if len(self._observations) == 0: raise ValueError("No observations registered yet.") - (configs, scores, metadatas) = self.get_observations() + (configs, scores, contexts, metadatas) = self.get_observations() idx = scores.nsmallest(n_max, columns=self._optimization_targets, keep="first").index return (configs.loc[idx], scores.loc[idx], - None if metadatas is None else metadatas.loc[idx]) + None if contexts is None else contexts.loc[idx], None if metadatas is None else metadatas.loc[idx]) def cleanup(self) -> None: """ diff --git a/mlos_core/mlos_core/optimizers/random_optimizer.py b/mlos_core/mlos_core/optimizers/random_optimizer.py index e9bbe12559..b1acec0d56 100644 --- a/mlos_core/mlos_core/optimizers/random_optimizer.py +++ b/mlos_core/mlos_core/optimizers/random_optimizer.py @@ -25,7 +25,7 @@ class RandomOptimizer(BaseOptimizer): """ def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, - metadata: Optional[pd.DataFrame] = None) -> None: + context: Optional[pd.DataFrame] = None, metadata: Optional[pd.DataFrame] = None) -> None: """Registers the given configurations and scores. Doesn't do anything on the RandomOptimizer except storing configurations for logging. @@ -34,10 +34,10 @@ def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame, ---------- configurations : pd.DataFrame Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations. - scores : pd.DataFrame Scores from running the configurations. The index is the same as the index of the configurations. - + context : None + Metadata is ignored for random_optimizer. metadata : None Metadata is ignored for random_optimizer. """ diff --git a/mlos_core/mlos_core/optimizers/utils.py b/mlos_core/mlos_core/optimizers/utils.py new file mode 100644 index 0000000000..2ba7af331f --- /dev/null +++ b/mlos_core/mlos_core/optimizers/utils.py @@ -0,0 +1,36 @@ +import inspect +from typing import Any, Callable, Dict, Optional +import pandas as pd + +def to_metadata(metadata: Optional[pd.DataFrame]) -> Optional[List[pd.Series]]: + if metadata is None: + return None + return [idx_series[1] for idx_series in metadata.iterrows()] + +def filter_kwargs(function: Callable, **kwargs: Any) -> Dict[str, Any]: + """ + Filters arguments provided in the kwargs dictionary to be restricted to the arguments legal for + the called function. + + Parameters + ---------- + function : Callable + function over which we filter kwargs for. + kwargs: + kwargs that we are filtering for the target function + + Returns + ------- + dict + kwargs with the non-legal argument filtered out + """ + sig = inspect.signature(function) + filter_keys = [ + param.name + for param in sig.parameters.values() + if param.kind == param.POSITIONAL_OR_KEYWORD + ] + filtered_dict = { + filter_key: kwargs[filter_key] for filter_key in filter_keys & kwargs.keys() + } + return filtered_dict \ No newline at end of file diff --git a/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py b/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py index 60f51c5c87..4f23ece41b 100644 --- a/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py +++ b/mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py @@ -99,27 +99,29 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: observation = objective(suggestion) assert isinstance(observation, pd.DataFrame) assert set(observation.columns) == {'main_score', 'other_score'} - optimizer.register(suggestion, observation, metadata) + optimizer.register(suggestion, observation, context=None, metadata=metadata) - (best_config, best_score, best_metadata) = optimizer.get_best_observations() + (best_config, best_score, best_metadata, best_context) = optimizer.get_best_observations() assert isinstance(best_config, pd.DataFrame) assert isinstance(best_score, pd.DataFrame) if optimizer_class is OptimizerType.SMAC: assert isinstance(best_metadata, pd.DataFrame) or best_metadata is None else: assert best_metadata is None + assert best_context is None assert set(best_config.columns) == {'x', 'y'} assert set(best_score.columns) == {'main_score', 'other_score'} assert best_config.shape == (1, 2) assert best_score.shape == (1, 2) - (all_configs, all_scores, all_metadata) = optimizer.get_observations() + (all_configs, all_scores, all_metadata, best_context) = optimizer.get_observations() assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) if optimizer_class is OptimizerType.SMAC: assert isinstance(all_metadata, pd.DataFrame) or all_metadata is None else: assert all_metadata is None + assert best_context is None assert set(all_configs.columns) == {'x', 'y'} assert set(all_scores.columns) == {'main_score', 'other_score'} assert all_configs.shape == (max_iterations, 2) diff --git a/mlos_core/mlos_core/tests/optimizers/optimizer_test.py b/mlos_core/mlos_core/tests/optimizers/optimizer_test.py index 3c29b832fd..68889701f3 100644 --- a/mlos_core/mlos_core/tests/optimizers/optimizer_test.py +++ b/mlos_core/mlos_core/tests/optimizers/optimizer_test.py @@ -103,9 +103,9 @@ def objective(x: pd.Series) -> pd.DataFrame: configuration.is_valid_configuration() observation = objective(suggestion['x']) assert isinstance(observation, pd.DataFrame) - optimizer.register(suggestion, observation, metadata) + optimizer.register(suggestion, observation, context=None, metadata=metadata) - (best_config, best_score, _) = optimizer.get_best_observations() + (best_config, best_score, _, _) = optimizer.get_best_observations() assert isinstance(best_config, pd.DataFrame) assert isinstance(best_score, pd.DataFrame) assert set(best_config.columns) == {'x', 'y', 'z'} @@ -114,7 +114,7 @@ def objective(x: pd.Series) -> pd.DataFrame: assert best_score.shape == (1, 1) assert best_score.score.iloc[0] < -5 - (all_configs, all_scores, _) = optimizer.get_observations() + (all_configs, all_scores, _, _) = optimizer.get_observations() assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) assert set(all_configs.columns) == {'x', 'y', 'z'} @@ -268,26 +268,26 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: # loop for optimizer suggestion, metadata = optimizer.suggest() observation = objective(suggestion) - optimizer.register(suggestion, observation, metadata) + optimizer.register(suggestion, observation, context=None, metadata=metadata) # loop for llamatune-optimizer suggestion, metadata = llamatune_optimizer.suggest() _x, _y = suggestion['x'].iloc[0], suggestion['y'].iloc[0] assert _x == pytest.approx(_y, rel=1e-3) or _x + _y == pytest.approx(3., rel=1e-3) # optimizer explores 1-dimensional space observation = objective(suggestion) - llamatune_optimizer.register(suggestion, observation, metadata) + llamatune_optimizer.register(suggestion, observation, context=None, metadata=metadata) # Retrieve best observations best_observation = optimizer.get_best_observations() llamatune_best_observation = llamatune_optimizer.get_best_observations() - for (best_config, best_score, best_metadata) in (best_observation, llamatune_best_observation): + for (best_config, best_score, _, _) in (best_observation, llamatune_best_observation): assert isinstance(best_config, pd.DataFrame) assert isinstance(best_score, pd.DataFrame) assert set(best_config.columns) == {'x', 'y'} assert set(best_score.columns) == {'score'} - (best_config, best_score, _metadata) = best_observation + (best_config, best_score, _, _) = best_observation (llamatune_best_config, llamatune_best_score, _metadata) = llamatune_best_observation # LlamaTune's optimizer score should better (i.e., lower) than plain optimizer's one, or close to that @@ -295,7 +295,7 @@ def objective(point: pd.DataFrame) -> pd.DataFrame: best_score.score.iloc[0] + 1e-3 > llamatune_best_score.score.iloc[0] # Retrieve and check all observations - for (all_configs, all_scores, all_metadata) in ( + for (all_configs, all_scores, _, _) in ( optimizer.get_observations(), llamatune_optimizer.get_observations()): assert isinstance(all_configs, pd.DataFrame) assert isinstance(all_scores, pd.DataFrame) From 48af70f122f03bb99cc1ffa53f462812ab4ee2a0 Mon Sep 17 00:00:00 2001 From: Brian Kroth Date: Wed, 12 Jun 2024 11:46:14 -0500 Subject: [PATCH 16/21] Apply suggestions from code review --- mlos_core/mlos_core/optimizers/README.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/mlos_core/mlos_core/optimizers/README.md b/mlos_core/mlos_core/optimizers/README.md index 509c7c8e6f..e1fd5334c0 100644 --- a/mlos_core/mlos_core/optimizers/README.md +++ b/mlos_core/mlos_core/optimizers/README.md @@ -3,18 +3,25 @@ This is a directory that contains wrappers for different optimizers to integrate into MLOS. This is implemented though child classes for the `BaseOptimizer` class defined in `optimizer.py`. -The main goal of these optimizers is to take a suggest configurations based on prior samples to find an optimum based on some objective. This process is interacted with through and ask and tell interface. +The main goal of these optimizers is to `suggest` configurations, possibly based on prior trial data to find an optimum based on some objective(s). +This process is interacted with through `register` and `suggest` interfaces. The following defintions are useful for understanding the implementation - `configuration`: a vector representation of a configuration of a system to be evaluated. - `score`: the objective(s) associated with a configuration - `metadata`: additional information about the evaluation, such as the runtime budget used during evaluation. -- `context`: additional information about the evaluation used to extend the internal model used for suggesting samples. This is not yet implemented. +- `context`: additional (static) information about the evaluation used to extend the internal model used for suggesting samples. + For instance, a descriptor of the VM size (vCore count and # of GB of RAM), and some descriptor of the workload. + The intent being to allow either sharing or indexing of trial info between "similar" experiments in order to help make the optimization process more efficient for new scenarios. + > Note: This is not yet implemented. The interface for these classes can be described as follows: - `register`: this is a function that takes a configuration, a score, and, optionally, metadata about the evaluation to update the model for future evaluations. -- `suggest`: this function returns a new confiugration for evaluation. Some optimizers will return additional metadata for evaluation, that should be used durin the register phase. This function can also optionally take context (not yet implemented), and an argument to force the function to return the default configuration. +- `suggest`: this function returns a new configuration for evaluation. + + Some optimizers will return additional metadata for evaluation, that should be used during the register phase. + This function can also optionally take context (not yet implemented), and an argument to force the function to return the default configuration. - `register_pending`: registers a configuration and metadata pair as pending to the optimizer. - `get_observations`: returns all observations reproted to the optimizer as a triplet of DataFrames (config, score, context, metadata). -- `get_best_observations`: returns the best observation as A triplet of best (config, score, metadata) DataFrames. \ No newline at end of file +- `get_best_observations`: returns the best observation as a triplet of best (config, score, context, metadata) DataFrames. \ No newline at end of file From 271a79b2267f7a519406aae4c9f43f57babca403 Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Wed, 12 Jun 2024 16:22:09 -0500 Subject: [PATCH 17/21] Update mlos_core/mlos_core/optimizers/optimizer.py Co-authored-by: Brian Kroth --- mlos_core/mlos_core/optimizers/optimizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlos_core/mlos_core/optimizers/optimizer.py b/mlos_core/mlos_core/optimizers/optimizer.py index 30ef49e4d2..60950831ee 100644 --- a/mlos_core/mlos_core/optimizers/optimizer.py +++ b/mlos_core/mlos_core/optimizers/optimizer.py @@ -83,7 +83,7 @@ def register(self, configurations: pd.DataFrame, scores: pd.DataFrame, context : pd.DataFrame Not implemented yet. metadata : pd.DataFrame - Implementaton depends on instance. + Implementation depends on instance (e.g., saved optimizer state to return). """ # Do some input validation. if type(self._optimization_targets) is str: From 98c7398ba9c802008389a70d7d40b14e404e5e3d Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Wed, 12 Jun 2024 16:22:30 -0500 Subject: [PATCH 18/21] Update mlos_core/mlos_core/optimizers/optimizer.py Co-authored-by: Brian Kroth --- mlos_core/mlos_core/optimizers/optimizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlos_core/mlos_core/optimizers/optimizer.py b/mlos_core/mlos_core/optimizers/optimizer.py index 60950831ee..bda37c7b29 100644 --- a/mlos_core/mlos_core/optimizers/optimizer.py +++ b/mlos_core/mlos_core/optimizers/optimizer.py @@ -91,7 +91,7 @@ def register(self, configurations: pd.DataFrame, scores: pd.DataFrame, if type(self._optimization_targets) is list: assert set(scores.columns) >= set(self._optimization_targets), "Mismatched optimization targets." assert self._has_context is None or self._has_context ^ (context is None), \ - "Metadata must always be added or never be added." + "Context must always be added or never be added." assert len(configurations) == len(scores), \ "Mismatched number of configurations and scores." if context is not None: From 9726410d242ce86cb645162d40b58c3064b8fcd4 Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Wed, 12 Jun 2024 17:04:07 -0500 Subject: [PATCH 19/21] Update mlos_core/mlos_core/optimizers/optimizer.py Co-authored-by: Brian Kroth --- mlos_core/mlos_core/optimizers/optimizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlos_core/mlos_core/optimizers/optimizer.py b/mlos_core/mlos_core/optimizers/optimizer.py index bda37c7b29..76eb0ff404 100644 --- a/mlos_core/mlos_core/optimizers/optimizer.py +++ b/mlos_core/mlos_core/optimizers/optimizer.py @@ -102,7 +102,7 @@ def register(self, configurations: pd.DataFrame, scores: pd.DataFrame, "Mismatched number of configurations and metadata." assert configurations.shape[1] == len(self.parameter_space.values()), \ "Mismatched configuration shape." - self._observations.append((configurations, scores, metadata, context)) + self._observations.append((configurations, scores, context, metadata)) self._has_context = context is not None if self._space_adapter: From bf4602b4e7cd839141aa80bb835ec33902002bb5 Mon Sep 17 00:00:00 2001 From: Johannes Freischuetz Date: Wed, 12 Jun 2024 17:04:37 -0500 Subject: [PATCH 20/21] Update mlos_core/mlos_core/optimizers/optimizer.py Co-authored-by: Brian Kroth --- mlos_core/mlos_core/optimizers/optimizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlos_core/mlos_core/optimizers/optimizer.py b/mlos_core/mlos_core/optimizers/optimizer.py index 76eb0ff404..37ed61310b 100644 --- a/mlos_core/mlos_core/optimizers/optimizer.py +++ b/mlos_core/mlos_core/optimizers/optimizer.py @@ -218,7 +218,7 @@ def register_pending(self, configurations: pd.DataFrame, def get_observations(self) -> Tuple[pd.DataFrame, pd.DataFrame, Optional[pd.DataFrame], Optional[pd.DataFrame]]: """ - Returns the observations as a triplet of DataFrames (config, score, metadata). + Returns the observations as a triplet of DataFrames (config, score, context, metadata). Returns ------- From 8d2a8948733560e0dda65329dc1ce97386d60472 Mon Sep 17 00:00:00 2001 From: jsfreischuetz Date: Fri, 14 Jun 2024 00:05:22 -0500 Subject: [PATCH 21/21] fix comments for python --- .../optimizers/bayesian_optimizers/smac_optimizer.py | 2 +- mlos_core/mlos_core/optimizers/flaml_optimizer.py | 2 +- mlos_core/mlos_core/optimizers/optimizer.py | 9 +++------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py index 2a557cfce2..a2c5b5d5e7 100644 --- a/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py +++ b/mlos_core/mlos_core/optimizers/bayesian_optimizers/smac_optimizer.py @@ -44,7 +44,7 @@ def __init__( self, # pylint: disable=too-many-locals *, # pylint: disable=too-many-locals parameter_space: ConfigSpace.ConfigurationSpace, - optimization_targets: Union[str, List[str], None] = None, + optimization_targets: List[str], objective_weights: Optional[List[float]] = None, space_adapter: Optional[BaseSpaceAdapter] = None, seed: Optional[int] = 0, diff --git a/mlos_core/mlos_core/optimizers/flaml_optimizer.py b/mlos_core/mlos_core/optimizers/flaml_optimizer.py index d07622dd0c..723bc4332f 100644 --- a/mlos_core/mlos_core/optimizers/flaml_optimizer.py +++ b/mlos_core/mlos_core/optimizers/flaml_optimizer.py @@ -138,7 +138,7 @@ def _suggest( return pd.DataFrame(config, index=[0]), None def register_pending(self, configurations: pd.DataFrame, - metadata: Optional[pd.DataFrame] = None) -> None: + context: Optional[pd.DataFrame] = None, metadata: Optional[pd.DataFrame] = None) -> None: raise NotImplementedError() def _target_function(self, config: dict) -> Union[dict, None]: diff --git a/mlos_core/mlos_core/optimizers/optimizer.py b/mlos_core/mlos_core/optimizers/optimizer.py index 37ed61310b..c0ff6e4aa4 100644 --- a/mlos_core/mlos_core/optimizers/optimizer.py +++ b/mlos_core/mlos_core/optimizers/optimizer.py @@ -26,7 +26,7 @@ class BaseOptimizer(metaclass=ABCMeta): def __init__(self, *, parameter_space: ConfigSpace.ConfigurationSpace, - optimization_targets: Optional[Union[str, List[str]]] = None, + optimization_targets: List[str], objective_weights: Optional[List[float]] = None, space_adapter: Optional[BaseSpaceAdapter] = None): """ @@ -86,10 +86,7 @@ def register(self, configurations: pd.DataFrame, scores: pd.DataFrame, Implementation depends on instance (e.g., saved optimizer state to return). """ # Do some input validation. - if type(self._optimization_targets) is str: - assert self._optimization_targets in scores.columns, "Mismatched optimization targets." - if type(self._optimization_targets) is list: - assert set(scores.columns) >= set(self._optimization_targets), "Mismatched optimization targets." + assert set(scores.columns) >= set(self._optimization_targets), "Mismatched optimization targets." assert self._has_context is None or self._has_context ^ (context is None), \ "Context must always be added or never be added." assert len(configurations) == len(scores), \ @@ -109,7 +106,7 @@ def register(self, configurations: pd.DataFrame, scores: pd.DataFrame, configurations = self._space_adapter.inverse_transform(configurations) assert configurations.shape[1] == len(self.optimizer_parameter_space.values()), \ "Mismatched configuration shape after inverse transform." - return self._register(configurations, scores, metadata, context) + return self._register(configurations, scores, context, metadata) @abstractmethod def _register(self, configurations: pd.DataFrame, scores: pd.DataFrame,