Rate this Page

Source code for torchrl.render.policy

# 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

from typing import Any

import torch
from tensordict import TensorDictBase

from torchrl._utils import logger as torchrl_logger
from torchrl.render.checkpoint import checkpoint_hash, infer_state_dict, load_checkpoint
from torchrl.render.config import RenderConfig, RenderPolicySpec
from torchrl.render.import_utils import call_with_supported_kwargs, import_from_string

__all__ = ["TensorDictPolicyAdapter", "load_render_policy", "normalize_policy"]


[docs] class TensorDictPolicyAdapter: """Adapts plain tensor policies to a TensorDict policy callable. Args: policy: Policy object or callable. obs_key: Observation key used for tensor-only policies. action_key: Action key written when tensor actions are returned. Examples: >>> import torch >>> from tensordict import TensorDict >>> from torchrl.render.policy import TensorDictPolicyAdapter >>> def policy(obs): ... if not torch.is_tensor(obs): ... raise TypeError("expected tensor input") ... return obs + 1 >>> adapter = TensorDictPolicyAdapter(policy, "obs", ("agent", "action")) >>> td = TensorDict({"obs": torch.zeros(1)}, []) >>> adapter(td).get(("agent", "action")) tensor([1.]) """ def __init__(self, policy: Any, obs_key: Any, action_key: Any) -> None: self.policy = policy self.obs_key = obs_key self.action_key = action_key def __call__(self, tensordict: TensorDictBase) -> TensorDictBase: try: output = self.policy(tensordict) except Exception as td_err: try: output = self.policy(tensordict.get(self.obs_key)) except Exception: raise td_err if isinstance(output, TensorDictBase): return output if isinstance(output, dict): tensordict.update(output) return tensordict tensordict.set(self.action_key, output) return tensordict
[docs] def load_render_policy( config: RenderConfig, env: Any | None = None, *, checkpoint: Any | None = None, checkpoint_digest: str | None = None, ) -> Any: """Builds and prepares a policy for rendering. Args: config: Render configuration. env: Optional environment used to expose specs to the policy factory. Keyword Args: checkpoint: Checkpoint payload supplied by the caller. When ``None``, the payload is loaded from ``config.ckpt``. checkpoint_digest: SHA256 digest of the checkpoint file. When ``None``, the digest is computed here. Returns: A TensorDict-compatible policy callable or module. """ factory = ( import_from_string(config.policy) if isinstance(config.policy, str) else config.policy ) if not callable(factory): raise TypeError( f"Policy factory must be callable, got {type(factory).__name__}." ) device = torch.device(config.policy_device or config.device) if checkpoint is None: checkpoint = load_checkpoint(config.ckpt, map_location=device) digest = checkpoint_digest or checkpoint_hash(config.ckpt) env_specs = getattr(env, "specs", None) spec = RenderPolicySpec( ckpt_path=config.ckpt, checkpoint=checkpoint, checkpoint_hash=digest, device=device, env_specs=env_specs, policy_kwargs=dict(config.policy_kwargs), config=config, ) kwargs = { "spec": spec, "ckpt_path": config.ckpt, "checkpoint": checkpoint, "device": device, "env": env, "env_specs": env_specs, "config": config, "policy_kwargs": dict(config.policy_kwargs), **config.policy_kwargs, } policy = call_with_supported_kwargs(factory, spec, kwargs) if config.auto_load_policy: _load_state_dict_if_possible(policy, checkpoint, config) if config.policy_eval: _set_policy_eval(policy) return normalize_policy(policy, config)
[docs] def normalize_policy( policy: Any, config: RenderConfig ) -> TensorDictPolicyAdapter | Any: """Normalizes a policy into a TensorDict-compatible callable.""" if isinstance(policy, TensorDictPolicyAdapter): return policy return TensorDictPolicyAdapter(policy, config.obs_key, config.action_key)
def _load_state_dict_if_possible( policy: Any, checkpoint: Any, config: RenderConfig ) -> None: loader = getattr(policy, "load_state_dict", None) if loader is None: return try: state_dict = infer_state_dict( checkpoint, key=config.state_dict_key or config.checkpoint_key ) except Exception as err: torchrl_logger.warning( "rlrender could not infer a state dict for automatic policy loading: %s", err, ) return loader(state_dict, strict=config.strict_load) def _set_policy_eval(policy: Any) -> None: eval_method = getattr(policy, "eval", None) if callable(eval_method): eval_method() return train_method = getattr(policy, "train", None) if callable(train_method): train_method(False)