Skip to content

[Algorithm] Add RLHF reward-model training recipe#3923

Open
theap06 wants to merge 4 commits into
pytorch:mainfrom
theap06:reward-model-recipe
Open

[Algorithm] Add RLHF reward-model training recipe#3923
theap06 wants to merge 4 commits into
pytorch:mainfrom
theap06:reward-model-recipe

Conversation

@theap06

@theap06 theap06 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What

Adds a dedicated sota-implementations/reward_model_training/ recipe that trains a scalar reward model from pairwise human-preference data using RewardModelLoss. This modernizes the legacy examples/rlhf/train_reward.py (originally from #1597) into the current sota-implementations + LLM-API style, and is model-agnostic: any HF AutoModelForSequenceClassification backbone works.

Stacked on #3922

This PR depends on #3922 (the RewardModelLoss objective) and is stacked on top of it — the recipe imports RewardModelLoss and the CI smoke test exercises it end to end. Please merge #3922 first; once it lands, the diff here reduces to just the recipe + CI files (the first commit shown belongs to #3922).

Contents

  • sota-implementations/reward_model_training/: reward_model.py (Hydra main), utils.py, config.yaml, requirements.txt, README.md.
  • Hermetic CI path: an empty model.name builds a tiny from-config model and an empty data.dataset_name generates synthetic preference pairs, so the smoke test needs no download and no HF datasets dependency (which the linux_sota env does not provide).
  • CI: a reward_model_training smoke entry in .github/unittest/linux_sota/scripts/test_sota.py.
  • sota-check/run_reward_model_training.sh for full release runs.

Tests

Ran the exact CI smoke command locally: builds a tiny model, trains on synthetic pairs, evaluates (val loss + accuracy), and checkpoints — exit 0. ruff + black clean.

Real usage: python reward_model.py model.name=gpt2 data.dataset_name=CarperAI/openai_summarize_comparisons.

theap06 and others added 2 commits June 30, 2026 00:17
Add a model-agnostic Bradley-Terry pairwise reward-model loss as a first-class
objective under torchrl/objectives/llm/, following the SFTLoss/GRPOLoss
conventions (_AcceptedKeys/set_keys, TensorClass output, runnable docstring with
paper references).

This resolves the "# TODO: move to objectives" left on
GPT2RewardModel.compute_reward_loss: the loss now lives in objectives and owns a
swappable score_network (any TensorDictModule producing a per-sequence scalar,
e.g. AutoModelForSequenceClassification(num_labels=1) or GPT2RewardModel). It
supports mean/sum/none reduction, an optional score-centering regularizer, and a
detached accuracy metric for logging.

- torchrl/objectives/llm/reward.py: RewardModelLoss, RewardModelLossOutput and
  the reward_model_loss helper.
- Export both from torchrl/objectives/llm/__init__.py.
- Docs: Reward Model Training sections in llms_objectives.rst and llms.rst.
- Tests: TestRewardModel in test/llm/test_llm_objectives.py (CPU-only, exercises
  a nested-key input per the contributor guide).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a dedicated sota-implementations recipe that trains a scalar reward model
from pairwise human-preference data using RewardModelLoss. This modernizes the
legacy examples/rlhf/train_reward.py into the current sota-implementations and
LLM-API style, and is model-agnostic: any Hugging Face
AutoModelForSequenceClassification backbone can be used.

- sota-implementations/reward_model_training/: reward_model.py (Hydra main),
  utils.py, config.yaml, requirements.txt, README.md.
- Hermetic CI path: an empty model.name builds a tiny from-config model and an
  empty data.dataset_name generates synthetic preference pairs, so the smoke
  test needs no download and no `datasets` dependency (which the linux_sota env
  does not provide).
- CI: add a reward_model_training smoke entry to
  .github/unittest/linux_sota/scripts/test_sota.py.
- sota-check/run_reward_model_training.sh for full release runs.

Depends on the RewardModelLoss objective (separate PR); the recipe imports it
and the smoke test exercises it end to end.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pytorch-bot

pytorch-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/rl/3923

Note: Links to docs will display an error until the docs builds have been completed.

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@github-actions github-actions Bot added Documentation Improvements or additions to documentation CI Has to do with CI setup (e.g. wheels & builds, tests...) Objectives llm/ LLM-related PR, triggers LLM CI tests sota-implementations/ new algo New algorithm request or PR labels Jun 30, 2026
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 30, 2026
@theap06 theap06 marked this pull request as draft June 30, 2026 07:22
@theap06 theap06 marked this pull request as ready for review June 30, 2026 07:30
@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results: PR 45c5f468 vs main 74abd6c3

Benchmark run: https://github.com/pytorch/rl/actions/runs/28442614939

Higher ops/sec is better. Tables are sorted by largest absolute change.

CPU

Compared 216 benchmarks. Regressions over 5%: 14. Improvements over 5%: 11.

Benchmark main ops PR ops Change
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictReplayBuffer-ListStorage-SamplerWithoutReplacement-400] 48.27 196.65 +307.39%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictReplayBuffer-ListStorage-RandomSampler-400] 195.10 36.26 -81.41%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictPrioritizedReplayBuffer-ListStorage-None-400] 186.20 56.49 -69.66%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[torchvision-256-256-64] 10.93 8.5178 -22.05%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[torchvision-480-640-16] 37.21 29.14 -21.70%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-LazyMemmapStorage-SamplerWithoutReplacement-10000] 3,435 2,713 -21.03%
benchmarks/test_objectives_benchmarks.py::test_dqn_speed[True-backward] 852.36 990.80 +16.24%
benchmarks/test_envs_benchmark.py::test_cat_frames_functional[4-same] 29.18 24.68 -15.41%
benchmarks/test_envs_benchmark.py::test_cat_frames_functional[16-same] 20.25 23.28 +14.95%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[torchvision-224-224-64] 12.58 10.96 -12.88%
benchmarks/test_replaybuffer_benchmark.py::test_rb_iterate[TensorDictReplayBuffer-LazyTensorStorage-SamplerWithoutReplacement-10000] 3,299 3,664 +11.06%
benchmarks/test_collectors_benchmark.py::test_single_with_rb 7.3840 6.6205 -10.34%
benchmarks/test_replaybuffer_benchmark.py::test_rb_iterate[TensorDictReplayBuffer-LazyMemmapStorage-RandomSampler-10000] 2,980 2,678 -10.14%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictPrioritizedReplayBuffer-LazyMemmapStorage-None-10000] 2,124 1,914 -9.85%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-LazyTensorStorage-RandomSampler-10000] 3,323 3,639 +9.49%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-LazyMemmapStorage-sampler6-10000] 789.62 716.80 -9.22%
benchmarks/test_compressed_storage_benchmark.py::TestCompressedStorageBenchmark::test_tensor_to_bytestream_speed[safetensors] 22,590 24,360 +7.84%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[torchvision-480-640-64] 6.5785 6.0691 -7.74%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-LazyMemmapStorage-RandomSampler-10000] 2,984 2,757 -7.60%
benchmarks/test_replaybuffer_benchmark.py::test_rb_iterate[TensorDictPrioritizedReplayBuffer-LazyMemmapStorage-None-10000] 2,008 2,153 +7.23%
benchmarks/test_replaybuffer_benchmark.py::test_rb_iterate[TensorDictReplayBuffer-LazyTensorStorage-RandomSampler-10000] 3,002 2,813 -6.28%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictReplayBuffer-LazyMemmapStorage-RandomSampler-400] 527.70 560.66 +6.24%
benchmarks/test_objectives_benchmarks.py::test_dqn_speed[True-None] 1,710 1,813 +6.00%
benchmarks/test_objectives_benchmarks.py::test_ppo_speed[True-backward] 110.31 116.80 +5.88%
benchmarks/test_objectives_benchmarks.py::test_reinforce_speed[reduce-overhead-None] 323.45 341.74 +5.65%
benchmarks/test_non_tensor_env_benchmark.py::test_non_tensor_env_rollout_speed[1000-single-True] 1.3294 1.3940 +4.86%
benchmarks/test_objectives_benchmarks.py::test_redq_speed[True-None] 224.07 234.95 +4.86%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-True-False-False-True] 30,909 29,434 -4.77%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictPrioritizedReplayBuffer-LazyTensorStorage-None-10000] 2,081 1,982 -4.75%
benchmarks/test_compressed_storage_benchmark.py::TestCompressedStorageBenchmark::test_tensor_to_bytestream_speed[untyped_storage] 8.5345 8.1331 -4.70%
benchmarks/test_replaybuffer_benchmark.py::test_rb_iterate[TensorDictPrioritizedReplayBuffer-LazyTensorStorage-None-10000] 2,138 2,239 +4.70%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictReplayBuffer-LazyTensorStorage-SamplerWithoutReplacement-400] 1,063 1,014 -4.66%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_collector_stack_then_write[100-img_shape2-large_img] 176.30 168.16 -4.61%
benchmarks/test_objectives_benchmarks.py::test_redq_speed[reduce-overhead-None] 227.68 238.17 +4.61%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictReplayBuffer-LazyTensorStorage-RandomSampler-400] 1,071 1,025 -4.25%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-LazyTensorStorage-sampler7-10000] 837.51 803.84 -4.02%
benchmarks/test_objectives_benchmarks.py::test_values[td0_return_estimate-False-False] 7,726 7,426 -3.88%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_storage_write_lazystack[100-img_shape1-atari] 691.99 718.69 +3.86%
benchmarks/test_objectives_benchmarks.py::test_iql_speed[reduce-overhead-None] 115.00 119.29 +3.74%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-False-True-False-True] 30,913 29,866 -3.39%
benchmarks/test_replaybuffer_benchmark.py::test_rb_extend_sample[ReplayBuffer-LazyTensorStorage-RandomSampler-100000-10000-100-False] 51.74 53.48 +3.35%
benchmarks/test_objectives_benchmarks.py::test_redq_deprec_speed[False-backward] 63.50 61.44 -3.25%
benchmarks/test_collectors_benchmark.py::test_sync 16.86 16.31 -3.21%
benchmarks/test_replaybuffer_benchmark.py::TestPrioritizedReplayBufferBenchmark::test_sample_mixed_devices[1000000-memmap_cpu_storage_cpu... 80.82 83.39 +3.18%
benchmarks/test_replaybuffer_benchmark.py::TestPrioritizedReplayBufferBenchmark::test_sampler_sample_scale[1000000-cpu] 96.11 99.06 +3.07%
benchmarks/test_objectives_benchmarks.py::test_redq_deprec_speed[True-backward] 140.07 135.94 -2.94%
benchmarks/test_objectives_benchmarks.py::test_iql_speed[True-backward] 59.80 61.53 +2.90%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-LazyTensorStorage-SamplerWithoutReplacement-10000] 3,633 3,738 +2.90%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-True-False-False-False] 49,261 50,685 +2.89%
benchmarks/test_envs_benchmark.py::test_parallel 0.9285 0.9551 +2.87%
benchmarks/test_envs_benchmark.py::test_simple 1.7691 1.7189 -2.84%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_storage_write_lazystack[200-img_shape3-large_batch] 330.38 339.72 +2.83%
benchmarks/test_objectives_benchmarks.py::test_ppo_speed[True-None] 259.79 267.09 +2.81%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictPrioritizedReplayBuffer-LazyTensorStorage-None-400] 878.18 902.64 +2.79%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-True-False-False-True] 38,528 37,507 -2.65%
benchmarks/test_objectives_benchmarks.py::test_a2c_speed[True-backward] 119.04 122.18 +2.65%
benchmarks/test_objectives_benchmarks.py::test_redq_deprec_speed[reduce-overhead-None] 273.27 280.46 +2.63%
benchmarks/test_envs_benchmark.py::test_transformed 0.9218 0.8977 -2.61%
benchmarks/test_replaybuffer_benchmark.py::test_rb_extend_sample[ReplayBuffer-LazyTensorStorage-RandomSampler-1000000-10000-100-True] 23.53 24.14 +2.59%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictPrioritizedReplayBuffer-LazyMemmapStorage-None-400] 481.66 469.17 -2.59%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-True-True-True-True] 20,630 20,102 -2.56%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-True-False-True-False] 38,182 39,158 +2.56%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_collector_stack_then_write[100-img_shape1-atari] 273.85 280.77 +2.53%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_collector_lazystack_then_write[200-img_shape3-large_batch] 309.04 316.80 +2.51%
benchmarks/test_replaybuffer_benchmark.py::test_rb_iterate[TensorDictPrioritizedReplayBuffer-ListStorage-None-4000] 163.05 166.99 +2.41%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-False-True-True-False] 29,064 29,764 +2.41%
benchmarks/test_objectives_benchmarks.py::test_a2c_speed[reduce-overhead-None] 288.97 295.88 +2.39%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-False-True-False-True] 36,768 37,632 +2.35%
benchmarks/test_objectives_benchmarks.py::test_ddpg_speed[False-None] 342.32 350.27 +2.32%
benchmarks/test_objectives_benchmarks.py::test_gae_speed[vec_generalized_advantage_estimate-True-1-512] 638.60 653.13 +2.28%
benchmarks/test_storage_write_benchmark.py::TestCollectorIntegrationBenchmark::test_collector_with_rb[200-img_shape1-large_batch] 10.43 10.19 -2.27%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictReplayBuffer-LazyMemmapStorage-SamplerWithoutReplacement-400] 531.84 519.77 -2.27%
benchmarks/test_envs_benchmark.py::test_cat_frames_functional[4-constant] 4,346 4,443 +2.23%
benchmarks/test_envs_benchmark.py::test_serial 0.5736 0.5864 +2.22%
benchmarks/test_replaybuffer_benchmark.py::test_rb_extend_sample[ReplayBuffer-LazyTensorStorage-RandomSampler-100000-10000-100-True] 24.46 25.00 +2.18%
benchmarks/test_collectors_benchmark.py::test_sync_preempt 16.76 16.40 -2.17%
benchmarks/test_replaybuffer_benchmark.py::test_rb_extend_sample[ReplayBuffer-LazyTensorStorage-RandomSampler-10000-10000-100-False] 53.32 54.44 +2.09%
benchmarks/test_objectives_benchmarks.py::test_td3_speed[True-None] 549.78 561.21 +2.08%
benchmarks/test_objectives_benchmarks.py::test_dqn_speed[reduce-overhead-None] 1,819 1,857 +2.08%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_collector_stack_then_write[200-img_shape3-large_batch] 140.54 143.41 +2.04%
benchmarks/test_replaybuffer_benchmark.py::TestPrioritizedReplayBufferBenchmark::test_sampler_sample_scale[10000000-cpu] 53.49 52.41 -2.02%
benchmarks/test_objectives_benchmarks.py::test_a2c_speed[True-None] 288.12 293.90 +2.01%
benchmarks/test_replaybuffer_benchmark.py::test_rb_extend_sample[ReplayBuffer-LazyTensorStorage-RandomSampler-1000000-10000-100-False] 48.44 49.40 +1.98%
benchmarks/test_objectives_benchmarks.py::test_ppo_speed[False-None] 158.94 162.09 +1.98%
benchmarks/test_objectives_benchmarks.py::test_values[generalized_advantage_estimate-True-True] 100.96 99.00 -1.95%
benchmarks/test_rnn_reset_backends_benchmark.py::test_rnn_rollout_with_intermediate_resets[b256-t128-i32-h512-scan-False-0-gru] 3.0542 3.1133 +1.94%
benchmarks/test_objectives_benchmarks.py::test_redq_deprec_speed[False-None] 89.80 88.09 -1.90%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-False-True-False-False] 63,387 64,586 +1.89%
benchmarks/test_replaybuffer_benchmark.py::test_rb_extend_sample[ReplayBuffer-LazyTensorStorage-RandomSampler-10000-10000-100-True] 25.52 25.98 +1.83%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_storage_write_contiguous[100-img_shape1-atari] 5,150 5,055 -1.83%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[pil-224-224-1] 284.56 279.41 -1.81%
benchmarks/test_objectives_benchmarks.py::test_sac_speed[reduce-overhead-None] 473.03 481.51 +1.79%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-ListStorage-SamplerWithoutReplacement-4000] 165.90 168.85 +1.78%
benchmarks/test_rnn_reset_backends_benchmark.py::test_rnn_rollout_with_intermediate_resets[b256-t128-i32-h512-cudnn-False-0-gru] 1.3509 1.3272 -1.76%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_collector_lazystack_then_write[100-img_shape1-atari] 638.58 649.54 +1.72%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-False-False-False-False] 54,980 55,917 +1.70%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-True-True-True-False] 41,943 42,654 +1.70%
benchmarks/test_objectives_benchmarks.py::test_ppo_speed[reduce-overhead-None] 266.20 270.56 +1.64%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_collector_lazystack_then_write[50-img_shape0-small] 3,541 3,485 -1.60%
benchmarks/test_rnn_reset_backends_benchmark.py::test_rnn_rollout_with_intermediate_resets[b256-t128-i32-h512-scan-True-0-gru] 4.2813 4.3488 +1.58%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_storage_write_lazystack[50-img_shape0-small] 4,443 4,375 -1.54%
benchmarks/test_objectives_benchmarks.py::test_td3_speed[True-backward] 281.55 285.87 +1.53%
benchmarks/test_objectives_benchmarks.py::test_cql_speed[False-None] 37.86 38.44 +1.53%
benchmarks/test_rnn_reset_backends_benchmark.py::test_rnn_rollout_with_intermediate_resets[b256-t128-i32-h512-scan-False-0-lstm] 2.0192 2.0499 +1.52%
benchmarks/test_objectives_benchmarks.py::test_td3_speed[reduce-overhead-None] 566.66 574.67 +1.41%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-True-True-False-True] 32,407 32,859 +1.40%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[torchvision-256-256-16] 43.60 44.20 +1.38%
benchmarks/test_non_tensor_env_benchmark.py::test_non_tensor_env_rollout_speed[1000-parallel-buffers-True] 0.5194 0.5265 +1.37%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-False-True-True-False] 34,495 34,961 +1.35%
benchmarks/test_objectives_benchmarks.py::test_gae_speed[vec_generalized_advantage_estimate-False-1-512] 2,241 2,270 +1.32%
benchmarks/test_compressed_storage_benchmark.py::TestCompressedStorageBenchmark::test_tensor_to_bytestream_speed[numpy] 372,800 377,459 +1.25%
benchmarks/test_non_tensor_env_benchmark.py::test_non_tensor_env_rollout_speed[1000-parallel-no-buffers-False] 0.2254 0.2226 -1.23%
benchmarks/test_objectives_benchmarks.py::test_ddpg_speed[False-backward] 241.99 244.91 +1.21%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-False-False-False-True] 28,862 28,522 -1.18%
benchmarks/test_objectives_benchmarks.py::test_reinforce_speed[False-backward] 133.69 132.15 -1.15%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[pil-224-224-4] 72.05 71.22 -1.15%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-True-False-False-False] 65,265 64,522 -1.14%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-True-False-True-True] 19,474 19,694 +1.13%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-ListStorage-RandomSampler-4000] 160.05 161.83 +1.11%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-True-False-True-True] 21,925 22,165 +1.10%
... ... ... Showing 120 of 216 comparisons, sorted by absolute change.

