Add GMPO to fused linear GRPO loss - #1320
Open
chinoll wants to merge 3 commits into
Open
Conversation
Author
Tcc0403
reviewed
Jul 28, 2026
Co-authored-by: Tcc0403 <76503978+Tcc0403@users.noreply.github.com>
Author
|
@Tcc0403 Thanks for the review! I've made the requested changes. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds native Geometric-Mean Policy Optimization (GMPO) support to
LigerFusedLinearGRPOLossthroughloss_type="gmpo".GMPO clips each token's log importance ratio before aggregating the valid tokens with a sequence-level geometric mean. Integrating the objective into Liger's existing fused linear and chunked-loss path avoids materializing the full
[batch, sequence, vocabulary]logits tensor while preserving the GMPO objective and gradients.The official GMPO implementation is available at callsys/GMPO.
Details
Example:
The implementation:
exp(masked_mean(clipped_log_ratio))for each completion;vllm_is_ratioordeltato the GMPO policy objective; andepsilon_lowandepsilon_highare GMPO log-space bounds[-epsilon_low, +epsilon_high], rather than the ratio-space bounds used by PPO-style losses.The test reference implements GMPO independently with full PyTorch operations. Tests cover:
chunk_sizevalues 1, 2, and 5;beta=0, K3 KL, and bias-corrected KL;torch.nextaftervalues;vllm_is_ratioanddeltabehavior; andTesting Done
The tested source content is commit
27eb5ce3ab3c56ad90b47e1a4201ae121e09b764.Targeted correctness tests
The final source-only snapshot was tested on an NVIDIA H100 with:
The command exited with code 0 and had no OOM, NaN, or hang. The exact-boundary test compares the forward loss, clipping metric, and log-probability gradient with
rtol=0andatol=0. The orthogonal tests cover BF16, compilation, KL/bias correction, and multiple outer chunk sizes.Forward and backward numerical comparison
An additional H100 sweep compared the Liger implementation with a naive full-logits BF16 implementation using a Qwen-0.5B LM-head shape:
hidden_size=896,vocab_size=151936;512, 1024, 2048, 4096, 8192, 16384, 32768;1, 2, 4, 8, 16, 32, 64, 128;epsilon_low=epsilon_high=0.4,beta=0;compiled=False, with Ligerchunk_size=batch_size.All 56 shapes completed with Liger. The 35 shapes for which the naive implementation fit in memory were compared directly in both forward and backward; the other 21 shapes were Liger-only finite scans because the naive forward pass had already OOMed under the same setup.
1.7881393e-073.6046363e-0709.5367432e-070.00364415030.999994586.1035156e-050.00366569310.99999453No non-finite loss, metric, hidden gradient, or weight gradient was observed in either implementation for any completed run. All 21 larger Liger-only shapes were also finite, including
batch_size=128, sequence_length=32768.H100 memory comparison
The same 56-shape matrix measured peak allocated memory for the LM head plus GMPO loss. This measurement does not include the transformer trunk or optimizer state.
56/56shapes.35/56shapes and OOMed during forward for21/56.B*T <= 65,536fit in the naive implementation, while all shapes withB*T >= 131,072OOMed during naive forward.90.6%; the largest was97.6%.B=2, T=32768, the full peak decreased from74.58 GiBto1.77 GiB.B=128, T=32768, Liger completed at a29.44 GiBfull peak while the naive implementation OOMed.B=1, T=512, is the exception: Liger used1.34 GiBversus the naive implementation's1.16 GiB(15.6%higher).1.16 GiB1.34 GiB-15.6%15.39 GiB1.44 GiB90.6%58.35 GiB1.77 GiB97.0%74.58 GiB1.77 GiB97.6%29.44 GiBLiger's fused chunked loss computes and stores gradients during its custom autograd forward. Consequently, its principal training-memory peak appears in the API's forward phase, while the naive implementation follows the conventional saved-graph/backward path. The full-peak values above use
max(forward_peak, backward_peak)for each provider.The repository's current
benchmark_grpo_loss.pydoes not exposeloss_type="gmpo", so these GMPO-specific measurements were run with an isolated benchmark rather than reporting the existing GRPO/GSPO benchmark as GMPO data.2.11.0+cu130make testto ensure correctnessmake checkstyleto ensure code stylemake test-convergenceto ensure convergence