Skip to content

Commit

Permalink
Upgrade to ConfigSpace 1.x (#1124)
Browse files Browse the repository at this point in the history
* Upgrad to ConfigSpace 1.x

* refactor: ConfigSpace API

* fix(jsondump): add numpy encoder

ConfigSpace integer hyperparameters do not return python int type but numpy int64, which is not json serializable

* fix(jsondump): add numpy encoder

* fix(ConfigSpace): parameter API

* Fix example (ConfigSpace API)

* Make format

* Fix pre-commit

* isort
  • Loading branch information
benjamc authored Jul 17, 2024
1 parent 73ff8d0 commit 03a2701
Show file tree
Hide file tree
Showing 50 changed files with 239 additions and 171 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
- Add example to specify total budget (fidelity units) instead of n_trials for multi-fidelity/Hyperband (#1121)

## Dependencies
- Update numpy NaN (#1122) and restrict numpy and ConfigSpace versions
- Update numpy NaN (#1122) and restrict numpy version
- Upgrade to ConfigSpace 1.x.x (#1124)

# 2.1.0

Expand Down
4 changes: 3 additions & 1 deletion benchmark/src/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from collections import defaultdict
from pathlib import Path

from smac.utils.numpyencoder import NumpyEncoder

import pandas as pd
from src.tasks import TASKS # noqa: E402
from src.utils.exceptions import NotSupportedError # noqa: E402
Expand Down Expand Up @@ -79,7 +81,7 @@ def _save_data(self) -> None:
"""Saves the internal data to the file."""
print("Saving data...")
with open(str(RAW_FILENAME), "w") as f:
json.dump(self._data, f, indent=4)
json.dump(self._data, f, indent=4, cls=NumpyEncoder)

def _fill_keys(self) -> None:
"""Fill data with keys based on computer name, tasks, and selected version."""
Expand Down
2 changes: 1 addition & 1 deletion benchmark/src/models/ac_branin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def configspace(self) -> ConfigurationSpace:
x2 = Float("x2", (0, 15), default=7.5)

# Add hyperparameters and conditions to our configspace
cs.add_hyperparameters([x2])
cs.add([x2])

return cs

Expand Down
2 changes: 1 addition & 1 deletion benchmark/src/models/branin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def configspace(self) -> ConfigurationSpace:
x2 = Float("x2", (0, 15), default=0)

# Add hyperparameters and conditions to our configspace
cs.add_hyperparameters([x1, x2])
cs.add([x1, x2])

return cs

Expand Down
2 changes: 1 addition & 1 deletion benchmark/src/models/himmelblau.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def configspace(self) -> ConfigurationSpace:
y = Float("y", (-5, 5))

# Add hyperparameters and conditions to our configspace
cs.add_hyperparameters([x, y])
cs.add([x, y])

return cs

Expand Down
4 changes: 2 additions & 2 deletions benchmark/src/models/mlp.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def configspace(self) -> ConfigurationSpace:
learning_rate_init = Float("learning_rate_init", (0.0001, 1.0), default=0.001, log=True)

# Add all hyperparameters at once:
cs.add_hyperparameters([n_layer, n_neurons, activation, solver, batch_size, learning_rate, learning_rate_init])
cs.add([n_layer, n_neurons, activation, solver, batch_size, learning_rate, learning_rate_init])

# Adding conditions to restrict the hyperparameter space...
# ... since learning rate is used when solver is 'sgd'.
Expand All @@ -44,7 +44,7 @@ def configspace(self) -> ConfigurationSpace:
use_batch_size = InCondition(child=batch_size, parent=solver, values=["sgd", "adam"])

# We can also add multiple conditions on hyperparameters at once:
cs.add_conditions([use_lr, use_batch_size, use_lr_init])
cs.add([use_lr, use_batch_size, use_lr_init])

return cs

Expand Down
4 changes: 2 additions & 2 deletions benchmark/src/models/svm.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ def configspace(self) -> ConfigurationSpace:
use_gamma_value = InCondition(child=gamma_value, parent=gamma, values=["value"])

# Add hyperparameters and conditions to our configspace
cs.add_hyperparameters([kernel, C, shrinking, degree, coef, gamma, gamma_value])
cs.add_conditions([use_degree, use_coef, use_gamma, use_gamma_value])
cs.add([kernel, C, shrinking, degree, coef, gamma, gamma_value])
cs.add([use_degree, use_coef, use_gamma, use_gamma_value])

return cs

Expand Down
2 changes: 1 addition & 1 deletion examples/1_basics/1_quadratic_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class QuadraticFunction:
def configspace(self) -> ConfigurationSpace:
cs = ConfigurationSpace(seed=0)
x = Float("x", (-5, 5), default=-5)
cs.add_hyperparameters([x])
cs.add([x])

return cs

Expand Down
4 changes: 2 additions & 2 deletions examples/1_basics/2_svm_cv.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ def configspace(self) -> ConfigurationSpace:
use_gamma_value = InCondition(child=gamma_value, parent=gamma, values=["value"])

# Add hyperparameters and conditions to our configspace
cs.add_hyperparameters([kernel, C, shrinking, degree, coef, gamma, gamma_value])
cs.add_conditions([use_degree, use_coef, use_gamma, use_gamma_value])
cs.add([kernel, C, shrinking, degree, coef, gamma, gamma_value])
cs.add([use_degree, use_coef, use_gamma, use_gamma_value])

return cs

Expand Down
2 changes: 1 addition & 1 deletion examples/1_basics/3_ask_and_tell.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def configspace(self) -> ConfigurationSpace:
cs = ConfigurationSpace(seed=0)
x0 = Float("x0", (-5, 10), default=-3)
x1 = Float("x1", (-5, 10), default=-4)
cs.add_hyperparameters([x0, x1])
cs.add([x0, x1])

return cs

Expand Down
2 changes: 1 addition & 1 deletion examples/1_basics/4_callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def configspace(self) -> ConfigurationSpace:
cs = ConfigurationSpace(seed=0)
x0 = Float("x0", (-5, 10), default=-3)
x1 = Float("x1", (-5, 10), default=-4)
cs.add_hyperparameters([x0, x1])
cs.add([x0, x1])

return cs

Expand Down
2 changes: 1 addition & 1 deletion examples/1_basics/5_continue.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class QuadraticFunction:
def configspace(self) -> ConfigurationSpace:
cs = ConfigurationSpace(seed=0)
x = Float("x", (-5, 5), default=-5)
cs.add_hyperparameters([x])
cs.add([x])

return cs

Expand Down
6 changes: 3 additions & 3 deletions examples/1_basics/6_priors.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,13 @@ def configspace(self) -> ConfigurationSpace:
"learning_rate_init",
lower=1e-5,
upper=1.0,
mu=np.log(1e-3),
sigma=np.log(10),
mu=1e-3, # will be transformed to log space later
sigma=10, # will be transformed to log space later
log=True,
)

# Add all hyperparameters at once:
cs.add_hyperparameters([n_layer, n_neurons, activation, optimizer, batch_size, learning_rate_init])
cs.add([n_layer, n_neurons, activation, optimizer, batch_size, learning_rate_init])

return cs

Expand Down
2 changes: 1 addition & 1 deletion examples/1_basics/7_parallelization_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def configspace(self) -> ConfigurationSpace:
cs = ConfigurationSpace(seed=0)
x0 = Float("x0", (-5, 10), default=-5, log=False)
x1 = Float("x1", (0, 15), default=2, log=False)
cs.add_hyperparameters([x0, x1])
cs.add([x0, x1])

return cs

Expand Down
4 changes: 2 additions & 2 deletions examples/2_multi_fidelity/1_mlp_epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def configspace(self) -> ConfigurationSpace:
learning_rate_init = Float("learning_rate_init", (0.0001, 1.0), default=0.001, log=True)

# Add all hyperparameters at once:
cs.add_hyperparameters([n_layer, n_neurons, activation, solver, batch_size, learning_rate, learning_rate_init])
cs.add([n_layer, n_neurons, activation, solver, batch_size, learning_rate, learning_rate_init])

# Adding conditions to restrict the hyperparameter space...
# ... since learning rate is only used when solver is 'sgd'.
Expand All @@ -76,7 +76,7 @@ def configspace(self) -> ConfigurationSpace:
use_batch_size = InCondition(child=batch_size, parent=solver, values=["sgd", "adam"])

# We can also add multiple conditions on hyperparameters at once:
cs.add_conditions([use_lr, use_batch_size, use_lr_init])
cs.add([use_lr, use_batch_size, use_lr_init])

return cs

Expand Down
2 changes: 1 addition & 1 deletion examples/2_multi_fidelity/2_sgd_datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def configspace(self) -> ConfigurationSpace:
learning_rate = Categorical("learning_rate", ["constant", "invscaling", "adaptive"], default="constant")
eta0 = Float("eta0", (0.00001, 1), default=0.1, log=True)
# Add the parameters to configuration space
cs.add_hyperparameters([alpha, l1_ratio, learning_rate, eta0])
cs.add([alpha, l1_ratio, learning_rate, eta0])

return cs

Expand Down
2 changes: 1 addition & 1 deletion examples/2_multi_fidelity/3_specify_HB_via_total_budget.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class QuadraticFunction:
def configspace(self) -> ConfigurationSpace:
cs = ConfigurationSpace(seed=0)
x = Float("x", (-5, 5), default=-5)
cs.add_hyperparameters([x])
cs.add([x])

return cs

Expand Down
4 changes: 2 additions & 2 deletions examples/3_multi_objective/2_parego.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ def configspace(self) -> ConfigurationSpace:
learning_rate = Categorical("learning_rate", ["constant", "invscaling", "adaptive"], default="constant")
learning_rate_init = Float("learning_rate_init", (0.0001, 1.0), default=0.001, log=True)

cs.add_hyperparameters([n_layer, n_neurons, activation, solver, batch_size, learning_rate, learning_rate_init])
cs.add([n_layer, n_neurons, activation, solver, batch_size, learning_rate, learning_rate_init])

use_lr = EqualsCondition(child=learning_rate, parent=solver, value="sgd")
use_lr_init = InCondition(child=learning_rate_init, parent=solver, values=["sgd", "adam"])
use_batch_size = InCondition(child=batch_size, parent=solver, values=["sgd", "adam"])

# We can also add multiple conditions on hyperparameters at once:
cs.add_conditions([use_lr, use_batch_size, use_lr_init])
cs.add([use_lr, use_batch_size, use_lr_init])

return cs

Expand Down
2 changes: 1 addition & 1 deletion examples/4_advanced_optimizer/1_turbo_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
# cs = ConfigurationSpace(seed=0)
# x0 = Float("x0", (-5, 10), default=-3)
# x1 = Float("x1", (-5, 10), default=-4)
# cs.add_hyperparameters([x0, x1])
# cs.add([x0, x1])

# return cs

Expand Down
2 changes: 1 addition & 1 deletion examples/4_advanced_optimizer/2_boing_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
# cs = ConfigurationSpace(seed=0)
# x0 = Float("x0", (-5, 10), default=-3)
# x1 = Float("x1", (-5, 10), default=-4)
# cs.add_hyperparameters([x0, x1])
# cs.add([x0, x1])

# return cs

Expand Down
2 changes: 1 addition & 1 deletion examples/4_advanced_optimizer/3_metadata_callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def configspace(self) -> ConfigurationSpace:
cs = ConfigurationSpace(seed=0)
x0 = Float("x0", (-5, 10), default=-3)
x1 = Float("x1", (-5, 10), default=-4)
cs.add_hyperparameters([x0, x1])
cs.add([x0, x1])

return cs

Expand Down
27 changes: 13 additions & 14 deletions examples/4_advanced_optimizer/4_intensify_crossvalidation.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,17 @@ def configspace(self) -> ConfigurationSpace:
cs = ConfigurationSpace(seed=0)

# First we create our hyperparameters
C = Float("C", (2 ** - 5, 2 ** 15), default=1.0, log=True)
gamma = Float("gamma", (2 ** -15, 2 ** 3), default=1.0, log=True)
C = Float("C", (2**-5, 2**15), default=1.0, log=True)
gamma = Float("gamma", (2**-15, 2**3), default=1.0, log=True)

# Add hyperparameters to our configspace
cs.add_hyperparameters([C, gamma])
cs.add([C, gamma])

return cs

def train(self, config: Configuration, instance: str, seed: int = 0) -> float:
"""Creates a SVM based on a configuration and evaluate on the given fold of the digits dataset
Parameters
----------
config: Configuration
Expand Down Expand Up @@ -81,15 +81,14 @@ def train(self, config: Configuration, instance: str, seed: int = 0) -> float:
scenario = Scenario(
classifier.configspace,
n_trials=50, # We want to run max 50 trials (combination of config and instances in the case of
# deterministic=True. In the case of deterministic=False, this would be the
# combination of instances, seeds and configs). The number of distinct configurations
# evaluated by SMAC will be lower than this number because some of the configurations
# will be executed on more than one instance (CV fold).
# deterministic=True. In the case of deterministic=False, this would be the
# combination of instances, seeds and configs). The number of distinct configurations
# evaluated by SMAC will be lower than this number because some of the configurations
# will be executed on more than one instance (CV fold).
instances=[f"{i}" for i in range(N_FOLDS)], # Specify all instances by their name (as a string)
instance_features={f"{i}": [i] for i in range(N_FOLDS)}, # breaks SMAC
instance_features={f"{i}": [i] for i in range(N_FOLDS)}, # breaks SMAC
deterministic=True # To simplify the problem we make SMAC believe that we have a deterministic
# optimization problem.

# optimization problem.
)

# We want to run the facade's default initial design, but we want to change the number
Expand All @@ -102,12 +101,12 @@ def train(self, config: Configuration, instance: str, seed: int = 0) -> float:
classifier.train,
initial_design=initial_design,
overwrite=True, # If the run exists, we overwrite it; alternatively, we can continue from last state
# The next line defines the intensifier, i.e., the module that governs the selection of
# The next line defines the intensifier, i.e., the module that governs the selection of
# instance-seed pairs. Since we set deterministic to True above, it only governs the instance in
# this example. Technically, it is not necessary to create the intensifier as a user, but it is
# necessary to do so because we change the argument max_config_calls (the number of instance-seed pairs
# per configuration to try) to the number of cross-validation folds, while the default would be 3.
intensifier=Intensifier(scenario=scenario, max_config_calls=N_FOLDS, seed=0)
intensifier=Intensifier(scenario=scenario, max_config_calls=N_FOLDS, seed=0),
)

incumbent = smac.optimize()
Expand All @@ -124,4 +123,4 @@ def train(self, config: Configuration, instance: str, seed: int = 0) -> float:
# at more configurations than would have been possible with regular cross-validation, where the number
# of configurations would be determined by the number of trials divided by the number of folds (50 / 10).
runhistory = smac.runhistory
print(f"Number of evaluated configurations: {len(runhistory.config_ids)}")
print(f"Number of evaluated configurations: {len(runhistory.config_ids)}")
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def read_file(filepath: str) -> str:
"scipy>=1.9.2",
"psutil",
"pynisher>=1.0.0",
"ConfigSpace>=0.6.1,<1.0.0",
"ConfigSpace>=1.0.0",
"joblib",
"scikit-learn>=1.1.2",
"pyrfr>=0.9.0",
Expand Down
1 change: 0 additions & 1 deletion smac/acquisition/maximizer/local_and_random_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ def _maximize(
previous_configs: list[Configuration],
n_points: int,
) -> list[tuple[float, Configuration]]:

if self._uniform_configspace is not None and self._prior_sampling_fraction is not None:
# Get configurations sorted by acquisition function value
next_configs_by_prior_random_search_sorted = self._prior_random_search._maximize(
Expand Down
2 changes: 1 addition & 1 deletion smac/acquisition/maximizer/local_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ def _search(
if acq_val[acq_index] > acq_val_candidates[i]:
is_valid = False
try:
neighbors[acq_index].is_valid_configuration()
neighbors[acq_index].check_valid_configuration()
is_valid = True
except (ValueError, ForbiddenValueError) as e:
logger.debug("Local search %d: %s", i, e)
Expand Down
3 changes: 2 additions & 1 deletion smac/callback/metadata_callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import smac
from smac.callback.callback import Callback
from smac.main.smbo import SMBO
from smac.utils.numpyencoder import NumpyEncoder

__copyright__ = "Copyright 2023, AutoML.org Freiburg-Hannover"
__license__ = "3-clause BSD"
Expand All @@ -31,4 +32,4 @@ def on_start(self, smbo: SMBO) -> None:
path.mkdir(parents=True, exist_ok=True)

with open(path / "metadata.json", "w") as fp:
json.dump(meta_dict, fp, indent=2)
json.dump(meta_dict, fp, indent=2, cls=NumpyEncoder)
1 change: 0 additions & 1 deletion smac/initial_design/abstract_initial_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ def _transform_continuous_designs(
"""
params = configspace.get_hyperparameters()
for idx, param in enumerate(params):

if isinstance(param, IntegerHyperparameter):
design[:, idx] = param._inverse_transform(param._transform(design[:, idx]))
elif isinstance(param, NumericalHyperparameter):
Expand Down
3 changes: 2 additions & 1 deletion smac/intensifier/abstract_intensifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from smac.scenario import Scenario
from smac.utils.configspace import get_config_hash, print_config_changes
from smac.utils.logging import get_logger
from smac.utils.numpyencoder import NumpyEncoder
from smac.utils.pareto_front import calculate_pareto_front, sort_by_crowding_distance

__copyright__ = "Copyright 2022, automl.org"
Expand Down Expand Up @@ -666,7 +667,7 @@ def save(self, filename: str | Path) -> None:
}

with open(filename, "w") as fp:
json.dump(data, fp, indent=2)
json.dump(data, fp, indent=2, cls=NumpyEncoder)

def load(self, filename: str | Path) -> None:
"""Loads the latest state of the intensifier including the incumbents and trajectory."""
Expand Down
Loading

0 comments on commit 03a2701

Please sign in to comment.