GPU

Compared 226 benchmarks. Regressions over 5%: 10. Improvements over 5%: 15.

Benchmark main ops PR ops Change
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictPrioritizedReplayBuffer-ListStorage-None-400] 44.28 188.22 +325.10%
benchmarks/test_objectives_benchmarks.py::test_iql_speed[False-None] 53.12 99.37 +87.06%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictReplayBuffer-ListStorage-RandomSampler-400] 195.36 48.67 -75.09%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictReplayBuffer-LazyTensorStorage-RandomSampler-400] 1,009 726.93 -27.95%
benchmarks/test_objectives_benchmarks.py::test_iql_speed[reduce-overhead-None] 105.89 77.95 -26.38%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictReplayBuffer-LazyTensorStorage-SamplerWithoutReplacement-400] 1,006 757.70 -24.69%
benchmarks/test_replaybuffer_benchmark.py::test_rb_iterate[TensorDictReplayBuffer-LazyMemmapStorage-SamplerWithoutReplacement-10000] 3,421 2,784 -18.61%
benchmarks/test_replaybuffer_benchmark.py::test_rb_iterate[TensorDictReplayBuffer-LazyTensorStorage-RandomSampler-10000] 3,647 3,090 -15.28%
benchmarks/test_replaybuffer_benchmark.py::test_rb_iterate[TensorDictReplayBuffer-LazyTensorStorage-SamplerWithoutReplacement-10000] 3,022 2,584 -14.49%
benchmarks/test_replaybuffer_benchmark.py::test_rb_iterate[TensorDictReplayBuffer-LazyMemmapStorage-RandomSampler-10000] 3,365 2,898 -13.89%
benchmarks/test_objectives_benchmarks.py::test_values[vec_generalized_advantage_estimate-True-True] 325.13 286.13 -11.99%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_storage_write_contiguous[200-img_shape3-large_batch] 781.84 708.29 -9.41%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_storage_write_contiguous[100-img_shape1-atari] 4,041 4,364 +7.99%
benchmarks/test_objectives_benchmarks.py::test_reinforce_speed[False-backward] 268.99 289.75 +7.72%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_collector_lazystack_then_write[100-img_shape2-large_img] 404.73 435.93 +7.71%
benchmarks/test_objectives_benchmarks.py::test_reinforce_speed[True-backward] 347.66 374.02 +7.58%
benchmarks/test_objectives_benchmarks.py::test_ppo_speed[True-backward] 335.51 360.39 +7.42%
benchmarks/test_objectives_benchmarks.py::test_values[generalized_advantage_estimate-True-True] 46.60 49.83 +6.92%
benchmarks/test_objectives_benchmarks.py::test_cql_speed[True-backward] 217.30 231.08 +6.34%
benchmarks/test_objectives_benchmarks.py::test_reinforce_speed[True-None] 748.80 792.14 +5.79%
benchmarks/test_objectives_benchmarks.py::test_ppo_speed[False-None] 222.81 235.66 +5.77%
benchmarks/test_rnn_reset_backends_benchmark.py::test_rnn_rollout_with_intermediate_resets[b256-t128-i32-h512-scan-True-0-gru] 46.92 49.52 +5.56%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-True-True-False-False] 74,689 78,632 +5.28%
benchmarks/test_non_tensor_env_benchmark.py::test_non_tensor_env_rollout_speed[1000-single-True] 1.3060 1.3730 +5.13%
benchmarks/test_objectives_benchmarks.py::test_a2c_speed[False-backward] 148.80 156.25 +5.01%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-LazyTensorStorage-sampler7-10000] 756.70 720.74 -4.75%
benchmarks/test_replaybuffer_benchmark.py::test_rb_iterate[TensorDictPrioritizedReplayBuffer-LazyTensorStorage-None-10000] 2,006 2,100 +4.67%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_collector_stack_then_write[200-img_shape3-large_batch] 132.28 138.36 +4.60%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-True-True-True-True] 22,483 23,514 +4.58%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-LazyMemmapStorage-SamplerWithoutReplacement-10000] 2,876 2,747 -4.51%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_collector_stack_then_write[100-img_shape2-large_img] 167.59 174.97 +4.40%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-True-True-False-True] 31,052 32,390 +4.31%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictReplayBuffer-LazyMemmapStorage-SamplerWithoutReplacement-400] 481.59 501.69 +4.17%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictReplayBuffer-LazyMemmapStorage-RandomSampler-400] 477.65 496.97 +4.04%
benchmarks/test_objectives_benchmarks.py::test_dqn_speed[reduce-overhead-None] 1,838 1,911 +3.99%
benchmarks/test_objectives_benchmarks.py::test_reinforce_speed[False-None] 387.87 402.55 +3.78%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_storage_write_contiguous[100-img_shape2-large_img] 565.49 544.16 -3.77%
benchmarks/test_replaybuffer_benchmark.py::test_rb_iterate[TensorDictPrioritizedReplayBuffer-LazyMemmapStorage-None-10000] 2,066 1,988 -3.76%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-False-False-False-False] 54,039 55,999 +3.63%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[torchvision-480-640-4] 143.10 148.23 +3.59%
benchmarks/test_objectives_benchmarks.py::test_gae_speed[generalized_advantage_estimate-False-1-512] 47.91 49.59 +3.50%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-True-False-True-True] 21,373 22,091 +3.36%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[pil-224-224-16] 17.61 18.20 +3.34%
benchmarks/test_replaybuffer_benchmark.py::test_rb_extend_sample[ReplayBuffer-LazyTensorStorage-RandomSampler-1000000-10000-100-False] 47.75 49.33 +3.32%
benchmarks/test_envs_benchmark.py::test_cat_frames_functional[4-constant] 4,872 5,034 +3.32%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-False-False-True-True] 19,041 19,660 +3.25%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictPrioritizedReplayBuffer-LazyTensorStorage-None-10000] 1,905 1,967 +3.25%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-False-True-False-True] 35,861 37,010 +3.20%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[torchvision-224-224-64] 12.38 12.78 +3.18%
benchmarks/test_objectives_benchmarks.py::test_a2c_speed[reduce-overhead-None] 843.55 869.89 +3.12%
benchmarks/test_objectives_benchmarks.py::test_ddpg_speed[False-None] 337.00 347.46 +3.11%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_storage_write_lazystack[100-img_shape2-large_img] 433.51 446.88 +3.08%
benchmarks/test_replaybuffer_benchmark.py::test_rb_extend_sample[ReplayBuffer-LazyTensorStorage-RandomSampler-10000-10000-100-False] 52.70 54.28 +3.01%
benchmarks/test_objectives_benchmarks.py::test_ppo_speed[False-backward] 133.00 136.97 +2.98%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[torchvision-256-256-64] 10.69 11.00 +2.96%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[pil-256-256-1] 185.29 190.64 +2.89%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[pil-224-224-64] 4.4342 4.5620 +2.88%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-True-False-False-True] 36,128 37,132 +2.78%
benchmarks/test_objectives_benchmarks.py::test_values[td_lambda_return_estimate-True-False] 12.31 12.65 +2.76%
benchmarks/test_objectives_benchmarks.py::test_ddpg_speed[True-None] 817.79 840.32 +2.76%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-False-False-False-True] 27,976 28,738 +2.73%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[pil-224-224-4] 70.17 72.08 +2.72%
benchmarks/test_envs_benchmark.py::test_simple 1.2432 1.2094 -2.72%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-LazyMemmapStorage-RandomSampler-10000] 3,332 3,419 +2.61%
benchmarks/test_objectives_benchmarks.py::test_ppo_speed[reduce-overhead-None] 836.76 858.47 +2.60%
benchmarks/test_replaybuffer_benchmark.py::test_rb_extend_sample[ReplayBuffer-LazyTensorStorage-RandomSampler-10000-10000-100-True] 23.83 23.22 -2.58%
benchmarks/test_objectives_benchmarks.py::test_values[td0_return_estimate-False-False] 11,842 12,131 +2.44%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-False-False-False-True] 33,455 34,270 +2.44%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-LazyTensorStorage-RandomSampler-10000] 3,585 3,671 +2.42%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-True-True-False-True] 41,194 42,190 +2.42%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[pil-224-224-1] 277.71 284.34 +2.39%
benchmarks/test_rnn_reset_backends_benchmark.py::test_rnn_rollout_with_intermediate_resets[b256-t128-i32-h512-scan-False-0-lstm] 21.91 21.39 -2.38%
benchmarks/test_objectives_benchmarks.py::test_a2c_speed[True-backward] 363.42 371.99 +2.36%
benchmarks/test_objectives_benchmarks.py::test_ddpg_speed[False-backward] 240.56 246.02 +2.27%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_storage_write_contiguous[50-img_shape0-small] 6,095 5,956 -2.27%
benchmarks/test_compressed_storage_benchmark.py::TestCompressedStorageBenchmark::test_tensor_to_bytestream_speed[numpy] 361,830 353,804 -2.22%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-False-False-True-False] 27,340 27,944 +2.21%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-False-True-True-False] 35,709 34,927 -2.19%
benchmarks/test_rnn_reset_backends_benchmark.py::test_rnn_rollout_with_intermediate_resets[b256-t128-i32-h512-scan-False-0-gru] 23.16 22.65 -2.18%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-True-False-False-False] 63,334 64,686 +2.13%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-True-False-True-False] 32,638 31,955 -2.09%
benchmarks/test_objectives_benchmarks.py::test_dqn_speed[False-None] 641.20 654.57 +2.09%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[torchvision-480-640-64] 7.1453 7.2938 +2.08%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[torchvision-480-640-16] 36.23 36.98 +2.07%
benchmarks/test_non_tensor_env_benchmark.py::test_non_tensor_env_rollout_speed[1000-parallel-buffers-False] 0.6007 0.5883 -2.05%
benchmarks/test_objectives_benchmarks.py::test_sac_speed[True-backward] 329.13 335.85 +2.04%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-False-True-False-True] 29,561 30,159 +2.02%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-True-True-True-True] 20,068 20,470 +2.01%
benchmarks/test_objectives_benchmarks.py::test_values[td1_return_estimate-False-False] 20.37 20.77 +1.95%
benchmarks/test_objectives_benchmarks.py::test_iql_speed[True-None] 510.28 520.22 +1.95%
benchmarks/test_objectives_benchmarks.py::test_dqn_speed[False-backward] 457.78 466.47 +1.90%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictReplayBuffer-ListStorage-SamplerWithoutReplacement-400] 192.28 195.79 +1.83%
benchmarks/test_objectives_benchmarks.py::test_iql_speed[False-backward] 69.27 70.53 +1.82%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[False-True-True-True-False] 34,960 34,326 -1.81%
benchmarks/test_objectives_benchmarks.py::test_dqn_speed[True-None] 1,933 1,898 -1.79%
benchmarks/test_objectives_benchmarks.py::test_ddpg_speed[True-backward] 480.14 488.41 +1.72%
benchmarks/test_envs_benchmark.py::test_serial 0.4241 0.4169 -1.69%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-LazyTensorStorage-SamplerWithoutReplacement-10000] 2,878 2,830 -1.69%
benchmarks/test_compressed_storage_benchmark.py::TestCompressedStorageBenchmark::test_tensor_to_bytestream_speed[pickle] 12,080 12,283 +1.68%
benchmarks/test_objectives_benchmarks.py::test_redq_deprec_speed[True-None] 419.69 426.73 +1.68%
benchmarks/test_replaybuffer_benchmark.py::test_rb_sample[TensorDictReplayBuffer-ListStorage-SamplerWithoutReplacement-4000] 165.88 168.67 +1.68%
benchmarks/test_compressed_storage_benchmark.py::TestCompressedStorageBenchmark::test_tensor_to_bytestream_speed[torch.save] 7,292 7,173 -1.63%
benchmarks/test_objectives_benchmarks.py::test_td3_speed[False-None] 112.12 113.93 +1.62%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-True-False-True-False] 39,071 38,445 -1.60%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_storage_write_lazystack[200-img_shape3-large_batch] 328.63 323.44 -1.58%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[torchvision-256-256-1] 509.13 517.01 +1.55%
benchmarks/test_objectives_benchmarks.py::test_redq_deprec_speed[True-backward] 274.10 278.34 +1.54%
benchmarks/test_compressed_storage_benchmark.py::TestCompressedStorageBenchmark::test_tensor_to_bytestream_speed[safetensors] 24,016 24,367 +1.46%
benchmarks/test_replaybuffer_benchmark.py::test_rb_populate[TensorDictPrioritizedReplayBuffer-LazyTensorStorage-None-400] 712.07 722.45 +1.46%
benchmarks/test_objectives_benchmarks.py::test_sac_speed[False-backward] 80.69 81.87 +1.45%
benchmarks/test_vla_preprocessing_benchmark.py::test_openvla_preprocessing_throughput[pil-256-256-4] 47.44 48.12 +1.44%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-False-False-True-False] 31,582 32,017 +1.38%
benchmarks/test_objectives_benchmarks.py::test_a2c_speed[True-None] 736.77 726.67 -1.37%
benchmarks/test_envs_benchmark.py::test_step_mdp_speed[True-False-True-True-True] 20,381 20,659 +1.36%
benchmarks/test_objectives_benchmarks.py::test_td3_speed[True-None] 741.62 751.62 +1.35%
benchmarks/test_collectors_benchmark.py::test_sync 10.55 10.41 -1.32%
benchmarks/test_collectors_benchmark.py::test_single_pixels 6.3131 6.3923 +1.25%
benchmarks/test_non_tensor_env_benchmark.py::test_non_tensor_env_rollout_speed[1000-parallel-no-buffers-True] 0.2114 0.2140 +1.25%
benchmarks/test_storage_write_benchmark.py::TestStorageWriteBenchmark::test_collector_lazystack_then_write[50-img_shape0-small] 3,479 3,435 -1.25%
benchmarks/test_collectors_benchmark.py::test_single 6.7705 6.8549 +1.25%
... ... ... Showing 120 of 226 comparisons, sorted by absolute change.

