Rate this Page

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

# 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 dataclasses import dataclass, field
from functools import partial
from typing import Any

import torch
from omegaconf import MISSING
from tensordict.nn import TensorDictModule, TensorDictSequential
from torchrl.modules import (
    AdditiveGaussianModule,
    DreamerV3DiscreteActor,
    LowLevelController,
    QValueActor,
    RSSMStateEstimatorV3,
    TanhModule,
    ValueOperator,
)
from torchrl.trainers.algorithms.configs.common import (
    _normalize_hydra_key,
    _normalize_hydra_keys,
    ConfigBase,
)


@dataclass
class ActivationConfig(ConfigBase):
    """A class to configure an activation function.

    Defaults to :class:`torch.nn.Tanh`.

    .. seealso:: :class:`torch.nn.Tanh`
    """

    _target_: str = "torch.nn.Tanh"
    _partial_: bool = False

    def __post_init__(self) -> None:
        """Post-initialization hook for activation configurations."""


@dataclass
class LayerConfig(ConfigBase):
    """A class to configure a layer.

    Defaults to :class:`torch.nn.Linear`.

    .. seealso:: :class:`torch.nn.Linear`
    """

    _target_: str = "torch.nn.Linear"
    _partial_: bool = False

    def __post_init__(self) -> None:
        """Post-initialization hook for layer configurations."""


