- in
- [B batch, T positions, 5120 d]
- out
- [B batch, T/2 compressed positions, 512 d_h]
- parameters
- 5,243,392
- mode
- prefill, empty cache
Parameters
5,243,392 parameters per instance, across its learned components
| Component | Calculation | Parameters |
|---|---|---|
| Compressed key–value projection | 2,621,440 | |
| Pooling scores | 2,621,440 | |
| Compressed RMSNorm | 512 | |
| Per-instance total | 5,243,392 | |
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
Only layers 2, 8, 14, 20 build compressed keys and values; every other layer with a compression ratio reads what the nearest earlier of them wrote (model.py 618–619, 746–748).
Codemodel.py 281–293, 429–485
class Compressor(nn.Module):
"""Pools `compress_ratio` consecutive tokens into one KV latent with a learned softmax gate.
Returns the latent before RoPE, or None while a group is still filling up -- so during decode it
only yields every `compress_ratio` steps, holding the partial group in `kv_state`/`score_state`.
Pre-RoPE is deliberate: the indexer needs the unrotated form, so Attention rotates afterwards.
"""
def __init__(self, args: ModelArgs, layer_id: int):
super().__init__()
compress_ratio = args.compress_ratios[layer_id]
head_dim = args.head_dim
self.compress_ratio = compress_ratio
self.head_dim = head_dim
self.norm = RMSNorm(head_dim, args.norm_eps)
# ratio 1 is a plain projection, so it stays in the checkpoint's bf16; the softmax pooling
# above ratio 1 runs in fp32, so those weights are promoted to fp32 to match
self.wkv = Linear(args.dim, head_dim, dtype=torch.float32 if compress_ratio > 1 else torch.bfloat16)
if compress_ratio > 1:
self.wgate = Linear(args.dim, head_dim, dtype=torch.float32)
# tail of an incomplete group, carried across decode steps
self.kv_state: torch.Tensor
self.score_state: torch.Tensor
state_shape = (args.max_batch_size, compress_ratio, head_dim)
self.register_buffer("kv_state", torch.zeros(state_shape, dtype=torch.float32), persistent=False)
self.register_buffer(
"score_state", torch.full(state_shape, -torch.inf, dtype=torch.float32), persistent=False
)
def forward(self, x: torch.Tensor, start_pos: int) -> torch.Tensor | None:
bsz, seqlen, _ = x.size()
ratio, dtype = self.compress_ratio, x.dtype
if ratio == 1: # one token per group: nothing to pool, so no gate and no fp32
return self.norm(self.wkv(x))
x = x.float()
kv, score = self.wkv(x), self.wgate(x)
if start_pos == 0:
should_compress = seqlen >= ratio
remainder = seqlen % ratio
cutoff = seqlen - remainder
if remainder: # trailing partial group waits in the state
kv, self.kv_state[:bsz, :remainder] = kv.split([cutoff, remainder], dim=1)
score, self.score_state[:bsz, :remainder] = score.split([cutoff, remainder], dim=1)
kv = kv.unflatten(1, (-1, ratio))
score = score.unflatten(1, (-1, ratio))
kv = (kv * score.softmax(dim=2)).sum(dim=2)
else: # one token per step: fill a slot, and pool only when the group just completed
should_compress = (start_pos + 1) % ratio == 0
slot = start_pos % ratio
self.kv_state[:bsz, slot] = kv.squeeze(1)
self.score_state[:bsz, slot] = score.squeeze(1)
if should_compress:
kv = (self.kv_state[:bsz] * self.score_state[:bsz].softmax(dim=1)).sum(dim=1, keepdim=True)
if not should_compress:
return None
return self.norm(kv.to(dtype))class RMSNorm(nn.Module):
def __init__(self, dim: int, eps: float = 1e-6):
super().__init__()
self.dim = dim
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim))
def forward(self, x: torch.Tensor):
dtype = x.dtype
x = x.float()
var = x.square().mean(-1, keepdim=True)
x = x * torch.rsqrt(var + self.eps)
return (self.weight * x).to(dtype)Sources2
Direct evidence
The compressor makes the compressed key–value latents: at ratio 1 one projection and a norm per token; above 1 a per-feature softmax over each group of tokens, with an unfinished group kept for the next call.
compressor · deepseek-ai/DeepSeek-V4.1-Flash / inference/model.py
Pinned revision 2cba9e42aa02 ↗ · lines 429–485
Read code excerpt
class Compressor(nn.Module):
"""Pools `compress_ratio` consecutive tokens into one KV latent with a learned softmax gate.
Returns the latent before RoPE, or None while a group is still filling up -- so during decode it
only yields every `compress_ratio` steps, holding the partial group in `kv_state`/`score_state`.
Pre-RoPE is deliberate: the indexer needs the unrotated form, so Attention rotates afterwards.
"""
def __init__(self, args: ModelArgs, layer_id: int):
super().__init__()
compress_ratio = args.compress_ratios[layer_id]
head_dim = args.head_dim
self.compress_ratio = compress_ratio
self.head_dim = head_dim
self.norm = RMSNorm(head_dim, args.norm_eps)
# ratio 1 is a plain projection, so it stays in the checkpoint's bf16; the softmax pooling
# above ratio 1 runs in fp32, so those weights are promoted to fp32 to match
self.wkv = Linear(args.dim, head_dim, dtype=torch.float32 if compress_ratio > 1 else torch.bfloat16)
if compress_ratio > 1:
self.wgate = Linear(args.dim, head_dim, dtype=torch.float32)
# tail of an incomplete group, carried across decode steps
self.kv_state: torch.Tensor
self.score_state: torch.Tensor
state_shape = (args.max_batch_size, compress_ratio, head_dim)
self.register_buffer("kv_state", torch.zeros(state_shape, dtype=torch.float32), persistent=False)
self.register_buffer(
"score_state", torch.full(state_shape, -torch.inf, dtype=torch.float32), persistent=False
)
def forward(self, x: torch.Tensor, start_pos: int) -> torch.Tensor | None:
bsz, seqlen, _ = x.size()
ratio, dtype = self.compress_ratio, x.dtype
if ratio == 1: # one token per group: nothing to pool, so no gate and no fp32
return self.norm(self.wkv(x))
x = x.float()
kv, score = self.wkv(x), self.wgate(x)
if start_pos == 0:
should_compress = seqlen >= ratio
remainder = seqlen % ratio
cutoff = seqlen - remainder
if remainder: # trailing partial group waits in the state
kv, self.kv_state[:bsz, :remainder] = kv.split([cutoff, remainder], dim=1)
score, self.score_state[:bsz, :remainder] = score.split([cutoff, remainder], dim=1)
kv = kv.unflatten(1, (-1, ratio))
score = score.unflatten(1, (-1, ratio))
kv = (kv * score.softmax(dim=2)).sum(dim=2)
else: # one token per step: fill a slot, and pool only when the group just completed
should_compress = (start_pos + 1) % ratio == 0
slot = start_pos % ratio
self.kv_state[:bsz, slot] = kv.squeeze(1)
self.score_state[:bsz, slot] = score.squeeze(1)
if should_compress:
kv = (self.kv_state[:bsz] * self.score_state[:bsz].softmax(dim=1)).sum(dim=1, keepdim=True)
if not should_compress:
return None
return self.norm(kv.to(dtype))Direct evidence
RMSNorm divides by the root mean square over the features, computed in float32, and multiplies by a learned gain.
rmsnorm · deepseek-ai/DeepSeek-V4.1-Flash / inference/model.py
Pinned revision 2cba9e42aa02 ↗ · lines 281–293
Read code excerpt
class RMSNorm(nn.Module):
def __init__(self, dim: int, eps: float = 1e-6):
super().__init__()
self.dim = dim
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim))
def forward(self, x: torch.Tensor):
dtype = x.dtype
x = x.float()
var = x.square().mean(-1, keepdim=True)
x = x * torch.rsqrt(var + self.eps)
return (self.weight * x).to(dtype)