Skip to content

Documentation for SMACOptimizer

SMAC Wrapper for PHOTONAI.

SMAC (sequential model-based algorithm configuration) is a versatile tool for optimizing algorithm parameters. The main core consists of Bayesian Optimization in combination with an aggressive racing mechanism to efficiently decide which of two configurations performs better.

SMAC usage and implementation details here.

References:

1
2
3
Hutter, F. and Hoos, H. H. and Leyton-Brown, K.
Sequential Model-Based Optimization for General Algorithm Configuration
In: Proceedings of the conference on Learning and Intelligent OptimizatioN (LION 5)

Examples:

1
2
3
4
5
6
my_pipe = Hyperpipe('smac_example',
                    optimizer='smac',
                    optimizer_params={"facade": "SMAC4BO",
                                      "wallclock_limit": 60.0*10,  # seconds
                                      "ta_run_limit": 100},  # limit of configurations
                    ...)
Source code in photonai/optimization/smac/smac.py
class SMACOptimizer(PhotonMasterOptimizer):
    """SMAC Wrapper for PHOTONAI.

    SMAC (sequential model-based algorithm configuration) is a
    versatile tool for optimizing algorithm parameters.
    The main core consists of Bayesian Optimization in
    combination with an aggressive racing mechanism to efficiently
    decide which of two configurations performs better.

    SMAC usage and implementation details [here](
    https://automl.github.io/SMAC3/master/quickstart.html).

    References:

        Hutter, F. and Hoos, H. H. and Leyton-Brown, K.
        Sequential Model-Based Optimization for General Algorithm Configuration
        In: Proceedings of the conference on Learning and Intelligent OptimizatioN (LION 5)

    Example:
        ``` python
        my_pipe = Hyperpipe('smac_example',
                            optimizer='smac',
                            optimizer_params={"facade": "SMAC4BO",
                                              "wallclock_limit": 60.0*10,  # seconds
                                              "ta_run_limit": 100},  # limit of configurations
                            ...)

        ```

    """
    def __init__(self, facade='SMAC4HPO',
                 run_obj: str = "quality",
                 deterministic: str = "true",
                 wallclock_limit: float = 60.0,
                 intensifier_kwargs: dict = None,
                 rng: int = 42, **kwargs):
        """
        Initialize the object.

        Parameters:
            facade:
                Choice of the SMAC backend strategy, [SMAC4BO, SMAC4HPO, SMAC4AC, BOHB4HPO].

            run_obj:
                Defines the optimization metric.
                When optimizing runtime, cutoff_time is required as well.

            wallclock_limit:
                Maximum amount of wallclock-time used for optimization.

            deterministic:
                If true, SMAC assumes that the target function or algorithm
                is deterministic (the same static seed of 0
                is always passed to the function/algorithm).
                If false, different random seeds are passed
                to the target function/algorithm.

            intensifier_kwargs:
                Dict for intensifier settings.

            rng:
                Random seed of SMAC.facade.

            **kwargs:
                All initial kwargs are passed to SMACs scenario.
                [List of all a vailable parameters](
                https://automl.github.io/SMAC3/master/options.html#scenario).

        """
        super(SMACOptimizer, self).__init__()

        if not __found__:
            msg = "Module smac not found or not installed as expected. " \
                  "Please install the smac/requirements.txt PHOTONAI provides."
            logger.error(msg)
            raise ModuleNotFoundError(msg)

        self.run_obj = run_obj
        self.deterministic = deterministic
        self.wallclock_limit = wallclock_limit
        self.kwargs = kwargs

        if facade in ["SMAC4BO", SMAC4BO, "SMAC4AC", SMAC4AC, "SMAC4HPO", SMAC4HPO, "BOHB4HPO", BOHB4HPO]:
            if type(facade) == str:

                self.facade = eval(facade)
            else:
                self.facade = facade
        else:
            msg = "SMAC.facade {} not known. Please use one of ['SMAC4BO', 'SMAC4AC', 'SMAC4HPO']."
            logger.error(msg.format(str(facade)))
            raise ValueError(msg.format(str(facade)))

        self.rng = rng
        if not intensifier_kwargs:
            self.intensifier_kwargs = {}
        else:
            self.intensifier_kwargs = intensifier_kwargs

        self.cspace = ConfigurationSpace()  # hyperparameter space for SMAC
        self.switch_optiones = {}
        self.hyperparameters = []

        self.maximize_metric = False
        self.constant_dictionary = {}

    def prepare(self, pipeline_elements: list, maximize_metric: bool, objective_function: Callable):
        """
        Initializes the SMAC Optimizer.

        Parameters:
            pipeline_elements:
                List of all PipelineElements to create hyperparameter space.

            maximize_metric:
                Boolean to distinguish between score and error.

            objective_function:
                The cost or objective function.

        """
        self.cspace = ConfigurationSpace()  # build space
        self._build_smac_space(pipeline_elements)
        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)
        self.maximize_metric = maximize_metric

        scenario_dict = self.kwargs
        scenario_dict.update({"run_obj":self.run_obj,
                              "deterministic":self.deterministic,
                              "wallclock_limit":self.wallclock_limit,
                              "cs":self.cspace,
                              "limit_resources": False})

        scenario = Scenario(scenario_dict)

        def smac_objective_function(current_config):
            current_config = {k: current_config[k] for k in current_config if (current_config[k] and 'algos' not in k)}
            return objective_function(current_config)

        self.smac = self.facade(scenario=scenario,
                                intensifier_kwargs=self.intensifier_kwargs,
                                rng=self.rng,
                                tae_runner=smac_objective_function)

    def optimize(self):
        """Start optimization process."""
        self.smac.optimize()

    def _build_smac_space(self, pipeline_elements: list):
        """
        Build the entire SMAC hyperparameter space.

        Parameters:
            pipeline_elements:
                List of all PipelineElements to create the hyperparameter space.

        """
        for pipe_element in pipeline_elements:
            # build conditions for switch elements
            if pipe_element.__class__.__name__ == 'Switch':
                algorithm_options = {}
                for algo in pipe_element.elements:
                    algo_params = []  # hyper params corresponding to "algo"
                    for name, value in algo.hyperparameters.items():
                        smac_param = self._convert_photonai_to_smac_param(value, (
                                pipe_element.name + "__" + name))  # or element.name__algo.name__name
                        algo_params.append(smac_param)
                    algorithm_options[(pipe_element.name + "__" + algo.name)] = algo_params

                algos = CategoricalHyperparameter(pipe_element.name + "__algos", choices=algorithm_options.keys())

                self.switch_optiones[pipe_element.name + "__algos"] = algorithm_options.keys()

                self.cspace.add_hyperparameter(algos)
                for algo, params in algorithm_options.items():
                    for param in params:
                        cond = InCondition(child=param, parent=algos, values=[algo])
                        self.cspace.add_hyperparameter(param)
                        self.cspace.add_condition(cond)
                continue

            if hasattr(pipe_element, 'hyperparameters'):
                for name, value in pipe_element.hyperparameters.items():
                    self.hyperparameters.append(name)
                    # 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
                    smac_param = self._convert_photonai_to_smac_param(value, name)
                    if smac_param is not None:
                        self.cspace.add_hyperparameter(smac_param)

    @staticmethod
    def _convert_photonai_to_smac_param(hyperparam: PhotonHyperparam, name: str):
        """
        Helper function: Convert PHOTONAI to SMAC hyperparameter.

        Parameters:
            hyperparam:
                One of photonai.optimization.hyperparameters.

            name:
                Name of hyperparameter.

        """
        if isinstance(hyperparam, PhotonCategorical) or isinstance(hyperparam, BooleanSwitch):
            return CategoricalHyperparameter(name, hyperparam.values)
        elif isinstance(hyperparam, list):
            return CategoricalHyperparameter(name, hyperparam)
        elif isinstance(hyperparam, FloatRange):
            if hyperparam.range_type in ['linspace', 'logspace']:
                return UniformFloatHyperparameter(name, hyperparam.start, hyperparam.stop,
                                                  log=(hyperparam.range_type == 'logspace'))
            msg = str(hyperparam.range_type) + "in your FloatRange is not implemented in SMAC."
            logger.error(msg)
            raise NotImplementedError(msg)
        elif isinstance(hyperparam, IntegerRange):
            return UniformIntegerHyperparameter(name, hyperparam.start, hyperparam.stop)

        msg = "Cannot convert hyperparameter " + str(hyperparam) + ". Supported types: Categorical, IntegerRange," \
                                                                   "FloatRange, list."
        logger.error(msg)
        raise ValueError(msg)

