Skip to content

fix(training): fail fast when dspark_loss runs multi-rank without a dp_group - #5243

Closed
khazic wants to merge 8 commits into
NVIDIA-NeMo:mainfrom
khazic:khazic/fix/dspark-dp-group-guard
Closed

khazic wants to merge 8 commits into
NVIDIA-NeMo:mainfrom
khazic:khazic/fix/dspark-dp-group-guard

Conversation

@khazic

@khazic khazic commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Stacked on #5233 and #5234: this fixes the DSpark core introduced there, so until those merge this PR also lists their commits. Only the last commit is new here; I will rebase once the base PRs land.

What

Commit a46c068 scoped the denominator all-reduce to the explicitly passed dp_group (correct: the default process group is not the DP replica set under TP/PP). But it left dp_group=None silently computing a locally normalized loss inside a multi-rank job. With uneven token counts across ranks, the DP-averaged gradient then no longer equals the gradient of the global position-weighted loss: ranks with few valid tokens are over-weighted, and training optimizes a biased objective with no visible failure.

Fix

dspark_loss now raises RuntimeError when torch.distributed is initialized with world size > 1 and dp_group is None. Single-process and world-size-1 usage is unchanged. Callers without data parallelism (e.g. TP-only jobs, or a reference path in tests) pass a size-1 group; this is a deliberate explicitness requirement, since the loss cannot know the DP degree on its own and guessing from the default group is exactly the bug a46c068 removed.

The 2-GPU functional test is updated accordingly: its single-process reference gradient now uses a per-rank size-1 group (identical numerics to the previous dp_group=None path), which the review caught as otherwise breaking under the new guard.

Tests

Two new unit tests via monkeypatched torch.distributed: multi-rank with dp_group=None raises; world-size-1 initialized still computes the local loss. The raise test fails on the parent commit and passes here. All 18 dspark unit tests pass; ruff is clean. The functional test needs a 2-GPU node and is unchanged in intent.

@copy-pr-bot

copy-pr-bot Bot commented Aug 2, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@khazic

khazic commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test c266402

@yaoyu-33 yaoyu-33 added area:training Training loop, callbacks, and runtime integration bug Something isn't working needs-more-tests Requires additional L0 and L1 test coverage before merge needs-review PR is ready for code review and waiting on a reviewer labels Aug 2, 2026
khazic added 8 commits August 6, 2026 17:16
Design proposal (RFC) for adding DSpark speculative-decoding draft training to
Megatron-Bridge, tracking NVIDIA-NeMo#5230. It describes the DSpark draft (a
semi-autoregressive parallel backbone plus Markov and confidence heads), its
three-term CE / TV / confidence training objective and acceptance metric, and
how it maps onto the existing ModelOpt distillation subsystem pattern (provider
hook, custom forward step, loss, thin entry, HF weight mapping). It includes a
phased implementation plan and references the NeMo AutoModel reference
implementation.

Signed-off-by: khazic <khazzz1c@gmail.com>
Requested in review: the Megatron-Bridge design doc does not need a breakdown
of the AutoModel implementation; the pointer in the overview is enough.

Signed-off-by: khazic <khazzz1c@gmail.com>
The design page carried a four-phase implementation plan and an open-questions
list. Neither describes what this PR (or the repo) actually contains: they
promise work that is not scheduled and has no owner, so they go stale the moment
priorities move. The sequencing and the open decisions belong in the tracking
issue (NVIDIA-NeMo#5230), which is where they can be updated.

The remaining page is the design itself: the algorithm, where it fits in
Megatron-Bridge, and the proposed file surface. The frozen-target supervision
section keeps both capture options, since the trade-off between them is design
content rather than a schedule.

Signed-off-by: khazic <khazzz1c@gmail.com>
First increment of DSpark speculative-decoding draft training (NVIDIA-NeMo#5230; design in
NVIDIA-NeMo#5233): the architecture-agnostic, pure-torch core.

Adds src/megatron/bridge/training/post_training/dspark/:
* heads.py: the sequential head (VanillaMarkov / GatedMarkovHead / RNNHead) that
  injects intra-block token dependency onto the parallel-backbone logits, plus the
  confidence head.
* loss.py: the three-term CE + total-variation + confidence-BCE objective with
  per-position decay, and the analytical acceptance metrics (accept_rate = 1 - TV,
  tau = sum cumprod(accept) + 1). The data-parallel denominator reduction is an
  injectable dp_group, so the same math wires to a Megatron-Core forward step later.

CPU unit tests cover the head math and the loss / acceptance math. The
parallel-aware draft backbone and the forward-step / provider integration (which
need Megatron-Core and multi-GPU validation) are follow-ups.

Signed-off-by: khazic <khazzz1c@gmail.com>
The DSpark loss denominator reduction now runs only when a dp_group is passed;
dp_group=None is local-only. Previously it fell back to the default process
group whenever torch.distributed was initialized, whose members are not the DP
replicas under Megatron-Core parallelism (TP/PP/CP), so the caller must pass the
data-parallel group explicitly.

Add a 2-GPU functional test
(tests/functional_tests/test_groups/training/test_dspark_loss_dp.py) that spawns
two NCCL ranks and asserts the DP-averaged gradient through a shared head matches
the single-process full-batch gradient. This validates the dp_group reduction,
which cannot be exercised on a single process, and needs a GPU node to run.

Signed-off-by: khazic <khazzz1c@gmail.com>
… metrics

Signed-off-by: khazic <khazzz1c@gmail.com>
…p_group

Signed-off-by: khazic <khazzz1c@gmail.com>
@khazic

khazic commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Closing this as obsolete rather than rebasing it.

@yaoyu-33's note on #5234 was that this PR's missing-group policy should follow the approved trainer and process-group contract rather than precede it. Settling that contract removed the thing this guard protects: dspark_loss no longer takes a dp_group or performs any reduction of its own. It returns the Megatron-Core 3-tuple (unnormalized numerator, num_tokens, reducible reporting pairs) and lets the framework reduce once over the data-parallel group, so there is no group argument left to omit.

The failure mode this PR was guarding against is real and is still guarded, one level up. Returning the 3-tuple is not by itself sufficient: with calculate_per_token_loss=False, schedules.py divides each micro-batch by its own num_tokens, which reintroduces exactly the mean-of-micro-batch-means bias. #5234 adds assert_per_token_loss_config to fail fast on that, measured at a 37% gradient error against the full-batch reference on 2 ranks with uneven micro-batch token counts.

This PR was also stacked on the now-closed #5241, so its diff carries the incorrect 0.5 * L1 commit as a base. #5242 and #5244 remain valid and will be rebased onto the reworked #5234.

@khazic khazic closed this Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:training Training loop, callbacks, and runtime integration bug Something isn't working community-request needs-more-tests Requires additional L0 and L1 test coverage before merge needs-review PR is ready for code review and waiting on a reviewer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants