nemo_automodel.components.speculative.dflash.draft_qwen3
nemo_automodel.components.speculative.dflash.draft_qwen3
DFlash draft model (Qwen3-style).
Ported from SpecForge’s specforge/modeling/draft/dflash.py. DFlash drafts a
whole block of block_size tokens in parallel: the block’s first position
holds the real anchor token and the rest are MASK tokens, and the draft
predicts the whole block in a single non-causal forward conditioned on the
target model’s context hidden states.
The draft attention is therefore not causal — a draft block’s queries
attend to (a) the projected target-hidden context strictly before its anchor and
(b) bidirectionally to the other (noise) tokens of the same block. The attention
mask that enforces this is built by the trainer wrapper in
nemo_automodel.components.speculative.dflash.core.
Module Contents
Classes
Functions
Data
API
Bases: Module
Non-causal attention whose keys/values are [context | noise-block].
Queries come from the draft (noise) tokens only; keys and values are the
concatenation of the projected target-hidden context and the noise tokens.
The bidirectional/block structure is supplied entirely by attention_mask.
Bases: GradientCheckpointingLayer
A DFlash decoder block: non-causal attention over [context | noise] + MLP.
Bases: Qwen3PreTrainedModel
DFlash draft model: a small non-causal Qwen3 stack over [context | noise].
Keep the RoPE inv_freq buffer in fp32 across dtype casts.
Qwen3RotaryEmbedding computes the rotary angles in fp32 but reads the
frequencies from a stored inv_freq buffer. model.to(bfloat16) — the
training build path — rounds that buffer to bf16, whereas the serving
runtime (SGLang keeps an fp32 RoPE cache) and HF’s from_pretrained reload
keep it in fp32. The resulting train/inference RoPE mismatch grows with
absolute position (the bf16 frequencies dephase) and erodes draft
acceptance, so inv_freq must stay fp32 on both the training and reload
paths. A bf16 round-trip cannot be undone by upcasting, so when a cast
rounds the buffer we recompute fresh fp32 frequencies from the rotary
config (the same values HF derives on the fp32 paths) instead of upcasting
the corrupted ones.
Block-parallel speculative decoding: draft a block, verify with the target, accept the matching prefix.
Band mask keeping keys within sliding_window of each query’s position.
The draft’s queries are the trailing q_len positions of the concatenated
[context | noise-block] key axis, so a query at row i sits at key
position k_len - q_len + i. Both bounds are strict, matching transformers’
sliding_window_overlay and the reference DFlash decode mask. The draft is
non-causal, so the band is symmetric; the forward half is inert in practice
because the block is far shorter than any real window.
Parameters:
Tensor of shape [batch, heads, query, head_dim].
Tensor of shape [batch, kv_heads, key, head_dim], where key
spans the context followed by the draft block.
Maximum absolute position distance, exclusive.
Returns: torch.Tensor
Floating-point tensor of shape [1, 1, query, key], in query.dtype;
Apply RoPE where queries (draft block) are a suffix of the key positions.
The keys span [context | noise-block] while the queries are only the
noise block, so q is rotated with the trailing q_len slice of the
rotary tables and k with the full table.
Reject targets whose cache cannot be rewound after a rejected block.
Speculative decoding verifies a whole block and then rewinds the target to
the accepted prefix. For attention layers that is just dropping KV rows, but
a linear-attention layer keeps a recurrent state that has already absorbed
the rejected tokens; Cache.crop truncates the KV entries and leaves that
state where it is. The target then predicts from a corrupted state, so the
“lossless” guarantee quietly stops holding — greedy decoding drifts away
from the target’s own output instead of reproducing it.
Training is unaffected: it is a single forward with no cache and no rewind.
Parameters:
The frozen verifier.
Raises:
ValueError: If the target has any layer whose state cropping cannot rewind.
Pick num_draft_layers target layers spread across the target’s depth.
Concatenate the selected target layers’ hidden states along the feature dim.
hidden_states follows HF’s output_hidden_states convention where
index 0 is the embedding output, so layer i’s output is at index
i + 1.
Greedy (temperature ~ 0) or temperature sampling over the last dim.