This guide is the opinionated "how to actually run a tuning job" companion to the
kit. It assumes you can already train a model and measure a validation metric;
the question is how to search the configuration space efficiently and trust the
result. Every code reference maps to a real symbol in src/hpo/.
define metric -> define search space -> pick sampler + pruner/scheduler
-> set a budget -> run study -> inspect best + importance
-> widen/narrow space -> repeat until improvements plateau
Tuning is iterative. The first run is a reconnaissance pass with wide ranges; the
second run narrows toward the promising region. Two or three short rounds almost
always beat one giant run, because you spend later budget where it matters.
Before sampling a single configuration, decide:
accuracy, AUC, RMSE), not a proxy. The kit ships common scorers in
hpo.objective (accuracy, mse, rmse, mae, r2, neg_mse); pass any
callable (y_true, y_pred) -> float for anything custom.
"maximize" for scores, "minimize" for losses. Keep itconsistent across the space, the pruner, and the study. The neg_* scorers
exist so a loss can be maximized uniformly when that is more convenient.
and easy to overfit through hundreds of trials. Prefer K-fold
cross-validation. hpo.objective.Objective wraps K-fold CV for you and returns
the mean fold score.
Pitfall — tuning on the test set. Never let the test set influence the
search. Tune against CV folds or a dedicated validation split, and evaluate the
single chosen configuration on the held-out test set exactly once.
The sampler decides *which configuration to try next*. The kit exposes the Optuna
samplers through StudyConfig(sampler=...):
| Sampler | Best when | Notes |
|---|---|---|
random | Baseline; high-dimensional or highly parallel runs | Embarrassingly parallel; surprisingly strong. Always run it first. |
tpe | The default for most problems (10–500 trials) | Bayesian; models good vs bad regions. Set n_startup_trials to seed it with random trials. |
cmaes | Continuous spaces, smooth landscapes, larger budgets | Strong on numeric-only spaces; weak with many categoricals. |
qmc | Space-filling exploration | Low-discrepancy (Sobol); great for the first exploratory round. |
grid | Small, discrete spaces where you want exhaustive coverage | Provide the grid via sampler_kwargs["search_space"]. |
Rule of thumb: start with random (or qmc) for one round to map the
terrain, then switch to tpe to exploit it. If a smart sampler cannot beat random
search by a meaningful margin, your space or metric is the problem, not the
sampler. The bundled examples/random_search_baseline.py gives you that baseline
with zero dependencies.
Most configurations are mediocre and reveal themselves early. Pruning stops their
training once the intermediate learning curve makes the outcome obvious, which can
multiply throughput several-fold. Pruning needs two things:
1. an objective that reports intermediate values (per epoch, per boosting
round, or — as in hpo.objective.Objective — per CV fold); and
2. a pruner that compares the running trial against completed ones.
The kit implements the decision logic in pure Python so you can reason about it:
MedianPruner — prune a trial whose best-so-far value is worse than themedian of completed trials at the same step. The sensible default.
PercentilePruner(percentile=p) — keep the best p%, prune the rest.MedianPruner is exactly PercentilePruner(50). Lower p is more aggressive.
ThresholdPruner(lower=..., upper=...) — a guard rail: abort trials thatcross an absolute bound (e.g. a diverged loss), independent of other trials.
EarlyStopping(patience=...) — classic in-loop stopping for a single trial;no cross-trial baseline required.
Two parameters control pruning aggressiveness:
n_startup_trials — do not prune until this many trials have *completed*, so thebaseline distribution is meaningful. Too low and you prune against noise.
n_warmup_steps — do not prune any trial before this step, so slow-starting butultimately-strong configurations are not killed prematurely. Models with warm-up
schedules or heavy regularization need a larger value here.
Convert any of these to the real Optuna pruner with to_optuna_pruner(...), or
just hand the kit pruner to StudyConfig(pruner=...) and run_study does it.
Ray Tune reframes pruning as *scheduling* across many parallel trials. The kit's
TuneConfig(scheduler=...) selects:
asha) — Async Successive Halving. Runs many trials for a shortgrace_period, then promotes the top 1/reduction_factor to train longer,
repeating up to max_t. The workhorse for parallel HPO; the Ray analogue of
median/percentile pruning.
pbt) — Population Based Training. Maintains a population, periodicallycopying weights from strong trials and perturbing their hyperparameters. Powerful
for schedules that should *change during training* (e.g. learning rate). Requires
scheduler_kwargs["hyperparam_mutations"].
median) — the simplest comparative rule, a good first try.grace_period, reduction_factor, and max_t trade exploration for speed:
larger grace_period is safer but slower; larger reduction_factor is faster but
riskier. See examples/ray_tune_torch.py for a complete ASHA run.
inspect convergence. The optimization_history helper returns the running best,
which is the convergence curve — if it has been flat for many trials, stop.
random/qmc/ASHA parallelize cleanly; tpe is partlysequential (later trials depend on earlier results) but still benefits from
modest parallelism.
study_name=..., load_if_exists=True)`. You can stop and resume, or point several
worker processes at the same database to fan trials out. examples/optuna_sklearn.py
does exactly this — re-run it and it continues the previous study.
When you care about more than one metric (accuracy *and* latency, recall *and*
precision), there is no single "best" — there is a Pareto front of
non-dominated trade-offs. To run it:
my_latency_scorer]) with directions=["maximize", "minimize"]` on the study;
study.best_trials (the front);pareto_front(records, directions) recomputes the front from backend-agnosticrecords, and you pick the trade-off that fits your product constraints.
Do not collapse multiple objectives into a hand-weighted sum unless the weights are
genuinely known — the weighting hides the trade-off you are trying to see.
After a study finishes, from_optuna_study(study) gives you TrialRecords that
feed every analysis helper:
best_trial / top_k — the winners and the runners-up (inspect the top few:if they cluster, you have found a stable region).
param_importance — a fast variance-reduction proxy telling you which knobsmoved the metric. Use it to redesign the next round: widen important params,
freeze unimportant ones at a good value to cut dimensionality.
optimization_history — the convergence curve.export_csv / export_json — dump everything for a notebook or a report.range — widen it and re-run. See search-space-design.md.
are pruned, raise n_startup_trials and n_warmup_steps.
often larger than the differences you are chasing.
configuration that is lucky, not good. Cross-validate and keep an untouched test
set.
seed on the sampler and CV so results are reproducibleand comparable across rounds.
The short version: baseline with random search, exploit with TPE/ASHA, prune the
losers, cross-validate, inspect importance, narrow the space, repeat.
Get the full Hyperparameter Tuning Kit and unlock everything.
Get the complete guide with every chapter unlocked, including code samples, diagrams, and best practices.
Access all interactive tools with complete data, all workload profiles, and the full scenario library.
Downloadable source code, configuration files, and working examples from every chapter.
Free updates for life. Every new chapter, tool, and improvement included.