The Phantom Mapping: Debugging a Silent Vocabulary Mismatch in EAGLE-3 Speculative Decoding
Introduction
In the intricate world of speculative decoding for large language models, a single incorrect tensor can render an entire pipeline useless. Message [msg 3566] captures a pivotal moment in a deep debugging session where an AI assistant is investigating why a newly trained EAGLE-3 draft model for Kimi-K2.5 — a massive 163,840-token vocabulary model running across 8 GPUs — produces essentially zero accepted tokens during inference. The assistant, having already fixed a weight key name mismatch between the training library (speculators) and the inference engine (SGLang), now turns its attention to the d2t tensor: the critical mapping from the draft model's 32,000-token vocabulary back to the target model's full 163,840-token vocabulary. This message represents a classic debugging pivot — the search for a silent data corruption that could explain why a model that trained successfully fails completely at inference time.
Context: The EAGLE-3 Training Pipeline
To understand the stakes, we must first understand the architecture. EAGLE-3 is a speculative decoding technique where a small "draft" model predicts multiple future tokens in a single forward pass, and the large "target" model verifies them in parallel. The draft model has its own vocabulary of 32,000 tokens — a compressed subset of the target model's 163,840-token vocabulary. The d2t (draft-to-target) tensor is the lookup table that maps each draft token ID to its corresponding target token ID. If this mapping is incorrect, the draft model's predictions become meaningless: it might predict draft token 42, which maps to target token 0 (a special token like <pad>), causing every prediction to be rejected.
The assistant has been training an EAGLE-3 draft model for Kimi-K2.5, a state-of-the-art multimodal model. The training pipeline extracts hidden states from the target model at three specific layers (layers 2, 30, and 58 in the 60-layer network), concatenates them into a 21,504-dimensional feature vector (3 × 7,168), projects them down to 7,168 dimensions via a fusion layer (fc), and uses these fused features to train the draft model to predict future tokens. After training on 10,000 samples (approximately 21 million tokens), the draft model achieves a validation loss of ~6.13 and step-0 accuracy of ~74.5% — metrics that suggest it has learned something meaningful.
Yet when deployed on SGLang for inference, the acceptance rate hovers at 0.20–0.21, meaning only about 1 in 5 draft tokens is accepted by the target model — essentially no better than random guessing. The assistant has already discovered and fixed one issue: the speculators library saves the draft model's decoder layer under the key layers.0.*, but SGLang's LlamaForCausalLMEagle3 expects midlayer.*. After renaming the keys, the acceptance rate barely budged from 0.20 to 0.21, suggesting the decoder weights are now loading but the model still doesn't work.
The Subject Message: Investigating the d2t Tensor
In [msg 3565], immediately preceding the subject message, the assistant noticed something suspicious about the d2t tensor stored in the trained checkpoint. The SGLang code computes hot_token_id — the set of valid target token IDs that the draft model can propose — using the formula hot_token_id = d2t + torch.arange(loaded_weight.shape[0]). When the assistant inspected d2t[:10], it found all zeros. This means draft tokens 0 through 9 would map to target tokens 0 through 9 — which seemed wrong. If multiple draft tokens map to the same target token, or if the mapping is identity for the first N tokens, the draft model's predictions would be degenerate.
The subject message [msg 3566] picks up this thread:
Wait — ourd2thas values like[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]at the start. That means draft token 0 maps to target token 0, draft token 1 also maps to... wait, that can't be right. Let me check what format d2t is supposed to be:
The assistant then executes a bash command to load the original d2t.pt file from the vocabulary mapping directory (/data/eagle3/synth_10k_sglang/vocab_mapping/d2t.pt), rather than the one embedded in the checkpoint. The output reveals:
d2t shape: torch.Size([32000]), dtype: torch.int64
d2t[:20]: tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
d2t min: 0, max: 131608
The first 20 entries are all zero. The minimum is 0 and the maximum is 131,608 — well within the target model's vocabulary size of 163,840.
The Reasoning Process: What the Assistant Is Thinking
The assistant's thinking process in this message is a textbook example of systematic debugging. Having ruled out the weight key name mismatch as the primary cause (the acceptance rate barely improved after the fix), the assistant is now working through a mental checklist of everything that could go wrong between training and inference:
- Weight shapes and values: Verified in earlier messages that the QKV projection weights have the correct input dimension (14,336 = 2 × 7,168), matching SGLang's expectation. The weights have reasonable magnitudes (mean ~0, std ~0.004), confirming the model was actually trained.
- Hidden state capture: Verified that SGLang's
set_eagle3_layers_to_capturemethod is properly delegated fromkimi_k25.pyto the underlying language model, and that the layer IDs[2, 30, 58]are correctly transformed to[3, 31, 59](SGLang uses 1-indexed layers internally). - Vocabulary mapping: Now investigating whether the
d2ttensor is correctly formatted. The assistant knows that SGLang computeshot_token_id = d2t + torch.arange(...), and is checking whether the rawd2tvalues are correct. The critical question the assistant is implicitly asking: Is the d2t tensor storing absolute target token IDs, or is it storing offsets/differences? The SGLang coded2t + torch.arange(...)suggests it expects offsets — but the raw values (0, 0, 0, ...) don't make sense as offsets either. The assistant's comment "draft token 0 maps to target token 0, draft token 1 also maps to... wait, that can't be right" shows the assistant is reasoning through the semantics: if d2t[0] = 0 and d2t[1] = 0, then both draft tokens 0 and 1 would map to target token 0 (after adding the arange offset), creating a collision.
Assumptions and Potential Missteps
The assistant makes several assumptions in this message:
- The d2t format is the problem: The assistant assumes that the d2t tensor might be incorrectly formatted, causing the draft model's predictions to be misinterpreted. This is a reasonable hypothesis — if the vocabulary mapping is wrong, the draft model would propose tokens that don't correspond to valid target tokens, leading to zero acceptance.
- The first entries being zero is suspicious: The assistant assumes that having zeros at the start of d2t is abnormal. However, in many vocabulary mapping schemes, the first N tokens (0–99 or so) are special tokens (BOS, EOS, PAD, UNK, etc.) that map to themselves. The zeros might actually be correct — draft token 0 maps to target token 0 (which is typically a padding token), draft token 1 maps to target token 1, etc. The non-zero entries starting around index 787 (as revealed in the subsequent message [msg 3567]) suggest the mapping only deviates from identity for tokens beyond the special token range.
- The d2t investigation is the right next step: Given the evidence so far, investigating d2t is a logical step. The weight keys are fixed, the hidden state capture mechanism appears correct, so the vocabulary mapping is a natural next suspect. However, the chunk summary reveals that the assistant is chasing a red herring. The real problem — as diagnosed later in the session — is that the hidden states passed to the draft model are 7,168-dimensional (single layer) instead of 21,504-dimensional (three layers concatenated). The
fcfusion layer (which projects 21,504 → 7,168) is never applied because the shape checkhidden_states.shape[-1] != embeds.shape[-1]evaluates to7168 != 7168→ False, bypassing the fusion entirely. The root cause is thateagle_use_aux_hidden_stateis not properly activated for the KimiK25 model, so the target model only returns the final layer's hidden state instead of the three intermediate layers' hidden states. This is a fascinating debugging trap: the assistant is investigating a secondary hypothesis (vocabulary mapping) while the primary cause (hidden state dimensionality) remains undetected. The d2t investigation is not wrong — it's a legitimate concern — but it's a distraction from the actual root cause.
Input Knowledge Required
To fully understand this message, one needs:
- EAGLE-3 architecture: Understanding that the draft model has a smaller vocabulary (32,000) than the target model (163,840), and that
d2tmaps between them. Also understanding the auxiliary hidden state mechanism where three intermediate layer hidden states are concatenated and fused. - SGLang's speculative decoding implementation: Knowing that SGLang computes
hot_token_idfromd2tusing the formulad2t + torch.arange(...), and that this determines which target tokens the draft model is allowed to propose. - The training pipeline: Understanding that the speculators library trains the draft model and saves weights with specific key names (e.g.,
layers.0.*), while SGLang expects different key names (e.g.,midlayer.*). - The vocabulary mapping process: Knowing that
d2tandt2dtensors are created during the data preparation phase by analyzing which tokens appear in the training data, and that the draft vocabulary is a subset of the target vocabulary. - The debugging context: Understanding that this is the second attempt at fixing the zero-acceptance-rate problem, following the weight key name fix that didn't produce meaningful improvement.
Output Knowledge Created
This message produces several important pieces of knowledge:
- d2t tensor characteristics: The d2t tensor has shape [32000], dtype int64, with values ranging from 0 to 131,608. The first ~100 entries are zeros, and non-zero entries begin appearing around index 787.
- The d2t format is likely correct: While the assistant doesn't explicitly conclude this in the message, the subsequent message [msg 3567] reveals that t2d (target-to-draft) has 32,000 True entries, and the first 10 target tokens (0-9) are in the draft vocabulary — confirming that the identity mapping for special tokens is expected behavior.
- The vocabulary mapping is not the root cause: By process of elimination, the d2t investigation helps rule out one more hypothesis, narrowing the search space for the actual problem.
- A methodological precedent: The message establishes a pattern of systematic hypothesis testing — formulate a hypothesis about what's broken, write a diagnostic script to test it, interpret the results, and either fix the issue or move to the next hypothesis.
The Broader Significance
This message is a microcosm of the challenges in deploying speculative decoding for large language models. The EAGLE-3 architecture involves multiple interacting components — the target model, the draft model, the vocabulary mapping, the hidden state capture mechanism, the weight loading logic — and any one of them can silently fail without producing obvious error messages. The assistant's debugging approach is methodical: verify each component independently, starting with the most likely failure points.
The d2t investigation also highlights a common pattern in ML engineering: the tension between training-time and inference-time assumptions. The speculators library and SGLang were developed independently, and they make different assumptions about weight key names, tensor formats, and architectural details. Bridging these gaps requires careful attention to every interface between the two systems.
Conclusion
Message [msg 3566] captures a moment of focused investigation in a complex debugging session. The assistant, faced with a trained model that refuses to produce useful speculative predictions, systematically works through possible causes. The d2t tensor investigation, while ultimately a red herring, represents sound debugging methodology: formulate a hypothesis, write a diagnostic test, interpret the results, and move on. The message also reveals the hidden complexity of speculative decoding systems, where a single incorrect tensor format can silently sabotage an entire pipeline. The real lesson is that in large-scale ML systems, correctness at every interface — between training and inference, between libraries, between components — is not optional; it's the difference between a model that works and one that produces 0.20 acceptance rate with no obvious explanation.