Skip to content

[fully_async, reward, docs] fix: skip reward function call when use_task_rewards=False#6870

Open
kenkenpa2126 wants to merge 4 commits into
verl-project:mainfrom
kenkenpa2126:fix/opd-reward-skip-task-rewards
Open

[fully_async, reward, docs] fix: skip reward function call when use_task_rewards=False#6870
kenkenpa2126 wants to merge 4 commits into
verl-project:mainfrom
kenkenpa2126:fix/opd-reward-skip-task-rewards

Conversation

@kenkenpa2126

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes a crash in OPD training when distillation.distillation_loss.use_task_rewards=False and the dataset's data_source is not registered in default_compute_score.

The use_task_rewards flag previously only suppressed the task-reward contribution to the loss (in distillation/losses.py) but did not prevent _compute_score from being invoked in agent_loop.py. When using a custom dataset whose data_source is not registered in the default reward registry, this raised NotImplementedError and crashed training before the first step.

OPD with use_task_rewards=False trains entirely on the distillation loss; invoking the reward function is unnecessary and error-prone with custom datasets.

Checklist Before Starting

  • Search for similar PRs. Paste at least one query link here: No related pr found. https://github.com/verl-project/verl/pulls?q=is%3Apr+use_task_rewards, https://github.com/verl-project/verl/pulls?q=is%3Apr+reward+skip+distillation
  • Format the PR title as [{modules}] {type}: {description} (This will be checked by the CI)
    • {modules} include fsdp, megatron, veomni, sglang, vllm, rollout, trainer, ci, training_utils, recipe, hardware, deployment, ray, worker, single_controller, misc, perf, model, algo, env, tool, ckpt, doc, data, cfg, reward, fully_async, one_step_off
    • If this PR involves multiple modules, separate them with , like [megatron, fsdp, doc]
    • {type} is in feat, fix, refactor, chore, test
    • If this PR breaks any API (CLI arguments, config, function signature, etc.), add [BREAKING] to the beginning of the title.
    • Example: [BREAKING][fsdp, megatron] feat: dynamic batching

Test

Verified under fully async OPD training with a custom QA dataset (data_source not registered in default_compute_score). Previously crashed at the first distillation step with NotImplementedError; now runs without invoking the reward function when use_task_rewards=False.

API and Usage Example

No API change. Existing config usage:

distillation:
  distillation_loss:
    use_task_rewards: false   # reward function is now skipped entirely

Design & Code Changes

  • verl/experimental/agent_loop/agent_loop.py: reads use_task_rewards from distillation config in init (defaults to True when distillation is disabled); wraps _compute_score call with if self.use_task_rewards.
  • docs/algo/opd.md: clarifies that use_task_rewards=false skips the reward function invocation entirely, not just the loss contribution.

Checklist Before Submitting

The fix is a single conditional guard in the agent loop. Testing requires a fully running async OPD stack (rollout server + teacher server + trainer), which is not feasible in CI. Verified by end-to-end training run with a custom dataset.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a configuration option 'use_task_rewards' to conditionally skip reward computation ('_compute_score') during agent loop post-processing. This is useful when training with custom datasets that do not have a compatible reward function. The documentation in 'docs/algo/opd.md' has been updated to reflect this behavior. There are no review comments, so we have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@kenkenpa2126 kenkenpa2126 force-pushed the fix/opd-reward-skip-task-rewards branch from 15180b4 to f5a0c7e Compare June 28, 2026 07:40
kenkenpa2126 and others added 2 commits June 28, 2026 16:55
When training with OPD (distillation.enabled=True) and
distillation_loss.use_task_rewards=False, the agent_loop still called
_compute_score unconditionally. If the dataset's data_source was not
registered in default_compute_score, this raised NotImplementedError
and crashed training before the first step.

The use_task_rewards flag only suppressed the task reward contribution
to the loss (in distillation/losses.py) but did not prevent the reward
function from being invoked. OPD trains entirely on the distillation
loss; calling the reward function is unnecessary and error-prone when
using custom datasets.

Fix: read use_task_rewards from distillation config in __init__ and
skip _compute_score when it is False.

Co-authored-by: Claude <noreply@anthropic.com>
…entirely

When use_task_rewards=false, _compute_score is not invoked at all, not just
zeroed in the loss. This matters for custom datasets whose data_source is not
registered in default_compute_score.

Co-authored-by: Claude <noreply@anthropic.com>
@kenkenpa2126 kenkenpa2126 force-pushed the fix/opd-reward-skip-task-rewards branch from f5a0c7e to 1158676 Compare June 28, 2026 07:55
@wuxibin89

Copy link
Copy Markdown
Collaborator

@kenkenpa2126 Which trainer do you use? In new V1 trainer, rm_scores is still needed to compute advantages even if use_task_rewards=False.
https://github.com/verl-project/verl/blob/main/verl/trainer/ppo/v1/trainer_base.py#L450-L452

kenkenpa2126 and others added 2 commits June 29, 2026 19:54
…iner compat

When use_task_rewards=False, _compute_score() is skipped and reward_score
stays None, so rm_scores is never added to the output batch. V1 trainer's
_compute_advantage unconditionally reads rm_scores from TransferQueue even
in distillation-only setups, causing a KeyError.

Fix by setting reward_score=0.0 in the else branch so rm_scores is always
populated (as all-zeros at the last response token) regardless of trainer.

Co-authored-by: Claude <noreply@anthropic.com>
…rewards=False

Regression test for the fix in e242dae. Verifies that when
use_task_rewards=False, _agent_loop_postprocess sets reward_score=0.0
(rather than leaving it None), so that _postprocess can populate
rm_scores in the training batch — required by V1 trainer's
_compute_advantage.

Co-authored-by: Claude
Signed-off-by: Kenta Izumi <kenta.izumi@bytedance.com>
@kenkenpa2126

Copy link
Copy Markdown
Contributor Author

@wuxibin89
Thanks for the review! To clarify: I was using FullyAsyncTrainer (experimental/fully_async_policy), not the V1 trainer. In my actual experiment I had a custom compute_score returning 0.0 unconditionally, so _compute_score() was always called and rm_scores was always populated. This means the use_task_rewards=False code path I added was never actually exercised — the fix and the validation were misaligned.

You're right that rm_scores must always be present. Addressed this in a follow-up commit (e242dae) by setting reward_score = 0.0 in the else branch, and added a CPU unit test (c2d8712).

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.

2 participants