Honor reward_processing_classes in XPO and Nash-MD trainers - #6952
Honor reward_processing_classes in XPO and Nash-MD trainers#695222elix3r wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f0fa85f. Configure here.
|
Thanks for picking this up, and for going with option 2 rather than dropping the parameter. I filed the issue, so I re-checked the implementation against source and measured the parts that a reading alone cannot settle. Everything the PR rests on holds. Claims I verified
On "matching Online DPO behavior"I expected a divergence here and did not find one. Online DPO calls the reward model directly with Routing through One thing worth flaggingThe PR changes reward scores on the path that already worked, not just the one that crashed, and neither the description nor the tests mention it. When the policy and reward tokenizers are the same, the old code fed raw policy ids straight to The head is randomly initialized, so treat the magnitude as illustrative rather than as a size estimate for a trained reward model. The point is that the scores move at all for users who were not hitting the bug. This is not a defect. Two smaller notes:
Nice fix, and thanks for keeping the two siblings aligned with Online DPO instead of removing the argument. |
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
XPO accepted a reward tokenizer but scored completions with policy token ids, and Nash-MD hardwired the policy tokenizer. Decode with the policy tokenizer and re-tokenize with the reward processing class, matching Online DPO. Score the last tokenizer-masked token rather than calling get_reward with context_length 0. Chat templates often reuse eos as pad, so the first pad id would otherwise be a turn boundary in the prompt. Same-tokenizer runs also change: skip_special_tokens / add_special_tokens=False drops BOS, as Online DPO does. Fixes huggingface#6951
c89e5bd to
602bdd0
Compare
|
Thanks for the re-check, @behroozazarkhalili — that same-tokenizer BOS drop is a real, silent change and was not called out before. I pushed a follow-up that:
Truncation of over-long re-tokenized sequences is still out of scope, as you said. |
|
@qgallouedec @albertvillanova @behroozazarkhalili could you take a look when you have a moment? This is the option-2 fix for #6951: XPO and Nash-MD now honor Happy to iterate on anything that looks off. |
|
Checked the follow-up. The turn-boundary fix is right, and it closes a gap my earlier measurement could not have caught. I had reported that routing through
Two notes on the new code:
Nothing further from me. Agreed that truncation of over-long re-tokenized sequences is a separate concern and not this PR's job. |

What does this PR do?
Fixes #6951.
XPOTraineralready acceptsreward_processing_classesand stores it throughOnlineDPOTrainer, but_compute_rewardsscored completions with the policy tokenizer'sinput_idsandpad_token_id.NashMDTrainerused the same scoring path and additionally hardwiredreward_processing_classes=processing_class, so a distinct reward tokenizer could not be passed at all.That silently ignores a user-supplied reward tokenizer. When the reward model has a smaller vocabulary than the policy (Llama reward + Qwen policy in the original report), scoring raises
IndexError: index out of range in selffar from the cause.Online DPO already decodes completions and re-tokenizes them with the reward processing class. XPO and Nash-MD now do the same:
skip_special_tokens=True)reward_processing_classes[0](add_special_tokens=False)attention_maskNashMDTrainernow exposesreward_processing_classesinstead of overwriting it with the policy tokenizer.Scoring uses the tokenizer mask rather than
get_reward(..., context_length=0). Chat templates often setpad_token_id == eos_token_idand emit that id at turn boundaries; the first-pad rule would otherwise score the prompt.Same-tokenizer behavior change. Decode/re-encode with
add_special_tokens=Falsedrops BOS, matching Online DPO. Existing XPO/Nash-MD runs that already used a matching reward tokenizer will see different scores even though they were not hitting theIndexError. This is intentional.Before submitting
Discussion: #6951 (comment)
This implements option 2 from the issue (honor the argument the way Online DPO does), rather than dropping the parameter. Docstrings for
NashMDTrainerwere updated to document the new argument; XPO already documented it.Tests
TestGetRewardFromPolicyTokens: policy token ids outside the reward vocab used to crashget_rewardand now score after re-tokenization; conversational last-token vs first eos; batched chat padding; same-tokenizer BOS dropTestXPOTrainerRewardProcessingClass: XPO scores with a Llama reward tokenizer and a Qwen policyTestNashMDTrainerRewardProcessingClass: Nash-MD keeps the passed reward tokenizer instead of replacing it with the policy tokenizerAI writing disclosure
We welcome the use of AI tools to help with contributions. For transparency and to help us improve our review process, please indicate the level of AI involvement in this PR.
Who can review?
Anyone in the community is free to review the PR once the tests have passed. Feel free to tag members/contributors who may be interested in your PR.
@qgallouedec @behroozazarkhalili
Note
Medium Risk
Changes reward computation in training loops for XPO/Nash-MD, including intentional score shifts for same-tokenizer setups; incorrect last-token or chat-template handling would skew optimization but is covered by new tests.
Overview
Fixes reward scoring in XPO and Nash-MD when the policy and reward models use different tokenizers (e.g. Qwen policy + Llama reward), which previously could ignore
reward_processing_classesand crash with out-of-vocabIndexError.Both trainers now score via new
get_reward_from_policy_tokens: decode completions with the policy tokenizer, rebuild prompt+completion text (including chat templates), re-encode withreward_processing_classes[0], and take the last non-padding token—aligned with Online DPO and avoiding wrong scores whenpad_token_id == eos_token_idat turn boundaries.NashMDTrainerstops overwritingreward_processing_classeswith the policy tokenizer and documents the argument.Behavior note: runs that already used a matching reward tokenizer will see different reward values (decode/re-encode with
add_special_tokens=Falsedrops BOS, same as Online DPO).get_rewardis refactored to share_compute_reward_logitsbut unchanged for direct callers.Reviewed by Cursor Bugbot for commit 602bdd0. Bugbot is set up for automated code reviews on this repo. Configure here.