forked from syne-tune/syne-tune
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmulti_fidelity_kde_searcher.py
146 lines (130 loc) · 5.88 KB
/
multi_fidelity_kde_searcher.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# 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 typing import Dict, Optional, List
import logging
import numpy as np
from syne_tune.optimizer.schedulers.searchers.kde.kde_searcher import (
KernelDensityEstimator,
)
__all__ = ["MultiFidelityKernelDensityEstimator"]
logger = logging.getLogger(__name__)
class MultiFidelityKernelDensityEstimator(KernelDensityEstimator):
"""
Adapts the KernelDensityEstimator to the multi-fidelity setting as proposed
by Falkner et al such that we can use it with Hyperband. Following Falkner
et al, we fit the KDE only on the highest resource level where we have at
least num_min_data_points. Code is based on the implementation by Falkner
et al: https://github.com/automl/HpBandSter/tree/master/hpbandster
BOHB: Robust and Efficient Hyperparameter Optimization at Scale
S. Falkner and A. Klein and F. Hutter
Proceedings of the 35th International Conference on Machine Learning
Parameters
----------
config_space: dict
Configuration space for trial evaluation function
metric : str
Name of metric to optimize, key in result's obtained via
`on_trial_result`
random_seed_generator : RandomSeedGenerator (optional)
If given, the random_seed for `random_state` is obtained from there,
otherwise `random_seed` is used
random_seed : int (optional)
This is used if `random_seed_generator` is not given.
mode : str
Mode to use for the metric given, can be 'min' or 'max', default to 'min'.
num_min_data_points: int
Minimum number of data points that we use to fit the KDEs. If set to None
than we set this to the number of hyperparameters.
top_n_percent: int
Determines how many datapoints we use use to fit the first KDE model for
modeling the well performing configurations.
min_bandwidth: float
The minimum bandwidth for the KDE models
num_candidates: int
Number of candidates that are sampled to optimize the acquisition function
bandwidth_factor: int
We sample continuous hyperparameter from a truncated Normal. This factor is
multiplied to the bandwidth to define the standard deviation of this
trunacted Normal.
random_fraction: float
Defines the fraction of configurations that are drawn uniformly at random
instead of sampling from the model
points_to_evaluate: List[Dict] or None
List of configurations to be evaluated initially (in that order).
Each config in the list can be partially specified, or even be an
empty dict. For each hyperparameter not specified, the default value
is determined using a midpoint heuristic.
If None (default), this is mapped to [dict()], a single default config
determined by the midpoint heuristic. If [] (empty list), no initial
configurations are specified.
"""
def __init__(
self,
config_space: Dict,
metric: str,
points_to_evaluate: Optional[List[Dict]] = None,
mode: str = "min",
num_min_data_points: int = None,
top_n_percent: int = 15,
min_bandwidth: float = 0.1,
num_candidates: int = 64,
bandwidth_factor: int = 3,
random_fraction: float = 0.33,
resource_attr: Optional[str] = None,
**kwargs
):
super().__init__(
config_space,
metric=metric,
points_to_evaluate=points_to_evaluate,
mode=mode,
num_min_data_points=num_min_data_points,
top_n_percent=top_n_percent,
min_bandwidth=min_bandwidth,
num_candidates=num_candidates,
bandwidth_factor=bandwidth_factor,
random_fraction=random_fraction,
**kwargs
)
self.resource_attr = resource_attr
self.resource_levels = []
def configure_scheduler(self, scheduler):
from syne_tune.optimizer.schedulers.hyperband import HyperbandScheduler
from syne_tune.optimizer.schedulers.synchronous.hyperband import (
SynchronousHyperbandScheduler,
)
assert isinstance(scheduler, HyperbandScheduler) or isinstance(
scheduler, SynchronousHyperbandScheduler
), (
"This searcher requires HyperbandScheduler or "
+ "SynchronousHyperbandScheduler scheduler"
)
self.resource_attr = scheduler._resource_attr
def train_kde(self, train_data, train_targets):
# find the highest resource level we have at least num_min_data_points data points
unique_resource_levels, counts = np.unique(
self.resource_levels, return_counts=True
)
idx = np.where(counts >= self.num_min_data_points)[0]
if len(idx) == 0:
return
# collect data on the highest resource level
highest_resource_level = unique_resource_levels[idx[-1]]
indices = np.where(self.resource_levels == highest_resource_level)[0]
train_data = np.array([self.X[i] for i in indices])
train_targets = np.array([self.y[i] for i in indices])
super().train_kde(train_data, train_targets)
def _update(self, trial_id: str, config: Dict, result: Dict):
super()._update(trial_id=trial_id, config=config, result=result)
resource_level = int(result[self.resource_attr])
self.resource_levels.append(resource_level)