nemo_automodel.components.speculative.eagle.vispec_draft
nemo_automodel.components.speculative.eagle.vispec_draft
ViSpec (vision-aware) draft model for speculative decoding on VLM targets.
ViSpec (Kang et al., NeurIPS 2025, arXiv:2509.15235) extends the EAGLE-1/2
draft with two vision-specific modules, keeping the rest of the draft
(embed_tokens / fc / decoder layers / norm) byte-identical to
:class:~nemo_automodel.components.speculative.eagle.draft_llama_v12.LlamaEagleDraftModel
so a text-only EAGLE-1/2 checkpoint can initialize stage-2 ViSpec training:
- :class:
VispecImageAdaptor—num_query_tokenslearnable queries cross-attend over the target’s image-token features and compress a whole image span (hundreds to thousands of tokens) intonum_query_tokensvectors.num_query_tokens - 1of them are spliced back into the draft sequence at the original trailing positions of the image span, so the positional layout of the surrounding text is untouched. img_fc— the remaining (“global”) image vector is broadcast onto every subsequent text position and mixed in with a[2*hidden -> hidden]projection, giving each text token vision context without paying for image tokens in the draft’s KV cache.
Reference implementation: vispec/model/cnets_ours.py in
https://github.com/KangJialiang/ViSpec.
Module Contents
Classes
Functions
API
Bases: LlamaEagleDraftModel
EAGLE-1/2 draft extended with ViSpec’s image compression and global image feature.
Adds two config fields on top of the EAGLE-1/2 draft config:
vispec_num_query_tokens(default 2) — queries per image span.draft_num_hidden_layers— inherited, unchanged.
The base draft’s parameters (embed_tokens, fc, layers, norm)
keep their names, so load_state_dict(..., strict=False) restores a
stage-1 EAGLE-1/2 checkpoint and leaves only img_adaptor / img_fc
freshly initialized.
Build the compressed draft sequence for one (batch-size-1) sample.
Walks the sample image span by image span. Each span contributes its
leading text positions (fused with the previous span’s global image
vector) followed by num_query_tokens - 1 compressed image tokens;
the trailing text after the last span is fused with the last span’s
global vector. Because every span ends with its image run, this
reordering preserves the original left-to-right token order.
Parameters:
Tensor of shape [sequence, hidden].
Tensor of shape [sequence, hidden].
Bool tensor of shape [sequence]; True at image positions.
Returns: torch.Tensor
Tuple of (hidden_states, source_index):
Mix the global image vector into the target hidden state, then run EAGLE’s fc.
Parameters:
Tensor of shape [tokens, hidden].
Tensor of shape [tokens, hidden].
Tensor of shape [1, hidden], broadcast over tokens.
Returns: torch.Tensor
Tensor of shape [tokens, hidden].
Predict the next-position target hidden states from vision-aware features.
Batch size must be 1: image spans have per-sample lengths, so the compressed sequences of two samples would not share a length. This matches the reference implementation, which raises on batch size > 1.
Parameters:
Tensor of shape [1, sequence, hidden] — the target’s
embedding-layer output (vision features already spliced in),
shifted left by one position so index i holds the embedding
of token i + 1.
Tensor of shape [1, sequence, hidden] — the target’s last hidden state, not shifted.
Tensor of shape [1, sequence]; 1 for real tokens, 0 for padding.
Bool tensor of shape [1, sequence], aligned with
inputs_embeds (True where token i + 1 is an image token).
Returns: torch.Tensor
Tensor of shape [1, sequence, hidden]. Positions consumed by the
Initialize the ViSpec-only modules (identity-start for img_fc).
img_fc starts as [I | 0]: it copies the target hidden state
through and ignores the global image vector, so a stage-2 run
initialized from a stage-1 EAGLE-1/2 checkpoint starts numerically
equal to that checkpoint and only then learns to use vision context.
Bases: Module
Compress an image-token span into a small set of learnable-query vectors.
A single non-causal cross-attention: num_query_tokens learnable queries
attend over the image-token features, so the output length is independent
of how many image tokens the target emitted.
Parameters:
Draft config supplying hidden_size and num_attention_heads.
Number of learnable queries (ViSpec’s num_q).
Compress image features into num_query_tokens vectors.
Parameters:
Tensor of shape [batch, image_tokens, hidden] holding the target’s embedding-layer output at the image positions of one image span.
Returns: torch.Tensor
Tensor of shape [batch, num_query_tokens, hidden].
Re-draw the learnable queries from N(0, head_dim ** -0.5) (ViSpec’s init).
Pin a target-derived draft config to ViSpec’s released draft architecture.
Both ViSpec stages derive the draft config from the target’s text config, so
without this the draft silently inherits whatever the target’s language tower
happens to use. The released draft is not that: qwen2.5_vl_7B_config.json
in the reference repository sets num_attention_heads: 28,
num_key_value_heads: 28 and qkv_bias: true, whereas the matching
Qwen2.5-VL-7B text tower is 28-head GQA over 4 KV heads. Copying the target
therefore produced a materially smaller draft than the paper’s, which is not
a configuration difference a reader of the recipe would notice.
The three settings cannot be read off the target: HF’s Qwen2_5_VLTextConfig
exposes neither attention_bias nor qkv_bias (its attention hard-codes
the qkv bias inside the module), so there is nothing to inherit. They are
properties of the ViSpec draft rather than of any one target, and are applied
unconditionally for every ViSpec target.
Parameters:
The draft config, already derived from the target’s text config. Modified in place.