from abc import ABC, abstractmethod
[docs]class Tuner(ABC):
"""Defines API for Tuners.
Tuners implement different strategies for sampling from a search space. They're used in EvalML to search the space of pipeline hyperparameters.
"""
[docs] def __init__(self, pipeline_hyperparameter_ranges, random_state=0):
"""Base Tuner class
Arguments:
pipeline_hyperparameter_ranges (dict): a set of hyperparameter ranges corresponding to a pipeline's parameters
random_state (int, np.random.RandomState): The random state
"""
self._pipeline_hyperparameter_ranges = pipeline_hyperparameter_ranges
self._parameter_names_map = dict()
self._search_space_names = []
self._search_space_ranges = []
if not isinstance(pipeline_hyperparameter_ranges, dict):
raise ValueError('pipeline_hyperparameter_ranges must be a dict but is of type {}'.format(type(pipeline_hyperparameter_ranges)))
self._component_names = list(pipeline_hyperparameter_ranges.keys())
for component_name, component_ranges in pipeline_hyperparameter_ranges.items():
if not isinstance(component_ranges, dict):
raise ValueError('pipeline_hyperparameter_ranges has invalid entry for {}: {}'.format(component_name, component_ranges))
for parameter_name, parameter_range in component_ranges.items():
if parameter_range is None:
raise ValueError('pipeline_hyperparameter_ranges has invalid dimensions for ' +
'{} parameter {}: None.'.format(component_name, parameter_name))
flat_parameter_name = '{}: {}'.format(component_name, parameter_name)
self._parameter_names_map[flat_parameter_name] = (component_name, parameter_name)
self._search_space_names.append(flat_parameter_name)
self._search_space_ranges.append(parameter_range)
def _convert_to_flat_parameters(self, pipeline_parameters):
"""Convert from pipeline parameters to a flat list of values"""
flat_parameter_values = []
for flat_parameter_name in self._search_space_names:
component_name, parameter_name = self._parameter_names_map[flat_parameter_name]
if component_name not in pipeline_parameters or parameter_name not in pipeline_parameters[component_name]:
raise TypeError('Pipeline parameters missing required field "{}" for component "{}"'.format(parameter_name, component_name))
flat_parameter_values.append(pipeline_parameters[component_name][parameter_name])
return flat_parameter_values
def _convert_to_pipeline_parameters(self, flat_parameters):
"""Convert from a flat list of values to a dict of pipeline parameters"""
pipeline_parameters = {component_name: dict() for component_name in self._component_names}
for flat_parameter_name, parameter_value in zip(self._search_space_names, flat_parameters):
component_name, parameter_name = self._parameter_names_map[flat_parameter_name]
pipeline_parameters[component_name][parameter_name] = parameter_value
return pipeline_parameters
[docs] @abstractmethod
def add(self, pipeline_parameters, score):
""" Register a set of hyperparameters with the score obtained from training a pipeline with those hyperparameters.
Arguments:
pipeline_parameters (dict): a dict of the parameters used to evaluate a pipeline
score (float): the score obtained by evaluating the pipeline with the provided parameters
Returns:
None
"""
[docs] @abstractmethod
def propose(self):
"""Returns a suggested set of parameters to train and score a pipeline with, based off the search space dimensions and prior samples.
Returns:
dict: proposed pipeline parameters
"""
[docs] def is_search_space_exhausted(self):
""" Optional. If possible search space for tuner is finite, this method indicates
whether or not all possible parameters have been scored.
Returns:
bool: Returns true if all possible parameters in a search space has been scored.
"""
return False