Source code for evalml.tuners.skopt_tuner

"""Bayesian Optimizer."""
import logging
import warnings

import pandas as pd
from skopt import Optimizer

from evalml.tuners.tuner import Tuner
from evalml.tuners.tuner_exceptions import ParameterError

logger = logging.getLogger(__name__)


[docs]class SKOptTuner(Tuner): """Bayesian Optimizer. Args: pipeline_hyperparameter_ranges (dict): A set of hyperparameter ranges corresponding to a pipeline's parameters. random_seed (int): The seed for the random number generator. Defaults to 0. Examples: >>> tuner = SKOptTuner({'My Component': {'param a': [0.0, 10.0], 'param b': ['a', 'b', 'c']}}) >>> proposal = tuner.propose() ... >>> assert proposal.keys() == {'My Component'} >>> assert proposal['My Component'] == {'param a': 5.928446182250184, 'param b': 'c'} Determines points using a Bayesian Optimizer approach. >>> for each in range(7): ... print(tuner.propose()) {'My Component': {'param a': 8.57945617622757, 'param b': 'c'}} {'My Component': {'param a': 6.235636967859724, 'param b': 'b'}} {'My Component': {'param a': 2.9753460654447235, 'param b': 'a'}} {'My Component': {'param a': 2.7265629458011325, 'param b': 'b'}} {'My Component': {'param a': 8.121687287754932, 'param b': 'b'}} {'My Component': {'param a': 3.927847961008298, 'param b': 'c'}} {'My Component': {'param a': 3.3739616041726843, 'param b': 'b'}} """ def __init__(self, pipeline_hyperparameter_ranges, random_seed=0): super().__init__(pipeline_hyperparameter_ranges, random_seed=random_seed) self.opt = Optimizer( self._search_space_ranges, "ET", acq_optimizer="sampling", random_state=random_seed, )
[docs] def add(self, pipeline_parameters, score): """Add score to sample. Args: 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 Raises: Exception: If skopt tuner errors. ParameterError: If skopt receives invalid parameters. """ # skip adding nan scores if pd.isnull(score): return flat_parameter_values = self._convert_to_flat_parameters(pipeline_parameters) try: self.opt.tell(flat_parameter_values, score) except Exception as e: logger.debug( "SKOpt tuner received error during add. Score: {}\nParameters: {}\nFlat parameter values: {}\nError: {}".format( pipeline_parameters, score, flat_parameter_values, e, ), ) if str(e) == "'<=' not supported between instances of 'int' and 'NoneType'": msg = "Invalid parameters specified to SKOptTuner.add: parameters {} error {}".format( pipeline_parameters, str(e), ) logger.error(msg) raise ParameterError(msg) raise (e)
[docs] 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. """ with warnings.catch_warnings(): warnings.simplefilter("ignore") if not len(self._search_space_ranges): return self._convert_to_pipeline_parameters({}) flat_parameters = self.opt.ask() return self._convert_to_pipeline_parameters(flat_parameters)