BOHB Search
foretools/bohb provides budgeted black-box optimization built on Hyperband-style successive halving and a multi-fidelity TPE proposer. Serial runs use synchronous successive halving; parallel_jobs > 1 uses ASHA.
The default sampler (sampler="mftpe") is a vectorized multivariate TPE. It is fit on the largest budget with enough results, and batch proposals are spread with a constant liar. The original per-parameter TPE, with trust regions, qNEI/Thompson batching and a GP surrogate, is still available as sampler="legacy" and is configured through TPEConf.
Use it when you want to tune hyperparameters, compare search strategies, or benchmark objective functions outside the differentiable architecture-search flow covered by darts.
Import surface
from foretools.bohb import BOHB, PruningConfig, TPEConf
from foretools.bohb.plotter import OptimizationPlotter
from foretools.bohb.trial import TrialPrunedConfig space format
The search space is a dictionary of parameter names to tuple specs.
Supported parameter types:
| Type | Example | Meaning |
|---|---|---|
float | ("float", (0.0, 1.0)) | continuous uniform range |
float with log scaling | ("float", (1e-5, 1e-1, "log")) | continuous log-uniform style range |
int | ("int", (16, 256)) | integer range |
choice | ("choice", ["adam", "adamw", "sgd"]) | categorical choice |
Any entry can take a third options element to make it conditional, for example ("float", (0.0, 0.99), {"condition": {"parent": "opt", "values": ["sgd"]}}).
Sampler options
bohb = BOHB(
config_space=config_space,
evaluate_fn=objective,
sampler_options={"n_ei_candidates": 64, "random_fraction": 0.1, "bandwidth_factor": 1.5},
)Objective function signatures
BOHB accepts either of these objective signatures:
objective(config, budget) -> floatobjective(config, budget, trial) -> float
Use the three-argument form when you want intermediate reporting and early pruning.
Example with Trial.report()
from foretools.bohb.trial import TrialPruned
def objective(config, budget, trial):
loss = 1.0
for epoch in range(int(budget)):
loss *= 0.85
trial.report(epoch, loss)
return float(loss)
try:
best_config, best_loss = BOHB(
config_space={"width": ("int", (32, 256))},
evaluate_fn=objective,
min_budget=1,
max_budget=9,
).run()
except TrialPruned:
passBOHB catches TrialPruned itself, so the try block is optional. A pruned trial's last reported loss is still passed to the sampler.
With sampler="legacy", tpe_overrides={...} is lighter than constructing a full TPEConf.
Pruning configuration
PruningConfig exposes the pruning thresholds that BOHB uses for both completed evaluations and intermediate Trial.report() calls. A completed evaluation that is "pruned" is still recorded; it is only kept from being promoted to the next budget.
from foretools.bohb import BOHB, PruningConfig
pruning = PruningConfig(
final_min_history=12,
final_prob_base_balanced=0.55,
step_min_history=10,
step_progress_tolerance=0.10,
step_quantile_balanced=0.95,
)
bohb = BOHB(
config_space=config_space,
evaluate_fn=objective,
pruning_conf=pruning,
)Inspecting results
The BOHB instance keeps a full optimization history plus ranked configurations.
history = bohb.get_optimization_history()
top_configs = bohb.get_top_configs(5)Plotting utilities
OptimizationPlotter builds lightweight analysis charts directly from a BOHB run.
from foretools.bohb.plotter import OptimizationPlotter
plotter = OptimizationPlotter.from_bohb(bohb)
plotter.plot_optimization_history()
plotter.plot_budget_vs_loss()
plotter.plot_bracket_best()
plotter.plot_param_effect("lr")
plotter.plot_param_importance(top_k=5)
plotter.plot_parallel_coordinates()