Note
Go to the end to download the full example code
Continue an Optimization¶
SMAC can also be continued. In this example, an optimization of a simple quadratic function is continued. We use a custom callback, to artificially stop the first optimization.
[INFO][abstract_initial_design.py:147] Using 10 initial design configurations and 0 additional configurations.
[INFO][abstract_intensifier.py:305] Using only one seed for deterministic scenario.
[INFO][abstract_intensifier.py:515] Added config 3ce33d as new incumbent because there are no incumbents yet.
[INFO][abstract_intensifier.py:590] Added config c53710 and rejected config 3ce33d as incumbent because it is not better than the incumbents on 1 instances:
[INFO][configspace.py:175] --- x: -2.331214789301157 -> 2.1608731895685196
[INFO][abstract_intensifier.py:590] Added config 50b580 and rejected config c53710 as incumbent because it is not better than the incumbents on 1 instances:
[INFO][configspace.py:175] --- x: 2.1608731895685196 -> 0.4316552169620991
[INFO][abstract_intensifier.py:590] Added config d16e41 and rejected config 50b580 as incumbent because it is not better than the incumbents on 1 instances:
[INFO][configspace.py:175] --- x: 0.4316552169620991 -> -0.3100988641381264
[INFO][smbo.py:223] A callback returned False. Abort is requested.
[INFO][smbo.py:312] Shutting down because the stop flag was set.
[INFO][abstract_initial_design.py:147] Using 10 initial design configurations and 0 additional configurations.
[INFO][smbo.py:473] Continuing from previous run.
[INFO][abstract_intensifier.py:287] Added existing seed 209652396 from runhistory to the intensifier.
[INFO][abstract_intensifier.py:305] Using only one seed for deterministic scenario.
[INFO][abstract_intensifier.py:590] Added config 4dcc97 and rejected config d16e41 as incumbent because it is not better than the incumbents on 1 instances:
[INFO][configspace.py:175] --- x: -0.3100988641381264 -> -0.22015188896172422
[INFO][abstract_intensifier.py:590] Added config 4d21be and rejected config 4dcc97 as incumbent because it is not better than the incumbents on 1 instances:
[INFO][configspace.py:175] --- x: -0.22015188896172422 -> -0.1481212818284119
[INFO][abstract_intensifier.py:590] Added config 348898 and rejected config 4d21be as incumbent because it is not better than the incumbents on 1 instances:
[INFO][configspace.py:175] --- x: -0.1481212818284119 -> -0.13225703373856135
[INFO][abstract_intensifier.py:590] Added config c56201 and rejected config 348898 as incumbent because it is not better than the incumbents on 1 instances:
[INFO][configspace.py:175] --- x: -0.13225703373856135 -> -0.008886632260822758
[INFO][abstract_intensifier.py:590] Added config b6eee0 and rejected config c56201 as incumbent because it is not better than the incumbents on 1 instances:
[INFO][configspace.py:175] --- x: -0.008886632260822758 -> 0.00353744136744627
[INFO][abstract_intensifier.py:590] Added config 06e28a and rejected config b6eee0 as incumbent because it is not better than the incumbents on 1 instances:
[INFO][configspace.py:175] --- x: 0.00353744136744627 -> 0.0029625967410442655
[INFO][abstract_intensifier.py:590] Added config 4026d1 and rejected config 06e28a as incumbent because it is not better than the incumbents on 1 instances:
[INFO][configspace.py:175] --- x: 0.0029625967410442655 -> 0.0009114180407276962
[INFO][abstract_intensifier.py:590] Added config 025ea5 and rejected config 4026d1 as incumbent because it is not better than the incumbents on 1 instances:
[INFO][configspace.py:175] --- x: 0.0009114180407276962 -> -0.00032238773647641494
[INFO][smbo.py:299] Finished 50 trials.
[INFO][smbo.py:307] Configuration budget is exhausted:
[INFO][smbo.py:308] --- Remaining wallclock time: inf
[INFO][smbo.py:309] --- Remaining cpu time: inf
[INFO][smbo.py:310] --- Remaining trials: 0
[INFO][abstract_intensifier.py:305] Using only one seed for deterministic scenario.
Default cost: 25.0
Incumbent cost of first run: 0.09616130553975616
[INFO][abstract_intensifier.py:305] Using only one seed for deterministic scenario.
Incumbent cost of continued run: 1.0393385263038637e-07
from __future__ import annotations
from ConfigSpace import Configuration, ConfigurationSpace, Float
from smac import Callback
from smac import HyperparameterOptimizationFacade as HPOFacade
from smac import Scenario
from smac.main.smbo import SMBO
from smac.runhistory import TrialInfo, TrialValue
__copyright__ = "Copyright 2021, AutoML.org Freiburg-Hannover"
__license__ = "3-clause BSD"
class StopCallback(Callback):
def __init__(self, stop_after: int):
self._stop_after = stop_after
def on_tell_end(self, smbo: SMBO, info: TrialInfo, value: TrialValue) -> bool | None:
"""Called after the stats are updated and the trial is added to the runhistory. Optionally, returns false
to gracefully stop the optimization.
"""
if smbo.runhistory.finished == self._stop_after:
return False
return None
class QuadraticFunction:
@property
def configspace(self) -> ConfigurationSpace:
cs = ConfigurationSpace(seed=0)
x = Float("x", (-5, 5), default=-5)
cs.add_hyperparameters([x])
return cs
def train(self, config: Configuration, seed: int = 0) -> float:
"""Returns the y value of a quadratic function with a minimum at x=0."""
x = config["x"]
return x * x
if __name__ == "__main__":
model = QuadraticFunction()
# Scenario object specifying the optimization "environment"
scenario = Scenario(model.configspace, deterministic=True, n_trials=50)
stop_after = 10
# Now we use SMAC to find the best hyperparameters
smac = HPOFacade(
scenario,
model.train, # We pass the target function here
callbacks=[StopCallback(stop_after=stop_after)],
overwrite=True, # Overrides any previous results that are found that are inconsistent with the meta-data
)
incumbent = smac.optimize()
assert smac.runhistory.finished == stop_after
# Now, we want to continue the optimization
# Make sure, we don't overwrite the last run
smac2 = HPOFacade(
scenario,
model.train,
overwrite=False,
)
# Check whether we get the same incumbent
assert smac.intensifier.get_incumbent() == smac2.intensifier.get_incumbent()
assert smac2.runhistory.finished == stop_after
# And now we finish the optimization
incumbent2 = smac2.optimize()
default_cost = smac.validate(model.configspace.get_default_configuration())
print(f"Default cost: {default_cost}")
incumbent_cost = smac.validate(incumbent)
print(f"Incumbent cost of first run: {incumbent_cost}")
incumbent_cost = smac2.validate(incumbent2)
print(f"Incumbent cost of continued run: {incumbent_cost}")
Total running time of the script: ( 0 minutes 0.881 seconds)