This is the release note of v2.5.0.
Highlights
Ask-and-Tell
The ask-and-tell interface is a new complement to Study.optimize
. It allows users to construct Trial
instances without the need of an objective function callback, giving more flexibility in how to define search spaces, ask for suggested hyperparameters and how to evaluate objective functions. The interface is made out of two methods, Study.ask
and Study.tell
.
Study.ask
returns a newTrial
object.Study.tell
takes either aTrial
object or a trial number along with the result of that trial, i.e. a value and/or the state, and saves it. SinceStudy.tell
accepts a trial number, a trial object can be disposed after parameters have been suggested. This allows objective function evaluations on a different thread or process.
import optuna
from optuna.trial import TrialState
study = optuna.create_study()
# Use a Python for-loop to iteratively optimize the study.
for _ in range(100):
trial = study.ask() # `trial` is a `Trial` and not a `FrozenTrial`.
# Objective function, in this case not as a function but at global scope.
x = trial.suggest_float("x", -1, 1)
y = x ** 2
study.tell(trial, y)
# Or, tell by trial number. This is equivalent to `study.tell(trial, y)`.
# study.tell(trial.number, y)
# Or, prune if the trial seems unpromising.
# study.tell(trial, state=TrialState.PRUNED)
assert len(study.trials) == 100
Heartbeat
Now, Optuna supports monitoring trial heartbeats with RDB storages. For example, if a process running a trial is killed by a scheduler in a cluster environment, Optuna will automatically change the state of the trial that was running on that process to TrialState.FAIL
from TrialState.RUNNING
.
# Consider running this script on several processes.
import optuna
def objective(trial):
(Very time-consuming computation)
# Recording heartbeats every 60 seconds.
# Other processes' trials where more than 120 seconds have passed
# since the last heartbeat was recorded will be automatically failed.
storage = optuna.storages.RDBStorage(url=..., heartbeat_interval=60, grace_period=120)
study = optuna.create_study(storage=storage)
study.optimize(objective, n_trials=100)
Constrained NSGA-II
NSGA-II experimentally supports constrained optimization. Users can introduce constraints with the new constraints_func
argument of NSGAIISampler.__init__
.
The following is an example using this argument, a bi-objective version of the knapsack problem. We have 100 pairs of items and two knapsacks, and would like to maximize the profits of items within the weight limitation.
import numpy as np
import optuna
# Define bi-objective knapsack problem.
n_items = 100
n_knapsacks = 2
feasible_rate = 0.5
seed = 1
rng = np.random.RandomState(seed=seed)
weights = rng.randint(10, 101, size=(n_knapsacks, n_items))
profits = rng.randint(10, 101, size=(n_knapsacks, n_items))
constraints = (np.sum(weights, axis=1) * feasible_rate).astype(np.int)
def objective(trial):
xs = np.array([trial.suggest_categorical(f"x_{i}", (0, 1)) for i in range(weights.shape[1])])
total_weights = np.sum(weights * xs, axis=1)
total_profits = np.sum(profits * xs, axis=1)
# Constraints which are considered feasible if less than or equal to zero.
constraints_violation = total_weights - constraints
trial.set_user_attr("constraint", constraints_violation.tolist())
return total_profits.tolist()
def constraints_func(trial):
return trial.user_attrs["constraint"]
sampler = optuna.samplers.NSGAIISampler(population_size=10, constraints_func=constraints_func)
study = optuna.create_study(directions=["maximize"] * n_knapsacks, sampler=sampler)
study.optimize(objective, n_trials=200)
New Features
- Ask-and-Tell API (
Study.ask
,Study.tell
) (#2158) - Add
constraints_func
argument to NSGA-II (#2175) - Add heartbeat functionality using
threading
(#2190) - Add
Study.add_trials
to simplify creating customized study (#2261)
Enhancements
- Support log scale in parallel coordinate (#2164, thanks @tohmae!)
- Warn if constraints are missing in constrained NSGA-II (#2205)
- Immediately persist suggested parameters with
_CachedStorage
(#2214) - Include the upper bound of uniform/loguniform distributions (#2223)
Bug Fixes
- Call base sampler's
after_trial
inPartialFixedSampler
(#2209) - Fix
trials_dataframe
for multi-objective optimization with fail or pruned trials (#2265) - Fix
calculate_weights_below
method ofMOTPESampler
(#2274, thanks @y0z!)
Installation
- Remove version constraint for AllenNLP and run
allennlp_*.py
on GitHub Actions (#2226) - Pin
mypy==0.790
(#2259) - Temporarily avoid AllenNLP v2 (#2276)
Documentation
- Add
callback
&(add|enqueue)_trial
recipe (#2125) - Make
create_trial
's documentation and tests richer (#2126) - Move import lines of the callback recipe (A follow-up of #2125) (#2221)
- Fix
optuna/samplers/_base.py
typo (#2239) - Introduce optuna-dashboard on README (#2224)
Examples
- Refactor
examples/multi_objective/pytorch_simple.py
(#2230) - Move BoTorch example to
examples/multi_objective
directory (#2244) - Refactor
examples/multi_objective/botorch_simple.py
(#2245, thanks @nzw0301!) - Fix typo in
examples/mlflow
(#2258, thanks @nzw0301!)
Tests
- Make
create_trial
's documentation and tests richer (#2126) - Fix unit test of median pruner (#2171)
SkoptSampler
acquisition function in test to more likely converge (#2194)- Diet
tests/test_study.py
(#2218) - Diet
tests/test_trial.py
(#2219) - Shorten the names of trial tests (#2228)
- Move
STORAGE_MODES
totesting/storage.py
(#2231) - Remove duplicate test on
study_tests/test_optimize.py
(#2232) - Add init files in test directories (#2257)
Code Fixes
- Code quality improvements (#2009, thanks @srijan-deepsource!)
- Refactor CMA-ES sampler with search space transform (#2193)
BoTorchSampler
minor code fix reducing dictionary lookup and clearer type behavior (#2195)- Fix bad warning message in
BoTorchSampler
(#2197) - Use
study.get_trials
instead ofstudy._storage.get_all_trials
(#2208) - Ensure uniform and loguniform distributions less than high boundary (#2243)
Continuous Integration
- Add
RDBStorage
tests in github actions (#2200) - Publish distributions to TestPyPI on each day (#2220)
- Rename GitHub Actions jobs for (Test)PyPI uploads (#2254)
Other
- Fix the syntax of
pypi-publish.yml
(#2187) - Fix
mypy
local fails intests/test_deprecated.py
andtests/test_experimental.py
(#2191) - Add an explanation of "no period in the PR title" to
CONTRIBUTING.md
(#2192) - Bump up version number to 2.5.0.dev (#2238)
- Fix mypy version to 0.790 (Follow-up of 2259) (#2260)
- Bump up version number to v2.5.0 (#2282)
Thanks to All the Contributors!
This release was made possible by authors, and everyone who participated in reviews and discussions.
@Crissman, @HideakiImamura, @c-bata, @crcrpar, @g-votte, @himkt, @hvy, @keisuke-umezawa, @not522, @nzw0301, @sile, @srijan-deepsource, @tohmae, @toshihikoyanase, @y0z, @ytsmiling