DeepSeek-V2 from Scratch: Multi-head Latent Attention and DeepSeekMoE
Building DeepSeek-V2's two core innovations from the ground up — low-rank KV joint compression, decoupled RoPE, matrix absorption, fine-grained expert segmentation, shared experts, and three-level load balancing — all derived step by step with concrete numbers.
The previous blogs derived how GQA reduces the KV cache by sharing key-value heads across query groups. GQA with groups cuts the cache by a factor of , and MQA () goes all the way down to a single KV head — but at a quality cost. The fundamental tension was clear: fewer KV heads means less memory, but also less representational capacity.
DeepSeek-V2 (DeepSeek-AI, 2024) resolves this tension with a completely different approach. Instead of reducing the number of KV heads, it compresses keys and values jointly into a single low-rank latent vector. The result is an attention mechanism called Multi-head Latent Attention (MLA) that uses less KV cache than MQA while achieving stronger performance than standard MHA. That is not a typo. Less cache and better quality.
On the feed-forward side, DeepSeek-V2 replaces standard FFNs with DeepSeekMoE, an architecture that segments experts into finer granularity and isolates shared experts from routed ones. Together, MLA and DeepSeekMoE produce a 236B-parameter model that activates only 21B parameters per token, saves 42.5% of training costs compared to the dense DeepSeek 67B, reduces the KV cache by 93.3%, and boosts generation throughput by 5.76x.
We will derive every equation from scratch. The linear algebra tools we need — matrix multiplication, the transpose identity, associativity, and low-rank factorization — are derived in Mathematical Prerequisites for DeepSeek-V2.
The Running Model
We continue with the same model from the previous blogs, but we now need to specify MLA-specific dimensions. We keep the base dimensions identical so that all KV cache comparisons are apples-to-apples:
- (embedding dimension, called previously)
- attention heads
- (per-head dimension, so )
- layers
- fp16 throughout (2 bytes per element)
For MLA, we introduce two new dimensions:
- (KV compression dimension — the size of the latent vector)
- (query compression dimension)
- (per-head dimension for decoupled RoPE queries and keys)
These are smaller than the DeepSeek-V2 production values (, , ) but preserve all the ratios and make arithmetic tractable. In particular, , which is the compression that makes MLA work.
1. Standard MHA: What We Are Replacing
We established the MHA formulas in previous blogs. Let us restate them in the notation of the DeepSeek-V2 paper so that every equation lines up exactly.
Let be the hidden state of the -th token at the input to an attention layer. Standard MHA produces queries, keys, and values through three projection matrices:
where . Since in our model, these are square matrices. The concatenated are each in — that is, .
We then split each into heads:
where — each is a 64-dimensional vector.
The output of head at position is:
The final output concatenates all heads and projects back:
where .
KV cache cost of MHA
During autoregressive generation, we must cache and for every previous token at every layer. For a single token at a single layer, we store:
Numerical check
Across all layers, the total number of elements cached per token is . In fp16, this is bytes KB per token. This matches the number we derived in the KV bottleneck blog.
2. The Core Idea: Low-Rank Key-Value Joint Compression
Here is the key insight of MLA. In standard MHA, the keys and values are each dimensional vectors. We cache both, so we store elements per token per layer. But keys and values are both linear projections of the same hidden state . They share the same source of information.
MLA exploits this by first compressing down to a much smaller latent vector , and then recovering keys and values from this compressed representation via up-projection matrices. The latent vector is what we cache — not the full keys and values.
2.1 The Down-Projection
We define a down-projection matrix that compresses the hidden state:
In our running model, . This takes the 512-dimensional hidden state and compresses it to a 128-dimensional latent vector. The ratio is — a 4x compression.
2.2 The Up-Projections
From this latent vector, we recover full-dimensional keys and values through two up-projection matrices:
where . In our model, these are matrices. The superscript stands for “content” — we will soon add a separate “RoPE” component to the keys.
Let us be precise about what happens dimensionally. , and , so . This 512-dimensional vector is then split into heads of dimension each, exactly like standard MHA:
KV cache cost of MLA (first attempt)
Here is the crucial difference. During inference, we do not cache or . We only cache the latent vector . When we need keys and values for a previous token, we reconstruct them by applying and to the cached latent.
The cache cost per token per layer is now:
Compare this to MHA’s . The ratio is . We have reduced the KV cache by 8x.
Numerical check
Across layers: elements per token. In fp16: bytes KB per token. Compare to MHA’s 24 KB per token. The ratio is . Correct.
2.3 Why This Is Not Just Low-Rank Factorization
This is the part that is easy to gloss over. One might think: “You replaced with and with . That is just a low-rank factorization of the weight matrices.” And that is true — mathematically, is identical to using a single rank- key projection. The same holds for values.
But the point is not about the weight matrices. The point is about what we cache. In standard MHA, we cache the outputs and — two separate 512-dimensional vectors. In MLA, we cache a single 128-dimensional latent vector from which both keys and values can be recovered. The keys and values share a compressed representation. This joint compression is what makes the cache reduction so dramatic: instead of caching elements (keys and values separately), we cache elements (one shared latent).
3. The RoPE Incompatibility Problem
We cannot simply plug the compressed keys into the attention formula and call it done. There is a fundamental incompatibility with Rotary Position Embedding (RoPE).
3.1 What RoPE Does
RoPE encodes position information by applying a position-dependent rotation matrix to queries and keys before computing attention scores. For a token at position , the RoPE operation transforms a query or key vector into , where the rotation depends on . The key property is that the dot product depends only on the relative position , which is what gives Transformers their relative position awareness.
3.2 Why RoPE Breaks Low-Rank KV Compression
In standard MHA with RoPE, the attention computation is:
Now consider what happens with MLA. The key is , where is the slice of corresponding to head . If we apply RoPE to this key:
This is a position-dependent transformation of . We cannot separate the position dependence from the content dependence. Specifically, we cannot precompute this and cache just , because the RoPE rotation is applied after the up-projection — so we would need to cache the rotated, up-projected key, which is 512-dimensional. That defeats the entire purpose of low-rank compression.
The mathematical issue is precise: RoPE is a rotation, and matrix multiplication does not commute with rotation in general. That is, because and the RoPE rotation matrix do not commute (non-commutativity of matrix multiplication). So we cannot “push” RoPE inside the up-projection and apply it to the latent vector instead.
4. The Solution: Decoupled RoPE
The DeepSeek-V2 solution is to decouple the position-dependent part from the content-dependent part entirely. We create additional query and key vectors specifically for carrying RoPE, separate from the content-based queries and keys.
4.1 Decoupled RoPE Queries
We produce extra “RoPE query” heads for each attention head. These come from a separate projection. But MLA also compresses the queries (to save activation memory during training, not KV cache). Let us derive the full query pathway.
First, we compress the hidden state for queries:
where . In our model, . The compressed query latent .
From this, we produce the content queries and the RoPE queries:
where is the query up-projection (size in our model), and produces the RoPE queries (size , since ).
The content queries split into heads: , with each .
The RoPE queries split into heads: , with each .
4.2 Decoupled RoPE Keys
For the key side, we produce a shared RoPE key (a single vector, not per-head):
where — a matrix in our model.
The key insight: is shared across all heads. This is a single 32-dimensional vector that carries position information for all 8 heads. This is efficient because position information is inherently the same across heads — token is at position regardless of which head is looking at it.
4.3 Assembling the Full Query and Key
For each head , the full query is the concatenation of the content part and the RoPE part:
This gives .
Similarly, for each head , the full key concatenates the content part (head-specific) with the shared RoPE part:
This also gives .
4.4 The Attention Computation
The attention score between query at position and key at position for head is:
We expand this dot product. By the definition of the dot product of concatenated vectors, this equals the sum of the dot products of the corresponding parts:
The first term captures content-based similarity with no position information. The second term captures relative position information via RoPE. The two concerns are cleanly separated.
The full attention output for head is:
Note that the scaling factor is , since the query-key dot product now operates in the concatenated -dimensional space.
The final output is:
4.5 What Gets Cached
During inference, we cache two things per token per layer:
- The KV latent vector
- The decoupled RoPE key
The RoPE key must be cached because it has already been rotated by the position-dependent RoPE matrix — we cannot reconstruct it from .
Numerical check
Across layers: elements per token. In fp16: bytes KB per token.
Compare to MHA’s 24 KB per token. The ratio is , or about a 6.4x reduction. Compare to MQA’s cache of elements KB per token. MLA at 3.75 KB is slightly larger than MQA’s 3 KB — but MLA achieves stronger performance than full MHA, while MQA degrades quality significantly.
Comparison with GQA
Let us verify the paper’s claim that MLA’s cache is equivalent to GQA with approximately 2.25 groups. In our model, GQA with groups caches elements per token. We want :
So in our model, MLA’s cache is equivalent to GQA with 1.25 groups. In the production DeepSeek-V2 model, , , and , giving groups. This matches the paper’s claim exactly.
5. The Matrix Absorption Trick
We have established that MLA caches only and . But there is a computational concern: to compute attention, we need the full keys and full values . For every previous token in the context, we would need to apply these up-projection matrices to the cached latent . This would add matrix-vector multiplications per layer per generated token, which is expensive.
The solution is to absorb the up-projection matrices into the query and output projections. This is possible because of the associative law of matrix multiplication: .
5.1 Absorbing Into
Consider the content part of the attention score for head :
Here , , and . We can rewrite this as:
We used the transpose identity: for any vectors and matrix of compatible dimensions.
Define . Then:
This is a dot product directly between the transformed query and the cached latent vector. We never need to compute at all. The up-projection has been absorbed into the query side.
In practice, we can precompute a combined matrix that maps directly from the query latent to the -dimensional space, so . By the associative law of matrix multiplication, , so this is mathematically identical.
Numerical check
has dimensions … let us be more careful. Actually, is the slice of for head , so . And . So . The product . The resulting query . This matches the dimension of , so the dot product is well-defined and produces a scalar. Correct.
5.2 Absorbing Into
Similarly, the output of head involves:
where are the attention weights (scalars). We pulled out of the sum by linearity of matrix multiplication: .
Now the output projection for head applies :
By the associative law, we precompute (that is, ), and then:
Again, we never compute the full values . The up-projection has been absorbed into the output projection.
5.3 What This Means
After absorption, the attention computation for the content part works directly in the -dimensional latent space:
- Compute from the current token’s query latent (one matrix-vector multiply per head)
- For each cached position , compute the attention score as (a dot product in dimensions)
- Compute the weighted sum (weighted sum of latent vectors)
- Apply to the result (one matrix-vector multiply per head)
We never up-project the keys or values for any previous token. The entire computation stays in the compressed latent space. This is the computational efficiency of MLA: not only is the cache smaller, but the per-token attention arithmetic is cheaper.
6. The Complete MLA Algorithm
Let us assemble all the pieces into the full computation. For the current token :
Query pathway:
KV pathway:
Attention:
The boxed vectors are the only quantities cached per token per layer. Everything else is either computed on-the-fly for the current token or absorbed into the query/output matrices.
7. KV Cache Comparison: MHA vs. GQA vs. MQA vs. MLA
Let us now derive the KV cache formula for every attention mechanism and compare them in a single table. For our running model (, , , , ):
| Mechanism | Elements per token per layer | Total elements () | Bytes (fp16) | Capability |
|---|---|---|---|---|
| MHA | 24 KB | Strong | ||
| GQA () | 6 KB | Moderate | ||
| MQA () | 3 KB | Weak | ||
| MLA | 3.75 KB | Stronger |
Numerical check
Let us verify the MLA entry. Per layer: . Across 12 layers: . In fp16: bytes KB. The cache sits between MQA (3 KB) and GQA with 2 groups (6 KB), yet delivers stronger performance than full MHA (24 KB). This is the headline result.
For the production DeepSeek-V2 model (, , , , ):
The ratio is , or a 56.9x reduction. The paper reports a 93.3% reduction in KV cache, which corresponds to a ratio of . The difference is because the paper compares to the actual DeepSeek 67B model (which has different dimensions), not to a hypothetical MHA version of DeepSeek-V2.
8. DeepSeekMoE: Fine-Grained Expert Specialization
We now turn to the second architectural innovation: the feed-forward network. In a standard Transformer, each layer has a dense FFN applied to every token. In DeepSeek-V2, all FFNs except the first layer’s are replaced with DeepSeekMoE layers (Dai et al., 2024).
DeepSeekMoE has two key ideas that distinguish it from conventional MoE architectures like GShard:
-
Fine-grained expert segmentation: Instead of a few large experts, use many small experts. This allows finer specialization — each expert can focus on a narrower subset of the input space.
-
Shared expert isolation: Designate some experts as “shared” — they process every token regardless of routing decisions. The remaining experts are “routed” by a gating mechanism. This prevents redundant knowledge from being duplicated across multiple routed experts.
8.1 The FFN Output
Let be the FFN input for the -th token (the output of the attention sublayer after the residual connection and normalization). The DeepSeekMoE output is:
where:
- is the number of shared experts (always active)
- is the number of routed experts (selectively activated)
- is the -th shared expert (a standard FFN)
- is the -th routed expert (a standard FFN)
- is the gate value for routed expert on token
- The leading is the residual connection
In DeepSeek-V2: shared experts and routed experts, with routed experts activated per token.
For our running example, let us use shared expert, routed experts, and activated per token. This keeps the arithmetic simple while preserving the architecture’s structure.
8.2 The Routing Mechanism
The gate values determine which routed experts are activated and with what weight. They are computed in three steps.
Step 1: Token-to-expert affinity scores. For each routed expert , we compute a raw affinity score:
where is the centroid of the -th routed expert — a learned parameter vector. The softmax is taken over all experts, so . Each can be interpreted as “the probability that expert is the right expert for token .”
Numerical check
In our running example, suppose token has hidden state and the dot products with the 8 expert centroids are . After softmax:
Computing: , , , , , , , . Sum .
Sum check: . Correct.
Step 2: Top-K selection. We select the experts with the highest affinity scores:
In our example with : the top-2 experts are expert 1 () and expert 2 ().
Step 3: Gate values. The gate value for expert is:
So in our example: , , and for .
Numerical check of the FFN output
The FFN output for token is:
Only 3 FFNs are evaluated: 1 shared expert and 2 routed experts. The remaining 6 routed experts are not computed at all. This is the computational savings of MoE: we have experts in total, but only evaluate per token.
In the production model: experts per layer, but only are evaluated per token. That is of the experts — a 20x sparsity ratio.
9. Why Fine-Grained Segmentation Helps
This is a design choice that deserves explanation. Why use 160 small routed experts instead of, say, 16 large ones? The answer lies in combinatorial specialization.
With large experts and , the number of possible expert combinations per token is:
This is the binomial coefficient formula .
With fine-grained experts and :
Let us compute this step by step. Numerator: . Then . Then . Then . Then .
Dividing by : .
So billion possible expert combinations. Compare to 120 with the coarse-grained design. The fine-grained design has times more possible specialization patterns. Each token can be served by a highly specific combination of micro-experts, enabling much finer-grained knowledge representation.
The total number of parameters is roughly the same in both designs (you can match total expert parameters by making each fine-grained expert proportionally smaller), but the combinatorial expressiveness is vastly greater.
10. Shared Expert Isolation
The second key idea in DeepSeekMoE is shared expert isolation. In a standard MoE without shared experts, common knowledge (e.g., basic language patterns, universal syntactic rules) must be learned independently by multiple routed experts, because any token might be routed to any subset of experts. This wastes capacity — multiple experts end up storing redundant copies of the same common knowledge.
Shared experts solve this by providing a dedicated pathway for common knowledge. Every token passes through all shared experts, so common patterns need only be learned once. The routed experts are then free to specialize in less common, more specific patterns.
Mathematically, the decomposition is clean:
The residual preserves the input, the shared experts add common transformations, and the routed experts add token-specific refinements. This three-way decomposition is analogous to a principal component decomposition: the shared experts capture the high-variance common modes, and the routed experts capture the low-variance specialized modes.
11. Load Balancing: Three Auxiliary Losses
A persistent problem with MoE models is routing collapse: the router learns to send most tokens to a small number of “popular” experts, leaving many experts undertrained and underutilized. DeepSeek-V2 addresses this with three auxiliary losses, each targeting a different level of the deployment hierarchy.
11.1 Expert-Level Balance Loss
The expert-level balance loss encourages each individual expert to receive an approximately equal share of tokens. It is defined as:
where is a hyperparameter (set to 0.003 in DeepSeek-V2), and and capture the actual and intended load on expert across a sequence of tokens:
Here is the fraction of tokens routed to expert , normalized so that perfectly balanced routing gives for all . The indicator function equals 1 when the condition is true and 0 otherwise. is the mean gate probability of expert across all tokens.
Numerical check
In our running example (, ), suppose over tokens, the routing decisions are:
| Token | Selected experts | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| 1 | 1, 2 | 0.35 | 0.21 | 0.07 | 0.04 | 0.11 | 0.03 | 0.13 | 0.06 |
| 2 | 1, 7 | 0.30 | 0.10 | 0.05 | 0.08 | 0.12 | 0.04 | 0.25 | 0.06 |
| 3 | 1, 5 | 0.28 | 0.15 | 0.09 | 0.06 | 0.22 | 0.03 | 0.11 | 0.06 |
| 4 | 2, 3 | 0.10 | 0.32 | 0.26 | 0.05 | 0.08 | 0.07 | 0.07 | 0.05 |
Expert 1 is selected by tokens 1, 2, 3 (3 times). Expert 2 is selected by tokens 1, 4 (2 times). Expert 3 is selected by token 4 (1 time). Expert 5 is selected by token 3 (1 time). Expert 7 is selected by token 2 (1 time). Experts 4, 6, 8 are never selected.
Computing : . . . . . .
Computing : . . And so on for each expert.
The loss penalizes experts that have both high actual load () and high routing probability (). If expert 1 is receiving too many tokens (, three times the balanced level) and also has high probability (), the product contributes a large term to the loss, pushing the router to spread tokens more evenly.
Why the product ?
This is the same formulation used in Switch Transformer (Fedus et al., 2021) and GShard (Lepikhin et al., 2021). The key insight is that involves a discrete selection (the indicator function ), which is not differentiable. But involves the softmax probabilities , which are differentiable. The product creates a differentiable loss that the router can optimize via gradient descent: the gradient flows through while acts as a coefficient that amplifies the gradient for overloaded experts.
11.2 Device-Level Balance Loss
When experts are distributed across multiple devices (GPUs), imbalanced routing causes some devices to become bottlenecks. The device-level balance loss encourages balanced computation across devices.
Partition the routed experts into groups , one per device. The loss is:
where:
is the average load fraction across experts on device , and is the total routing probability mass directed to device .
11.3 Communication Balance Loss
Even if devices have balanced computation, they may have unbalanced communication: some devices receive tokens from many other devices while others receive few. The communication balance loss addresses this:
where:
Here is the maximum number of devices each token can be sent to (from the device-limited routing constraint). The three hyperparameters are set to , , and in DeepSeek-V2.
12. Device-Limited Routing
Standard top-K routing has a communication problem when experts are spread across devices. If a token’s top- experts happen to live on different devices, that token must be sent to devices — generating cross-device communications. With fine-grained expert segmentation (, ), the worst case means each token communicates with 6 different devices.
DeepSeek-V2 introduces device-limited routing to cap this. For each token:
- Compute affinity scores for all experts
- Identify the top devices (by total affinity of their hosted experts)
- Select the top- experts only from experts on these devices
In DeepSeek-V2, and . So each token communicates with at most 3 out of 8 devices, regardless of how many experts are activated. The paper reports that achieves performance comparable to unrestricted routing.
13. Token-Dropping Strategy
Balance losses encourage but do not guarantee perfect balance. To handle residual imbalance during training, DeepSeek-V2 uses a token-dropping strategy:
- Compute the average computational budget per device (capacity factor 1.0)
- On each device, drop the tokens with the lowest affinity scores until the device’s load does not exceed its budget
- Ensure that no more than approximately 10% of tokens in any training sequence are dropped
This is applied only during training. During inference, no tokens are dropped — the model processes all tokens, accepting any load imbalance.
14. Putting It All Together: The DeepSeek-V2 Transformer Block
A single DeepSeek-V2 Transformer block processes a token through two sublayers:
Sublayer 1 — Multi-head Latent Attention (MLA):
Sublayer 2 — DeepSeekMoE (or dense FFN for layer 1):
The model has such blocks stacked. The first block uses a dense FFN instead of MoE. All other blocks use MoE with shared experts and routed experts.
Parameter count
The full DeepSeek-V2 model has 236B total parameters. Per token, only 21B are activated: the MLA parameters (which are used for every token) plus the active expert FFNs per layer (out of 162 total). The ratio is — over 91% of parameters are inactive for any given token.
Summary
DeepSeek-V2 contributes two architectural innovations that are independent and complementary.
Multi-head Latent Attention replaces the standard practice of caching separate key and value vectors with a single low-rank latent vector , from which keys and values are recovered via learned up-projections. Position information is carried by a separate decoupled RoPE key that avoids the incompatibility between RoPE and low-rank compression. During inference, the up-projection matrices are absorbed into the query and output projections via the associative law of matrix multiplication, so keys and values are never explicitly computed for cached tokens. The result: a KV cache equivalent to GQA with only 2.25 groups, but with performance stronger than full MHA.
DeepSeekMoE replaces dense FFNs with a mixture of fine-grained experts (160 small routed experts plus 2 shared experts), of which only 8 are evaluated per token. Fine-grained segmentation enables billion possible expert combinations per token, vastly more than coarse-grained alternatives. Shared expert isolation prevents common knowledge from being redundantly stored across routed experts. Three levels of auxiliary losses (expert, device, communication) and device-limited routing ensure balanced, efficient training across multi-device setups.
Previous: Mathematical Prerequisites for DeepSeek-V2 Next: Mathematical Prerequisites for Sparse and Sliding Window Attention
Enjoyed this post?
Subscribe to get notified when I publish new posts. No spam, unsubscribe anytime.