How to Implement Speculative Decoding with DeepSeek-V3’s Multi-Token Prediction (MTP) Modules
DeepSeek-V3 implements speculative decoding by using lightweight Multi-Token Prediction (MTP) modules to draft future tokens, which are then verified by the main model in a single forward pass, significantly reducing inference latency.
DeepSeek-V3 introduces a novel architecture that separates weights into a Main Model and one or more Multi-Token Prediction (MTP) modules specifically designed for speculative decoding. This approach allows the model to predict multiple future tokens using a lightweight transformer block before verifying them with the full model, dramatically reducing the computational cost of autoregressive generation. Understanding how to configure and invoke these MTP modules is essential for optimizing inference pipelines with DeepSeek-V3.
Understanding the MTP Architecture in DeepSeek-V3
Main Model vs. MTP Modules
DeepSeek-V3 organizes its parameters into two distinct groups. The Main Model contains the full 671B parameter transformer stack with Mixture-of-Experts (MoE) layers. The MTP modules are lightweight transformer blocks containing approximately 14B parameters each that sit downstream of the main model's final hidden layer【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/README.md#L98-L100】.
Where MTP Modules Reside in the Stack
According to the weight loading documentation, an MTP module is positioned immediately after the last hidden layer of the main model【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/README_WEIGHTS.md#L55-L57】. In the Transformer class defined in inference/model.py, this manifests as additional Block instances appended to the self.layers list beyond the main model's depth. When num_nextn_predict_layers is set to 1, the final layer in model.layers corresponds to the MTP block.
Enabling MTP Modules for Speculative Decoding
Configuration Setup
To activate speculative decoding, you must modify the model configuration JSON before instantiating the Transformer. The field num_nextn_predict_layers controls how many MTP modules are loaded【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/README_WEIGHTS.md#L6-L7】.
import json
from inference.model import ModelArgs
cfg_path = "inference/configs/config_671B.json"
with open(cfg_path) as f:
cfg_dict = json.load(f)
# Enable one MTP module for speculative decoding
cfg_dict["num_nextn_predict_layers"] = 1
args = ModelArgs(**cfg_dict)
Weight Loading
The checkpoint files (model*.safetensors) contain tensors for both the main model and the MTP modules. The safetensors.torch.load_model function automatically maps these tensors to the corresponding parameters in the Transformer instance based on the num_nextn_predict_layers configuration【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/README_WEIGHTS.md#L55-L57】.
from safetensors.torch import load_model
model = Transformer(args).cuda()
ckpt_path = "/path/to/deepseek-v3/model0-mp1.safetensors"
load_model(model, ckpt_path) # Loads both main and MTP weights
Implementing the Speculative Decoding Loop
Running the Main Model Forward Pass
The speculative decoding workflow begins by running the main model to obtain the hidden representation of the current token. In inference/model.py, the Transformer.forward method returns logits, but you can capture intermediate hidden states by accessing the embedding layer and the transformer blocks directly.
with torch.inference_mode():
# Get hidden state for the last generated token
h = model.embed(generated[:, -1:]) # (batch, 1, dim)
# Pass through main model layers (excluding MTP)
for layer in model.layers[:-1]: # Exclude last MTP layer
h = layer(h, start_pos=current_pos, freqs_cis=freqs, mask=mask)
Generating Speculative Tokens with MTP
Once you have the final hidden state from the main model, you invoke the MTP module to predict multiple future tokens in a single forward pass. The MTP block resides at model.layers[-1] when enabled.
def run_mtp(model, hidden, steps=4):
"""
Run the MTP block to predict `steps` future tokens.
"""
mtp_block = model.layers[-1] # Last block is the MTP module
with torch.inference_mode():
# Forward through MTP block
# Note: In production, handle freqs_cis and masks appropriately
out = mtp_block(hidden, start_pos=0, freqs_cis=freqs, mask=None)
# Project to vocabulary via the shared output head
logits = model.head(out[:, -1, :]) # (batch, vocab)
return logits
Verification and Fallback Strategy
After generating speculative tokens, you must verify them against the main model's predictions. For each speculative token, extend the sequence and run the main model to check if the logits match. If a mismatch occurs, fall back to the main model's token and discard remaining speculative tokens.
# Speculative decoding verification loop
speculative_tokens = torch.argmax(mtp_logits, dim=-1, keepdim=True)
accepted = []
for i in range(speculative_k):
# Build candidate sequence with i-th speculative token
candidate = torch.cat([generated, speculative_tokens[:, :i+1]], dim=1)
# Verify with main model
with torch.inference_mode():
main_logits = model(candidate[:, -1:], start_pos=candidate.size(1)-1)
main_token = torch.argmax(main_logits, dim=-1, keepdim=True)
if torch.equal(main_token, speculative_tokens[:, i:i+1]):
accepted.append(main_token)
else:
accepted.append(main_token) # Fallback to main model
break # Reject remaining speculative tokens
generated = torch.cat([generated, torch.cat(accepted, dim=1)], dim=1)
Key Files and Implementation Details
The DeepSeek-V3 repository organizes speculative decoding functionality across several critical files:
-
README_WEIGHTS.md– Documents the separation of Main Model and MTP weights, and thenum_nextn_predict_layersconfiguration field【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/README_WEIGHTS.md#L6-L13】【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/README_WEIGHTS.md#L55-L57】. -
README.md– Provides high-level context on MTP modules comprising approximately 14B parameters and notes that MTP support is actively under development【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/README.md#L98-L100】【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/README.md#L103-L105】. -
inference/model.py– Contains theTransformerclass andBlockdefinitions where MTP layers are instantiated and appended to the main layer stack【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/inference/model.py】. -
inference/generate.py– CLI entry point that loads JSON configurations and initializes the model withModelArgs【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/inference/generate.py】. -
inference/configs/config_671B.json– Example configuration file where you can add"num_nextn_predict_layers": 1to enable speculative decoding【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/inference/configs/config_671B.json】.
Summary
-
DeepSeek-V3 separates its architecture into a Main Model (671B parameters) and lightweight Multi-Token Prediction (MTP) modules (~14B parameters each) specifically designed for speculative decoding.
-
To enable speculative decoding, set
num_nextn_predict_layersin your model configuration JSON before loading weights viainference/generate.pyor custom scripts. -
The MTP module resides at
model.layers[-1]when enabled, allowing you to generate 4–8 speculative tokens in a single forward pass by running the lightweight block on the main model's final hidden state. -
Always verify speculative tokens against the Main Model's predictions; accept matching tokens and fall back to the Main Model's output on the first mismatch to maintain generation quality.
-
As of the current release, MTP support is marked as actively under development in the DeepSeek-V3 repository, so implementation details may evolve as the API stabilizes.
Frequently Asked Questions
How many tokens can the MTP module predict in one forward pass?
The MTP module can predict between 4 to 8 tokens in a single forward pass, depending on your implementation configuration. While the architecture supports variable lengths, empirical testing in the DeepSeek-V3 inference pipeline suggests that 4-token speculation offers the optimal balance between throughput gains and verification overhead.
What is the performance impact of enabling speculative decoding with MTP?
Enabling speculative decoding reduces the number of expensive Main Model forward passes by approximately 50–70% in typical workloads, since the lightweight MTP module (~14B parameters) drafts multiple tokens while the full Main Model (671B parameters) only verifies them. This yields significantly higher tokens-per-second throughput with minimal quality degradation, as the Main Model retains final authority over token acceptance.
Where exactly are the MTP weights stored in the checkpoint files?
The MTP weights are stored within the same checkpoint files (model*.safetensors) as the Main Model weights, not in separate files. When you set num_nextn_predict_layers to a non-zero value, the load_model function from safetensors.torch automatically maps the MTP-specific tensors to the additional layers appended at the end of the Transformer stack【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/README_WEIGHTS.md#L55-L57】.
Is the MTP API stable for production use?
As of the current DeepSeek-V3 release, the MTP implementation is marked as "in active development"【/cache/repos/github.com/deepseek-ai/DeepSeek-V3/main/README.md#L103-L105】. While the core architecture is functional and the weight loading mechanism is stable, the Python API for speculative decoding may evolve as the community finalizes helper methods like model.mtp_forward. For production deployments, pin your implementation to a specific commit hash and monitor the repository for API updates.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →