Mask padded MiniLLM completion tokens out of the advantage and its length - #7044
Mask padded MiniLLM completion tokens out of the advantage and its length#7044behroozazarkhalili wants to merge 1 commit into
Conversation
…out of its length `compute_loss` built the advantage mask from `input_ids`, which never holds -100, so every padded slot kept a reward of `teacher_logp - student_logp` on the pad token and every earlier position summed it in. The mask now comes from `labels`, where the -100 fill lives; the gather keeps indexing `input_ids`, since -100 is not a valid index. Under `length_normalization=True`, `_compute_advantage` replaced masked slots with 1e-4 before building the discounted length, so a short completion's denominator grew with the longest completion in its batch (a single valid token padded to 512 got 0.9514 instead of 1.0 for a constant reward). Only unmasked slots count now, and a clamp on the finished length keeps a fully masked row finite. Tests: constant reward gives exactly 1.0 at every valid position for lengths 1, 128 and 512 padded to 512; a fully masked row stays finite and zero; the mask handed to `_compute_advantage` by `compute_loss` equals the completion mask. Fixes #7024
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 0cdf67f. Configure here.
| # Only unmasked positions count toward the discounted length, so the advantage of a completion does | ||
| # not depend on how much padding the batch carries. The clamp keeps a fully masked row finite. | ||
| lengths = (mask * gamma_pow).flip(1).cumsum(dim=1).flip(1) | ||
| advantages = advantages / lengths.clamp(min=1e-4) |
There was a problem hiding this comment.
Length clamp corrupts late advantages
Medium Severity
The clamp(min=1e-4) on discounted lengths also triggers for valid tokens once gamma^t is small, so later positions get a deflated length-normalized advantage. That hits the long-context RKL path (gamma > 0) at default max_completion_length for common discount values.
Reviewed by Cursor Bugbot for commit 0cdf67f. Configure here.
|
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. |


Fixes #7024. Both defects are in
trl/experimental/minillm/minillm_trainer.pyand are independent of #6635, which only changes how the discounted sum is computed.Right-padding leaked into the advantage
compute_lossbuilt the mask it hands to_compute_advantageasinput_ids[:, prompt_lengths:] != -100.input_idsnever contains -100; the fill is applied tolabels, built a few lines earlier fromattention_mask. So the mask was all ones, padded slots kept a reward on the pad token, and every earlier position's advantage summed those in. The mask now readslabels[:, prompt_lengths:] != -100. The gather index stays oninput_ids: the issue's one-line suggestion of slicinglabelsfor both would feed -100 intotorch.gather.Length normalization depended on batch padding
_compute_advantagereplaced masked slots with 1e-4 before the discounted length, so the denominator of a short completion included1e-4 * gamma^(i-t)for each pad slot after it. With a constant reward of 1.0 and gamma 1.0 a single valid token padded to 512 got 0.9514 instead of 1.0. Only unmasked slots count toward the length now, andclamp(min=1e-4)on the finished length keeps a fully masked row finite, which is what the fill was for.Tests
test_length_normalization_ignores_padding: constant reward, gamma 1.0, lengths 1, 128 and 512 padded to 512; the advantage is exactly 1.0 at every valid position and 0 after, and a fully masked row stays finite and zero. Fails on main at lengths 1 and 128.test_advantage_mask_excludes_padded_completion_tokens: callscompute_losson a bare trainer with a stub model and a two-row batch whose first row is half padding, and checks the mask handed to_compute_advantageequals the completion mask. Fails on main, where the mask is all ones.Both tests are hermetic (no model download) and run in about ten seconds.
Verification
ruff checkandruff format --checkat the CI-pinned 0.13.3 and the pinned doc-builder at--max-len 119pass on both files. On a compute node (job 58038396) the full MiniLLM test file gives 7 passed on the branch, and the two new tests fail on main as described.Note
Medium Risk
Changes experimental training loss/advantage math and can shift MiniLLM optimization dynamics, but the scope is limited to padding handling in
minillm_trainer.py.Overview
Fixes two padding bugs in experimental MiniLLM reverse-KL advantage computation that skewed training when completions were right-padded in a batch.
In
compute_loss, the mask passed to_compute_advantageis now derived fromlabels(where padding is-100) instead ofinput_ids, so padded completion slots no longer contribute rewards that bleed into earlier token advantages. Log-probabilitygatherstill usesinput_idsso-100is never used as an index.In
_compute_advantage, length normalization no longer treats padded positions as tiny contributions via a1e-4mask fill; only valid tokens count in the discounted length denominator, withclamp(min=1e-4)on the divisor for fully masked rows. Short completions now get the correct normalized advantage regardless of batch padding length.Hermetic tests cover constant-reward length normalization across pad lengths and assert
compute_lossforwards the completion mask into_compute_advantage.Reviewed by Cursor Bugbot for commit 0cdf67f. Bugbot is set up for automated code reviews on this repo. Configure here.