[Feature] Add end-to-end TV loss for MTP SFT - #2070
Open
JT-Ushio wants to merge 2 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
It introduces a new distributed, full-vocabulary loss path with nontrivial masking/SP behavior that warrants final human validation in real training runs beyond unit tests.
Pull request overview
This PR adds an opt-in end-to-end total-variation (TV) loss for Multi-Token Prediction (MTP) in XTuner SFT, enabling joint optimization of multi-step rejection-sampling acceptance (per Eq. 13 in Bebop / referenced by the GLM-5.2 blog) while preserving existing per-depth CE behavior by default.
Changes:
- Add
MTPConfig.loss_type("ce"default,"e2e_tv"opt-in) andtv_loss_chunk_sizeto control the new objective and its memory/perf tradeoff. - Implement the exact full-vocabulary e2e TV objective as a new
MTPE2ETVLossConfig/Context, including packed-sequence masking and SP-aware target shifting. - Integrate the joint loss path into MoE training (regular + intra-layer microbatch) and add focused unit tests plus an example config toggle via env vars.
File summaries
| File | Description |
|---|---|
| xtuner/v1/module/mtp/config.py | Adds config knobs for selecting CE vs e2e TV loss and chunk sizing. |
| xtuner/v1/module/lm_head/lm_head.py | Extends LMHead typing to accept tuple-shaped hidden states for the joint TV loss context. |
| xtuner/v1/model/moe/moe.py | Wires loss_type into loss-context building and computes either per-depth CE or joint e2e TV MTP loss. |
| xtuner/v1/loss/mtp_loss.py | Implements the end-to-end TV loss config/context, including SP halo shifting and token-chunked exact TV overlap. |
| xtuner/v1/loss/init.py | Exports MTPE2ETVLossContext. |
| tests/loss/test_mtp_e2e_tv_loss.py | Adds numerical and gradient-flow tests for the e2e TV loss (teacher detachment + optional head detachment). |
| examples/v1/config/sft_glm5p2.py | Exposes MTP_LOSS_TYPE, MTP_LOSS_WEIGHT, and MTP_TV_LOSS_CHUNK_SIZE environment toggles. |
Review details
- Files reviewed: 7/7 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+315
to
+323
| assert dist.is_initialized(), "Sequence parallelism requires torch.distributed to be initialized." | ||
| assert sp_mesh is not None | ||
| group = sp_mesh.get_group() | ||
| sp_rank = dist.get_rank(group) | ||
| prefix = target_hidden_states[:, :num_steps].contiguous() | ||
| prefixes = [torch.empty_like(prefix) for _ in range(sp_size)] | ||
| dist.all_gather(prefixes, prefix, group=group) | ||
| halo = prefixes[sp_rank + 1] if sp_rank + 1 < sp_size else torch.zeros_like(prefix) | ||
| extended = torch.cat((target_hidden_states, halo), dim=1) |
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.
Motivation
The official GLM-5.2 technical blog explicitly states that GLM-5.2 uses end-to-end TV loss training together with rejection sampling for its recurrent MTP design. Its coding ablation reports an acceptance length of 4.56 for the baseline and 5.47 (+20%) for the final configuration that includes end-to-end TV loss.
The blog cites Bebop, whose Eq. 13 directly optimizes normalized multi-step rejection-sampling acceptance:
In the current XTuner SFT path, MTP only constructs independent per-depth cross-entropy losses (or KL when rollout log-probabilities are supplied). There is no selectable joint end-to-end TV objective, so the GLM-5.2 training objective cannot currently be reproduced.
Changes
MTPConfig.loss_typewithceande2e_tvoptions;ceremains the default for backward compatibility.examples/v1/config/sft_glm5p2.py.Example:
Scope and compatibility
pis detached as required by the TV objective. Existingdetach_mtp_inputsanddetach_mtp_lm_head_weightswitches continue to control whether the draft branch reaches backbone inputs and the shared LM head.0.1preserves XTuner's existing MTP loss scale and is not claimed to be an official GLM-5.2 value.Validation
git diff --checkFull CUDA/Triton integration testing is left to CI.