__init__(self, facade='SMAC4HPO', run_obj='quality', deterministic='true', wallclock_limit=60.0, intensifier_kwargs=None, rng=42, **kwargs) special

Initialize the object.

Parameters:

Name Type Description Default
facade

Choice of the SMAC backend strategy, [SMAC4BO, SMAC4HPO, SMAC4AC, BOHB4HPO].

'SMAC4HPO'
run_obj str

Defines the optimization metric. When optimizing runtime, cutoff_time is required as well.

'quality'
wallclock_limit float

Maximum amount of wallclock-time used for optimization.

60.0
deterministic str

If true, SMAC assumes that the target function or algorithm is deterministic (the same static seed of 0 is always passed to the function/algorithm). If false, different random seeds are passed to the target function/algorithm.

'true'
intensifier_kwargs dict

Dict for intensifier settings.

None
rng int

Random seed of SMAC.facade.

42
**kwargs

All initial kwargs are passed to SMACs scenario. List of all a vailable parameters.

{}
Source code in photonai/optimization/smac/smac.py
def __init__(self, facade='SMAC4HPO',
             run_obj: str = "quality",
             deterministic: str = "true",
             wallclock_limit: float = 60.0,
             intensifier_kwargs: dict = None,
             rng: int = 42, **kwargs):
    """
    Initialize the object.

    Parameters:
        facade:
            Choice of the SMAC backend strategy, [SMAC4BO, SMAC4HPO, SMAC4AC, BOHB4HPO].

        run_obj:
            Defines the optimization metric.
            When optimizing runtime, cutoff_time is required as well.

        wallclock_limit:
            Maximum amount of wallclock-time used for optimization.

        deterministic:
            If true, SMAC assumes that the target function or algorithm
            is deterministic (the same static seed of 0
            is always passed to the function/algorithm).
            If false, different random seeds are passed
            to the target function/algorithm.

        intensifier_kwargs:
            Dict for intensifier settings.

        rng:
            Random seed of SMAC.facade.

        **kwargs:
            All initial kwargs are passed to SMACs scenario.
            [List of all a vailable parameters](
            https://automl.github.io/SMAC3/master/options.html#scenario).

    """
    super(SMACOptimizer, self).__init__()

    if not __found__:
        msg = "Module smac not found or not installed as expected. " \
              "Please install the smac/requirements.txt PHOTONAI provides."
        logger.error(msg)
        raise ModuleNotFoundError(msg)

    self.run_obj = run_obj
    self.deterministic = deterministic
    self.wallclock_limit = wallclock_limit
    self.kwargs = kwargs

    if facade in ["SMAC4BO", SMAC4BO, "SMAC4AC", SMAC4AC, "SMAC4HPO", SMAC4HPO, "BOHB4HPO", BOHB4HPO]:
        if type(facade) == str:

            self.facade = eval(facade)
        else:
            self.facade = facade
    else:
        msg = "SMAC.facade {} not known. Please use one of ['SMAC4BO', 'SMAC4AC', 'SMAC4HPO']."
        logger.error(msg.format(str(facade)))
        raise ValueError(msg.format(str(facade)))

    self.rng = rng
    if not intensifier_kwargs:
        self.intensifier_kwargs = {}
    else:
        self.intensifier_kwargs = intensifier_kwargs

    self.cspace = ConfigurationSpace()  # hyperparameter space for SMAC
    self.switch_optiones = {}
    self.hyperparameters = []

    self.maximize_metric = False
    self.constant_dictionary = {}