[docs] @dataclass class NetworkConfig(ConfigBase): """Parent class to configure a network.""" _partial_: bool = False def __post_init__(self) -> None: """Post-initialization hook for network configurations."""
@dataclass class LowLevelControllerConfig(NetworkConfig): """Hydra configuration for :class:`~torchrl.modules.LowLevelController`. Pass the pretrained policy to Hydra's instantiate and declare the unbatched Composite decision spec with a Hydra target. Adapter and nested keys can also be configured in YAML. """ policy: Any = MISSING decision_spec: Any = MISSING adapter: Any = None group_key: Any = None state_key: Any = "controller" policy_action_key: Any = "action" action_key: Any = "action" reset_key: Any = None _target_: str = ( "torchrl.trainers.algorithms.configs.modules._make_low_level_controller" ) def _make_low_level_controller(**kwargs: Any) -> LowLevelController: """Convert Hydra lists to nested TensorDict keys before construction.""" return LowLevelController( policy=kwargs.pop("policy"), decision_spec=kwargs.pop("decision_spec"), adapter=kwargs.pop("adapter", None), group_key=_normalize_hydra_key(kwargs.pop("group_key", None)), state_key=_normalize_hydra_key(kwargs.pop("state_key", "controller")), policy_action_key=_normalize_hydra_key( kwargs.pop("policy_action_key", "action") ), action_key=_normalize_hydra_key(kwargs.pop("action_key", "action")), reset_key=_normalize_hydra_key(kwargs.pop("reset_key", None)), **kwargs, )
[docs] @dataclass class MLPConfig(NetworkConfig): """A class to configure a multi-layer perceptron. Example: >>> cfg = MLPConfig(in_features=10, out_features=5, depth=2, num_cells=32) >>> net = instantiate(cfg) >>> y = net(torch.randn(1, 10)) >>> assert y.shape == (1, 5) .. seealso:: :class:`torchrl.modules.MLP` """ in_features: int | None = None out_features: Any = None depth: int | None = None num_cells: Any = None activation_class: ActivationConfig = field( default_factory=partial( ActivationConfig, _target_="torch.nn.Tanh", _partial_=True ) ) activation_kwargs: Any = None norm_class: Any = None norm_kwargs: Any = None dropout: float | None = None bias_last_layer: bool = True single_bias_last_layer: bool = False layer_class: LayerConfig = field( default_factory=partial(LayerConfig, _target_="torch.nn.Linear", _partial_=True) ) layer_kwargs: dict | None = None activate_last_layer: bool = False device: Any = None _target_: str = "torchrl.modules.MLP" def __post_init__(self): if isinstance(self.activation_class, str): self.activation_class = ActivationConfig( _target_=self.activation_class, _partial_=True ) if isinstance(self.layer_class, str): self.layer_class = LayerConfig(_target_=self.layer_class, _partial_=True)
[docs] @dataclass class DreamerV3MLPConfig(NetworkConfig): """A class to configure a DreamerV3 multilayer perceptron. Example: >>> import torch >>> from hydra.utils import instantiate >>> from torchrl.trainers.algorithms.configs import DreamerV3MLPConfig >>> cfg = DreamerV3MLPConfig( ... in_features=6, out_features=4, depth=2, num_cells=8 ... ) >>> net = instantiate(cfg) >>> y = net(torch.randn(3, 2), torch.randn(3, 4)) >>> assert y.shape == (3, 4) .. seealso:: :class:`~torchrl.modules.DreamerV3MLP` """ in_features: int = MISSING out_features: int | None = MISSING depth: int = 3 num_cells: int = 1024 outscale: float = 1.0 norm_eps: float = 1e-4 device: Any = None _target_: str = "torchrl.modules.DreamerV3MLP"
[docs] @dataclass class DreamerV3ImageEncoderConfig(NetworkConfig): """Hydra configuration for :class:`~torchrl.modules.DreamerV3ImageEncoder`. Example: >>> import torch >>> from hydra.utils import instantiate >>> from torchrl.trainers.algorithms.configs import DreamerV3ImageEncoderConfig >>> cfg = DreamerV3ImageEncoderConfig(depth=8, mults=[1, 2]) >>> net = instantiate(cfg) >>> image = torch.randint(0, 256, (4, 3, 16, 16), dtype=torch.uint8) >>> assert net(image).shape == (4, 256) .. seealso:: :class:`~torchrl.modules.DreamerV3ImageEncoder` """ in_channels: int = 3 depth: int = 64 mults: list[int] = field(default_factory=partial(list, (2, 3, 4, 4))) kernel_size: int = 5 norm_eps: float = 1e-4 device: Any = None _target_: str = "torchrl.modules.DreamerV3ImageEncoder"
[docs] @dataclass class DreamerV3ImageDecoderConfig(NetworkConfig): """Hydra configuration for :class:`~torchrl.modules.DreamerV3ImageDecoder`. Example: >>> import torch >>> from hydra.utils import instantiate >>> from torchrl.trainers.algorithms.configs import DreamerV3ImageDecoderConfig >>> cfg = DreamerV3ImageDecoderConfig( ... in_features=12, image_shape=[3, 16, 16], depth=8, mults=[1, 2], num_blocks=2 ... ) >>> net = instantiate(cfg) >>> assert net(torch.randn(4, 12)).shape == (4, 3, 16, 16) .. seealso:: :class:`~torchrl.modules.DreamerV3ImageDecoder` """ in_features: int = MISSING image_shape: list[int] = field(default_factory=partial(list, (3, 64, 64))) depth: int = 64 mults: list[int] = field(default_factory=partial(list, (2, 3, 4, 4))) kernel_size: int = 5 num_blocks: int = 8 norm_eps: float = 1e-4 device: Any = None _target_: str = "torchrl.modules.DreamerV3ImageDecoder"
[docs] @dataclass class DreamerV3DiscreteActorConfig(NetworkConfig): """Hydra configuration for :class:`~torchrl.modules.DreamerV3DiscreteActor`. Examples: >>> import torch >>> from hydra.utils import instantiate >>> from tensordict import TensorDict >>> from torchrl.trainers.algorithms.configs import DreamerV3DiscreteActorConfig >>> actor = instantiate(DreamerV3DiscreteActorConfig(in_features=12, out_features=3)) >>> data = TensorDict({"state": torch.randn(4, 8), "belief": torch.randn(4, 4)}, [4]) >>> actor(data)["action"].shape torch.Size([4, 3]) """ in_features: int = MISSING out_features: int = MISSING depth: int = 3 num_cells: int = 1024 norm_eps: float = 1e-4 unimix: float = 0.01 in_keys: Any = None action_key: Any = "action" logits_key: Any = "logits" log_prob_key: Any = "action_log_prob" device: Any = None _target_: str = ( "torchrl.trainers.algorithms.configs.modules._make_dreamer_v3_discrete_actor" )
def _make_dreamer_v3_discrete_actor(**kwargs) -> DreamerV3DiscreteActor: """Normalize Hydra's nested key lists before constructing the actor.""" in_keys = _normalize_hydra_keys(kwargs.pop("in_keys", None)) for key in ("action_key", "logits_key", "log_prob_key"): if key in kwargs: kwargs[key] = _normalize_hydra_key(kwargs[key]) return DreamerV3DiscreteActor(in_keys=in_keys, **kwargs)
[docs] @dataclass class DreamerV3SeededPolicyConfig(NetworkConfig): """Hydra configuration for :class:`~torchrl.modules.DreamerV3SeededPolicy`. Examples: >>> from hydra.utils import instantiate >>> from torchrl.trainers.algorithms.configs import DreamerV3DiscreteActorConfig, DreamerV3SeededPolicyConfig >>> config = DreamerV3SeededPolicyConfig( ... module=DreamerV3DiscreteActorConfig(in_features=6, out_features=3), seed=7, ... ) >>> policy = instantiate(config) >>> policy.get_extra_state() {'seed': 7, 'counter': 0} """ module: Any = MISSING seed: int = MISSING _target_: str = "torchrl.modules.DreamerV3SeededPolicy"
@dataclass class RSSMStateEstimatorV3Config(NetworkConfig): """Hydra configuration for :class:`~torchrl.modules.RSSMStateEstimatorV3`. Examples: Given the shared prior and posterior in the estimator's example: >>> from hydra.utils import instantiate >>> from torchrl.trainers.algorithms.configs import RSSMStateEstimatorV3Config >>> estimator = instantiate( # doctest: +SKIP ... RSSMStateEstimatorV3Config(), prior=prior, posterior=posterior, ... ) """ prior: Any = MISSING posterior: Any = MISSING in_keys: Any = None out_keys: Any = None _target_: str = ( "torchrl.trainers.algorithms.configs.modules._make_rssm_state_estimator_v3" ) def _make_rssm_state_estimator_v3(**kwargs) -> RSSMStateEstimatorV3: """Normalize configured nested keys before constructing the estimator.""" in_keys = _normalize_hydra_keys(kwargs.pop("in_keys", None)) out_keys = _normalize_hydra_keys(kwargs.pop("out_keys", None)) return RSSMStateEstimatorV3(in_keys=in_keys, out_keys=out_keys, **kwargs) @dataclass class NormConfig(ConfigBase): """A class to configure a normalization layer. Defaults to :class:`torch.nn.BatchNorm1d`. .. seealso:: :class:`torch.nn.BatchNorm1d` """ _target_: str = "torch.nn.BatchNorm1d" _partial_: bool = False def __post_init__(self) -> None: """Post-initialization hook for normalization configurations.""" @dataclass class AggregatorConfig(ConfigBase): """A class to configure an aggregator layer. Defaults to :class:`torchrl.modules.models.utils.SquashDims`. .. seealso:: :class:`torchrl.modules.models.utils.SquashDims` """ _target_: str = "torchrl.modules.models.utils.SquashDims" _partial_: bool = False def __post_init__(self) -> None: """Post-initialization hook for aggregator configurations."""
[docs] @dataclass class ConvNetConfig(NetworkConfig): """A class to configure a convolutional network. Defaults to :class:`torchrl.modules.ConvNet`. Example: >>> cfg = ConvNetConfig(in_features=3, depth=2, num_cells=[32, 64], kernel_sizes=[3, 5], strides=[1, 2], paddings=[1, 2]) >>> net = instantiate(cfg) >>> y = net(torch.randn(1, 3, 32, 32)) >>> assert y.shape == (1, 64) .. seealso:: :class:`torchrl.modules.ConvNet` """ in_features: int | None = None depth: int | None = None num_cells: Any = None kernel_sizes: Any = 3 strides: Any = 1 paddings: Any = 0 activation_class: ActivationConfig = field( default_factory=partial( ActivationConfig, _target_="torch.nn.ELU", _partial_=True ) ) activation_kwargs: Any = None norm_class: NormConfig | None = None norm_kwargs: Any = None bias_last_layer: bool = True aggregator_class: AggregatorConfig = field( default_factory=partial( AggregatorConfig, _target_="torchrl.modules.models.utils.SquashDims", _partial_=True, ) ) aggregator_kwargs: dict | None = None squeeze_output: bool = False device: Any = None _target_: str = "torchrl.modules.ConvNet" def __post_init__(self): if self.activation_class is None and isinstance(self.activation_class, str): self.activation_class = ActivationConfig( _target_=self.activation_class, _partial_=True ) if self.norm_class is None and isinstance(self.norm_class, str): self.norm_class = NormConfig(_target_=self.norm_class, _partial_=True) if self.aggregator_class is None and isinstance(self.aggregator_class, str): self.aggregator_class = AggregatorConfig( _target_=self.aggregator_class, _partial_=True )
@dataclass class QMixerNetworkConfig(NetworkConfig): """A class to configure a QMIX mixer network. .. seealso:: :class:`torchrl.modules.models.multiagent.QMixer` """ state_shape: Any = MISSING mixing_embed_dim: int = 32 n_agents: int = MISSING device: Any = None _target_: str = "torchrl.modules.models.multiagent.QMixer" def __post_init__(self) -> None: super().__post_init__() @dataclass class VDNMixerNetworkConfig(NetworkConfig): """A class to configure a VDN mixer network. .. seealso:: :class:`torchrl.modules.models.multiagent.VDNMixer` """ n_agents: int = MISSING device: Any = None _target_: str = "torchrl.modules.models.multiagent.VDNMixer" def __post_init__(self) -> None: super().__post_init__()
[docs] @dataclass class ModelConfig(ConfigBase): """Parent class to configure a model. A model can be made of several networks. It is always a :class:`~tensordict.nn.TensorDictModuleBase` instance. .. seealso:: :class:`TanhNormalModelConfig`, :class:`ValueModelConfig` """ _partial_: bool = False in_keys: Any = None out_keys: Any = None shared: bool = False def __post_init__(self) -> None: """Post-initialization hook for model configurations."""
[docs] @dataclass class TensorDictModuleConfig(ModelConfig): """A class to configure a TensorDictModule. Example: >>> cfg = TensorDictModuleConfig(module=MLPConfig(in_features=10, out_features=10, depth=2, num_cells=32), in_keys=["observation"], out_keys=["action"]) >>> module = instantiate(cfg) >>> assert isinstance(module, TensorDictModule) >>> assert module(observation=torch.randn(10, 10)).shape == (10, 10) .. seealso:: :class:`tensordict.nn.TensorDictModule` """ module: MLPConfig = MISSING _target_: str = ( "torchrl.trainers.algorithms.configs.modules._make_tensordict_module" ) _partial_: bool = False def __post_init__(self) -> None: """Post-initialization hook for TensorDict module configurations.""" return super().__post_init__()
[docs] @dataclass class TensorDictSequentialConfig(ModelConfig): """A class to configure a TensorDictSequential. Example: >>> cfg = TensorDictSequentialConfig( ... modules=[ ... TensorDictModuleConfig(module=MLPConfig(in_features=10, out_features=10, depth=2, num_cells=32), in_keys=["observation"], out_keys=["hidden"]), ... TensorDictModuleConfig(module=MLPConfig(in_features=10, out_features=5, depth=2, num_cells=32), in_keys=["hidden"], out_keys=["action"]) ... ] ... ) >>> seq = instantiate(cfg) >>> assert isinstance(seq, TensorDictSequential) .. seealso:: :class:`tensordict.nn.TensorDictSequential` """ modules: Any | None = None partial_tolerant: bool = False selected_out_keys: Any | None = None inplace: bool | str | None = None _target_: str = ( "torchrl.trainers.algorithms.configs.modules._make_tensordict_sequential" ) _partial_: bool = False def __post_init__(self) -> None: return super().__post_init__()
[docs] @dataclass class TanhNormalModelConfig(ModelConfig): """A class to configure a TanhNormal model. Example: >>> cfg = TanhNormalModelConfig(network=MLPConfig(in_features=10, out_features=5, depth=2, num_cells=32)) >>> net = instantiate(cfg) >>> y = net(torch.randn(1, 10)) >>> assert y.shape == (1, 5) Args: low: lower bound of the action support handed to :class:`~torchrl.modules.TanhNormal` (a scalar or a per-dimension sequence). Defaults to ``None``, i.e. the distribution default of ``-1``. high: upper bound of the action support. Defaults to ``None``, i.e. ``1``. tanh_loc: if ``True``, the location is squashed to ``[-upscale, upscale]`` before the tanh transform, which keeps the log-probability of actions at the bounds finite (see :class:`~torchrl.modules.TanhNormal`). Defaults to ``False``. .. seealso:: :class:`torchrl.modules.TanhNormal` """ network: MLPConfig = MISSING eval_mode: bool = False extract_normal_params: bool = True scale_mapping: str = "biased_softplus_1.0" scale_lb: float = 1e-4 low: Any = None high: Any = None tanh_loc: bool = False param_keys: Any = None exploration_type: Any = "RANDOM" return_log_prob: bool = False _target_: str = ( "torchrl.trainers.algorithms.configs.modules._make_tanh_normal_model" ) def __post_init__(self): """Post-initialization hook for TanhNormal model configurations.""" super().__post_init__() if self.in_keys is None: self.in_keys = ["observation"] if self.param_keys is None: self.param_keys = ["loc", "scale"] if self.out_keys is None: self.out_keys = ["action"]
[docs] @dataclass class ValueModelConfig(ModelConfig): """A class to configure a Value model. Example: >>> cfg = ValueModelConfig(network=MLPConfig(in_features=10, out_features=5, depth=2, num_cells=32)) >>> net = instantiate(cfg) >>> y = net(torch.randn(1, 10)) >>> assert y.shape == (1, 5) .. seealso:: :class:`torchrl.modules.ValueOperator` """ _target_: str = "torchrl.trainers.algorithms.configs.modules._make_value_model" network: NetworkConfig = MISSING def __post_init__(self) -> None: """Post-initialization hook for value model configurations.""" super().__post_init__()
[docs] @dataclass class TanhModuleConfig(ModelConfig): """A class to configure a TanhModule. Example: >>> cfg = TanhModuleConfig(in_keys=["action"], out_keys=["action"], low=-1.0, high=1.0) >>> module = instantiate(cfg) >>> assert isinstance(module, TanhModule) .. seealso:: :class:`torchrl.modules.TanhModule` """ spec: Any = None low: Any = None high: Any = None clamp: bool = False _target_: str = "torchrl.trainers.algorithms.configs.modules._make_tanh_module" def __post_init__(self) -> None: """Post-initialization hook for TanhModule configurations.""" super().__post_init__()
[docs] @dataclass class AdditiveGaussianModuleConfig(ModelConfig): """A class to configure an AdditiveGaussianModule. Example: >>> cfg = AdditiveGaussianModuleConfig( ... spec=None, ... sigma_init=1.0, ... sigma_end=0.1, ... mean=0.0, ... std=1.0, ... action_key="action", ... ) >>> module = instantiate(cfg) >>> assert isinstance(module, AdditiveGaussianModule) .. seealso:: :class:`torchrl.modules.AdditiveGaussianModule` """ spec: Any = None sigma_init: float = 1.0 sigma_end: float = 0.1 annealing_num_steps: int = 1000 mean: float = 0.0 std: float = 1.0 action_key: Any = "action" safe: bool = False device: Any = None _target_: str = ( "torchrl.trainers.algorithms.configs.modules._make_additive_gaussian_module" ) _partial_: bool = False def __post_init__(self) -> None: super().__post_init__()
def _make_tensordict_module(*args, **kwargs) -> TensorDictModule: """Helper function to create a TensorDictModule.""" from hydra.utils import instantiate module = kwargs.pop("module") shared = kwargs.pop("shared", False) for key in ("in_keys", "out_keys"): if key in kwargs: kwargs[key] = _normalize_hydra_keys(kwargs[key]) # Instantiate the module if it's a config if hasattr(module, "_target_"): module = instantiate(module) elif callable(module) and hasattr(module, "func"): # partial function module = module() # Create the TensorDictModule tensordict_module = TensorDictModule(module, **kwargs) # Apply share_memory if needed if shared: tensordict_module = tensordict_module.share_memory() return tensordict_module def _make_tensordict_sequential(*args, **kwargs) -> TensorDictSequential: """Helper function to create a TensorDictSequential.""" from hydra.utils import instantiate from omegaconf import DictConfig, ListConfig modules = kwargs.pop("modules") shared = kwargs.pop("shared", False) partial_tolerant = kwargs.pop("partial_tolerant", False) selected_out_keys = _normalize_hydra_keys(kwargs.pop("selected_out_keys", None)) inplace = kwargs.pop("inplace", None) def _instantiate_module(module): if hasattr(module, "_target_"): return instantiate(module) elif callable(module) and hasattr(module, "func"): return module() else: return module if isinstance(modules, (dict, DictConfig)): instantiated_modules = { key: _instantiate_module(module) for key, module in modules.items() } elif isinstance(modules, (list, ListConfig)): instantiated_modules = [_instantiate_module(module) for module in modules] else: raise ValueError( f"modules must be a dict or list, got {type(modules).__name__}" ) tensordict_sequential = TensorDictSequential( instantiated_modules, partial_tolerant=partial_tolerant, selected_out_keys=selected_out_keys, inplace=inplace, ) if shared: tensordict_sequential = tensordict_sequential.share_memory() return tensordict_sequential def _make_tanh_normal_model(*args, **kwargs): """Helper function to create a TanhNormal model with ProbabilisticTensorDictSequential.""" from hydra.utils import instantiate from tensordict.nn import ( ProbabilisticTensorDictModule, ProbabilisticTensorDictSequential, TensorDictModule, ) from torchrl.modules import NormalParamExtractor, TanhNormal # Extract parameters network = kwargs.pop("network") in_keys = _normalize_hydra_keys(kwargs.pop("in_keys", ["observation"])) param_keys = _normalize_hydra_keys(kwargs.pop("param_keys", ["loc", "scale"])) out_keys = _normalize_hydra_keys(kwargs.pop("out_keys", ["action"])) extract_normal_params = kwargs.pop("extract_normal_params", True) scale_mapping = kwargs.pop("scale_mapping", "biased_softplus_1.0") scale_lb = kwargs.pop("scale_lb", 1e-4) return_log_prob = kwargs.pop("return_log_prob", False) eval_mode = kwargs.pop("eval_mode", False) exploration_type = kwargs.pop("exploration_type", "RANDOM") shared = kwargs.pop("shared", False) distribution_kwargs = dict(kwargs.pop("distribution_kwargs", None) or {}) for bound in ("low", "high"): value = kwargs.pop(bound, None) if value is None: continue # omegaconf hands sequences over as ListConfig if not isinstance(value, (int, float)): value = torch.as_tensor(list(value), dtype=torch.get_default_dtype()) distribution_kwargs[bound] = value if kwargs.pop("tanh_loc", False): distribution_kwargs["tanh_loc"] = True if distribution_kwargs: kwargs["distribution_kwargs"] = distribution_kwargs # Now instantiate the network if hasattr(network, "_target_"): network = instantiate(network) elif callable(network) and hasattr(network, "func"): # partial function network = network() # Create the sequential if extract_normal_params: # Add NormalParamExtractor to split the output network = torch.nn.Sequential( network, NormalParamExtractor(scale_mapping=scale_mapping, scale_lb=scale_lb), ) module = TensorDictModule(network, in_keys=in_keys, out_keys=param_keys) if shared: module = module.share_memory() # Create ProbabilisticTensorDictModule prob_module = ProbabilisticTensorDictModule( in_keys=param_keys, out_keys=out_keys, distribution_class=TanhNormal, return_log_prob=return_log_prob, default_interaction_type=exploration_type, **kwargs, ) result = ProbabilisticTensorDictSequential(module, prob_module) if eval_mode: result.eval() return result def _make_value_model(*args, **kwargs) -> ValueOperator: """Helper function to create a ValueOperator with the given network.""" from hydra.utils import instantiate network = kwargs.pop("network") shared = kwargs.pop("shared", False) for key in ("in_keys", "out_keys"): if key in kwargs: kwargs[key] = _normalize_hydra_keys(kwargs[key]) # Instantiate the network if it's a config if hasattr(network, "_target_"): network = instantiate(network) elif callable(network) and hasattr(network, "func"): # partial function network = network() # Create the ValueOperator value_operator = ValueOperator(network, **kwargs) # Apply share_memory if needed if shared: value_operator = value_operator.share_memory() return value_operator def _make_tanh_module(*args, **kwargs) -> TanhModule: """Helper function to create a TanhModule.""" kwargs.pop("shared", False) if "in_keys" in kwargs: kwargs["in_keys"] = _normalize_hydra_keys(kwargs["in_keys"]) if "out_keys" in kwargs: kwargs["out_keys"] = _normalize_hydra_keys(kwargs["out_keys"]) return TanhModule(**kwargs) def _make_additive_gaussian_module(*args, **kwargs) -> AdditiveGaussianModule: """Helper function to create an AdditiveGaussianModule.""" kwargs.pop("shared", False) kwargs.pop("in_keys", None) kwargs.pop("out_keys", None) if "action_key" in kwargs: kwargs["action_key"] = _normalize_hydra_key(kwargs["action_key"]) return AdditiveGaussianModule(**kwargs)
[docs] @dataclass class QValueModelConfig(ModelConfig): """A class to configure a QValueActor model. .. seealso:: :class:`torchrl.modules.QValueActor` """ _target_: str = "torchrl.trainers.algorithms.configs.modules._make_qvalue_model" network: NetworkConfig = MISSING action_space: Any = None action_key: Any = None action_value_key: Any = None chosen_action_value_key: Any = None action_mask_key: Any = None def __post_init__(self) -> None: super().__post_init__()
def _make_qvalue_model(*args, **kwargs) -> QValueActor: """Helper function to create a QValueActor with the given network.""" from hydra.utils import instantiate network = kwargs.pop("network") shared = kwargs.pop("shared", False) kwargs.pop("out_keys", None) if "in_keys" in kwargs: kwargs["in_keys"] = _normalize_hydra_keys(kwargs["in_keys"]) for key in ( "action_key", "action_value_key", "chosen_action_value_key", "action_mask_key", ): if key in kwargs: kwargs[key] = _normalize_hydra_key(kwargs[key]) if hasattr(network, "_target_"): network = instantiate(network) elif callable(network) and hasattr(network, "func"): network = network() qvalue_actor = QValueActor(network, **kwargs) if shared: qvalue_actor = qvalue_actor.share_memory() return qvalue_actor