Skip to content

feat: basic ppo training implementation#2027

Draft
hXl3s wants to merge 44 commits into
NVIDIA-NeMo:mainfrom
hXl3s:lukaszp/ppo
Draft

feat: basic ppo training implementation#2027
hXl3s wants to merge 44 commits into
NVIDIA-NeMo:mainfrom
hXl3s:lukaszp/ppo

Conversation

@hXl3s

@hXl3s hXl3s commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

DO NOT MERGE! WORK IN PROGRESS!

What does this PR do ?

This PR adds basic Proximal Policy Optimization training loop to Nemo-RL.

What is added:

  • Support for value model. Current value model is a separate worker. Case where value model is just a head of Policy is not covered yet
  • PPO training loop and example of training math model (no convergence tested yet)
  • Basic logging and validation during PPO training

Issues

No direct issue

closes #2047

Usage

uv run example/run_ppo.py

Before your PR is "Ready for review"

Pre checks:

  • Make sure you read and followed Contributor guidelines
  • Did you write any new necessary tests?
  • Did you run the unit tests and functional tests locally? Visit our Testing Guide for how to run tests
  • Did you add or update any necessary documentation? Visit our Document Development Guide for how to write, build and test the docs.

Additional Information

  • ...

@github-actions

Copy link
Copy Markdown

✅ Submodule Fast-Forward Check Results

