Persistent storage and resuming

Optuna · Practical · 10 min

Long studies crash, notebooks restart and teams want to look at results; persistence makes studies durable.

By default a study lives in memory and disappears with the process. Pass a storage to persist it:

study = optuna.create_study(study_name="rf-tuning", storage="sqlite:///optuna.db",
                            direction="maximize", load_if_exists=True)
  • study_name identifies the study inside the storage.
  • load_if_exists=True resumes an existing study instead of failing, so the same script can be re-run to add more trials.
  • optuna.load_study(study_name=..., storage=...) opens a study for analysis elsewhere.

Storage options:

  • RDB URLs (SQLite, PostgreSQL, MySQL) via SQLAlchemy. SQLite is fine for one machine; use PostgreSQL or MySQL for many concurrent workers.
  • JournalStorage with JournalFileBackend("journal.log"): an append-only log file, simple and robust for multiple processes on one machine or a shared filesystem, with no database server.
  • JournalRedisBackend for Redis.

Utilities: optuna.get_all_study_names(storage), optuna.delete_study, optuna.copy_study, and the optuna CLI (optuna studies --storage ...).

optuna-dashboard (pip install optuna-dashboard, then optuna-dashboard sqlite:///optuna.db) gives a live web UI with history, importances and parameter plots.

Trials interrupted by a crash stay RUNNING; enable heartbeats (RDBStorage(url, heartbeat_interval=60)) so they are marked failed and optionally retried.

Example

import optuna

optuna.logging.set_verbosity(optuna.logging.WARNING)

def objective(trial):
    x = trial.suggest_float("x", -10, 10)
    return (x - 3) ** 2

storage = "sqlite:///optuna_demo.db"
for session in range(2):                  # e.g. two separate script runs
    study = optuna.create_study(study_name="quadratic", storage=storage,
                                load_if_exists=True)
    study.optimize(objective, n_trials=10)
    print(f"session {session}: {len(study.trials)} trials")
# session 0: 10 trials
# session 1: 20 trials   (resumed)

loaded = optuna.load_study(study_name="quadratic", storage=storage)
print(round(loaded.best_params["x"], 1))  # near 3.0
optuna.delete_study(study_name="quadratic", storage=storage)

from optuna.storages import JournalStorage
from optuna.storages.journal import JournalFileBackend
journal = JournalStorage(JournalFileBackend("optuna_journal.log"))

Remember

  • Pass storage= and study_name= to persist a study
  • load_if_exists=True lets the same script resume
  • SQLite or journal files for one machine; PostgreSQL/MySQL for many workers

Watch out for

  • Pointing many parallel workers at one SQLite file, which leads to lock contention and errors.

Check yourself

  1. You rerun a script that calls create_study(study_name='s', storage=url) without load_if_exists. The study already exists. What happens?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: optuna.readthedocs.io/en/stable/tutorial/20_recipes/001_rdb.html