Source code for syne_tune.optimizer.schedulers.searchers.bayesopt.models.estimator

# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License.
# A copy of the License is located at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# or in the "license" file accompanying this file. This file is distributed
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.
from dataclasses import dataclass
from typing import Dict, Any, Optional, Union

import numpy as np

from syne_tune.optimizer.schedulers.searchers.bayesopt.datatypes.common import (
    FantasizedPendingEvaluation,
    INTERNAL_METRIC_NAME,
)
from syne_tune.optimizer.schedulers.searchers.bayesopt.datatypes.tuning_job_state import (
    TuningJobState,
)
from syne_tune.optimizer.schedulers.searchers.bayesopt.tuning_algorithms.base_classes import (
    Predictor,
)
from syne_tune.optimizer.schedulers.searchers.bayesopt.utils.debug_log import (
    DebugLogPrinter,
)


[docs] class Estimator: """ Interface for surrogate models used in :class:`ModelStateTransformer`. In general, a surrogate model is probabilistic (or Bayesian), in that predictions are driven by a posterior distribution, represented in a posterior state of type :class:`~syne_tune.optimizer.schedulers.searchers.bayesopt.tuning_algorithms.base_classes.Predictor`. The model may also come with tunable (hyper)parameters, such as for example covariance function parameters for a Gaussian process model. These parameters can be accessed with :meth:`get_params`, :meth:`set_params`. """
[docs] def get_params(self) -> Dict[str, Any]: """ :return: Current tunable model parameters """ raise NotImplementedError()
[docs] def set_params(self, param_dict: Dict[str, Any]): """ :param param_dict: New model parameters """ raise NotImplementedError()
[docs] def fit_from_state(self, state: TuningJobState, update_params: bool) -> Predictor: """ Creates a :class:`~syne_tune.optimizer.schedulers.searchers.bayesopt.tuning_algorithms.base_classes.Predictor` object based on data in ``state``. For a Bayesian model, this involves computing the posterior state, which is wrapped in the ``Predictor`` object. If the model also has (hyper)parameters, these are learned iff ``update_params == True``. Otherwise, these parameters are not changed, but only the posterior state is computed. The idea is that in general, model fitting is much more expensive than just creating the final posterior state (or predictor). It then makes sense to partly work with stale model parameters. If your surrogate model is not Bayesian, or does not have hyperparameters, you can ignore the ``update_params`` argument, :param state: Current data model parameters are to be fit on, and the posterior state is to be computed from :param update_params: See above :return: Predictor, wrapping the posterior state """ raise NotImplementedError()
@property def debug_log(self) -> Optional[DebugLogPrinter]: return None
[docs] def configure_scheduler(self, scheduler): """ Called by :meth:`configure_scheduler` of searchers which make use of an class:``Estimator``. Allows the estimator to depend on parameters of the scheduler. :param scheduler: Scheduler object """ pass
# Convenience type allowing for multi-output HPO. These are used for methods # that work both in the standard case of a single output model and in the # multi-output case OutputEstimator = Union[Estimator, Dict[str, Estimator]]
[docs] @dataclass class TransformedData: features: np.ndarray targets: np.ndarray mean: float std: float
[docs] def transform_state_to_data( state: TuningJobState, active_metric: Optional[str] = None, normalize_targets: bool = True, num_fantasy_samples: int = 1, ) -> TransformedData: """ Transforms :class:`~syne_tune.optimizer.schedulers.searchers.bayesopt.datatypes.tuning_job_state.TuningJobState` object ``state`` to features and targets. The former are encoded vectors from ``state.hp_ranges``. The latter are normalized to zero mean, unit variance if ``normalize_targets == True``, in which case the original mean and stddev is also returned. If ``state.pending_evaluations`` is not empty, it must contain entries of type :class:`~syne_tune.optimizer.schedulers.searchers.bayesopt.datatypes.common.FantasizedPendingEvaluation`, which contain the fantasy samples. This is the case only for internal states. :param state: ``TuningJobState`` to transform :param active_metric: Name of target metric (optional) :param normalize_targets: Normalize targets? Defaults to ``True`` :param num_fantasy_samples: Number of fantasy samples. Defaults to 1 :return: Transformed data """ if active_metric is None: active_metric = INTERNAL_METRIC_NAME candidates, evaluation_values = state.observed_data_for_metric( metric_name=active_metric ) hp_ranges = state.hp_ranges features = hp_ranges.to_ndarray_matrix(candidates) # Normalize # Note: The fantasy values in state.pending_evaluations are sampled # from the model fit to normalized targets, so they are already # normalized targets = np.vstack(evaluation_values).reshape((-1, 1)) mean = 0.0 std = 1.0 if normalize_targets: std = max(np.std(targets).item(), 1e-9) mean = np.mean(targets).item() targets = (targets - mean) / std if state.pending_evaluations: # In this case, y becomes a matrix, where the observed values are # broadcast cand_lst = [ hp_ranges.to_ndarray(config) for config in state.pending_configurations() ] fanta_lst = [] for pending_eval in state.pending_evaluations: assert isinstance( pending_eval, FantasizedPendingEvaluation ), "state.pending_evaluations has to contain FantasizedPendingEvaluation" fantasies = pending_eval.fantasies[active_metric] assert ( fantasies.size == num_fantasy_samples ), "All state.pending_evaluations entries must have length {}".format( num_fantasy_samples ) fanta_lst.append(fantasies.reshape((1, -1))) targets = np.vstack([targets * np.ones((1, num_fantasy_samples))] + fanta_lst) features = np.vstack([features] + cand_lst) return TransformedData(features, targets, mean, std)