Skip to content

Commit 945939d

Browse files
vmoenscursoragent
andcommitted
[Doc] Migrate shifted=True callers to legacy/compact + docstring polish
- Switch benchmarks/test_objectives_benchmarks.py, examples/rlhf/utils.py and knowledge_base/ISAACLAB.md callers from the deprecated ``shifted=True`` to the explicit ``shifted="legacy"`` / ``shifted="compact"`` API. - Update data_layout.rst to mention the new shifted modes instead of ``single_call=True``. - Expand the ``compact_obs`` docstring on Collector to call out clean composition with ``GAE(shifted="compact")``. - Clarify in the GAE docstring how shifted="legacy", shifted="compact" and shifted=False differ in the recurrent-value-net case. - Clarify on MultiCollector that ``policy_version`` / ``get_policy_version`` expose only the parent-side tracker state; the per-frame ``"policy_version"`` tensor is the source of truth for collected data. - Minor formatting fix in examples/collectors/isaaclab_rnn_ppo_memory.py. Authored with Claude. Co-authored-by: Cursor <cursoragent@cursor.com> ghstack-source-id: bf58ce4 Pull-Request: #3776
1 parent f10c2e3 commit 945939d

8 files changed

Lines changed: 61 additions & 21 deletions

File tree

benchmarks/test_objectives_benchmarks.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,11 @@ def test_a2c_speed(
844844

845845
loss = A2CLoss(actor_network=actor, critic_network=critic)
846846
advantage = GAE(
847-
value_network=critic, gamma=0.99, lmbda=0.95, shifted=True, device=device
847+
value_network=critic,
848+
gamma=0.99,
849+
lmbda=0.95,
850+
shifted="legacy",
851+
device=device,
848852
)
849853
advantage(td)
850854
loss(td)
@@ -949,7 +953,11 @@ def test_ppo_speed(
949953

950954
loss = ClipPPOLoss(actor_network=actor, critic_network=critic)
951955
advantage = GAE(
952-
value_network=critic, gamma=0.99, lmbda=0.95, shifted=True, device=device
956+
value_network=critic,
957+
gamma=0.99,
958+
lmbda=0.95,
959+
shifted="legacy",
960+
device=device,
953961
)
954962
advantage(td)
955963
loss(td)
@@ -1054,7 +1062,11 @@ def test_reinforce_speed(
10541062

10551063
loss = ReinforceLoss(actor_network=actor, critic_network=critic)
10561064
advantage = GAE(
1057-
value_network=critic, gamma=0.99, lmbda=0.95, shifted=True, device=device
1065+
value_network=critic,
1066+
gamma=0.99,
1067+
lmbda=0.95,
1068+
shifted="legacy",
1069+
device=device,
10581070
)
10591071
advantage(td)
10601072
loss(td)

docs/source/reference/data_layout.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ Two main patterns coexist in TorchRL:
3232
trajectory structure (recurrent modules under
3333
:func:`~torchrl.modules.set_recurrent_mode`,
3434
:class:`~torchrl.data.SliceSampler`, value estimators in
35-
``single_call=True`` mode) consumes this layout natively.
35+
explicit shifted modes such as ``shifted="compact"`` or
36+
``shifted="legacy"``) consumes this layout natively.
3637

3738
The rest of this page walks through the building blocks.
3839

examples/collectors/isaaclab_rnn_ppo_memory.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,7 @@ def main() -> None:
308308
raise ValueError("--num-envs must be divisible by --num-collectors.")
309309
if args.compile_update or args.cudagraph_update:
310310
torch._dynamo.config.capture_scalar_outputs = True
311-
gae_shifted: bool | str = (
312-
False if args.gae_shifted == "false" else args.gae_shifted
313-
)
311+
gae_shifted: bool | str = False if args.gae_shifted == "false" else args.gae_shifted
314312

315313
torch.manual_seed(args.seed)
316314
torch.set_float32_matmul_precision("high")

examples/rlhf/utils.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,11 @@ def make_reward_model(reward_model_cfg, sys_cfg):
375375

376376
def make_loss(actor, critic, critic_head):
377377
advantage = GAE(
378-
value_network=critic, gamma=0.99, lmbda=0.95, average_gae=True, shifted=True
378+
value_network=critic,
379+
gamma=0.99,
380+
lmbda=0.95,
381+
average_gae=True,
382+
shifted="legacy",
379383
)
380384
loss_fn = ClipPPOLoss(actor, critic_head)
381385
return loss_fn, advantage

knowledge_base/ISAACLAB.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ from tensordict import TensorDict
320320
collector.update_policy_weights_(weights=TensorDict.from_module(actor).data)
321321
```
322322

323-
With compact rollout data, prefer `shifted=True` value estimation so the PPO
323+
With compact rollout data, prefer `shifted="compact"` value estimation so the PPO
324324
batch does not need `("next", "policy")` rehydration. If a backend requires
325325
canonical strides, `td.contiguous()` and `td.clone()` may not be enough for
326326
size-1 dimensions; `torch.empty_like(td).update_(td)` is the stronger

torchrl/collectors/_multi_base.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,12 @@ class MultiCollector(BaseCollector, metaclass=_MultiCollectorMeta):
329329
the version under which each individual frame was produced, not as a batch-level
330330
label.
331331
332+
For multi-process collectors, the ``"policy_version"`` entries in the
333+
collected tensordict are produced by worker-local transforms and are the
334+
source of truth for data provenance. The parent collector's
335+
:attr:`policy_version` property exposes only the parent-side tracker state
336+
and should not be used as a label for a returned batch.
337+
332338
The recommended path is ``track_policy_version=True``: let the collector own
333339
the transform. Passing a :class:`~torchrl.envs.llm.transforms.policy_version.PolicyVersion`
334340
instance directly is reserved for advanced use cases that wire up a
@@ -1986,19 +1992,28 @@ def increment_version(self):
19861992

19871993
@property
19881994
def policy_version(self) -> str | int | None:
1989-
"""The current policy version."""
1995+
"""The parent-side policy version.
1996+
1997+
For multi-process collectors, worker-local
1998+
:class:`~torchrl.envs.llm.transforms.policy_version.PolicyVersion`
1999+
transforms write the per-frame ``"policy_version"`` values in returned
2000+
batches. Those tensor entries are the source of truth for collected
2001+
data; this property is only the parent-side tracker state.
2002+
"""
19902003
if not hasattr(self.policy_version_tracker, "version"):
19912004
return None
19922005
return self.policy_version_tracker.version
19932006

19942007
def get_policy_version(self) -> str | int | None:
1995-
"""Get the current policy version.
2008+
"""Get the parent-side policy version.
19962009
19972010
This method exists to support remote calls in Ray actors, since properties
19982011
cannot be accessed directly through Ray's RPC mechanism.
19992012
20002013
Returns:
2001-
The current version number (int) or UUID (str), or None if version tracking is disabled.
2014+
The parent-side version number (int) or UUID (str), or ``None`` if
2015+
version tracking is disabled. For collected data, prefer the
2016+
per-frame ``"policy_version"`` tensor in returned batches.
20022017
"""
20032018
return self.policy_version
20042019

torchrl/collectors/_single.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,15 @@ class Collector(BaseCollector):
287287
keys can be re-hydrated at sampling time with
288288
:class:`~torchrl.envs.transforms.rb_transforms.NextStateReconstructor`
289289
when consuming a :class:`~torchrl.data.SliceSampler`-backed replay
290-
buffer. Defaults to ``False``.
290+
buffer.
291+
292+
``compact_obs=True`` composes cleanly with
293+
:class:`~torchrl.objectives.value.advantages.GAE` configured with
294+
``shifted="compact"``: the compact shifted path can run the
295+
on-policy advantage pass without rehydrating every per-step
296+
``("next", "observation")`` mirror. For vectorized environments
297+
with large observations this is typically a sizeable GPU-memory
298+
win at near-zero CPU cost. Defaults to ``False``.
291299
292300
Examples:
293301
>>> from torchrl.envs.libs.gym import GymEnv

torchrl/objectives/value/advantages.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,14 +1690,16 @@ class GAE(ValueEstimatorBase):
16901690
16911691
.. note:: GAE can be used with value networks that rely on recurrent neural networks, provided that the
16921692
init markers (`"is_init"`) and terminated / truncated markers are properly set.
1693-
If `shifted=True`, the trajectory batch will be flattened and the last step of each trajectory will
1694-
be placed within the flat tensordict after the last step from the root, such that each trajectory has
1695-
`T+1` elements. If `shifted=False`, the root and `"next"` trajecotries will be stacked and the value
1696-
network will be called with `vmap` over the stack of trajectories. Because RNNs require fair amount of
1697-
control flow, they are currently not compatible with `torch.vmap` and, as such, the `deactivate_vmap` option
1698-
must be turned on in these cases.
1699-
Similarly, if `shifted=False`, the `"is_init"` entry of the root tensordict will be copied onto the
1700-
`"is_init"` of the `"next"` entry, such that trajectories are well separated both for root and `"next"` data.
1693+
With ``shifted="legacy"``, the trajectory batch is flattened and the next state of each done step is
1694+
interleaved after its root state, giving exact ``V(next_obs)`` values at the cost of a data-dependent
1695+
shape. With ``shifted="compact"``, root and next streams are concatenated into a constant-shape
1696+
batch, which is friendlier to ``torch.compile`` and scan-style recurrent backends. If ``shifted=False``,
1697+
the root and ``"next"`` trajectories are stacked and the value network is called with ``vmap`` over the
1698+
stack of trajectories. Because RNNs require a fair amount of control flow, they are currently not
1699+
compatible with ``torch.vmap`` and, as such, the ``deactivate_vmap`` option must be turned on in these
1700+
cases. Similarly, if ``shifted=False``, the ``"is_init"`` entry of the root tensordict will be copied
1701+
onto the ``"is_init"`` of the ``"next"`` entry, such that trajectories are well separated both for root
1702+
and ``"next"`` data.
17011703
"""
17021704

17031705
value_network: TensorDictModule | None

0 commit comments

Comments
 (0)