Rate this Page

Source code for torchrl.trainers.algorithms.configs.entrypoint

# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import annotations

import pathlib
from collections.abc import Mapping, Sequence
from typing import Any

import hydra
from hydra.utils import to_absolute_path
from omegaconf import DictConfig, OmegaConf, open_dict

from torchrl._utils import logger as torchrl_logger
from torchrl.checkpoint import Checkpoint, resolve_checkpoint_path, resume_config
from torchrl.checkpoint._hydra import _CONFIG_COMPONENT, _hydra_task_overrides
from torchrl.trainers.trainers import Trainer


[docs] def instantiate_trainer( cfg: DictConfig, *, overrides: Sequence[str] | None = None ) -> Trainer: """Instantiate ``cfg.trainer``, resuming from ``cfg.resume`` when it is set. Without ``resume`` this is :func:`hydra.utils.instantiate` on ``cfg.trainer`` plus registration of the composed configuration on the trainer checkpoint. With ``resume`` set to a checkpoint or a :class:`~torchrl.checkpoint.CheckpointRotation` directory, :func:`~torchrl.checkpoint.resume_config` rebuilds the configuration from the saved one with the current command-line overrides on top; the saved logger run is reattached before the logger is constructed (W&B resumes the saved id with ``resume="must"``, CSV and TensorBoard keep the saved directory); checkpoints keep accumulating in the resumed rotation directory unless ``checkpoint_rotation.directory`` is overridden; and :meth:`~torchrl.trainers.Trainer.load_from_file` restores the trainer state. Args: cfg (DictConfig): the composed Hydra configuration. It must hold a ``trainer`` node and may hold ``resume``. overrides (Sequence[str], optional): command-line overrides applied over the saved configuration. Defaults to the task overrides of the current Hydra run, or none outside a Hydra application. Returns: The instantiated trainer, restored from the checkpoint when resuming. Examples: >>> @hydra.main(config_path="config", config_name="config", version_base="1.3") # doctest: +SKIP ... def main(cfg): ... trainer = instantiate_trainer(cfg) ... with trainer.stop_on_signal(): ... trainer.train() """ resume = cfg.get("resume", None) if resume in (None, ""): trainer = hydra.utils.instantiate(cfg.trainer) _register_config(trainer, cfg) return trainer resume_path = pathlib.Path(to_absolute_path(str(resume))) checkpoint_path = resolve_checkpoint_path(resume_path) if overrides is None: overrides = _hydra_task_overrides() cfg = resume_config(cfg, checkpoint_path, overrides=overrides) logger_cfg = cfg.trainer.get("logger", None) saved_logger = Checkpoint.read_component(checkpoint_path, "logger", default=None) if logger_cfg is not None and saved_logger is not None: _reattach_logger(logger_cfg, saved_logger) _continue_rotation(cfg, resume_path, checkpoint_path, overrides) trainer = hydra.utils.instantiate(cfg.trainer) try: trainer.load_from_file(checkpoint_path) except BaseException: # Release collector workers so a failed restore does not hang at exit. trainer.shutdown() raise # Registered after the restore so the checkpoint does not overwrite the # merged configuration with the saved one. _register_config(trainer, cfg) torchrl_logger.info( "Resumed trainer from %s at %s collected frames.", checkpoint_path, trainer.collected_frames, ) return trainer
def _register_config(trainer: Trainer, cfg: DictConfig) -> None: checkpoint = trainer.checkpoint if checkpoint is not None and _CONFIG_COMPONENT not in checkpoint: checkpoint.register( _CONFIG_COMPONENT, OmegaConf.to_container(cfg, resolve=False) ) def _reattach_logger(logger_cfg: DictConfig, saved_logger: Mapping[str, Any]) -> None: target = str(logger_cfg.get("_target_", "")).lower() local = saved_logger.get("local") or {} with open_dict(logger_cfg): if "wandb" in target: run_id = local.get("id") if not run_id: raise ValueError( "The saved W&B logger state has no run ID; the run cannot be " "resumed." ) logger_cfg.id = run_id wandb_kwargs = dict(logger_cfg.get("wandb_kwargs", None) or {}) wandb_kwargs["resume"] = "must" logger_cfg.wandb_kwargs = wandb_kwargs elif "csv" in target or "tensorboard" in target: logger_cfg.exp_name = saved_logger["exp_name"] if saved_logger.get("log_dir"): logger_cfg.log_dir = saved_logger["log_dir"] else: torchrl_logger.warning( "The saved logger run is not restored for %r; a new run is created.", target, ) def _continue_rotation( cfg: DictConfig, resume_path: pathlib.Path, checkpoint_path: pathlib.Path, overrides: Sequence[str], ) -> None: rotation_cfg = cfg.trainer.get("checkpoint_rotation", None) if rotation_cfg is None: return directory_keys = ( "checkpoint_rotation.directory=", "trainer.checkpoint_rotation.directory=", ) if any(override.lstrip("+").startswith(directory_keys) for override in overrides): return if resume_path.is_dir() and not Checkpoint.is_checkpoint(resume_path): directory = resume_path else: directory = pathlib.Path(checkpoint_path).parent with open_dict(rotation_cfg): rotation_cfg.directory = str(directory.resolve())