Repository navigation
Conversation
…head DSpark's pseudo_speculative_generate drafts the block left to right, one markov_step per position, while training applies the Markov head to the whole block at once through _draft_logits. Only shape tests covered the decode, so nothing checked that the two agree, and the next commit moves the decode loop into HFDFlashModel. The test mirrors Domino's TestDominoGenerate: with head weights large enough to overturn the backbone, the greedy drafts must equal the argmax of _draft_logits teacher-forced on the anchor and those drafts. It runs for the vanilla, gated and rnn heads. Verified: passes on the parent commit for all three heads, in under 0.2 s per case on 4 CPUs. On the next commit it is the only unit test that fails when DSpark's step drops the rnn head's state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: h-guo18 <67671475+h-guo18@users.noreply.github.com>
…ion loop DSpark's and Domino's pseudo_speculative_generate each carried the same left-to-right loop over the block. Each copy ran the base forward and the draft block, and moved the lm_head's logits onto the draft's device (the #2762 fix, present in both copies). Each also kept the previous token and the head's state, capped the drafts at the block size, stacked them and moved them back to the caller's device. Only the step at each position differed. The loop is now HFDFlashModel._decode_left_to_right. A variant supplies _correction_step(k, prev_token, hidden, state), which returns the correction to block position k's backbone logits (None for none) and the head's new state: - DSpark's step is markov_step. - Domino's step runs the GRU over the previous token's embedding, which it still moves onto the draft's device, and adds embed_proj's correction from pure_draft_prefix_len on. Both pseudo_speculative_generate overrides keep their docstrings and call the shared loop. DFlash's parallel decode, which DFlash2 and LiLiCorr also use, is unchanged. Verified against the parent commit: - Bitwise A/B: the drafted tokens of the online DSpark (vanilla, gated and rnn heads) and Domino (pure_draft_prefix_len 1 and 0) cases are identical in fp32 and bf16, at init scale and with head weights large enough to decide the drafts. Loss, gradients and metrics of all 30 cases, offline and online, are identical too. - Sharded on one GPU with the CPU as the first device: accelerate dispatch with embed_tokens and lm_head on the CPU, and a split that also puts base layers 0-1 there. The drafts are identical for DFlash, DSpark and Domino in fp32 and bf16. - Unit tests in test_hf_{dflash,dflash2,dspark,domino,lilicorr}.py and test_hf_spec_rope_export.py: 348 passed on transformers 5.5.4, 5.12.1 and 5.18.0. GPU plugin tests: 20 passed, 1 skipped (needs 2 GPUs). - Mutation check: 11 of 12 edits to the loop or the steps fail a unit test, the lm_head GPU test or the A/B. Dropping Domino's embedding move passes everything, because accelerate's hook on the GRU moves its input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: h-guo18 <67671475+h-guo18@users.noreply.github.com>
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Comment |
This was referenced Oct 24, 2026
Draft
This branch has not been deployed
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.
What does this PR do?
Type of change: Refactor. No behavior change: the drafted tokens are bitwise identical.
This PR is stacked on #2990 (branch
haoguo/dflash-shared-forward, head 28f4f45), whose branch is its base; merge it after that one. The stack below it, on main 0638a27, is #2757 ← #2762 ← #2989 [1/2] (branchhaoguo/dflash-loss-context) ← #2990 [2/2]. Three other PRs also sit on #2990 and are independent of this one: #2991 (DSpark and Domino followdflash_loss_objective), #2992 (loggingdraft_metrics) and #2993 (loss terms in config). This branch merges with each of them without conflicts, and none of them touches the generation path.#2762 gave Domino an AR-validation decode modeled on DSpark's, so
pseudo_speculative_generateinhf_dspark.pyandhf_domino.pycarried the same loop over the block:lm_head's logits onto the draft's device (fix(speculative): DSpark and Domino AR validation and label alignment #2762'sdevice_map="auto"fix, in both copies);Only the step at each block position differed. A fix to the loop had to be made twice, and a third variant with a sequential head would have copied it again.
HFDFlashModel._decode_left_to_right(input_ids, steps)holds the loop and the device handling. Block positionkdrafts the token after the anchor and thektokens drafted before it (_label_shift = 1)._correction_step(k, prev_token, hidden, state). It returns the correction to positionk's backbone logits, orNoneto leave them as they are, together with the head's new state.markov_step. Only the rnn head carries state.pure_draft_prefix_lenon, it returnsembed_proj's correction.pseudo_speculative_generateoverrides keep their docstrings and call the shared loop. DFlash's parallel decode, which DFlash2 and LiLiCorr also use, is unchanged.TestDSparkGeneratemirrors fix(speculative): DSpark and Domino AR validation and label alignment #2762'sTestDominoGeneratefor the vanilla, gated and rnn heads: with head weights large enough to overturn the backbone, the greedy drafts must equal the argmax of_draft_logitsteacher-forced on them. It lands first and passes on [2/2] refactor(speculative): train the DFlash family through one forward with shared loss terms #2990's head.Usage
No API or config change. A variant with a sequential head now plugs in like this:
Testing
The DFlash-family unit tests (
tests/unit/torch/speculative/plugins/test_hf_{dflash,dflash2,dspark,domino,lilicorr}.py) plustests/unit/torch/export/test_hf_spec_rope_export.py: 348 passed on transformers 5.5.4, 5.12.1 and 5.18.0. That is [2/2] refactor(speculative): train the DFlash family through one forward with shared loss terms #2990's 345 tests in these files plus the 3 new cases, each under 0.2 s on 4 CPUs. 5.5.4 and 5.18.0 fall within CI'stf_minandtf_latestpins. fix(speculative): DSpark and Domino AR validation and label alignment #2762'sTestDominoGenerate(pure_draft_prefix_len0 and 1) is unchanged and passes.tests/gpu/torch/speculative/pluginson one GPU: 20 passed, 1 skipped (it needs 2 GPUs). This includes fix(speculative): DSpark and Domino AR validation and label alignment #2762'sTestLmHeadOffTheDraftDevicefor DFlash, DSpark and Domino, unchanged.Bitwise A/B against [2/2] refactor(speculative): train the DFlash family through one forward with shared loss terms #2990's head. It compares loss, every gradient,
train_acc,draft_metricsand the drafted tokens. All 30 cases are identical in fp32 and bf16 (60/60).pure_draft_prefix_len1 and 0) cases draft withsteps0, 1, 2, 4 and 6, the last one past the block size.Sharded generation on one GPU, with the CPU standing in for the first GPU (accelerate's
dispatch_model). DFlash, the three DSpark heads and Domino withpure_draft_prefix_len1 and 0 were drafted in fp32 and bf16, withsteps0, 1, 2, 4 and 6 from three prefix lengths, under two device maps:embed_tokensandlm_headon the CPU, the rest on the GPU, as inTestLmHeadOffTheDraftDevice;target_layer_ids[0, 3]so the target hidden states come off both devices.All 24 runs are bitwise identical to [2/2] refactor(speculative): train the DFlash family through one forward with shared loss terms #2990's head, and the base token and drafts come back on the caller's device.
Mutation check. Each edit below fails at least one of the unit tests above, the GPU test or the A/B:
lm_heador return-device move;Removing Domino's move of the token embedding passes everything. Under accelerate's dispatch, the GRU's own hook moves its input. The move stays as fix(speculative): DSpark and Domino AR validation and label alignment #2762 wrote it.
FSDP2 probe (2 CPU ranks on gloo, the draft as its own
fully_shardunit, one rank on the zero-loss branch). Training is untouched, and DFlash under CE and KD finishes with the same losses as on [2/2] refactor(speculative): train the DFlash family through one forward with shared loss terms #2990's head. Domino fails under this probe with a DTensor/Tensor mix, identically on [2/2] refactor(speculative): train the DFlash family through one forward with shared loss terms #2990's head; that predates this PR.pre-commitpasses on the changed files.Before your PR is "Ready for review"
Make sure you read and follow Contributor guidelines and your commits are signed (
git commit -s -S).Make sure you read and follow the Security Best Practices (e.g. avoiding hardcoded
trust_remote_code=True,torch.load(..., weights_only=False),pickle, etc.).pseudo_speculative_generate's outputs are unchanged.CONTRIBUTING.md: N/ATestDSparkGenerate.Additional Information
Source changes: +50 / −41 lines in
hf_dflash.py,hf_dspark.pyandhf_domino.py. Two commits: the DSpark test, which passes on the parent, then the refactor.Each variant opts in with a one-line
pseudo_speculative_generateoverride rather than by defining_correction_stepalone. Switching on whether a variant defines the step would need aNonesentinel onHFDFlashModeland a branch in DFlash's decode. Making the loop the family's only decode, with no correction for DFlash, would turn DFlash's parallel decode into a loop and move DFlash2 and LiLiCorr onto it. Both are larger than this refactor. The base_correction_stepraisesNotImplementedError, so a variant that opts in without a step fails on its first draft.Merge order: #2757, #2762, #2989, #2990, then this PR.
🤖 Generated with Claude Code