40 layers › 4 groups of 4 layers › Re-indexing layer
learnedMoE(layer_id, args) · 854–904
Each position runs 6 of 384 routed experts and one shared expert.
Why
Kept. The shared expert and the many small routed experts are kept from DeepSeekMoE; the report gives no further reason for the counts. DeepSeek V4.1 report §2.1
All 22 steps
- in
- [B batch, T positions, 5120 d]
- out
- [B batch, T positions, 5120 d]
- parameters
- 13,626,901,248 · × 4 groups = 54,507,604,992
Parameters
13,626,901,248 parameters per instance, across its learned components
| Component | Calculation | Parameters |
|---|---|---|
| Router | 1,966,848 across its components | 1,966,848 |
| Routed expert | 13,589,544,960 | |
| Shared expert | 35,389,440 across its components | 35,389,440 |
| Per-instance total | 13,626,901,248 | |
Across 4 independent groups: parameters.
Derived from the pinned reference implementation and configuration; equal to the element counts of the checkpoint’s own tensors on the text path. The quantisation scales, the vision encoder and the DSpark blocks are not counted.
Explanation
Every one of the 40 layers has this mixture; none has a dense feed-forward (model.py 929).
Codemodel.py 792–904 · config.json 1–66
class MoE(nn.Module):
"""Top-k routed experts plus one shared expert every token goes through. Experts are split
across ranks, so `self.experts` is None for those another rank owns."""
def __init__(self, layer_id: int, args: ModelArgs):
super().__init__()
n_routed_experts, n_activated_experts = args.get_moe_config(layer_id)
self.layer_id = layer_id
self.dim = args.dim
assert n_routed_experts % world_size == 0, (
f"Number of experts must be divisible by world size (world_size={world_size})"
)
self.n_routed_experts = n_routed_experts
self.n_local_experts = n_routed_experts // world_size
self.n_activated_experts = n_activated_experts
self.experts_start_idx = rank * self.n_local_experts
self.experts_end_idx = self.experts_start_idx + self.n_local_experts
self.gate = Gate(layer_id, args)
expert_dtype = torch.float4_e2m1fn_x2 if args.expert_dtype == "fp4" else None
self.experts = nn.ModuleList(
[
Expert(
args.dim,
args.moe_inter_dim,
dtype=expert_dtype,
swiglu_limit=args.swiglu_limit,
)
if self.experts_start_idx <= i < self.experts_end_idx
else None
for i in range(self.n_routed_experts)
]
)
assert args.n_shared_experts == 1
self.shared_experts = Expert(args.dim, args.moe_inter_dim, swiglu_limit=args.swiglu_limit)
def forward(self, x: torch.Tensor, image_mask: torch.Tensor | None = None) -> torch.Tensor:
shape = x.size()
x = x.view(-1, self.dim)
weights, indices = self.gate(x, None if image_mask is None else image_mask.flatten())
y = torch.zeros_like(x, dtype=torch.float32)
counts = torch.bincount(indices.flatten(), minlength=self.n_routed_experts).tolist()
for i in range(self.experts_start_idx, self.experts_end_idx):
if counts[i] == 0:
continue
expert = self.experts[i]
idx, top = torch.where(indices == i)
y[idx] += expert(x[idx], weights[idx, top, None])
if world_size > 1:
dist.all_reduce(y)
y += self.shared_experts(x)
return y.type_as(x).view(shape)class Gate(nn.Module):
"""MoE gating. The correction bias steers expert selection only; the routing weights come from the
unbiased scores. Image-span tokens use a separate bias (training `noaux_tc_for_vl`)."""
def __init__(self, layer_id: int, args: ModelArgs):
super().__init__()
n_routed_experts, n_activated_experts = args.get_moe_config(layer_id)
self.dim = args.dim
self.topk = n_activated_experts
self.score_func = args.score_func
self.gate_temp = args.gate_temp
self.norm_topk_prob = args.norm_topk_prob
self.route_scale = args.route_scale
self.weight = nn.Parameter(torch.empty(n_routed_experts, args.dim))
self.bias = nn.Parameter(torch.empty(n_routed_experts, dtype=torch.float32))
self.bias_vl = nn.Parameter(torch.empty(n_routed_experts, dtype=torch.float32)) if args.vision_enabled else None
def forward(self, x: torch.Tensor, image_mask: torch.Tensor | None = None) -> tuple[torch.Tensor, torch.Tensor]:
"""x: [n, dim]; image_mask: [n] bool, True for tokens inside an image span."""
scores = linear(x.float(), self.weight.float()) / self.gate_temp
if self.score_func == "softmax":
scores = scores.softmax(dim=-1)
elif self.score_func == "sigmoid":
scores = scores.sigmoid()
else:
scores = F.softplus(scores).sqrt()
bias = self.bias
if image_mask is not None and self.bias_vl is not None:
bias = torch.where(image_mask.unsqueeze(-1), self.bias_vl, bias)
# the bias picks experts but does not scale them: weights come from the raw scores
indices = (scores + bias).topk(self.topk, dim=-1)[1]
weights = scores.gather(1, indices)
if self.norm_topk_prob and self.topk > 1:
weights /= weights.sum(dim=-1, keepdim=True) + 1e-20 # not norm_eps, matches training
weights *= self.route_scale
return weights, indicesclass Expert(nn.Module):
"""One SwiGLU FFN. The clamps come straight from training, where they keep fp8/fp4 activations in
range: the up branch is clamped on both sides, the gate branch only from above."""
def __init__(self, dim: int, inter_dim: int, dtype=None, swiglu_limit=0.0):
super().__init__()
self.w1 = Linear(dim, inter_dim, dtype=dtype)
self.w2 = Linear(inter_dim, dim, dtype=dtype)
self.w3 = Linear(dim, inter_dim, dtype=dtype)
self.swiglu_limit = swiglu_limit
def forward(self, x: torch.Tensor, weights: torch.Tensor | None = None) -> torch.Tensor:
dtype = x.dtype
gate = self.w1(x).float()
up = self.w3(x).float()
if self.swiglu_limit > 0:
up = torch.clamp(up, min=-self.swiglu_limit, max=self.swiglu_limit)
gate = torch.clamp(gate, max=self.swiglu_limit)
x = F.silu(gate) * up
if weights is not None:
x = weights * x
return self.w2(x.to(dtype)){
"vocab_size": 129280,
"dim": 5120,
"moe_inter_dim": 2304,
"n_layers": 40,
"n_mtp_layers": 3,
"dspark_block_size": 5,
"dspark_noise_token_id": 128799,
"dspark_target_layer_ids": [37, 38, 39],
"dspark_markov_rank": 256,
"dspark_n_routed_experts": 128,
"dspark_n_activated_experts": 3,
"n_heads": 64,
"n_routed_experts": 384,
"n_shared_experts": 1,
"n_activated_experts": 6,
"score_func": "sqrtsoftplus",
"route_scale": 1.5,
"swiglu_limit": 10.0,
"q_lora_rank": 1280,
"head_dim": 512,
"rope_head_dim": 64,
"norm_eps": 1e-20,
"o_groups": 8,
"o_lora_rank": 1024,
"window_size": 128,
"kv_source_layers": [2, 8, 14, 20],
"index_source_layers": [2, 8, 14, 20, 24, 28, 32, 36],
"original_seq_len": 65536,
"rope_theta": 10000,
"rope_factor": 16,
"beta_fast": 32,
"beta_slow": 1,
"index_n_heads": 32,
"index_head_dim": 128,
"index_topk": 512,
"candidate_source_layer": 20,
"candidate_topk_blocks": 2048,
"candidate_block_size": 8,
"hc_mult": 4,
"hc_sinkhorn_iters": 20,
"hc_eps": 1e-06,
"engram_layer_ids": [1, 14],
"engram_vocab_size": 16000000,
"engram_num_embeddings": [384006168, 384016682],
"engram_max_ngram_size": 4,
"engram_pad_id": 2,
"engram_compressed_vocab_size": 99092,
"dtype": "fp8",
"expert_dtype": "fp4",
"compress_rope_theta": 160000,
"compress_ratios": [0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
"vision_n_layers": 32,
"vision_dim": 1024,
"vision_n_heads": 16,
"vision_inter_dim": 2816,
"vision_patch_size": 14,
"vision_downsample_ratio": 3,
"vision_max_n_token": 1024,
"vision_min_pixels": 295936,
"vision_max_wh_ratio": null,
"image_token_id": 129264,
"engram_n_heads": 8,
"engram_head_dim": 256,
"vision_rope_theta": 10000
}Sources4
Direct evidence
The mixture adds the selected routed experts’ outputs and the shared expert’s; routed experts are stored in FP4.
moe · deepseek-ai/DeepSeek-V4.1-Flash / inference/model.py
Pinned revision 2cba9e42aa02 ↗ · lines 854–904
Read code excerpt
class MoE(nn.Module):
"""Top-k routed experts plus one shared expert every token goes through. Experts are split
across ranks, so `self.experts` is None for those another rank owns."""
def __init__(self, layer_id: int, args: ModelArgs):
super().__init__()
n_routed_experts, n_activated_experts = args.get_moe_config(layer_id)
self.layer_id = layer_id
self.dim = args.dim
assert n_routed_experts % world_size == 0, (
f"Number of experts must be divisible by world size (world_size={world_size})"
)
self.n_routed_experts = n_routed_experts
self.n_local_experts = n_routed_experts // world_size
self.n_activated_experts = n_activated_experts
self.experts_start_idx = rank * self.n_local_experts
self.experts_end_idx = self.experts_start_idx + self.n_local_experts
self.gate = Gate(layer_id, args)
expert_dtype = torch.float4_e2m1fn_x2 if args.expert_dtype == "fp4" else None
self.experts = nn.ModuleList(
[
Expert(
args.dim,
args.moe_inter_dim,
dtype=expert_dtype,
swiglu_limit=args.swiglu_limit,
)
if self.experts_start_idx <= i < self.experts_end_idx
else None
for i in range(self.n_routed_experts)
]
)
assert args.n_shared_experts == 1
self.shared_experts = Expert(args.dim, args.moe_inter_dim, swiglu_limit=args.swiglu_limit)
def forward(self, x: torch.Tensor, image_mask: torch.Tensor | None = None) -> torch.Tensor:
shape = x.size()
x = x.view(-1, self.dim)
weights, indices = self.gate(x, None if image_mask is None else image_mask.flatten())
y = torch.zeros_like(x, dtype=torch.float32)
counts = torch.bincount(indices.flatten(), minlength=self.n_routed_experts).tolist()
for i in range(self.experts_start_idx, self.experts_end_idx):
if counts[i] == 0:
continue
expert = self.experts[i]
idx, top = torch.where(indices == i)
y[idx] += expert(x[idx], weights[idx, top, None])
if world_size > 1:
dist.all_reduce(y)
y += self.shared_experts(x)
return y.type_as(x).view(shape)Direct evidence
The router: the square root of softplus of a projection, a selection bias (a second one at image positions), the best six, their unbiased scores normalised and scaled.
gate · deepseek-ai/DeepSeek-V4.1-Flash / inference/model.py
Pinned revision 2cba9e42aa02 ↗ · lines 792–827
Read code excerpt
class Gate(nn.Module):
"""MoE gating. The correction bias steers expert selection only; the routing weights come from the
unbiased scores. Image-span tokens use a separate bias (training `noaux_tc_for_vl`)."""
def __init__(self, layer_id: int, args: ModelArgs):
super().__init__()
n_routed_experts, n_activated_experts = args.get_moe_config(layer_id)
self.dim = args.dim
self.topk = n_activated_experts
self.score_func = args.score_func
self.gate_temp = args.gate_temp
self.norm_topk_prob = args.norm_topk_prob
self.route_scale = args.route_scale
self.weight = nn.Parameter(torch.empty(n_routed_experts, args.dim))
self.bias = nn.Parameter(torch.empty(n_routed_experts, dtype=torch.float32))
self.bias_vl = nn.Parameter(torch.empty(n_routed_experts, dtype=torch.float32)) if args.vision_enabled else None
def forward(self, x: torch.Tensor, image_mask: torch.Tensor | None = None) -> tuple[torch.Tensor, torch.Tensor]:
"""x: [n, dim]; image_mask: [n] bool, True for tokens inside an image span."""
scores = linear(x.float(), self.weight.float()) / self.gate_temp
if self.score_func == "softmax":
scores = scores.softmax(dim=-1)
elif self.score_func == "sigmoid":
scores = scores.sigmoid()
else:
scores = F.softplus(scores).sqrt()
bias = self.bias
if image_mask is not None and self.bias_vl is not None:
bias = torch.where(image_mask.unsqueeze(-1), self.bias_vl, bias)
# the bias picks experts but does not scale them: weights come from the raw scores
indices = (scores + bias).topk(self.topk, dim=-1)[1]
weights = scores.gather(1, indices)
if self.norm_topk_prob and self.topk > 1:
weights /= weights.sum(dim=-1, keepdim=True) + 1e-20 # not norm_eps, matches training
weights *= self.route_scale
return weights, indicesDirect evidence
An expert is SwiGLU with its gate and up branches clamped; the routing weight multiplies the hidden features before the down projection.
expert · deepseek-ai/DeepSeek-V4.1-Flash / inference/model.py
Pinned revision 2cba9e42aa02 ↗ · lines 830–851
Read code excerpt
class Expert(nn.Module):
"""One SwiGLU FFN. The clamps come straight from training, where they keep fp8/fp4 activations in
range: the up branch is clamped on both sides, the gate branch only from above."""
def __init__(self, dim: int, inter_dim: int, dtype=None, swiglu_limit=0.0):
super().__init__()
self.w1 = Linear(dim, inter_dim, dtype=dtype)
self.w2 = Linear(inter_dim, dim, dtype=dtype)
self.w3 = Linear(dim, inter_dim, dtype=dtype)
self.swiglu_limit = swiglu_limit
def forward(self, x: torch.Tensor, weights: torch.Tensor | None = None) -> torch.Tensor:
dtype = x.dtype
gate = self.w1(x).float()
up = self.w3(x).float()
if self.swiglu_limit > 0:
up = torch.clamp(up, min=-self.swiglu_limit, max=self.swiglu_limit)
gate = torch.clamp(gate, max=self.swiglu_limit)
x = F.silu(gate) * up
if weights is not None:
x = weights * x
return self.w2(x.to(dtype))Direct evidence
The reference configuration: 40 layers of width 5,120, 64 heads of 512 features with a 64-feature rotary part, a 1,280-feature query latent, a grouped output of 8 groups at rank 1,024, a window of 128, the per-layer compression ratios and the two source lists, the indexer and its candidate stage, hyper-connections of 4 streams, 384 routed experts of which 6 are used with 1 shared, Engram before layers 1 and 14, and three DSpark blocks.
config · deepseek-ai/DeepSeek-V4.1-Flash / inference/config.json
Pinned revision 2cba9e42aa02 ↗ · lines 1–66
Read code excerpt
{
"vocab_size": 129280,
"dim": 5120,
"moe_inter_dim": 2304,
"n_layers": 40,
"n_mtp_layers": 3,
"dspark_block_size": 5,
"dspark_noise_token_id": 128799,
"dspark_target_layer_ids": [37, 38, 39],
"dspark_markov_rank": 256,
"dspark_n_routed_experts": 128,
"dspark_n_activated_experts": 3,
"n_heads": 64,
"n_routed_experts": 384,
"n_shared_experts": 1,
"n_activated_experts": 6,
"score_func": "sqrtsoftplus",
"route_scale": 1.5,
"swiglu_limit": 10.0,
"q_lora_rank": 1280,
"head_dim": 512,
"rope_head_dim": 64,
"norm_eps": 1e-20,
"o_groups": 8,
"o_lora_rank": 1024,
"window_size": 128,
"kv_source_layers": [2, 8, 14, 20],
"index_source_layers": [2, 8, 14, 20, 24, 28, 32, 36],
"original_seq_len": 65536,
"rope_theta": 10000,
"rope_factor": 16,
"beta_fast": 32,
"beta_slow": 1,
"index_n_heads": 32,
"index_head_dim": 128,
"index_topk": 512,
"candidate_source_layer": 20,
"candidate_topk_blocks": 2048,
"candidate_block_size": 8,
"hc_mult": 4,
"hc_sinkhorn_iters": 20,
"hc_eps": 1e-06,
"engram_layer_ids": [1, 14],
"engram_vocab_size": 16000000,
"engram_num_embeddings": [384006168, 384016682],
"engram_max_ngram_size": 4,
"engram_pad_id": 2,
"engram_compressed_vocab_size": 99092,
"dtype": "fp8",
"expert_dtype": "fp4",
"compress_rope_theta": 160000,
"compress_ratios": [0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0],
"vision_n_layers": 32,
"vision_dim": 1024,
"vision_n_heads": 16,
"vision_inter_dim": 2816,
"vision_patch_size": 14,
"vision_downsample_ratio": 3,
"vision_max_n_token": 1024,
"vision_min_pixels": 295936,
"vision_max_wh_ratio": null,
"image_token_id": 129264,
"engram_n_heads": 8,
"engram_head_dim": 256,
"vision_rope_theta": 10000
}