@vmoens vmoens left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this together. I think the RewardModelLoss itself is a useful TorchRL primitive, good pieces to have in-tree

My main concern is the positioning of the recipe. Moving forward I'd like to be foreseeing on how we can bring torchrl to a state where we can do serious distributed training for LLM features. As written, this is a single-process training loop, with GPT-2 as the visible example. Fine for a tuto/smoke recipe, but it is not really a serious reward-model training stack for modern LLMs. I guess that, at scale, users will still want TRL/Accelerate/FSDP, TorchTitan/FSDP2, Megatron/NeMo, OpenRLHF, etc. for parallelism, sharded checkpointing, fault tolerance, packed/streamed datasets, mixed precision, activation checkpointing, and multi-node orchestration. So the end goal should be to interface with these.

Don't get me wrong, I do not think this PR should grow a Megatron/NeMo/TorchTitan integration. That would be too much scope. But I think we should be very explicit about what TorchRL is providing here:

  • a canonical TensorDict preference-data format,
  • a canonical reward-model loss,
  • a small reference trainer,
  • and eventually adapters/scorers that let externally trained reward models plug back into TorchRL GRPO/PPO/SFT pipelines.

Concretely, I would suggest adding a short README section along the lines of “Scaling and integration”. It could say that this recipe is a minimal single-node baseline, and that large-scale RM training should use an external backend while preserving the TorchRL data/scoring contract. Essentially we need to pave the way for the real stuff that is to come. If you're happy to work on this with me we can start a topic in discussion.

I would also avoid presenting GPT-2 as the “real” default. Keeping the tiny synthetic model for CI is great, but the docs/examples should probably use a small Qwen model.

So overall: I like the objective and the idea of a reference recipe. I would just make the scope honest: TorchRL should own the data contract, loss semantics, and scoring interface. Overall, we need to think about how to scale this (and the rest of the LLM stack) up.

… a small Qwen

Addresses reviewer feedback on the RLHF reward-model recipe:

- Add a 'Scaling and integration' README section making the scope explicit: TorchRL owns the TensorDict preference-data format, RewardModelLoss semantics, a small reference trainer, and (future) adapters/scorers into GRPO/PPO/SFT; large-scale RM training is delegated to external backends (TRL/Accelerate/FSDP, TorchTitan/FSDP2, Megatron/NeMo, OpenRLHF) while preserving the data/scoring contract.

- Use Qwen/Qwen2.5-0.5B as the example default in the config and docs instead of gpt2. The tiny from-scratch synthetic model used by CI is unchanged.
@theap06

theap06 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@vmoens Thanks, this framing makes more sense! On the bigger picture, I fully agree the end goal is the adapters/scorers layer and interfacing with external backends. The way I'm picturing it: TensorDict is the shared format that holds the preference data, rollouts, scores, and rewards, and the backends (TRL/Accelerate/FSDP, etc.) bolt on at the edges. They consume the exported preference data, do the heavy distributed training, and hand back a checkpoint we re-import as a scorer. That way GRPO/SFT/reward all just read and write the same keys, and the parallelism can be done with the backends.

@theap06 theap06 requested a review from vmoens July 1, 2026 07:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI Has to do with CI setup (e.g. wheels & builds, tests...) CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Documentation Improvements or additions to documentation llm/ LLM-related PR, triggers LLM CI tests new algo New algorithm request or PR Objectives sota-implementations/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants