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 the num_nextn_predict_layers configuration 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 the Transformer class and Block definitions 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 with ModelArgs【/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": 1 to 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_layers in your model configuration JSON before loading weights via inference/generate.py or 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →