Skip to content

Commit 60bd0d6

Browse files
authored
Merge pull request #1212 from automl/feature/1206_ask_and_tell_with_initial_design
ask and tell without initial design
2 parents 177d8c9 + a3ce8a3 commit 60bd0d6

File tree

5 files changed

+36
-8
lines changed

5 files changed

+36
-8
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@
1414
## Improvements
1515
- `target_function` becomes optional in Facade when using ask and tell exclusively (#946)
1616

17+
## Documentation
18+
- Ask and tell without initial design and warmstarting
19+
20+
## Bugfixes
21+
- Ask and tell without initial design may no longer return a config from the initial design - if it is not "removed".
22+
1723
# 2.3.0
1824

1925
## Documentation

examples/1_basics/3_ask_and_tell.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
# Flags: doc-Runnable
33
44
This examples show how to use the Ask-and-Tell interface.
5+
6+
Notice, that the ask-and-tell interface will still use the initial design specified in the facade.
7+
Should you wish to add your own evaluated configurations instead or deactivate the initial
8+
design all together, please refer to the warmstarting example in conjunction with this one.
59
"""
610

711
from ConfigSpace import Configuration, ConfigurationSpace, Float
@@ -52,7 +56,7 @@ def train(self, config: Configuration, seed: int = 0) -> float:
5256
# Now we use SMAC to find the best hyperparameters
5357
smac = HyperparameterOptimizationFacade(
5458
scenario,
55-
model.train,
59+
target_function=model.train,
5660
intensifier=intensifier,
5761
overwrite=True,
5862
)
@@ -68,7 +72,14 @@ def train(self, config: Configuration, seed: int = 0) -> float:
6872
smac.tell(info, value)
6973

7074
# After calling ask+tell, we can still optimize
71-
# Note: SMAC will optimize the next 90 trials because 10 trials already have been evaluated
75+
# Note: SMAC will optimize the next 90 trials because 10 trials already have been evaluated.
76+
# If we however choose not to call optimize; e.g. because we want to manage heavy
77+
# computation of model.train completely outside smac, but still use it to suggest new
78+
# configurations, then n_trials will only be relevant for the initial design in combination
79+
# with initial design max_ratio! In fact in an only ask+tell case, we could even set
80+
# target_function=None in the constructor, because smac wouldn't even need to know
81+
# what the target function is. But that will prevent us from calling optimize and validate later
82+
# on.
7283
incumbent = smac.optimize()
7384

7485
# Get cost of default configuration

examples/1_basics/8_warmstart.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,19 @@ def evaluate(self, config: Configuration, seed: int = 0) -> float:
5656
intensifier = HyperparameterOptimizationFacade.get_intensifier(scenario, max_config_calls=1)
5757
smac = HyperparameterOptimizationFacade(
5858
scenario,
59-
task.evaluate,
59+
target_function=task.evaluate,
6060
intensifier=intensifier,
6161
overwrite=True,
6262

6363
# Modify the initial design to use our custom initial design
6464
initial_design=HyperparameterOptimizationFacade.get_initial_design(
6565
scenario,
66-
n_configs=0, # Do not use the default initial design
67-
additional_configs=configurations # Use the configurations previously evaluated as initial design
66+
n_configs=0, # Do not use the default initial design at all
67+
68+
# You can pass the configurations as additional_configs, which will specify their
69+
# origin to be the initial design. However, this is not necessary and we can just
70+
# smac.tell the configurations.
71+
# additional_configs=configurations # Use the configurations previously evaluated as initial design
6872
# This only passes the configurations but not the cost!
6973
# So in order to actually use the custom, pre-evaluated initial design
7074
# we need to tell those trials, like below.
@@ -80,4 +84,6 @@ def evaluate(self, config: Configuration, seed: int = 0) -> float:
8084
smac.tell(info, value)
8185

8286
# Optimize as usual
83-
smac.optimize()
87+
# Notice, that since we added three configurations, n_trials for the remaining optimization
88+
# is effectively 27 in optimize().
89+
smac.optimize()

smac/initial_design/abstract_initial_design.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,11 @@ def __init__(
8282
)
8383

8484
# If the number of configurations is too large, we reduce it
85-
_n_configs = int(max(1, min(self._n_configs, (max_ratio * scenario.n_trials))))
85+
if self._n_configs > 1:
86+
_n_configs = int(max(1, min(self._n_configs, (max_ratio * scenario.n_trials))))
87+
else:
88+
_n_configs = self._n_configs
89+
8690
if self._n_configs != _n_configs:
8791
logger.info(
8892
f"Reducing the number of initial configurations from {self._n_configs} to "

smac/main/config_selector.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ def _set_components(
103103

104104
self._initial_design_configs = initial_design.select_configurations()
105105
if len(self._initial_design_configs) == 0:
106-
raise RuntimeError("SMAC needs initial configurations to work.")
106+
# raise RuntimeError("SMAC needs initial configurations to work.")
107+
logger.warning("No initial configurations were sampled.")
107108

108109
@property
109110
def meta(self) -> dict[str, Any]:

0 commit comments

Comments
 (0)