Note
Go to the end to download the full example code.
Saving/Resuming Study with RDB Backend
An RDB backend enables persistent experiments (i.e., to save and resume a study) as well as access to history of studies. In addition, we can run multi-node optimization tasks with this feature, which is described in Easy Parallelization.
In this section, let’s try simple examples running on a local environment with SQLite DB.
Note
You can also utilize other RDB backends, e.g., PostgreSQL or MySQL, by setting the storage argument to the DB’s URL. Please refer to SQLAlchemy’s document for how to set up the URL.
New Study
We can create a persistent study by calling create_study()
function as follows.
An SQLite file example.db
is automatically initialized with a new study record.
import logging
import sys
import optuna
# Add stream handler of stdout to show the messages
optuna.logging.get_logger("optuna").addHandler(logging.StreamHandler(sys.stdout))
study_name = "example-study" # Unique identifier of the study.
storage_name = "sqlite:///{}.db".format(study_name)
study = optuna.create_study(study_name=study_name, storage=storage_name)
A new study created in RDB with name: example-study
To run a study, call optimize()
method passing an objective function.
def objective(trial):
x = trial.suggest_float("x", -10, 10)
return (x - 2) ** 2
study.optimize(objective, n_trials=3)
Trial 0 finished with value: 12.906754820947363 and parameters: {'x': -1.5925972249818603}. Best is trial 0 with value: 12.906754820947363.
Trial 1 finished with value: 22.5398426325297 and parameters: {'x': -2.747614414896149}. Best is trial 0 with value: 12.906754820947363.
Trial 2 finished with value: 69.19636762064775 and parameters: {'x': -6.318435407013013}. Best is trial 0 with value: 12.906754820947363.
Resume Study
To resume a study, instantiate a Study
object
passing the study name example-study
and the DB URL sqlite:///example-study.db
.
study = optuna.create_study(study_name=study_name, storage=storage_name, load_if_exists=True)
study.optimize(objective, n_trials=3)
Using an existing study with name 'example-study' instead of creating a new one.
Trial 3 finished with value: 2.7215936816078656 and parameters: {'x': 3.649725335202156}. Best is trial 3 with value: 2.7215936816078656.
Trial 4 finished with value: 104.98362958141102 and parameters: {'x': -8.246151940187644}. Best is trial 3 with value: 2.7215936816078656.
Trial 5 finished with value: 21.506019294151855 and parameters: {'x': 6.637458279505257}. Best is trial 3 with value: 2.7215936816078656.
Note that the storage doesn’t store the state of the instance of samplers
and pruners
.
When we resume a study with a sampler whose seed
argument is specified for
reproducibility, you need to restore the sampler with using pickle
as follows:
import pickle
# Save the sampler with pickle to be loaded later.
with open("sampler.pkl", "wb") as fout:
pickle.dump(study.sampler, fout)
restored_sampler = pickle.load(open("sampler.pkl", "rb"))
study = optuna.create_study(
study_name=study_name, storage=storage_name, load_if_exists=True, sampler=restored_sampler
)
study.optimize(objective, n_trials=3)
Experimental History
Note that this section requires the installation of Pandas:
$ pip install pandas
We can access histories of studies and trials via the Study
class.
For example, we can get all trials of example-study
as:
study = optuna.create_study(study_name=study_name, storage=storage_name, load_if_exists=True)
df = study.trials_dataframe(attrs=("number", "value", "params", "state"))
Using an existing study with name 'example-study' instead of creating a new one.
The method trials_dataframe()
returns a pandas dataframe like:
print(df)
number value params_x state
0 0 12.906755 -1.592597 COMPLETE
1 1 22.539843 -2.747614 COMPLETE
2 2 69.196368 -6.318435 COMPLETE
3 3 2.721594 3.649725 COMPLETE
4 4 104.983630 -8.246152 COMPLETE
5 5 21.506019 6.637458 COMPLETE
A Study
object also provides properties
such as trials
, best_value
,
best_params
(see also Lightweight, versatile, and platform agnostic architecture).
print("Best params: ", study.best_params)
print("Best value: ", study.best_value)
print("Best Trial: ", study.best_trial)
print("Trials: ", study.trials)
Best params: {'x': 3.649725335202156}
Best value: 2.7215936816078656
Best Trial: FrozenTrial(number=3, state=1, values=[2.7215936816078656], datetime_start=datetime.datetime(2025, 3, 17, 9, 59, 56, 42723), datetime_complete=datetime.datetime(2025, 3, 17, 9, 59, 56, 69470), params={'x': 3.649725335202156}, user_attrs={}, system_attrs={}, intermediate_values={}, distributions={'x': FloatDistribution(high=10.0, log=False, low=-10.0, step=None)}, trial_id=4, value=None)
Trials: [FrozenTrial(number=0, state=1, values=[12.906754820947363], datetime_start=datetime.datetime(2025, 3, 17, 9, 59, 55, 883873), datetime_complete=datetime.datetime(2025, 3, 17, 9, 59, 55, 919001), params={'x': -1.5925972249818603}, user_attrs={}, system_attrs={}, intermediate_values={}, distributions={'x': FloatDistribution(high=10.0, log=False, low=-10.0, step=None)}, trial_id=1, value=None), FrozenTrial(number=1, state=1, values=[22.5398426325297], datetime_start=datetime.datetime(2025, 3, 17, 9, 59, 55, 938851), datetime_complete=datetime.datetime(2025, 3, 17, 9, 59, 55, 959796), params={'x': -2.747614414896149}, user_attrs={}, system_attrs={}, intermediate_values={}, distributions={'x': FloatDistribution(high=10.0, log=False, low=-10.0, step=None)}, trial_id=2, value=None), FrozenTrial(number=2, state=1, values=[69.19636762064775], datetime_start=datetime.datetime(2025, 3, 17, 9, 59, 55, 975678), datetime_complete=datetime.datetime(2025, 3, 17, 9, 59, 55, 995949), params={'x': -6.318435407013013}, user_attrs={}, system_attrs={}, intermediate_values={}, distributions={'x': FloatDistribution(high=10.0, log=False, low=-10.0, step=None)}, trial_id=3, value=None), FrozenTrial(number=3, state=1, values=[2.7215936816078656], datetime_start=datetime.datetime(2025, 3, 17, 9, 59, 56, 42723), datetime_complete=datetime.datetime(2025, 3, 17, 9, 59, 56, 69470), params={'x': 3.649725335202156}, user_attrs={}, system_attrs={}, intermediate_values={}, distributions={'x': FloatDistribution(high=10.0, log=False, low=-10.0, step=None)}, trial_id=4, value=None), FrozenTrial(number=4, state=1, values=[104.98362958141102], datetime_start=datetime.datetime(2025, 3, 17, 9, 59, 56, 87070), datetime_complete=datetime.datetime(2025, 3, 17, 9, 59, 56, 107173), params={'x': -8.246151940187644}, user_attrs={}, system_attrs={}, intermediate_values={}, distributions={'x': FloatDistribution(high=10.0, log=False, low=-10.0, step=None)}, trial_id=5, value=None), FrozenTrial(number=5, state=1, values=[21.506019294151855], datetime_start=datetime.datetime(2025, 3, 17, 9, 59, 56, 122531), datetime_complete=datetime.datetime(2025, 3, 17, 9, 59, 56, 142570), params={'x': 6.637458279505257}, user_attrs={}, system_attrs={}, intermediate_values={}, distributions={'x': FloatDistribution(high=10.0, log=False, low=-10.0, step=None)}, trial_id=6, value=None)]
Total running time of the script: (0 minutes 0.996 seconds)