From 079437c979339db59f258d528ed83fe9c0807668 Mon Sep 17 00:00:00 2001 From: dengdifan Date: Fri, 1 Nov 2024 19:29:04 +0100 Subject: [PATCH 1/6] add smac sampler --- package/samplers/smac_sampler/LICENSE | 21 ++ package/samplers/smac_sampler/README.md | 44 +++ package/samplers/smac_sampler/__init__.py | 4 + package/samplers/smac_sampler/example.py | 29 ++ .../image/smac_sampler_history.png | Bin 0 -> 35456 bytes .../samplers/smac_sampler/requirements.txt | 3 + package/samplers/smac_sampler/sampler.py | 274 ++++++++++++++++++ 7 files changed, 375 insertions(+) create mode 100644 package/samplers/smac_sampler/LICENSE create mode 100644 package/samplers/smac_sampler/README.md create mode 100644 package/samplers/smac_sampler/__init__.py create mode 100644 package/samplers/smac_sampler/example.py create mode 100644 package/samplers/smac_sampler/image/smac_sampler_history.png create mode 100644 package/samplers/smac_sampler/requirements.txt create mode 100644 package/samplers/smac_sampler/sampler.py diff --git a/package/samplers/smac_sampler/LICENSE b/package/samplers/smac_sampler/LICENSE new file mode 100644 index 00000000..d5d21403 --- /dev/null +++ b/package/samplers/smac_sampler/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Difan Deng + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/package/samplers/smac_sampler/README.md b/package/samplers/smac_sampler/README.md new file mode 100644 index 00000000..675f895d --- /dev/null +++ b/package/samplers/smac_sampler/README.md @@ -0,0 +1,44 @@ +--- +author: Difan Deng +title: SMAC3 +description: SMAC offers a robust and flexible framework for Bayesian Optimization to support users in determining well-performing hyperparameter configurations for their (Machine Learning) algorithms, datasets and applications at hand. The main core consists of Bayesian Optimization in combination with an aggressive racing mechanism to efficiently decide which of two configurations performs better. +tags: [sampler, Bayesian optimization, Gaussian process, Random Forest] +optuna_versions: [3.6.1] +license: MIT License +--- + +## Class or Function Names + +- SAMCSampler + +## Installation + +```bash +pip install -r https://hub.optuna.org/samplers/hebo/requirements.txt +pip install smac==2.2.0 +``` + +## Example + +```python +search_space = { + "x": FloatDistribution(-10, 10), + "y": IntDistribution(0, 10), + +} +sampler = SMACSampler(search_space) +study = optuna.create_study(sampler=sampler) +``` + +See [`example.py`](https://github.com/optuna/optunahub-registry/blob/main/package/samplers/hebo/example.py) for a full example. +![History Plot](images/smac_optimization_history.png "History Plot") + +## Others + +SMAC is maintained by the SMAC team in [automl.org](https://www.automl.org/). If you have trouble using SMAC, a concrete question or found a bug, please create an issue under the [SMAC](https://github.com/automl/SMAC3) repository. + +For all other inquiries, please write an email to smac\[at\]ai\[dot\]uni\[dash\]hannover\[dot\]de. + +### Reference + +Lindauer et al. "SMAC3: A Versatile Bayesian Optimization Package for Hyperparameter Optimization", Journal of Machine Learning Research, http://jmlr.org/papers/v23/21-0888.html diff --git a/package/samplers/smac_sampler/__init__.py b/package/samplers/smac_sampler/__init__.py new file mode 100644 index 00000000..44425691 --- /dev/null +++ b/package/samplers/smac_sampler/__init__.py @@ -0,0 +1,4 @@ +from .sampler import SMACSampler + + +__all__ = ["SMACSampler"] diff --git a/package/samplers/smac_sampler/example.py b/package/samplers/smac_sampler/example.py new file mode 100644 index 00000000..6e790f24 --- /dev/null +++ b/package/samplers/smac_sampler/example.py @@ -0,0 +1,29 @@ +import optuna +import optunahub + + +module = optunahub.load_module("sampler/smac_sampler") +SMACSampler = module.SMACSampler + + +def objective(trial: optuna.trial.Trial) -> float: + x = trial.suggest_float("x", -10, 10) + y = trial.suggest_int("y", -10, 10) + return x**2 + y**2 + + +if __name__ == "__main__": + n_trials = 100 + sampler = SMACSampler( + { + "x": optuna.distributions.FloatDistribution(-10, 10), + "y": optuna.distributions.IntDistribution(-10, 10), + }, + n_trials=n_trials, + ) + study = optuna.create_study(sampler=sampler) + study.optimize(objective, n_trials=n_trials) + print(study.best_trial.params) + + fig = optuna.visualization.plot_optimization_history(study) + fig.write_image("smac_optimization_history.png") diff --git a/package/samplers/smac_sampler/image/smac_sampler_history.png b/package/samplers/smac_sampler/image/smac_sampler_history.png new file mode 100644 index 0000000000000000000000000000000000000000..f75bc552943eb7c7024d2b24550c177bc690969f GIT binary patch literal 35456 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 literal 0 HcmV?d00001 diff --git a/package/samplers/smac_sampler/requirements.txt b/package/samplers/smac_sampler/requirements.txt new file mode 100644 index 00000000..bc1caa66 --- /dev/null +++ b/package/samplers/smac_sampler/requirements.txt @@ -0,0 +1,3 @@ +optuna +optunahub +smac \ No newline at end of file diff --git a/package/samplers/smac_sampler/sampler.py b/package/samplers/smac_sampler/sampler.py new file mode 100644 index 00000000..c6677fbe --- /dev/null +++ b/package/samplers/smac_sampler/sampler.py @@ -0,0 +1,274 @@ +from __future__ import annotations + +from collections.abc import Sequence + +from ConfigSpace import Categorical +from ConfigSpace import Configuration +from ConfigSpace import ConfigurationSpace +from ConfigSpace import Float +from ConfigSpace import Integer +import numpy as np +from optuna.distributions import BaseDistribution +from optuna.distributions import CategoricalDistribution +from optuna.distributions import FloatDistribution +from optuna.distributions import IntDistribution +from optuna.study import Study +from optuna.trial import FrozenTrial +from optuna.trial import TrialState +import optunahub +from smac.acquisition.function import AbstractAcquisitionFunction +from smac.acquisition.function import EI +from smac.acquisition.function import LCB +from smac.acquisition.function import PI +from smac.facade import BlackBoxFacade +from smac.facade import HyperparameterOptimizationFacade +from smac.initial_design import AbstractInitialDesign +from smac.initial_design import LatinHypercubeInitialDesign +from smac.initial_design import RandomInitialDesign +from smac.initial_design import SobolInitialDesign +from smac.model.abstract_model import AbstractModel +from smac.runhistory.dataclasses import TrialInfo +from smac.runhistory.dataclasses import TrialValue +from smac.runhistory.enumerations import StatusType +from smac.scenario import Scenario +from smac.utils.configspace import get_config_hash + + +SimpleBaseSampler = optunahub.load_module("samplers/simple").SimpleBaseSampler + + +def dummmy_target_func(config: Configuration, seed: int = 0) -> float: + # This is only a placed holder function that allows us to initialize a new SMAC facade + return 0 + + +class SMACSampler(SimpleBaseSampler): # type: ignore + def __init__( + self, + search_space: dict[str, BaseDistribution], + n_trials: int = 100, # This is required for initial design + *, + surrogate_model_type: str = "rf", + acq_func_type: str = "ei_log", + init_design_type: str = "sobol", + surrogate_model_rf_num_trees: int = 10, + surrogate_model_rf_ratio_features: float = 1.0, + surrogate_model_rf_min_samples_split: int = 2, + surrogate_model_rf_min_samples_leaf: int = 1, + init_design_n_configs: int | None = None, + init_design_n_configs_per_hyperparameter: int = 10, + init_design_max_ratio: float = 0.25, + ) -> None: + super().__init__(search_space) + self._cs, self._hp_scale_value = self._convert_to_config_space_design_space(search_space) + # TODO init SMAC facade according to the given arguments + scenario = Scenario(configspace=self._cs, deterministic=True, n_trials=n_trials) + surrogate_model = self._get_surrogate_model( + scenario, + surrogate_model_type, + rf_num_trees=surrogate_model_rf_num_trees, + rf_ratio_features=surrogate_model_rf_ratio_features, + rf_min_samples_split=surrogate_model_rf_min_samples_split, + rf_min_samples_leaf=surrogate_model_rf_min_samples_leaf, + ) + acq_func = self._get_acq_func(acq_func_type) + init_design = self._get_init_design( + scenario=scenario, + init_design_type=init_design_type, + n_configs=init_design_n_configs, + n_configs_per_hyperparameter=init_design_n_configs_per_hyperparameter, + max_ratio=init_design_max_ratio, + ) + config_selector = HyperparameterOptimizationFacade.get_config_selector( + scenario=scenario, retrain_after=1 + ) + smac = HyperparameterOptimizationFacade( + scenario, + target_function=dummmy_target_func, + model=surrogate_model, + acquisition_function=acq_func, + config_selector=config_selector, + initial_design=init_design, + overwrite=True, + ) + self.smac = smac + + # this value is used to store the instance-seed paris of each evaluated configuraitons + self._runs_instance_seed_keys: dict[str, tuple[str | None, int]] = {} + + def _get_surrogate_model( + self, + scenario: Scenario, + model_type: str = "rf", + rf_num_trees: int = 10, + rf_ratio_features: float = 1.0, + rf_min_samples_split: int = 2, + rf_min_samples_leaf: int = 1, + ) -> AbstractModel: + if model_type == "gp": + return BlackBoxFacade.get_model(scenario=scenario) + elif model_type == "gp_mcmc": + return BlackBoxFacade.get_model(scenario=scenario, model_type="mcmc") + elif model_type == "rf": + return HyperparameterOptimizationFacade.get_model( + scenario=scenario, + n_trees=rf_num_trees, + ratio_features=rf_ratio_features, + min_samples_split=rf_min_samples_split, + min_samples_leaf=rf_min_samples_leaf, + ) + else: + raise ValueError(f"Unknown Surrogate Model Type {model_type}") + + def _get_acq_func(self, acq_func_type: str) -> AbstractAcquisitionFunction: + all_acq_func = {"ei": EI(), "ei_log": EI(log=True), "pi": PI(), "lcb": LCB()} + return all_acq_func[acq_func_type] + + def _get_init_design( + self, + scenario: Scenario, + init_design_type: str, + n_configs: int | None = None, + n_configs_per_hyperparameter: int | None = 10, + max_ratio: float = 0.25, + ) -> AbstractInitialDesign: + if init_design_type == "sobol": + init_design = SobolInitialDesign( + scenario=scenario, + n_configs=n_configs, + n_configs_per_hyperparameter=n_configs_per_hyperparameter, + max_ratio=max_ratio, + ) + elif init_design_type == "lhd": + init_design = LatinHypercubeInitialDesign( + scenario=scenario, + n_configs=n_configs, + n_configs_per_hyperparameter=n_configs_per_hyperparameter, + max_ratio=max_ratio, + ) + elif init_design_type == "random": + init_design = RandomInitialDesign( + scenario=scenario, + n_configs=n_configs, + n_configs_per_hyperparameter=n_configs_per_hyperparameter, + max_ratio=max_ratio, + ) + else: + raise NotImplementedError(f"Unknown Initial Design Type: {init_design_type}") + return init_design + + def sample_relative( + self, study: Study, trial: FrozenTrial, search_space: dict[str, BaseDistribution] + ) -> dict[str, float]: + trial_info: TrialInfo = self.smac.ask() + cfg = trial_info.config + self._runs_instance_seed_keys[get_config_hash(cfg)] = ( + trial_info.instance, + trial_info.seed, + ) + params = {} + for name, hp_value in cfg.items(): + if name in self._hp_scale_value: + hp_value = self._integer_to_step_hp( + integer_value=hp_value, scale_info=self._hp_scale_value[name] + ) + params[name] = hp_value + + return params + + def after_trial( + self, + study: Study, + trial: FrozenTrial, + state: TrialState, + values: Sequence[float] | None, + ) -> None: + # Transform the trail info to smac + params = trial.params + cfg_params = {} + for name, hp_value in params.items(): + if name in self._hp_scale_value: + hp_value = self._step_hp_to_intger(hp_value, scale_info=self._hp_scale_value[name]) + cfg_params[name] = hp_value + + # params to smac HP + y = np.asarray(values) + if state == TrialState.COMPLETE: + status = StatusType.SUCCESS + elif state == TrialState.RUNNING: + status = StatusType.RUNNING + else: + status = StatusType.CRASHED + trial_value = TrialValue(y, status=status) + + cfg = Configuration(configuration_space=self._cs, values=cfg_params) + # Since Optuna does not provide us the + instance, seed = self._runs_instance_seed_keys[get_config_hash(cfg)] + info = TrialInfo(cfg, seed=seed, instance=instance) + self.smac.tell(info=info, value=trial_value, save=False) + + def _transform_step_hp_to_integer( + self, distribution: FloatDistribution | IntDistribution + ) -> tuple[int, tuple[int | float]]: + """ + Given that step discretises the target Float distribution and this is not supported by ConfigSpace, we need to + manually transform this type of HP into integral values. To construct a new integer value, we need to know the + amount of possible values contained in the hyperparameter and the information required to transform the integral + values back to the target function + """ + assert distribution.step is not None + n_discrete_values = int( + np.round((distribution.high - distribution.low) / distribution.step) + ) + return n_discrete_values, (distribution.low, distribution.step) # type: ignore + + def _step_hp_to_intger( + self, hp_value: float | int, scale_info: tuple[int | float, int | float] + ) -> int: + return int(np.round((hp_value - scale_info[0]) / scale_info[1])) + + def _integer_to_step_hp( + self, integer_value: int, scale_info: tuple[int | float, int | float] + ) -> float | int: + """ + This function is the inverse of _transform_step_hp_to_intger, we will transform the integer_value back to the + target hyperparameter values + """ + return integer_value * scale_info[1] + scale_info[0] + + def _convert_to_config_space_design_space( + self, search_space: dict[str, BaseDistribution] + ) -> tuple[ConfigurationSpace, dict]: + config_space = ConfigurationSpace() + scale_values: dict[str, tuple] = {} + for name, distribution in search_space.items(): + if isinstance(distribution, (FloatDistribution, IntDistribution)): + if distribution.step is not None: + # Given that step discretises the target Float distribution and this is not supported by + # ConfigSpace, we need to manually transform this type of HP into integral values to sampler and + # transform them back to the raw HP values during evaluation phases. Hence, + n_discrete_values, scale_values_hp = self._transform_step_hp_to_integer( + distribution + ) + scale_values[name] = scale_values_hp + hp = Integer(name, bounds=(0, n_discrete_values)) + else: + if isinstance(distribution, FloatDistribution): + hp = Float( + name, + bounds=(distribution.low, distribution.high), + log=distribution.log, + ) + else: + hp = Integer( + name, + bounds=(distribution.low, distribution.high), + log=distribution.log, + ) + elif isinstance(distribution, CategoricalDistribution): + hp = Categorical(name, items=distribution.choices) + else: + raise NotImplementedError(f"Unknown Hyperparameter Type: {type(distribution)}") + if hp is not None: + config_space.add_hyperparameter(hp) + return config_space, scale_values From d9d4e5cddbd54b16fa2556eda2d21dcad5bbe639 Mon Sep 17 00:00:00 2001 From: dengdifan Date: Thu, 7 Nov 2024 17:27:16 +0100 Subject: [PATCH 2/6] fix requirements and README --- package/samplers/smac_sampler/README.md | 3 +-- package/samplers/smac_sampler/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/package/samplers/smac_sampler/README.md b/package/samplers/smac_sampler/README.md index 675f895d..d232f646 100644 --- a/package/samplers/smac_sampler/README.md +++ b/package/samplers/smac_sampler/README.md @@ -14,8 +14,7 @@ license: MIT License ## Installation ```bash -pip install -r https://hub.optuna.org/samplers/hebo/requirements.txt -pip install smac==2.2.0 +pip install -r https://hub.optuna.org/samplers/smac_sampler/requirements.txt ``` ## Example diff --git a/package/samplers/smac_sampler/requirements.txt b/package/samplers/smac_sampler/requirements.txt index bc1caa66..4dd49382 100644 --- a/package/samplers/smac_sampler/requirements.txt +++ b/package/samplers/smac_sampler/requirements.txt @@ -1,3 +1,3 @@ optuna optunahub -smac \ No newline at end of file +smac==2.2.0 \ No newline at end of file From e99d423fd76e0ae05571edb6c2ed43e70dd7a173 Mon Sep 17 00:00:00 2001 From: dengdifan Date: Thu, 7 Nov 2024 17:29:14 +0100 Subject: [PATCH 3/6] fix examples --- package/samplers/smac_sampler/example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/samplers/smac_sampler/example.py b/package/samplers/smac_sampler/example.py index 6e790f24..44781fbd 100644 --- a/package/samplers/smac_sampler/example.py +++ b/package/samplers/smac_sampler/example.py @@ -2,7 +2,7 @@ import optunahub -module = optunahub.load_module("sampler/smac_sampler") +module = optunahub.load_module("samplers/smac_sampler") SMACSampler = module.SMACSampler From dcd3a59a9bcfb8d4628af9c93be7cfe7d92da41a Mon Sep 17 00:00:00 2001 From: dengdifan Date: Thu, 7 Nov 2024 17:31:00 +0100 Subject: [PATCH 4/6] maint sampler --- package/samplers/smac_sampler/sampler.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package/samplers/smac_sampler/sampler.py b/package/samplers/smac_sampler/sampler.py index c6677fbe..e09e21f4 100644 --- a/package/samplers/smac_sampler/sampler.py +++ b/package/samplers/smac_sampler/sampler.py @@ -12,7 +12,7 @@ from optuna.distributions import CategoricalDistribution from optuna.distributions import FloatDistribution from optuna.distributions import IntDistribution -from optuna.study import Study +from optuna.study import Study, StudyDirection from optuna.trial import FrozenTrial from optuna.trial import TrialState import optunahub @@ -61,7 +61,6 @@ def __init__( ) -> None: super().__init__(search_space) self._cs, self._hp_scale_value = self._convert_to_config_space_design_space(search_space) - # TODO init SMAC facade according to the given arguments scenario = Scenario(configspace=self._cs, deterministic=True, n_trials=n_trials) surrogate_model = self._get_surrogate_model( scenario, @@ -191,8 +190,9 @@ def after_trial( hp_value = self._step_hp_to_intger(hp_value, scale_info=self._hp_scale_value[name]) cfg_params[name] = hp_value - # params to smac HP - y = np.asarray(values) + # params to smac HP, in SMAC, we always do the minimization + values_to_minimize = [v if d == StudyDirection.MINIMIZE else -v for d, v in zip(study.directions, values)] + y = np.asarray(values_to_minimize) if state == TrialState.COMPLETE: status = StatusType.SUCCESS elif state == TrialState.RUNNING: @@ -209,7 +209,7 @@ def after_trial( def _transform_step_hp_to_integer( self, distribution: FloatDistribution | IntDistribution - ) -> tuple[int, tuple[int | float]]: + ) -> tuple[int, tuple[int | float, int | float]]: """ Given that step discretises the target Float distribution and this is not supported by ConfigSpace, we need to manually transform this type of HP into integral values. To construct a new integer value, we need to know the From 1ce78f3e23d415debdff055cbf8d3b8a18ac8c93 Mon Sep 17 00:00:00 2001 From: dengdifan Date: Thu, 7 Nov 2024 17:32:45 +0100 Subject: [PATCH 5/6] fix some typos --- package/samplers/smac_sampler/sampler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/samplers/smac_sampler/sampler.py b/package/samplers/smac_sampler/sampler.py index e09e21f4..2c1c4c05 100644 --- a/package/samplers/smac_sampler/sampler.py +++ b/package/samplers/smac_sampler/sampler.py @@ -222,7 +222,7 @@ def _transform_step_hp_to_integer( ) return n_discrete_values, (distribution.low, distribution.step) # type: ignore - def _step_hp_to_intger( + def _step_hp_to_integer( self, hp_value: float | int, scale_info: tuple[int | float, int | float] ) -> int: return int(np.round((hp_value - scale_info[0]) / scale_info[1])) From 55c6b57794b2ae2ff8d8f15396de2adef3b5808f Mon Sep 17 00:00:00 2001 From: dengdifan Date: Thu, 7 Nov 2024 17:37:49 +0100 Subject: [PATCH 6/6] pre commit fix --- package/samplers/smac_sampler/sampler.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/package/samplers/smac_sampler/sampler.py b/package/samplers/smac_sampler/sampler.py index 2c1c4c05..eae1cf3b 100644 --- a/package/samplers/smac_sampler/sampler.py +++ b/package/samplers/smac_sampler/sampler.py @@ -12,7 +12,8 @@ from optuna.distributions import CategoricalDistribution from optuna.distributions import FloatDistribution from optuna.distributions import IntDistribution -from optuna.study import Study, StudyDirection +from optuna.study import Study +from optuna.study import StudyDirection from optuna.trial import FrozenTrial from optuna.trial import TrialState import optunahub @@ -191,7 +192,10 @@ def after_trial( cfg_params[name] = hp_value # params to smac HP, in SMAC, we always do the minimization - values_to_minimize = [v if d == StudyDirection.MINIMIZE else -v for d, v in zip(study.directions, values)] + values_to_minimize = [ + v if d == StudyDirection.MINIMIZE else -v + for d, v in zip(study.directions, values) # type: ignore + ] y = np.asarray(values_to_minimize) if state == TrialState.COMPLETE: status = StatusType.SUCCESS