Qwen3 DSpark draft model for semi-autoregressive drafting.
DSpark drafts a whole block in one parallel pass (DFlash-style: context-KV precompute + a non-causal query-block forward) and then injects intra-block dependency with a lightweight sequential Markov head.
The parallel backbone is a standard Qwen3 decoder stack reused from the DFlash Qwen3 draft (see qwen3_dflash.py). DSpark adds: * markov_head: low-rank V x r / r x V transition bias added to the base logits, sampled left-to-right by the speculator (the sequential stage).
DSparkMarkovHead is shared with the DSV4-style DSpark model.
Classes:
DSparkMarkovHead
Bases: Module
Sequential transition-bias head (low-rank V x r, r x V).
markov_w1[token] embeds the previously sampled token (target vocab, vocab_size); markov_w2 projects it to a draft-vocab bias (draft_vocab_size) added to the base draft logits. The two sizes coincide for full-vocab drafts.
Methods:
-
bias – Vocab-size transition bias from a Markov embedding ([B, r] -> [B, V]).
-
embed – r-dim Markov embedding of token_ids ([B] -> [B, r]).
Source code in vllm/model_executor/models/qwen3_dspark.py
| class DSparkMarkovHead(nn.Module):
"""Sequential transition-bias head (low-rank V x r, r x V).
``markov_w1[token]`` embeds the previously sampled token (target vocab,
``vocab_size``); ``markov_w2`` projects it to a draft-vocab bias
(``draft_vocab_size``) added to the base draft logits. The two sizes
coincide for full-vocab drafts.
"""
def __init__(
self,
vocab_size: int,
draft_vocab_size: int,
markov_rank: int,
prefix: str,
) -> None:
super().__init__()
# TODO(ben): profile for which (if any) it makes sense to replicate or TP-shard
self.markov_w1 = VocabParallelEmbedding(
vocab_size, markov_rank, prefix=maybe_prefix(prefix, "markov_w1")
)
self.markov_w2 = ParallelLMHead(
draft_vocab_size, markov_rank, prefix=maybe_prefix(prefix, "markov_w2")
)
def embed(self, token_ids: torch.Tensor) -> torch.Tensor:
"""r-dim Markov embedding of ``token_ids`` ([B] -> [B, r])."""
return self.markov_w1(token_ids)
def bias(self, markov_embed: torch.Tensor, logits_processor) -> torch.Tensor:
"""Vocab-size transition bias from a Markov embedding ([B, r] -> [B, V])."""
return logits_processor(self.markov_w2, markov_embed)
|
bias(markov_embed, logits_processor)
Vocab-size transition bias from a Markov embedding ([B, r] -> [B, V]).
Source code in vllm/model_executor/models/qwen3_dspark.py
| def bias(self, markov_embed: torch.Tensor, logits_processor) -> torch.Tensor:
"""Vocab-size transition bias from a Markov embedding ([B, r] -> [B, V])."""
return logits_processor(self.markov_w2, markov_embed)
|
embed(token_ids)
r-dim Markov embedding of token_ids ([B] -> [B, r]).
Source code in vllm/model_executor/models/qwen3_dspark.py
| def embed(self, token_ids: torch.Tensor) -> torch.Tensor:
"""r-dim Markov embedding of ``token_ids`` ([B] -> [B, r])."""
return self.markov_w1(token_ids)
|
Qwen3DSparkModel
Bases: DFlashQwen3Model
DFlash Qwen3 backbone + DSpark Markov head.
Source code in vllm/model_executor/models/qwen3_dspark.py
| class Qwen3DSparkModel(DFlashQwen3Model):
"""DFlash Qwen3 backbone + DSpark Markov head."""
def __init__(
self,
*,
vllm_config: VllmConfig,
start_layer_id: int = 0,
prefix: str = "",
) -> None:
super().__init__(
vllm_config=vllm_config, start_layer_id=start_layer_id, prefix=prefix
)
config = self.config
draft_vocab_size = (
getattr(config, "draft_vocab_size", None) or config.vocab_size
)
self.markov_head = DSparkMarkovHead(
config.vocab_size,
draft_vocab_size,
config.markov_rank,
prefix=maybe_prefix(prefix, "markov_head"),
)
|