Documentation for SkOptOptimizer
Wrapper for Scikit-Optimize with PHOTONAI.
Scikit-Optimize, or skopt, is a simple and efficient library to minimize (very) expensive and noisy black-box functions. It implements several methods for sequential model-based optimization. skopt aims to be accessible and easy to use in many contexts.
Scikit-optimize usage and implementation details
A detailed parameter documentation here.
Examples:
1 2 3 4 5 6 |
|
Source code in photonai/optimization/scikit_optimize/sk_opt.py
class SkOptOptimizer(PhotonSlaveOptimizer):
"""Wrapper for Scikit-Optimize with PHOTONAI.
Scikit-Optimize, or skopt, is a simple and efficient library to
minimize (very) expensive and noisy black-box functions.
It implements several methods for sequential model-based optimization.
skopt aims to be accessible and easy to use in many contexts.
Scikit-optimize [usage and implementation details](https://scikit-optimize.github.io/stable/)
A detailed parameter documentation [here.](
https://scikit-optimize.github.io/stable/modules/generated/skopt.optimizer.Optimizer.html#skopt.optimizer.Optimizer)
Example:
``` python
my_pipe = Hyperpipe('skopt_example',
optimizer='sk_opt',
optimizer_params={'n_configurations': 25,
'acq_func': 'LCB',
'acq_func_kwargs': {'kappa': 1.96}},
...)
```
"""
def __init__(self,
n_configurations: int = 20,
n_initial_points: int = 10,
limit_in_minutes: Union[float, None] = None,
base_estimator: Union[str, sklearn.base.RegressorMixin] = "ET",
initial_point_generator: str = "random",
acq_func: str = 'gp_hedge',
acq_func_kwargs: dict = None):
"""
Initialize the object.
Parameters:
n_configurations:
Number of configurations to be calculated.
n_initial_points:
Number of evaluations with initialization points
before approximating it with `base_estimator`.
limit_in_minutes:
Total time in minutes.
base_estimator:
Estimator for returning std(Y | x) along with E[Y | x].
initial_point_generator:
Generator for initial points.
acq_func:
Function to minimize over the posterior distribution.
acq_func_kwargs:
Additional arguments to be passed to the acquisition function.
"""
self.metric_to_optimize = ''
self.n_configurations = n_configurations
self.n_initial_points = n_initial_points
self.base_estimator = base_estimator
self.initial_point_generator = initial_point_generator
self.acq_func = acq_func
self.acq_func_kwargs = acq_func_kwargs
self.limit_in_minutes = limit_in_minutes
self.start_time, self.end_time = None, None
self.optimizer = None
self.maximize_metric = None
self.hyperparameter_list = []
self.constant_dictionary = {}
self.ask = self.ask_generator()
def ask_generator(self) -> Generator:
"""
Generator for new configs - ask method.
Returns:
Yields the next config.
"""
if self.start_time is None and self.limit_in_minutes is not None:
self.start_time = datetime.datetime.now()
self.end_time = self.start_time + datetime.timedelta(minutes=self.limit_in_minutes)
if self.optimizer is None:
yield {}
else:
for i in range(self.n_configurations):
next_config_list = self.optimizer.ask()
next_config_dict = {self.hyperparameter_list[number]:
self._convert_to_native(value) for number, value in enumerate(next_config_list)}
if self.limit_in_minutes is None or datetime.datetime.now() < self.end_time:
yield next_config_dict
else:
yield {}
break
def prepare(self, pipeline_elements: list, maximize_metric: bool) -> None:
"""
Initializes hyperparameter search with scikit-optimize.
Assembles all hyperparameters of the list of PipelineElements
in order to prepare the hyperparameter space.
Hyperparameters can be accessed via pipe_element.hyperparameters.
Parameters:
pipeline_elements:
List of all PipelineElements to create the hyperparameter space.
maximize_metric:
Boolean to distinguish between score and error.
"""
self.start_time = None
self.optimizer = None
self.hyperparameter_list = []
self.maximize_metric = maximize_metric
# build skopt space
space = []
for pipe_element in pipeline_elements:
if pipe_element.__class__.__name__ == 'Switch':
error_msg = 'Scikit-Optimize cannot operate in the specified hyperparameter space with a Switch ' \
'element. We recommend the use of SMAC.'
logger.error(error_msg)
raise ValueError(error_msg)
if hasattr(pipe_element, 'hyperparameters'):
for name, value in pipe_element.hyperparameters.items():
# if we only have one value we do not need to optimize
if isinstance(value, list) and len(value) < 2:
self.constant_dictionary[name] = value[0]
continue
if isinstance(value, PhotonCategorical) and len(value.values) < 2:
self.constant_dictionary[name] = value.values[0]
continue
skopt_param = self._convert_photonai_to_skopt_space(value, name)
if skopt_param is not None:
space.append(skopt_param)
if self.constant_dictionary:
msg = "PHOTONAI has detected some one-valued params in your hyperparameters. Pleas use the kwargs for " \
"constant values. This run ignores following settings: " + str(self.constant_dictionary.keys())
logger.warning(msg)
warnings.warn(msg)
if len(space) == 0:
msg = "Did not find any hyperparameter to convert into skopt space."
logger.warning(msg)
warnings.warn(msg)
else:
self.optimizer = Optimizer(space,
base_estimator=self.base_estimator,
n_initial_points=self.n_initial_points,
initial_point_generator=self.initial_point_generator,
acq_func=self.acq_func,
acq_func_kwargs=self.acq_func_kwargs)
self.ask = self.ask_generator()
def tell(self, config: dict, performance: float) -> None:
"""
Provide a config result to calculate new ones.
Parameters:
config:
The configuration that has been trained and tested.
performance:
Metrics about the configuration's generalization capabilities.
"""
# convert dictionary to list in correct order
if self.optimizer is not None:
config_values = [config[name] for name in self.hyperparameter_list]
best_config_metric_performance = performance
if self.maximize_metric:
best_config_metric_performance = -best_config_metric_performance
self.optimizer.tell(config_values, best_config_metric_performance)
def _convert_photonai_to_skopt_space(self, hyperparam: Union[PhotonHyperparam, list], name: str) -> Dimension:
self.hyperparameter_list.append(name)
if isinstance(hyperparam, PhotonCategorical) or isinstance(hyperparam, BooleanSwitch):
return skoptCategorical(hyperparam.values, name=name)
elif isinstance(hyperparam, list):
return skoptCategorical(hyperparam, name=name)
elif isinstance(hyperparam, FloatRange):
if hyperparam.range_type == 'linspace':
return Real(hyperparam.start, hyperparam.stop, name=name, prior='uniform')
elif hyperparam.range_type == 'logspace':
return Real(hyperparam.start, hyperparam.stop, name=name, prior='log-uniform')
else:
msg = "The hyperparam.range_type "+hyperparam.range_type+" is not supported by scikit-optimize."
logger.error(msg)
raise ValueError(msg)
elif isinstance(hyperparam, IntegerRange):
return Integer(hyperparam.start, hyperparam.stop, name=name)
msg = "Cannot convert hyperparameter " + str(hyperparam) + ". " \
"Supported types: Categorical, IntegerRange, FloatRange, list."
logger.error(msg)
raise ValueError(msg)
@staticmethod
def _convert_to_native(obj):
# check if we have a numpy object, if so convert it to python native
if type(obj).__module__ == np.__name__:
return obj.item()
else:
return obj
__init__(self, n_configurations=20, n_initial_points=10, limit_in_minutes=None, base_estimator='ET', initial_point_generator='random', acq_func='gp_hedge', acq_func_kwargs=None)
special
Initialize the object.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
n_configurations |
int |
Number of configurations to be calculated. |
20 |
n_initial_points |
int |
Number of evaluations with initialization points
before approximating it with |
10 |
limit_in_minutes |
Optional[float] |
Total time in minutes. |
None |
base_estimator |
Union[str, sklearn.base.RegressorMixin] |
Estimator for returning std(Y | x) along with E[Y | x]. |
'ET' |
initial_point_generator |
str |
Generator for initial points. |
'random' |
acq_func |
str |
Function to minimize over the posterior distribution. |
'gp_hedge' |
acq_func_kwargs |
dict |
Additional arguments to be passed to the acquisition function. |
None |
Source code in photonai/optimization/scikit_optimize/sk_opt.py
def __init__(self,
n_configurations: int = 20,
n_initial_points: int = 10,
limit_in_minutes: Union[float, None] = None,
base_estimator: Union[str, sklearn.base.RegressorMixin] = "ET",
initial_point_generator: str = "random",
acq_func: str = 'gp_hedge',
acq_func_kwargs: dict = None):
"""
Initialize the object.
Parameters:
n_configurations:
Number of configurations to be calculated.
n_initial_points:
Number of evaluations with initialization points
before approximating it with `base_estimator`.
limit_in_minutes:
Total time in minutes.
base_estimator:
Estimator for returning std(Y | x) along with E[Y | x].
initial_point_generator:
Generator for initial points.
acq_func:
Function to minimize over the posterior distribution.
acq_func_kwargs:
Additional arguments to be passed to the acquisition function.
"""
self.metric_to_optimize = ''
self.n_configurations = n_configurations
self.n_initial_points = n_initial_points
self.base_estimator = base_estimator
self.initial_point_generator = initial_point_generator
self.acq_func = acq_func
self.acq_func_kwargs = acq_func_kwargs
self.limit_in_minutes = limit_in_minutes
self.start_time, self.end_time = None, None
self.optimizer = None
self.maximize_metric = None
self.hyperparameter_list = []
self.constant_dictionary = {}
self.ask = self.ask_generator()
prepare(self, pipeline_elements, maximize_metric)
Initializes hyperparameter search with scikit-optimize.
Assembles all hyperparameters of the list of PipelineElements in order to prepare the hyperparameter space. Hyperparameters can be accessed via pipe_element.hyperparameters.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
pipeline_elements |
list |
List of all PipelineElements to create the hyperparameter space. |
required |
maximize_metric |
bool |
Boolean to distinguish between score and error. |
required |
Source code in photonai/optimization/scikit_optimize/sk_opt.py
def prepare(self, pipeline_elements: list, maximize_metric: bool) -> None:
"""
Initializes hyperparameter search with scikit-optimize.
Assembles all hyperparameters of the list of PipelineElements
in order to prepare the hyperparameter space.
Hyperparameters can be accessed via pipe_element.hyperparameters.
Parameters:
pipeline_elements:
List of all PipelineElements to create the hyperparameter space.
maximize_metric:
Boolean to distinguish between score and error.
"""
self.start_time = None
self.optimizer = None
self.hyperparameter_list = []
self.maximize_metric = maximize_metric
# build skopt space
space = []
for pipe_element in pipeline_elements:
if pipe_element.__class__.__name__ == 'Switch':
error_msg = 'Scikit-Optimize cannot operate in the specified hyperparameter space with a Switch ' \
'element. We recommend the use of SMAC.'
logger.error(error_msg)
raise ValueError(error_msg)
if hasattr(pipe_element, 'hyperparameters'):
for name, value in pipe_element.hyperparameters.items():
# if we only have one value we do not need to optimize
if isinstance(value, list) and len(value) < 2:
self.constant_dictionary[name] = value[0]
continue
if isinstance(value, PhotonCategorical) and len(value.values) < 2:
self.constant_dictionary[name] = value.values[0]
continue
skopt_param = self._convert_photonai_to_skopt_space(value, name)
if skopt_param is not None:
space.append(skopt_param)
if self.constant_dictionary:
msg = "PHOTONAI has detected some one-valued params in your hyperparameters. Pleas use the kwargs for " \
"constant values. This run ignores following settings: " + str(self.constant_dictionary.keys())
logger.warning(msg)
warnings.warn(msg)
if len(space) == 0:
msg = "Did not find any hyperparameter to convert into skopt space."
logger.warning(msg)
warnings.warn(msg)
else:
self.optimizer = Optimizer(space,
base_estimator=self.base_estimator,
n_initial_points=self.n_initial_points,
initial_point_generator=self.initial_point_generator,
acq_func=self.acq_func,
acq_func_kwargs=self.acq_func_kwargs)
self.ask = self.ask_generator()
tell(self, config, performance)
Provide a config result to calculate new ones.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
config |
dict |
The configuration that has been trained and tested. |
required |
performance |
float |
Metrics about the configuration's generalization capabilities. |
required |
Source code in photonai/optimization/scikit_optimize/sk_opt.py
def tell(self, config: dict, performance: float) -> None:
"""
Provide a config result to calculate new ones.
Parameters:
config:
The configuration that has been trained and tested.
performance:
Metrics about the configuration's generalization capabilities.
"""
# convert dictionary to list in correct order
if self.optimizer is not None:
config_values = [config[name] for name in self.hyperparameter_list]
best_config_metric_performance = performance
if self.maximize_metric:
best_config_metric_performance = -best_config_metric_performance
self.optimizer.tell(config_values, best_config_metric_performance)