optimize(self)

Start optimization process.

Source code in photonai/optimization/smac/smac.py
def optimize(self):
    """Start optimization process."""
    self.smac.optimize()

prepare(self, pipeline_elements, maximize_metric, objective_function)

Initializes the SMAC Optimizer.

Parameters:

Name Type Description Default
pipeline_elements list

List of all PipelineElements to create hyperparameter space.

required
maximize_metric bool

Boolean to distinguish between score and error.

required
objective_function Callable

The cost or objective function.

required
Source code in photonai/optimization/smac/smac.py
def prepare(self, pipeline_elements: list, maximize_metric: bool, objective_function: Callable):
    """
    Initializes the SMAC Optimizer.

    Parameters:
        pipeline_elements:
            List of all PipelineElements to create hyperparameter space.

        maximize_metric:
            Boolean to distinguish between score and error.

        objective_function:
            The cost or objective function.

    """
    self.cspace = ConfigurationSpace()  # build space
    self._build_smac_space(pipeline_elements)
    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)
    self.maximize_metric = maximize_metric

    scenario_dict = self.kwargs
    scenario_dict.update({"run_obj":self.run_obj,
                          "deterministic":self.deterministic,
                          "wallclock_limit":self.wallclock_limit,
                          "cs":self.cspace,
                          "limit_resources": False})

    scenario = Scenario(scenario_dict)

    def smac_objective_function(current_config):
        current_config = {k: current_config[k] for k in current_config if (current_config[k] and 'algos' not in k)}
        return objective_function(current_config)

    self.smac = self.facade(scenario=scenario,
                            intensifier_kwargs=self.intensifier_kwargs,
                            rng=self.rng,
                            tae_runner=smac_objective_function)