Check based on commit: 08aa60d (PR #2027 from lukaszp/ppo)

✅ Submodules that are properly updated:

Automodel: ✅ PR branch is ahead of main branch (fast-forward)

All submodule changes look good! ✨

@github-actions

Copy link
Copy Markdown

⚠️ File Consistency Check

Check based on commit: 08aa60d (PR #2027 from lukaszp/ppo)

⚠️ DTensor Policy Worker Synchronization Warning

The file nemo_rl/models/policy/workers/dtensor_policy_worker_v2.py was modified in this PR, but nemo_rl/models/policy/workers/dtensor_policy_worker.py was not updated.

Why this matters:
These files contain related DTensor policy worker implementations that should be kept synchronized to ensure consistency across different versions.

Action required:

  • Please review if the changes in nemo_rl/models/policy/workers/dtensor_policy_worker_v2.py should also be applied to nemo_rl/models/policy/workers/dtensor_policy_worker.py
  • Update nemo_rl/models/policy/workers/dtensor_policy_worker.py if necessary to maintain consistency
  • If the files are intentionally different, please add a comment in the PR explaining why

Files to check:

  • Modified: nemo_rl/models/policy/workers/dtensor_policy_worker_v2.py
  • Not modified: nemo_rl/models/policy/workers/dtensor_policy_worker.py

This check ensures that related file implementations remain synchronized across the codebase. If you believe this warning is incorrect or the files should intentionally differ, please add a comment explaining the reasoning.

@github-actions

Copy link
Copy Markdown

✅ Submodule Fast-Forward Check Results

Check based on commit: efd71bb (PR #2027 from lukaszp/ppo)

✅ Submodules that are properly updated:

Automodel: ✅ PR branch is ahead of main branch (fast-forward)

All submodule changes look good! ✨

@copy-pr-bot

copy-pr-bot Bot commented Mar 10, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

hXl3s and others added 18 commits March 16, 2026 13:48
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
Signed-off-by: Gerald Shen <geshen@nvidia.com>
@hXl3s hXl3s force-pushed the lukaszp/ppo branch 2 times, most recently from 8f01c5b to 24e1db0 Compare March 30, 2026 14:56
hXl3s added 2 commits March 30, 2026 17:13
Signed-off-by: Lukasz Pierscieniewski <lukaszp@nvidia.com>
Signed-off-by: Lukasz Pierscieniewski <lukaszp@nvidia.com>
hXl3s added 2 commits March 31, 2026 08:53
Signed-off-by: Lukasz Pierscieniewski <lukaszp@nvidia.com>
Signed-off-by: Lukasz Pierscieniewski <lukaszp@nvidia.com>
@github-actions

Copy link
Copy Markdown

❌ Submodule Fast-Forward Check Failed

Check based on commit: 061fa41 (PR #2027 from lukaszp/ppo)

❌ Submodules that need attention:

Automodel: ❌ Commits have DIVERGED from a common ancestor
TARGET (main branch): https://github.com/NVIDIA-NeMo/Automodel/commits/92635e74f4fb16784268b9a9fd7b7d6a83fff6c5/
CURRENT (PR #2027 from lukaszp/ppo): https://github.com/NVIDIA-NeMo/Automodel/commits/519201d11b8dba3088c759df952d87295793e020/

Please ensure all submodule commits are fast-forwards of the main branch before merging.

Signed-off-by: Lukasz Pierscieniewski <lukaszp@nvidia.com>
@github-actions

Copy link
Copy Markdown

✅ Submodule Fast-Forward Check Results

Check based on commit: ed36260 (PR #2027 from lukaszp/ppo)

✅ Submodules that are properly updated:

Automodel: ✅ PR branch is ahead of main branch (fast-forward)

All submodule changes look good! ✨

Signed-off-by: Lukasz Pierscieniewski <lukaszp@nvidia.com>
@github-actions

github-actions Bot commented Apr 1, 2026

Copy link
Copy Markdown

✅ Submodule Fast-Forward Check Results

Check based on commit: 17e5433 (PR #2027 from lukaszp/ppo)

✅ Submodules that are properly updated:

Automodel: ✅ PR branch is ahead of main branch (fast-forward)

All submodule changes look good! ✨

@github-actions

Copy link
Copy Markdown

✅ Submodule Fast-Forward Check Results

Check based on commit: 11da7f3 (PR #2027 from lukaszp/ppo)

✅ Submodules that are properly updated:

Automodel: ✅ PR branch is ahead of main branch (fast-forward)

All submodule changes look good! ✨

@github-actions

Copy link
Copy Markdown

✅ Submodule Fast-Forward Check Results

Check based on commit: 553f741 (PR #2027 from lukaszp/ppo)

✅ Submodules that are properly updated:

Automodel: ✅ PR branch is ahead of main branch (fast-forward)

All submodule changes look good! ✨

jinglinglingling pushed a commit to jinglinglingling/RL that referenced this pull request May 28, 2026
Job 12246898 failed at value worker __init__ with:
  TypeError: setup_model_and_optimizer() got an unexpected keyword
  argument 'distributed_manager'. Did you mean 'distributed_context'?

Path A's worker (current ablation HEAD) documents 3 signature-drift
fixes its own __init__ needed against this fork's APIs. Apply the same
3 fixes to the pre-Path-A worker (mechanical mirror of Path A's notes,
no semantic divergence):

1. setup_model_and_optimizer(distributed_manager=...)
   -> setup_model_and_optimizer(distributed_context=...)
   (this fork renamed the kwarg in PR NVIDIA-NeMo#2027)

2. ModelAndOptimizerState unpacking: 11 slots -> 10
   Drop self.model_state_dict_keys -- this fork's ModelAndOptimizerState
   NamedTuple (nemo_rl/models/automodel/config.py) has 10 fields and
   does not include model_state_dict_keys. Without this:
     ValueError: not enough values to unpack (expected 11, got 10)

3. RuntimeConfig unpacking: 12 slots -> 13
   Add _runtime_sampling_params slot before _runtime_is_reward_model.
   This fork's RuntimeConfig has 13 fields with sampling_params right
   before is_reward_model. bg51717/ppo stripped sampling_params from
   train()/get_values() call sites too (which is what we want and what
   the pre-Path-A worker already has), so we discard the value into
   _runtime_sampling_params rather than assigning self.sampling_params.
   Without this:
     ValueError: too many values to unpack (expected 12)

All 3 fixes are mechanical and documented verbatim in Path A's worker
NOTE comments (commit fb38a59 lines 297-339).
jinglinglingling pushed a commit to jinglinglingling/RL that referenced this pull request May 28, 2026
… calls

Job 12247164 reached value-worker init (3 prior signature fixes worked),
got into the rollout/get_values cycle, then crashed with:

  TypeError: forward_with_post_processing_fn() got an unexpected
  keyword argument 'cfg'

Pre-Path-A worker passed `cfg=self.cfg` to two functions that this fork
no longer accepts it on (PR NVIDIA-NeMo#2027 stripped the param):

- automodel_forward_backward (train() microbatch loop, line 437)
- forward_with_post_processing_fn (get_values() forward, line 561)

LossPostProcessor.__init__ and ScorePostProcessor.__init__ DO still
accept cfg, so those call sites are left alone.

This is the same pattern as the previous setup_model_and_optimizer +
ModelAndOptimizerState/RuntimeConfig fixes -- pre-Path-A worker was
inherited from upstream bg51717/ppo and never re-tested against this
fork's APIs. Path A worked around it by writing a custom microbatch
loop that didn't go through these functions at all.
jinglinglingling pushed a commit to jinglinglingling/RL that referenced this pull request May 29, 2026
Job 12246898 failed at value worker __init__ with:
  TypeError: setup_model_and_optimizer() got an unexpected keyword
  argument 'distributed_manager'. Did you mean 'distributed_context'?

Path A's worker (current ablation HEAD) documents 3 signature-drift
fixes its own __init__ needed against this fork's APIs. Apply the same
3 fixes to the pre-Path-A worker (mechanical mirror of Path A's notes,
no semantic divergence):

1. setup_model_and_optimizer(distributed_manager=...)
   -> setup_model_and_optimizer(distributed_context=...)
   (this fork renamed the kwarg in PR NVIDIA-NeMo#2027)

2. ModelAndOptimizerState unpacking: 11 slots -> 10
   Drop self.model_state_dict_keys -- this fork's ModelAndOptimizerState
   NamedTuple (nemo_rl/models/automodel/config.py) has 10 fields and
   does not include model_state_dict_keys. Without this:
     ValueError: not enough values to unpack (expected 11, got 10)

3. RuntimeConfig unpacking: 12 slots -> 13
   Add _runtime_sampling_params slot before _runtime_is_reward_model.
   This fork's RuntimeConfig has 13 fields with sampling_params right
   before is_reward_model. bg51717/ppo stripped sampling_params from
   train()/get_values() call sites too (which is what we want and what
   the pre-Path-A worker already has), so we discard the value into
   _runtime_sampling_params rather than assigning self.sampling_params.
   Without this:
     ValueError: too many values to unpack (expected 12)

All 3 fixes are mechanical and documented verbatim in Path A's worker
NOTE comments (commit fb38a59 lines 297-339).
jinglinglingling pushed a commit to jinglinglingling/RL that referenced this pull request May 29, 2026
… calls

Job 12247164 reached value-worker init (3 prior signature fixes worked),
got into the rollout/get_values cycle, then crashed with:

  TypeError: forward_with_post_processing_fn() got an unexpected
  keyword argument 'cfg'

Pre-Path-A worker passed `cfg=self.cfg` to two functions that this fork
no longer accepts it on (PR NVIDIA-NeMo#2027 stripped the param):

- automodel_forward_backward (train() microbatch loop, line 437)
- forward_with_post_processing_fn (get_values() forward, line 561)

LossPostProcessor.__init__ and ScorePostProcessor.__init__ DO still
accept cfg, so those call sites are left alone.

This is the same pattern as the previous setup_model_and_optimizer +
ModelAndOptimizerState/RuntimeConfig fixes -- pre-Path-A worker was
inherited from upstream bg51717/ppo and never re-tested against this
fork's APIs. Path A worked around it by writing a custom microbatch
loop that didn't go through these functions at all.
yuekaizhang added a commit to yuekaizhang/RL that referenced this pull request Jun 2, 2026
…o#2027)

Repin the Automodel submodule from 26108096 to 6eb5e862 ("fix: Propagate
torch_dtype to sub-configs correctly", from NVIDIA-NeMo/Automodel#2027)
as a temporary pin.

Note: 6eb5e862 is an unmerged PR commit (an older force-pushed revision
of NVIDIA-NeMo#2027, not its current head and not on main) and predates the
Nemotron-Omni RADIO post-load patches in 26108096. It still pins
transformers==5.5.0 in its own metadata, so the transformers override
stays consistent. The refreshed uv.lock reflects the reverse-delta
(drops the later s3 / msc extras and the wandb>=0.26.1 pin).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: root <zhangyuekai@foxmail.com>
yuekaizhang added a commit to yuekaizhang/RL that referenced this pull request Jun 2, 2026
Bump the Automodel submodule to 5dcc9abe9 ("fix: Propagate torch_dtype to
sub-configs correctly", NVIDIA-NeMo/Automodel#2027). This is the oldest
commit on Automodel main that carries the NVIDIA-NeMo#2027 torch_dtype-propagation
fix, so it is reachable by a plain `git submodule update` (unlike the
orphaned, force-pushed PR-head revision of the same change, which lives in
Automodel's pre-rewrite history and is on no upstream branch).

It pins transformers==5.5.0 in its own metadata, keeping the transformers
override consistent. uv.lock refreshed accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: root <zhangyuekai@foxmail.com>
yuki-97 pushed a commit that referenced this pull request Jun 12, 2026
Job 12246898 failed at value worker __init__ with:
  TypeError: setup_model_and_optimizer() got an unexpected keyword
  argument 'distributed_manager'. Did you mean 'distributed_context'?

Path A's worker (current ablation HEAD) documents 3 signature-drift
fixes its own __init__ needed against this fork's APIs. Apply the same
3 fixes to the pre-Path-A worker (mechanical mirror of Path A's notes,
no semantic divergence):

1. setup_model_and_optimizer(distributed_manager=...)
   -> setup_model_and_optimizer(distributed_context=...)
   (this fork renamed the kwarg in PR #2027)

2. ModelAndOptimizerState unpacking: 11 slots -> 10
   Drop self.model_state_dict_keys -- this fork's ModelAndOptimizerState
   NamedTuple (nemo_rl/models/automodel/config.py) has 10 fields and
   does not include model_state_dict_keys. Without this:
     ValueError: not enough values to unpack (expected 11, got 10)

3. RuntimeConfig unpacking: 12 slots -> 13
   Add _runtime_sampling_params slot before _runtime_is_reward_model.
   This fork's RuntimeConfig has 13 fields with sampling_params right
   before is_reward_model. bg51717/ppo stripped sampling_params from
   train()/get_values() call sites too (which is what we want and what
   the pre-Path-A worker already has), so we discard the value into
   _runtime_sampling_params rather than assigning self.sampling_params.
   Without this:
     ValueError: too many values to unpack (expected 12)

All 3 fixes are mechanical and documented verbatim in Path A's worker
NOTE comments (commit fb38a59 lines 297-339).
yuki-97 pushed a commit that referenced this pull request Jun 12, 2026
… calls

Job 12247164 reached value-worker init (3 prior signature fixes worked),
got into the rollout/get_values cycle, then crashed with:

  TypeError: forward_with_post_processing_fn() got an unexpected
  keyword argument 'cfg'

Pre-Path-A worker passed `cfg=self.cfg` to two functions that this fork
no longer accepts it on (PR #2027 stripped the param):

- automodel_forward_backward (train() microbatch loop, line 437)
- forward_with_post_processing_fn (get_values() forward, line 561)

LossPostProcessor.__init__ and ScorePostProcessor.__init__ DO still
accept cfg, so those call sites are left alone.

This is the same pattern as the previous setup_model_and_optimizer +
ModelAndOptimizerState/RuntimeConfig fixes -- pre-Path-A worker was
inherited from upstream bg51717/ppo and never re-tested against this
fork's APIs. Path A worked around it by writing a custom microbatch
loop that didn't go through these functions at all.
yuki-97 pushed a commit that referenced this pull request Jun 12, 2026
Job 12246898 failed at value worker __init__ with:
  TypeError: setup_model_and_optimizer() got an unexpected keyword
  argument 'distributed_manager'. Did you mean 'distributed_context'?

Path A's worker (current ablation HEAD) documents 3 signature-drift
fixes its own __init__ needed against this fork's APIs. Apply the same
3 fixes to the pre-Path-A worker (mechanical mirror of Path A's notes,
no semantic divergence):

1. setup_model_and_optimizer(distributed_manager=...)
   -> setup_model_and_optimizer(distributed_context=...)
   (this fork renamed the kwarg in PR #2027)

2. ModelAndOptimizerState unpacking: 11 slots -> 10
   Drop self.model_state_dict_keys -- this fork's ModelAndOptimizerState
   NamedTuple (nemo_rl/models/automodel/config.py) has 10 fields and
   does not include model_state_dict_keys. Without this:
     ValueError: not enough values to unpack (expected 11, got 10)

3. RuntimeConfig unpacking: 12 slots -> 13
   Add _runtime_sampling_params slot before _runtime_is_reward_model.
   This fork's RuntimeConfig has 13 fields with sampling_params right
   before is_reward_model. bg51717/ppo stripped sampling_params from
   train()/get_values() call sites too (which is what we want and what
   the pre-Path-A worker already has), so we discard the value into
   _runtime_sampling_params rather than assigning self.sampling_params.
   Without this:
     ValueError: too many values to unpack (expected 12)

All 3 fixes are mechanical and documented verbatim in Path A's worker
NOTE comments (commit fb38a59 lines 297-339).
yuki-97 pushed a commit that referenced this pull request Jun 12, 2026
… calls

Job 12247164 reached value-worker init (3 prior signature fixes worked),
got into the rollout/get_values cycle, then crashed with:

  TypeError: forward_with_post_processing_fn() got an unexpected
  keyword argument 'cfg'

Pre-Path-A worker passed `cfg=self.cfg` to two functions that this fork
no longer accepts it on (PR #2027 stripped the param):

- automodel_forward_backward (train() microbatch loop, line 437)
- forward_with_post_processing_fn (get_values() forward, line 561)

LossPostProcessor.__init__ and ScorePostProcessor.__init__ DO still
accept cfg, so those call sites are left alone.

This is the same pattern as the previous setup_model_and_optimizer +
ModelAndOptimizerState/RuntimeConfig fixes -- pre-Path-A worker was
inherited from upstream bg51717/ppo and never re-tested against this
fork's APIs. Path A worked around it by writing a custom microbatch
loop that didn't go through these functions at all.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[dtensor] PPO

2 participants