TensorDictModule#
- class tensordict.nn.TensorDictModule(module: Callable, in_keys, out_keys: NestedKey | List[NestedKey], *, out_to_in_map: bool | None = None, inplace: bool | str = True, method: str | None = None, method_kwargs: dict | None = None, strict: bool = False, get_kwargs: dict | None = None)#
Bases:
TensorDictModuleBaseWraps a callable so that it reads its inputs from a tensordict and writes its outputs to it.
When you call the module with a tensordict, it reads the entries at
in_keys, passes them tomodule, and writes the results atout_keys. By default it writes into the input tensordict and returns it. You can also call it with tensors as positional or keyword arguments (seedispatch()). It then returns the output tensors. See Modules: TensorDictModule and tensordict.nn for a guide.- Parameters:
module (Callable) – the callable to wrap, typically a
torch.nn.Module. It can return a tensor, a tuple of tensors, or a dict or a tensordict. A dict or a tensordict that holds all theout_keysis read by key, so its order does not matter.in_keys (NestedKey, list of NestedKey, or dict) – the entries that the module reads from the input tensordict. If a list, the values are passed as positional arguments, in this order. If a dict, the values are passed as keyword arguments. By default, the dict maps an argument name to a tensordict key:
in_keys={"x": "obs"}callsmodule(x=td["obs"]). One tensordict key can feed several arguments.out_to_in_map=Falsereverses the direction.out_keys (NestedKey or list of NestedKey) – the keys where the outputs of
moduleare written, one key per output, in order. An output whose key is"_"is not written.
- Keyword Arguments:
out_to_in_map (bool, optional) – the direction of a dict
in_keys.Truereads it as{argument: tensordict_key},Falseas{tensordict_key: argument}. Only used whenin_keysis a dict. If you pass it with another type ofin_keys, the constructor warns. Defaults toTruefor a dictin_keys.inplace (bool or str, optional) –
where the module writes its outputs.
True: into the input tensordict, whichforwardreturns.False: into a newTensorDictthat holds only the outputs, with an empty batch size and no device, whatever the type of the input."empty": intotensordict.empty(), which keeps the type, batch size and device of the input.
If you pass
tensordict_outtoforward(), the outputs are written into it, whatever the value ofinplace. Defaults toTrue.method (str, optional) – the name of the method of
moduleto call. Unlike a lambda around the method, this keeps the parameters and buffers ofmoduleregistered in the wrapper. Defaults toNone, which callsmoduleitself.method_kwargs (dict, optional) – keyword arguments passed to
module(or tomethod) at each call. Defaults toNone.strict (bool, optional) – if
True, a missing input raises aKeyError. IfFalse,Noneis passed in its place, and ifmodulethen fails, the error names the missing keys. Defaults toFalse.get_kwargs (dict, optional) – keyword arguments passed to
get()when the inputs are read, for example{"as_padded_tensor": True}for ragged inputs. Defaults toNone.
Attributes that the wrapper does not have, such as
weightfor aLinear, are read frommodule. Inside aset_skip_existingblock, the module does not run when all itsout_keysare already in the input.Examples
>>> import torch >>> from torch import nn >>> from tensordict import TensorDict >>> from tensordict.nn import TensorDictModule >>> module = TensorDictModule(nn.Linear(3, 4), in_keys=["x"], out_keys=["y"]) >>> data = TensorDict(x=torch.ones(2, 3), batch_size=[2]) >>> data = module(data) >>> print(data) TensorDict(batch_size=[2], fields={x: f32[3], y: f32[4]}) >>> module(torch.ones(2, 3)).shape torch.Size([2, 4])
A plain function works too. This one reads a nested key and writes two nested keys:
>>> module = TensorDictModule( ... lambda x: (x - 1, x + 1), ... in_keys=[("input", "x")], ... out_keys=[("output", "x-1"), ("output", "x+1")], ... ) >>> td = module(TensorDict({("input", "x"): torch.zeros(())})) >>> td["output", "x+1"] tensor(1.)
With empty
in_keys, the module only writes entries:>>> module = TensorDictModule(lambda: torch.zeros(3), in_keys=[], out_keys=["x"]) >>> module(TensorDict())["x"] tensor([0., 0., 0.])
A dict
in_keyspasses the values as keyword arguments. By default (out_to_in_map=True), its keys are the argument names and its values are the tensordict keys. One tensordict key can feed several arguments:>>> module = TensorDictModule(lambda x, *, y, z: x+y+z, ... in_keys={'x': '1', 'y': '2', 'z': '2'}, out_keys=['t'] ... ) >>> td = module(TensorDict({'1': torch.ones(()), '2': torch.ones(())*2}, [])) >>> td['t'] tensor(5.)
With
out_to_in_map=False, the keys of the dict are the tensordict keys and its values are the argument names:>>> module = TensorDictModule(lambda x, *, y: x+y, ... in_keys={'1': 'x', '2': 'y'}, out_keys=['z'], out_to_in_map=False ... ) >>> td = module(TensorDict({'1': torch.ones(()), '2': torch.ones(())*2}, [])) >>> td['z'] tensor(3.)
methodcalls another method of the module, here with a keyword argument frommethod_kwargs:>>> class MyNet(nn.Module): ... def my_func(self, tensor, *, an_integer): ... return tensor + an_integer ... >>> module = TensorDictModule( ... MyNet(), ... in_keys=["a"], ... out_keys=["b"], ... method="my_func", ... method_kwargs={"an_integer": 2}, ... ) >>> module(TensorDict(a=0))["b"] tensor(2)
To call the module with other parameters, put them in a tensordict and use
to_module()as a context manager:>>> module = TensorDictModule( ... nn.GRUCell(4, 8), in_keys=["input", "hidden"], out_keys=["output"] ... ) >>> params = TensorDict.from_module(module) >>> params = params.apply(torch.zeros_like) >>> td = TensorDict(input=torch.ones(3, 4), hidden=torch.ones(3, 8), batch_size=[3]) >>> with params.to_module(module): ... td = module(td) >>> # with zero weights, the output is half the hidden state >>> (td["output"] == 0.5).all() tensor(True)
- property device: device#
The device of the first parameter of the module, or
cpuif it has no parameter.Deprecated since version 0.15: This property will be removed in TensorDict 0.17 because modules may contain parameters and buffers on multiple devices.
- forward(tensordict: TensorDictBase = None, args=None, *, tensordict_out: TensorDictBase | None = None, **kwargs: Any) TensorDictBase#
Reads
in_keysfromtensordict, calls the wrapped module, and writesout_keys.If you call the module with tensors instead of a tensordict, the tensors are put in a new tensordict first, and the call returns the output tensors (see
dispatch()).- Parameters:
tensordict (TensorDictBase) – the input tensordict.
- Keyword Arguments:
tensordict_out (TensorDictBase, optional) – if given, the outputs are written into it and it is returned, whatever the value of
inplace. Defaults toNone.**kwargs – passed to the wrapped module.
- Returns:
The tensordict that holds the outputs (see
inplace).
- property in_keys#
The keys that the module reads from the input tensordict.
- property is_functional: bool#
Whether the wrapped module is a legacy functorch functional module.
Deprecated since version 0.15: This property will be removed in TensorDict 0.17. It only checks for the
FunctionalModuleclasses of the legacyfunctorch.make_functionalAPI and has no replacement.
- static is_tdmodule_compatible(module)#
Returns
Trueifmodulehas both anin_keysand anout_keysattribute.TensorDictSequentialwraps the callables that fail this check in aWrapModule.- Parameters:
module (object) – the object to check.
- property out_keys#
The keys that the module writes to the output tensordict.
After
select_out_keys(), only the selected keys. The full list is inout_keys_source.
- property out_keys_source#
All the keys that the module writes, before
select_out_keys().
- reset_out_keys()#
Undoes
select_out_keys(): the module writes all itsout_keysagain.- Returns:
self.
Examples
>>> import torch >>> from tensordict import TensorDict >>> from tensordict.nn import TensorDictModule >>> mod = TensorDictModule( ... lambda x, y: (x + 2, y + 2), in_keys=["a", "b"], out_keys=["c", "d"] ... ) >>> _ = mod.select_out_keys("d") >>> mod.out_keys ['d'] >>> _ = mod.reset_out_keys() >>> mod.out_keys ['c', 'd'] >>> td = mod(TensorDict(a=torch.zeros(()), b=torch.ones(()))) >>> sorted(td.keys()) ['a', 'b', 'c', 'd']
- reset_parameters_recursive(parameters: TensorDictBase | None = None) TensorDictBase | None#
Resets the parameters of every submodule that has a
reset_parametersmethod.Without
parameters, the parameters of the module are reset in place. Withparameters, the module is left as it is: the values inparametersare reset in place, as if they were loaded into the module (seeto_module()). Use this for parameters kept outside the module, for example in a functional call. Values that share their storage with the module parameters, such as those offrom_module(), reset the module too.- Parameters:
parameters (TensorDictBase, optional) – the parameters to reset, with the structure of
from_module()and an empty batch size. Defaults toNone, which resets the parameters of the module.- Returns:
Nonewithoutparameters. Otherwise a new tensordict of tensors that share their storage withparametersand require gradients.- Raises:
RuntimeError – if
parametershas batch dimensions.
Without
parameters, the call warns if no submodule has areset_parametersmethod.Examples
>>> import torch >>> from tensordict import TensorDict >>> from tensordict.nn import TensorDictModule >>> from torch import nn >>> net = nn.Sequential(nn.Linear(2, 3), nn.ReLU()) >>> old_weight = net[0].weight.clone() >>> module = TensorDictModule(net, in_keys=["x"], out_keys=["y"]) >>> module.reset_parameters_recursive() >>> torch.equal(old_weight, net[0].weight) False
The parameters can also live outside the module:
>>> params = TensorDict.from_module(module).clone() >>> old_params = params.clone() >>> _ = module.reset_parameters_recursive(params) >>> (old_params == params).any() False >>> torch.equal(net[0].weight, old_params["module", "0", "weight"]) True
- select_out_keys(*out_keys) TensorDictModuleBase#
Keeps only some of the
out_keysin the output of the module, and returns the module.Use it to drop intermediate outputs, for example those of a
TensorDictSequential. The module changes in place:out_keysbecomes the selected keys, andout_keys_sourcekeeps the full list. A call still computes all the outputs, and then removes the ones that are not selected. It removes nothing else, so the input entries stay in the output. When you call the module with tensors (seedispatch()), it returns only the selected outputs.reset_out_keys()undoes the selection.- Parameters:
*out_keys (NestedKey) – the keys to keep. Each one must be in
out_keys.- Returns:
self.- Raises:
ValueError – if a key is not in
out_keys. The error names the missing keys, and is also aRuntimeError.
Examples
>>> import torch >>> from tensordict import TensorDict >>> from tensordict.nn import TensorDictModule >>> mod = TensorDictModule( ... lambda x, y: (x + 2, y + 2), in_keys=["a", "b"], out_keys=["c", "d"] ... ) >>> _ = mod.select_out_keys("d") >>> mod.out_keys, mod.out_keys_source (['d'], ['c', 'd']) >>> td = mod(TensorDict(a=torch.zeros(()), b=torch.ones(()))) >>> sorted(td.keys()) ['a', 'b', 'd'] >>> mod(torch.zeros(()), torch.ones(())) tensor(3.) >>> _ = mod.reset_out_keys() >>> sorted(mod(TensorDict(a=torch.zeros(()), b=torch.ones(()))).keys()) ['a', 'b', 'c', 'd']
It works on a
TensorDictSequentialtoo:>>> from tensordict.nn import TensorDictSequential >>> seq = TensorDictSequential( ... TensorDictModule(lambda x: x + 1, in_keys=["x"], out_keys=["y"]), ... TensorDictModule(lambda y: y + 1, in_keys=["y"], out_keys=["z"]), ... ) >>> _ = seq.select_out_keys("z") >>> sorted(seq(TensorDict(x=torch.zeros(()))).keys()) ['x', 'z']