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 = f"sqlite:///{study_name}.db"
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: 15.733417710492468 and parameters: {'x': 5.966537244309256}. Best is trial 0 with value: 15.733417710492468.
Trial 1 finished with value: 5.516993743326328 and parameters: {'x': 4.348828163856677}. Best is trial 1 with value: 5.516993743326328.
Trial 2 finished with value: 6.77269574701392 and parameters: {'x': -0.6024403445639095}. Best is trial 1 with value: 5.516993743326328.

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: 0.1514362496771542 and parameters: {'x': 2.3891481076366095}. Best is trial 3 with value: 0.1514362496771542.
Trial 4 finished with value: 16.10321817181318 and parameters: {'x': 6.012881529750558}. Best is trial 3 with value: 0.1514362496771542.
Trial 5 finished with value: 0.09148382277574596 and parameters: {'x': 2.3024629279362117}. Best is trial 5 with value: 0.09148382277574596.

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  15.733418  5.966537  COMPLETE
1       1   5.516994  4.348828  COMPLETE
2       2   6.772696 -0.602440  COMPLETE
3       3   0.151436  2.389148  COMPLETE
4       4  16.103218  6.012882  COMPLETE
5       5   0.091484  2.302463  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': 2.3024629279362117}
Best value:  0.09148382277574596
Best Trial:  FrozenTrial(number=5, state=<TrialState.COMPLETE: 1>, values=[0.09148382277574596], datetime_start=datetime.datetime(2026, 3, 16, 5, 14, 45, 108930), datetime_complete=datetime.datetime(2026, 3, 16, 5, 14, 45, 126852), params={'x': 2.3024629279362117}, user_attrs={}, system_attrs={}, intermediate_values={}, distributions={'x': FloatDistribution(high=10.0, log=False, low=-10.0, step=None)}, trial_id=6, value=None)
Trials:  [FrozenTrial(number=0, state=<TrialState.COMPLETE: 1>, values=[15.733417710492468], datetime_start=datetime.datetime(2026, 3, 16, 5, 14, 44, 910004), datetime_complete=datetime.datetime(2026, 3, 16, 5, 14, 44, 939468), params={'x': 5.966537244309256}, 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=<TrialState.COMPLETE: 1>, values=[5.516993743326328], datetime_start=datetime.datetime(2026, 3, 16, 5, 14, 44, 953909), datetime_complete=datetime.datetime(2026, 3, 16, 5, 14, 44, 972471), params={'x': 4.348828163856677}, 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=<TrialState.COMPLETE: 1>, values=[6.77269574701392], datetime_start=datetime.datetime(2026, 3, 16, 5, 14, 44, 984064), datetime_complete=datetime.datetime(2026, 3, 16, 5, 14, 45, 2165), params={'x': -0.6024403445639095}, 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=<TrialState.COMPLETE: 1>, values=[0.1514362496771542], datetime_start=datetime.datetime(2026, 3, 16, 5, 14, 45, 42185), datetime_complete=datetime.datetime(2026, 3, 16, 5, 14, 45, 66006), params={'x': 2.3891481076366095}, 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=<TrialState.COMPLETE: 1>, values=[16.10321817181318], datetime_start=datetime.datetime(2026, 3, 16, 5, 14, 45, 80458), datetime_complete=datetime.datetime(2026, 3, 16, 5, 14, 45, 98468), params={'x': 6.012881529750558}, 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=<TrialState.COMPLETE: 1>, values=[0.09148382277574596], datetime_start=datetime.datetime(2026, 3, 16, 5, 14, 45, 108930), datetime_complete=datetime.datetime(2026, 3, 16, 5, 14, 45, 126852), params={'x': 2.3024629279362117}, 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.923 seconds)

Gallery generated by Sphinx-Gallery