1. 从 DeepSeek-V1 到 DeepSeek-V2 概述
DeepSeek-V1 的主要贡献,是用 Scaling Law 指导稠密模型训练;DeepSeek-V2 的核心,则是通过 DeepSeekMoE 降低每个 token 的计算量,通过 MLA 降低每个 token 的 KV Cache。
因此,V2 并不是简单地把 67B 扩大到 236B,而是把"模型容量""单 token 计算量""推理缓存"三件事拆开了。
这里的 DeepSeek-V1,指官方论文中的 DeepSeek LLM 7B/67B 。由于 V2 主要以 67B 为比较基线,下面也重点讨论 DeepSeek LLM 67B → DeepSeek-V2。
1.1. 整体对比
| 维度 | DeepSeek-V1/DeepSeek LLM | DeepSeek-V2 |
|---|---|---|
| 代表模型 | 7B、67B | 236B,另有 V2-Lite |
| 模型类型 | 稠密 Dense Transformer | 稀疏 MoE Transformer |
| 总参数量 | 67B | 236B |
| 每 token 激活参数 | 基本使用全部 67B | 约 21B |
| 注意力 | 7B 用 MHA;67B 用 GQA | MLA,多头潜在注意力 |
| FFN | 稠密 FFN,SwiGLU | DeepSeekMoE |
| 层数/隐藏维度 | 67B:95 层/8192 | 60 层/5120 |
| 预训练数据 | 2T tokens | 8.1T tokens |
| 上下文 | 4K | 128K |
| 长上下文方式 | 原生 4K 训练 | 4K 预训练后,用 YaRN 扩展 |
| 后训练 | SFT+DPO | SFT+基于 GRPO 的 RL |
| 核心目标 | 把稠密模型训练好、训练可预测 | 同时提高能力、训练经济性和推理效率 |
V1 的官方配置为:7B 使用 MHA,67B 使用 GQA;两者均在 2T tokens、4096 序列长度上预训练。
V2 则采用 60 层、5120 隐藏维度,每层注意力使用 MLA;除第一层外,其余 FFN 均替换为 MoE,最终形成 236B 总参数、21B 激活参数的配置。
1.2.DeepSeek-V1:重点不是新架构,而是"可预测地扩展"
1. 架构基本延续 LLaMA
V1 是典型的 Decoder-only 自回归 Transformer,主要组件包括:
- Pre-Norm+RMSNorm:提高深层网络训练的稳定性;
- SwiGLU FFN:当时 LLaMA 系列已验证的高性能前馈结构;
- RoPE:通过旋转位置编码注入位置信息;
- MHA/GQA:7B 使用标准多头注意力,67B 使用分组查询注意力。
其中,67B 的 GQA 让多个 Query 头共享一组 Key/Value 头,已经是在降低推理时的 KV Cache。但它减少缓存的方式是减少 KV 头数量、让多个查询头共享缓存,还没有改变 K、V 本身的表示方式。
V1 的稠密 FFN 则意味着:每处理一个 token,模型中的 FFN 参数基本都要参与计算。于是:
模型容量增加 ⟹ 单 token 计算量近似同步增加 \text{模型容量增加} \quad\Longrightarrow\quad \text{单 token 计算量近似同步增加} 模型容量增加⟹单 token 计算量近似同步增加
这正是 V2 要打破的第一重耦合。
2. V1 的真正研究重点是 Scaling Law
V1 论文花了大量篇幅研究三个问题:
- 计算预算增大时,学习率应该怎么变;
- 计算预算增大时,Batch Size 应该怎么变;
- 固定计算预算时,模型规模和训练数据量应该怎么分配。
它没有简单沿用近似的 C = 6 N D C=6ND C=6ND,而是用"非嵌入部分的每 token FLOPs" M M M 表示模型规模,采用更精确的:
C = M D C=MD C=MD
其中 C C C 是计算预算, D D D 是训练 token 数。团队再通过小规模实验拟合缩放曲线,预测 7B、67B 的训练配置与性能。论文报告,这种方法能把小规模实验得到的规律外推到约 1000 倍计算预算的模型上。
所以,V1 的思想可以概括为:
先用小实验研究扩展规律,再把大算力投入到可预测的训练方案中。
3. 数据与后训练
V1 使用约 2T tokens 的中英文数据,覆盖互联网文本、数学、代码、书籍等,并进行规则与模型过滤、文档级和字符串级去重。
聊天模型采用:
Base → SFT → DPO \text{Base}\rightarrow\text{SFT}\rightarrow\text{DPO} Base→SFT→DPO
SFT 使用约 150 万条中英文指令数据;DPO 则利用偏好数据进一步优化有帮助性与安全性。V1 论文也明确观察到,DPO 主要改善偏好对齐,并不会显著提升模型的全部基础能力。
1.3. V2 的第一项核心变化:Dense FFN → DeepSeekMoE
1. MoE 到底解决了什么?
传统稠密 FFN 中,所有 token 都经过同一套参数。
MoE 则准备很多个专家 FFN,由路由器为每个 token 选择少数专家:
token → Router → Top-K Experts \text{token} \rightarrow\text{Router} \rightarrow\text{Top-K Experts} token→Router→Top-K Experts
这样可以让模型拥有很大的总参数容量 ,但每个 token 只计算其中一小部分,从而降低激活参数量和实际 FLOPs。
这就是 V2 看似矛盾的地方:
- 总参数从 67B 增至 236B;
- 每 token 激活参数却降至约 21B。
换句话说:
V2 用更大的"知识仓库",但每个 token 只取出少量相关工具。
2. DeepSeekMoE 的两个关键设计
V2 并非简单套用普通 MoE,而是继承此前 DeepSeekMoE 论文的两项设计。
细粒度专家切分
把少数"大专家"拆成更多"小专家",同时增加每个 token 选择的专家数量。
例如,不再只是"8 个专家选 2 个",而可以采用更丰富的专家组合。这样有利于:
- 更细地拆分知识;
- 提高专家组合的灵活性;
- 减少每个专家需要覆盖的知识范围。
共享专家隔离
将专家分成两类:
- 共享专家:所有 token 都经过,学习通用知识;
- 路由专家:由 Router 按 token 选择,学习差异化知识。
其直观意义是:语法、通用语义等共性能力,不必在每个路由专家里重复学习;路由专家则可以更专注于特定模式。
V2 每个 MoE 层的具体配置是:
- 2 个共享专家;
- 160 个路由专家;
- 每个 token 选择其中 6 个路由专家;
- 即每个 token 实际经过 2+6=8 个专家;
- 除第一层 FFN 外,其余 FFN 都采用这种 MoE 结构。
其输出可以简化表示为:
y = ∑ i ∈ Shared FFN i ( x ) + ∑ j ∈ TopK ( x , 6 ) g j ( x ) FFN j ( x ) y= \sum_{i\in\text{Shared}}\operatorname{FFN}i(x) + \sum{j\in\operatorname{TopK}(x,6)} g_j(x)\operatorname{FFN}_j(x) y=i∈Shared∑FFNi(x)+j∈TopK(x,6)∑gj(x)FFNj(x)
前一项始终计算,后一项按 token 动态选择。
3. MoE 的难点不只是"少算",还有通信与负载均衡
专家分布在不同 GPU 上时,一个 token 被路由到过多设备,会产生大量跨卡通信;如果大量 token 挤到少数专家,还会出现:
- 部分专家训练不足;
- 部分 GPU 过载;
- 其他 GPU 等待,降低整体利用率。
V2 为此加入三层工程设计:
- 设备限制路由:每个 token 的目标专家最多分布在 3 台设备上;
- 三种负载均衡损失:分别约束专家级、设备级和通信负载;
- 训练期 Token Dropping:设备计算预算不足时,优先丢弃路由亲和度较低的 token;评测时不丢 token。
因此,V2 的低成本不是 MoE 公式自然带来的,还依赖专家并行、通信重叠、自定义路由与计算内核等系统优化。
1.4. V2 的第二项核心变化:GQA → MLA
如果说 MoE 解决的是"每个 token 要算多少参数",那么 MLA 解决的是:
每多一个历史 token,需要在显存里保存多少东西。
1. 为什么 KV Cache 会成为瓶颈?
自回归生成时,模型每生成一个新 token,都要关注此前的 token。为了避免重复计算,需要缓存历史 K、V。
标准 MHA 每个 token 的缓存元素数为:
2 n h d h L 2n_h d_h L 2nhdhL
其中:
- n h n_h nh:注意力头数;
- d h d_h dh:每个头的维度;
- L L L:层数;
- 系数 2 对应 K 和 V。
缓存会随上下文长度、层数、并发请求数线性增长。长上下文服务中,它不仅占显存,还会在解码时带来大量显存读取。
GQA 的做法是让多个 Query 头共享 KV 头,把缓存降为:
2 n g d h L , n g < n h 2n_g d_h L,\qquad n_g<n_h 2ngdhL,ng<nh
但本质上仍是"少存几组完整 K、V"。
2. MLA:不再缓存完整 K、V,而缓存压缩后的潜在向量
MLA 先把当前隐藏状态 h t h_t ht 压缩成低维向量:
c t K V = W D K V h t c_t^{KV}=W^{DKV}h_t ctKV=WDKVht
需要时,再从潜在向量恢复内容相关的 K、V:
k t C = W U K c t K V , v t C = W U V c t K V k_t^C=W^{UK}c_t^{KV},\qquad v_t^C=W^{UV}c_t^{KV} ktC=WUKctKV,vtC=WUVctKV
推理时主要缓存 c t K V c_t^{KV} ctKV,而不是所有注意力头完整的 K、V。
并且,在特定实现中:
- W U K W^{UK} WUK 可以吸收进 Query 投影;
- W U V W^{UV} WUV 可以吸收进输出投影;
因此不必总是显式还原完整 K、V。MLA 同时对 Query 做低秩压缩,但这部分主要是为了减少训练激活显存,并不是降低 KV Cache 的原因。
可以将 GQA 与 MLA 的区别记成:
- GQA:多个头共享同一份 KV;
- MLA:把整份 KV 联合压缩成潜在表示。
3. 为什么还要"解耦 RoPE"?
RoPE 与位置有关。如果直接对压缩后的内容 Key 施加 RoPE,位置相关矩阵会插在投影矩阵之间,使前面所说的矩阵吸收难以成立,影响高效推理。
V2 的解决方案是把注意力信息拆成两部分:
- 内容部分 :低秩压缩的 q C 、 k C 、 v C q^C、k^C、v^C qC、kC、vC;
- 位置部分 :单独设置携带 RoPE 的 q R 、 k R q^R、k^R qR、kR。
最后拼接:
q t , i = q t , i C ; q t , i R , k t , i = k t , i C ; k t R q_{t,i}=q_{t,i}\^{C};q_{t,i}\^{R},\qquad k_{t,i}=k_{t,i}\^{C};k_t\^{R} qt,i=qt,iC;qt,iR,kt,i=kt,iC;ktR
推理时除了缓存 512 维的 KV 潜在向量,还要缓存 64 维的解耦位置 Key,即每层每 token 缓存:
512 + 64 = 576 个元素 512+64=576\text{ 个元素} 512+64=576 个元素
60 层合计 34,560 个元素;按 BF16 粗略计算约为 69KB/token。V2 的具体设置为 128 个注意力头、每头 128 维,KV 压缩维度 512,Query 压缩维度 1536,解耦 RoPE 维度 64。
所以,MLA 的关键不是单纯"压得更狠",而是:
在压缩缓存的同时,保留足够多的内容表达能力,并用独立通道保留位置信息。
1.5. 数据、长上下文与后训练也一起升级了
1. 预训练:2T → 8.1T tokens
V2 沿用 V1 的数据处理流程和约 100K 词表的 BBPE 分词器,但做了三方面改进:
- 扩大互联网数据规模,并改进清洗流程,恢复部分被误删的数据;
- 加入更多中文数据,中文 token 数约比英文多 12%;
- 改进质量过滤算法,并过滤部分争议性内容。
最终预训练语料达到 8.1T tokens 。因此,V2 的能力提升不能全部归因于 MoE 和 MLA,数据量、数据配比和数据质量也发生了变化。
2. 上下文不是直接从 4K 训练到 128K
V2 的主预训练仍以 4K 序列进行,之后使用 YaRN 扩展上下文:
- YaRN 只作用于 MLA 中负责携带 RoPE 的解耦共享 Key;
- 追加训练 1000 步;
- 追加训练序列长度为 32K;
- 最终将可用上下文扩展到 128K,并通过"大海捞针"测试验证。
这意味着"支持 128K"并不等于"整个预训练都在 128K 序列上完成"。同时,大海捞针表现良好,也不能证明所有类型的长文档推理在 128K 下都同样可靠。
3. 后训练:DPO → GRPO
V1 的路线是:
SFT + DPO \text{SFT}+\text{DPO} SFT+DPO
V2 则使用约 150 万段会话进行 SFT,再采用 DeepSeekMath 中提出的 GRPO 进行强化学习对齐:
SFT + RL(GRPO) \text{SFT}+\text{RL(GRPO)} SFT+RL(GRPO)
不过,DeepSeek-V2 Chat(RL)不是后来的 DeepSeek-R1。V2 的重点仍是通用能力、偏好对齐和效率,而不是以长思维链为核心的推理模型训练。
1.6. 能力提升有多大?
在 V2 官方使用同一内部评测框架进行的 Base 模型比较中:
| Benchmark | DeepSeek 67B | DeepSeek-V2 | 变化 |
|---|---|---|---|
| MMLU | 71.3 | 78.5 | +7.2 |
| BBH | 68.7 | 78.9 | +10.2 |
| C-Eval | 66.1 | 81.7 | +15.6 |
| CMMLU | 70.8 | 84.0 | +13.2 |
| GSM8K | 63.4 | 79.2 | +15.8 |
| MATH | 18.7 | 43.6 | +24.9 |
| HumanEval | 45.1 | 48.8 | +3.7 |
| MBPP | 57.4 | 66.6 | +9.2 |
中文、数学和综合推理提升尤其明显,代码也有提升,但并非所有基准都同步上涨。例如,部分英语常识和语言理解测试上,V2 与 LLaMA 3 70B 等模型仍有差距。
Chat 模型中,强化学习对代码和数学也有明显帮助。官方结果里,V2 Chat(RL)的 HumanEval 为 81.1、GSM8K 为 92.2、MATH 为 53.9;相比 V1 Chat 的 73.8、84.1、32.6 均有提高。
因此,更准确的理解不是"21B 激活参数在所有方面击败 67B",而是:
V2 用明显更少的单 token 计算量,取得了整体上更强、且中英文更均衡的能力。
1.7. "省 42.5%""快 5.76 倍"应该怎么理解?
官方报告的三项效率结果是:
- 训练成本节省 42.5%;
- KV Cache 减少 93.3%;
- 最大生成吞吐量达到 5.76 倍。
但有三个重要口径。
1. 42.5% 是"每训练 1T tokens"的成本比较
H800 集群上,每训练 1T tokens:
- DeepSeek 67B:300.6K GPU 小时;
- DeepSeek-V2:172.8K GPU 小时。
因此不能简单说"V2 整个训练项目只花了 V1 的 57.5%",因为两者的训练 token 总量分别为 2T 和 8.1T,比较口径并不是完整项目总成本。
2. 5.76 倍是最大生成吞吐量,不是单次回答都快 5.76 倍
它反映特定部署配置下的系统吞吐能力,不等于:
- 单请求延迟必然降低 5.76 倍;
- 任意 GPU、任意框架都能达到 5.76 倍;
- 这全部只是 MLA 的独立贡献。
它还受到激活参数、KV Cache、量化、批大小和推理系统优化等因素共同影响。
3. 21B 激活参数不等于只需加载 21B 权重
MoE 虽然每个 token 只计算部分专家,但完整部署仍需要保存全部 236B 参数。官方给出的 BF16 本地推理配置为 8 张 80GB GPU。
所以 V2 降低的是计算量和 KV Cache,并没有让模型权重存储也按 21B 计算。
1.8. 这次升级的本质
可以把两代模型分别概括为:
DeepSeek-V1
Scaling Law + 稠密 Transformer + 2T 数据 + SFT/DPO \boxed{ \text{Scaling Law} +\text{稠密 Transformer} +\text{2T 数据} +\text{SFT/DPO} } Scaling Law+稠密 Transformer+2T 数据+SFT/DPO
主要回答:怎样更科学、更可预测地训练一个强大的稠密模型?
DeepSeek-V2
DeepSeekMoE + MLA + 8.1T 数据 + 128K 扩展 + SFT/GRPO \boxed{ \text{DeepSeekMoE} +\text{MLA} +\text{8.1T 数据} +\text{128K 扩展} +\text{SFT/GRPO} } DeepSeekMoE+MLA+8.1T 数据+128K 扩展+SFT/GRPO
主要回答:怎样在模型容量继续增长时,不让每个 token 的计算和缓存成本同步爆炸?
最终形成了两条解耦:
-
MoE:模型总容量与单 token 计算量解耦
总参数可以达到 236B,但每 token 只激活约 21B。
-
MLA:注意力表达能力与 KV Cache 大小解耦
不再为每个头保存完整 K、V,而是保存低维潜在表示和少量位置编码信息。
所以,从 V1 到 V2 最重要的变化,不是"参数更多、数据更多、榜单更高",而是设计目标从单纯扩大有效训练规模 ,转向了模型能力、训练成本、推理显存和系统吞吐的协同优化。这也是 V2 在 DeepSeek 后续技术路线中承上启下的原因。
2. 详解 deepseek-v2 的 MoE 的结构和原理
DeepSeek-V2 的 MoE,可以概括为:
把 Transformer 中原来的一个大 FFN,改成"2 个永远工作的共享专家+160 个按需选择的路由专家";每个 token 选择其中 6 个路由专家,再与 2 个共享专家的输出合并。
这样,模型可以拥有很大的总参数量 ,但每个 token 只计算其中一小部分,即所谓的稀疏激活。DeepSeek-V2 总参数量为 236B,每个 token 激活约 21B 参数。
下面按"整体结构 → 前向计算 → 两个核心创新 → 路由与通信 → 负载均衡 → 实际代价"的顺序拆解。
2.1. MoE 替换的是 Transformer 的哪一部分?
一个标准 Transformer Block 大致包含:
text
输入
│
├─ RMSNorm → Attention → 残差连接
│
├─ RMSNorm → FFN → 残差连接
│
输出
DeepSeek-V2 对两部分分别做了改造:
- Attention:使用 MLA,即多头潜在注意力,主要解决 KV Cache 和推理效率问题;
- FFN:使用 DeepSeekMoE,主要解决模型容量与计算量之间的矛盾。
所以要注意:
MoE 不是把每个专家都做成一个完整的 Transformer,而是把层内的 FFN 复制、拆分成许多个小 FFN。Attention、归一化、残差连接等仍然存在。
普通稠密 FFN 可以写成:
h t ′ = u t + FFN ( u t ) \mathbf h'_t=\mathbf u_t+\operatorname{FFN}(\mathbf u_t) ht′=ut+FFN(ut)
其中 (\mathbf u_t 是第 (t 个 token 当前层的隐藏状态。无论输入是什么,所有 FFN 参数都会参与计算。
MoE 则变成:
h t ′ = u t + ∑ i = 1 N g i , t FFN i ( u t ) \mathbf h't= \mathbf u_t+ \sum{i=1}^{N}g_{i,t}\operatorname{FFN}_i(\mathbf u_t) ht′=ut+i=1∑Ngi,tFFNi(ut)
其中:
- (\operatorname{FFN}_i 是第 (i 个专家;
- (g_{i,t}$ 是路由权重;
- 对没有被选中的专家,(g_{i,t}=0$,因此不用计算这个专家。
这就是"条件计算":不同 token 使用不同的参数子集。
2.2. DeepSeek-V2 的具体 MoE 配置
DeepSeek-V2 一共有 60 层,隐藏维度为 5120。第 1 层保留稠密 FFN,其余 FFN 层替换为 MoE。每个 MoE 层的配置如下:
| 项目 | DeepSeek-V2 配置 |
|---|---|
| 共享专家 | 2 个 |
| 路由专家 | 160 个 |
| 每个 token 选择的路由专家 | 6 个 |
| 每个专家的中间隐藏维度 | 1536 |
| 每个 token 实际经过的专家 | 2 个共享+6 个路由,共 8 个 |
| 模型总参数量 | 236B |
| 每个 token 激活参数量 | 约 21B |
可以把单层结构画成:
text
┌─ 共享专家 1:所有 token 都经过 ─┐
│ │
输入隐藏状态 u_t ────────┼─ 共享专家 2:所有 token 都经过 ─┤
│ ├─ 求和
│ │ +
└─ Router │ 残差
│ │
├─ 从 160 个专家中选 Top-6 │
│ │
└─ 按路由分数加权求和 ──────┘
这里有几个容易误解的地方:
-
路由是按 token、按层进行的。
不是给整句话分配一次专家,也不是一个 token 在整个模型里固定使用同一组专家。同一个 token 在不同 MoE 层可以选择不同专家。
-
共享专家也是 FFN,不是路由器。
它们直接参与特征变换,负责处理每个 token 都需要的信息。
-
6 个路由专家不是投票关系。
它们分别处理输入,然后按照路由权重进行加权求和。
-
"激活 21B"不等于"只需要保存 21B 权重"。
不同 token 可能选择不同专家,因此完整部署通常仍需保存全部专家参数,或者通过专家并行、分层放置、权重卸载等方式管理。
2.3. 单个 MoE 层的计算过程
1. Router 计算专家亲和度
对于输入 (\mathbf u_t$,Router 为每个路由专家计算一个分数:
s i , t = Softmax i ( u t T e i ) s_{i,t}= \operatorname{Softmax}_i \left( \mathbf u_t^{T}\mathbf e_i \right) si,t=Softmaxi(utTei)
其中:
- (\mathbf e_i 是第 (i 个路由专家对应的可学习向量,论文称其为专家的中心向量;
- (\mathbf u_t^T\mathbf e_i$ 表示当前 token 与该专家的匹配程度;
- Softmax 在全部路由专家维度上计算,得到 (s_{i,t}$。
从实现角度看,可以把所有 (\mathbf e_i$ 组成一个矩阵,那么 Router 本质上就是一个很小的线性分类器:
text
隐藏状态 → 线性变换 → 160 个专家分数 → Softmax
但它分类的对象不是"数学、代码、法律"等人工标签,而是模型自己学习出来的专家编号。
2. 选择 Top-K 专家
DeepSeek-V2 取分数最高的 6 个路由专家:
g i , t = { s i , t , s i , t ∈ TopK ( { s j , t } j = 1 160 , 6 ) 0 , 其他情况 g_{i,t}= \begin{cases} s_{i,t}, & s_{i,t}\in \operatorname{TopK}(\{s_{j,t}\}_{j=1}^{160},6)\\ 0, & \text{其他情况} \end{cases} gi,t={si,t,0,si,t∈TopK({sj,t}j=1160,6)其他情况
按照论文公式,被选中的专家使用原始 Softmax 分数作为门控权重,未选中的专家权重为零;这里不是把 Top-6 的权重重新归一化为总和 1。
3. 合并共享专家和路由专家
完整输出为:
h t ′ = u t + ∑ i = 1 N s FFN i ( s ) ( u t ) ⏟ 共享专家 + ∑ i = 1 N r g i , t FFN i ( r ) ( u t ) ⏟ 路由专家 \mathbf h't= \mathbf u_t + \underbrace{\sum{i=1}^{N_s} \operatorname{FFN}^{(s)}i(\mathbf u_t)}{\text{共享专家}} + \underbrace{\sum_{i=1}^{N_r} g_{i,t}\operatorname{FFN}^{(r)}i(\mathbf u_t)}{\text{路由专家}} ht′=ut+共享专家 i=1∑NsFFNi(s)(ut)+路由专家 i=1∑Nrgi,tFFNi(r)(ut)
代入 DeepSeek-V2 的配置,就是:
h t ′ = u t + E 1 ( s ) ( u t ) + E 2 ( s ) ( u t ) + ∑ i ∈ Top6 s i , t E i ( r ) ( u t ) \mathbf h'_t= \mathbf u_t + E^{(s)}1(\mathbf u_t) + E^{(s)}2(\mathbf u_t) + \sum{i\in\operatorname{Top6}} s{i,t}E^{(r)}_i(\mathbf u_t) ht′=ut+E1(s)(ut)+E2(s)(ut)+i∈Top6∑si,tEi(r)(ut)
最后再加上残差输入,得到该层输出。
2.4. 核心创新一:细粒度专家拆分
传统 MoE 通常是"专家数量少,但每个专家很大",例如:
text
8 个大专家,每个 token 选 2 个
DeepSeekMoE 的思路是:
把大专家进一步拆成更多、更小的专家,同时增加每个 token 可以选择的专家数量。
假设保持专家总参数量和激活参数量不变:
text
方案 A:8 个大专家,选 2 个
方案 B:16 个专家,每个宽度减半,选 4 个
两种方案单次计算的专家总宽度近似相同,但方案 B 的组合空间明显更大:
- 方案 A:(\binom{8}{2}=28$ 种专家组合;
- 方案 B:(\binom{16}{4}=1820$ 种专家组合。
这意味着 Router 不只是从少数几种"大包知识"里挑一个,而是能够组合更多细粒度的功能单元。
DeepSeek-V2 将这一思想扩展到每层 160 个路由专家、每 token 选择 6 个。其目的不是简单地"专家越多越好",而是提高:
- 知识组合的灵活性;
- 专家专门化的潜力;
- 相同激活计算预算下的表达能力。
不过,"专门化"不应机械理解成某个专家一定只懂代码、另一个只懂数学。专家功能由训练自发形成,可能表现为某些 token 模式、语义特征或中间变换,而不一定具有清晰的人类语义标签。
2.5. 核心创新二:共享专家隔离
如果所有专家都靠路由选择,会出现一个问题:
很多通用变换是所有 token 都需要的,但不同路由专家可能不得不重复学习这些能力。
例如,基础句法、常见语义变换、通用特征整理,如果都由路由专家分别学习,就会产生参数冗余。
DeepSeekMoE 因此把专家分成两类:
共享专家
- 对每个 token 都激活;
- 不经过 Top-K 竞争;
- 倾向学习通用、跨 token 共享的知识和变换;
- 相当于一条始终存在的公共 FFN 通路。
路由专家
- 根据 token 的隐藏状态动态选择;
- 更适合学习差异化、条件化的特征;
- 不必在每个专家里重复保存所有公共知识。
论文将这一设计称为 Shared Expert Isolation,其目标就是减少路由专家之间的知识冗余,并与细粒度专家拆分配合,提高专家专门化潜力。
可以用一个不完全严谨但很直观的类比:
text
共享专家:所有员工都必须掌握的通用基础能力
路由专家:根据当前任务临时组合的专项能力
共享专家的代价也很明确:它们对每个 token 都产生计算量,因此不能无限增大,否则模型会重新接近稠密网络。
2.6. 细粒度路由带来的问题:跨设备通信
MoE 的计算稀疏,并不代表系统实现简单。
训练时通常采用专家并行:
text
GPU 0:专家 0~19
GPU 1:专家 20~39
......
GPU 7:专家 140~159
假设一个 token 被选中的 6 个专家分布在 6 张 GPU 上,就需要:
- 把该 token 的隐藏状态发送到这些 GPU;
- 各 GPU 执行对应专家;
- 把专家输出传回;
- 加权合并结果。
这类通信通常通过 All-to-All 完成。 token 数量大、专家又细粒度时,通信开销可能抵消稀疏计算带来的收益。
Device-Limited Routing:限制设备数量
DeepSeek-V2 因此加入设备受限路由:
- 先根据设备上专家的亲和度,选出 (M$ 台目标设备;
- 只在这 (M$ 台设备所包含的专家中执行 Top-K;
- 保证一个 token 的目标专家最多分布在 (M$ 台设备上。
其训练配置中,160 个路由专家均匀部署在 8 台设备上,每个 token 最多发送到 3 台设备,然后再从中选择 6 个专家。论文报告称,(M\geq 3$ 时,性能可以大致接近不受限的 Top-K 路由。
这实际上是在做一种权衡:
text
完全自由路由:候选专家最多,但通信可能最贵
设备受限路由:牺牲少量选择自由,限制通信扇出
训练系统还将共享专家计算与专家并行通信重叠执行:共享专家本来就不依赖路由通信结果,因此可以在 All-to-All 进行的同时计算,从而隐藏一部分通信延迟。
2.7. 为什么需要三种负载均衡损失?
1. 什么是路由塌缩?
训练初期,如果少数专家偶然获得较高分数,就可能形成正反馈:
text
少数专家分数较高
↓
得到更多 token
↓
接受更多训练、表现更好
↓
进一步获得更多 token
最终可能导致:
- 大量专家很少被使用,训练不足;
- 总参数虽然很大,实际有效容量却不高;
- 热门专家所在设备过载,其他设备等待;
- 训练和推理吞吐下降。
因此,DeepSeek-V2 不只优化语言模型损失,还加入三类辅助损失:
L = L L M + L E x p B a l + L D e v B a l + L C o m m B a l \mathcal L= \mathcal L_{\mathrm{LM}} + \mathcal L_{\mathrm{ExpBal}} + \mathcal L_{\mathrm{DevBal}} + \mathcal L_{\mathrm{CommBal}} L=LLM+LExpBal+LDevBal+LCommBal
2. 专家级负载均衡
专家级损失为:
L E x p B a l = α 1 ∑ i = 1 N r f i P i \mathcal L_{\mathrm{ExpBal}}= \alpha_1\sum_{i=1}^{N_r}f_iP_i LExpBal=α1i=1∑NrfiPi
其中:
f i = N r K r T ∑ t = 1 T 1 ( token t 选择了专家 i ) f_i= \frac{N_r}{K_rT} \sum_{t=1}^{T} \mathbb 1(\text{token }t\text{ 选择了专家 }i) fi=KrTNrt=1∑T1(token t 选择了专家 i)
P i = 1 T ∑ t = 1 T s i , t P_i=\frac{1}{T}\sum_{t=1}^{T}s_{i,t} Pi=T1t=1∑Tsi,t
直观理解:
- (f_i$:专家实际收到的 token 负载,是离散的硬选择统计;
- (P_i$:Router 对专家的平均偏好,是可微的软概率;
- 两者乘积用于让实际分配与路由偏好不要过度集中到少数专家。
它主要解决的是:不要让部分专家"饿死",也不要让少数专家吃下绝大部分 token。
3. 设备级负载均衡
仅保证专家级均衡还不够。
假设专家总体上都很忙,但负载较高的专家恰好集中在同一张 GPU,那么这张卡仍然会成为瓶颈。因此,DeepSeek-V2 将专家按设备分组,对每组专家的负载和路由概率进行聚合:
L D e v B a l = α 2 ∑ d = 1 D f d ′ P d ′ \mathcal L_{\mathrm{DevBal}}= \alpha_2\sum_{d=1}^{D}f'_dP'_d LDevBal=α2d=1∑Dfd′Pd′
它关心的是:每张 GPU 的计算量是否大致均衡。
4. 通信负载均衡
设备受限路由限制了每个 token 最多发送到多少设备,却不能保证每台设备接收的 token 数相同。
所以又加入:
L C o m m B a l = α 3 ∑ d = 1 D f d ′ ′ P d ′ ′ \mathcal L_{\mathrm{CommBal}}= \alpha_3\sum_{d=1}^{D}f''_dP''_d LCommBal=α3d=1∑Dfd′′Pd′′
其中 (f''_d$ 衡量设备实际接收通信的频率。它主要解决:不要让某些设备成为集中接收热点。
三种损失分别对应:
| 损失 | 主要优化对象 |
|---|---|
| 专家级均衡 | 专家是否得到充分、相对均衡的训练 |
| 设备级均衡 | 各 GPU 的专家计算量是否均衡 |
| 通信级均衡 | 各 GPU 的接收通信量是否均衡 |
DeepSeek-V2 的训练系数分别为 (\alpha_1=0.003KaTeX parse error: Can't use function '\(' in math mode at position 2: 、\̲(̲\alpha_2=0.05、(\alpha_3=0.02$。
2.8. Token-Dropping:辅助损失之外的硬约束
辅助损失只能"鼓励"均衡,不能保证任何时刻都严格均衡。某些设备仍可能突然收到过多 token,导致整批计算被最慢设备拖住。
DeepSeek-V2 在训练时进一步采用设备级 token 丢弃:
- 按平均计算预算设置每台设备的容量,容量因子相当于 1.0;
- 如果某台设备超载,优先丢弃亲和度较低的 token-专家分配;
- 约 10% 的训练序列受到保护,其中的 token 不会被丢弃;
- 评估时不丢弃 token。
这里的"丢弃 token"通常不是把文字从训练序列中删除,而是让相应的 token-专家计算不再执行,隐藏状态仍可通过残差等路径继续传播。
其本质是:
用少量专家计算的缺失,换取可控的设备计算上限和训练吞吐。
2.9. 为什么能省计算,但不能简单理解成"速度快 11 倍"?
236B 总参数、21B 激活参数,很容易让人误以为推理成本必然降低约 (236/21\approx11$ 倍。实际不能这样换算。
MoE 主要降低的是单 token 的计算量
每个 token 不需要执行全部 160 个路由专家,只执行:
- 2 个共享专家;
- 6 个路由专家;
- 以及所有 token 都要执行的 Attention 等稠密部分。
因此,21B 也不能简单通过"8 个专家÷162 个专家×236B"得到,因为模型里还有大量非专家参数。
但以下成本仍然存在
-
权重存储成本
完整模型仍有 236B 参数,显存和内存压力不会因为单 token 稀疏激活而同比消失。
-
跨设备通信成本
token 需要被发送到不同专家所在设备,再将结果传回。
-
小矩阵计算效率问题
专家变小、变多后,计算可能更碎片化,不一定像大型稠密矩阵乘法那样容易充分利用 GPU。
-
动态负载问题
一个批次内不同 token 选择不同专家,实际部署时可能会同时激活很多专家。
-
内存访问成本
解码阶段不仅受 FLOPs 影响,也受权重读取、显存带宽和批量大小影响。
所以 MoE 更准确的收益描述是:
用较大的参数存储和更复杂的系统实现,换取较高的模型容量,同时把每个 token 的计算量控制在较低水平。
2.10. 不要和 DeepSeek-V3 的机制混淆
DeepSeek-V2 的主要特征是:
- Softmax 计算 token-专家亲和度;
- 2 个共享专家+160 个路由专家;
- Top-6 路由;
- 设备受限路由;
- 三种辅助负载均衡损失;
- 训练时使用 token-dropping,评估时不丢弃。
DeepSeek-V3 才进一步改为以动态偏置为核心的无辅助损失负载均衡策略,并在亲和度计算、选中分数归一化、路由限制和 token-dropping 等方面做了调整。不能把 V3 的"auxiliary-loss-free"直接套到 V2 上。
2.11. 总结
DeepSeek-V2 的 MoE 可以用四句话串起来:
- 稀疏激活:每层 160 个路由专家只选 6 个,以较小的单 token 计算量支撑较大的总参数量。
- 细粒度组合:专家更小、更多,可组合的知识单元更丰富。
- 共享与专用分工:2 个共享专家处理通用变换,路由专家学习条件化能力,减少重复学习。
- 算法与系统协同:设备受限路由控制通信,三级负载均衡减少专家塌缩和设备热点,token-dropping 进一步限制训练时的超载。
它真正巧妙的地方,不只是"把 FFN 换成很多专家",而是把模型容量、专家组合方式、路由算法、GPU 拓扑和通信开销放在一起设计。
3. DeepSeek-v2 MoE 拆分FFN细节
前文提过,v2 把 Transformer 中原来的一个大 FFN,改成"2 个永远工作的共享专家+160 个按需选择的路由专家";每个 token 选择其中 6 个路由专家,再与 2 个共享专家的输出合并。
这里的叙述虽然正确,但是初接触的朋友可能会听的一头雾水。也就是说有些细节并没有讲出来。
比方说,难道这里是指将一个 解码器层中原先的一个大FFN(原始transformer中是 512x512,deepseek-v2 中相当于多大呢?),切成2+6=8块数据的组合么? 2块共享,6块临时路由选择,得到8块数据,重新拼出一个 临时的大FNN么?
可以这样理解,但要改两个关键点:
- 不是把原来的 FFN 固定切成 8 块,而是准备了 162 个独立的小 FFN:2 个共享专家+160 个路由专家,每个 token 临时使用其中 8 个。
- 8 个专家的输出不是拼接 ,而是求和:共享专家直接相加,6 个路由专家按路由权重加权相加。
不过,从数学上等效地看,你说的"临时拼出一个大 FFN"是很准确的直觉------只是"拼"的是中间计算结构,而不是把最终输出向量拼起来。
3.1. 先纠正一下原始 Transformer 的 FFN 尺寸
原始 Transformer 中,隐藏维度 d model = 512 d_{\text{model}}=512 dmodel=512,但 FFN 不是简单的 512 × 512 512\times512 512×512,而是先把 512 维扩展到 2048 维,再投影回来:
FFN ( x ) = W 2 ϕ ( W 1 x ) \operatorname{FFN}(x)=W_2\,\phi(W_1x) FFN(x)=W2ϕ(W1x)
矩阵形状为:
text
输入 x: 512
W1: 512 × 2048
中间隐藏状态: 2048
W2: 2048 × 512
输出: 512
所以 FFN 的核心结构是:
text
512 → 2048 → 512
"大 FFN"的"大",主要指中间的 2048 维,而不是输入输出维度变大了。
3.2. DeepSeek-V2 的一个专家有多大?
DeepSeek-V2 的隐藏维度是 5120,每个专家的中间维度是 1536;每个 token 使用 2 个共享专家和 6 个路由专家。
它采用 SwiGLU 风格的 FFN,因此一个专家可以简化写成:
E ( x ) = W down SiLU ( W gate x ) ⊙ W up x E(x)= W_{\text{down}} \left \\operatorname{SiLU}(W_{\\text{gate}}x) \\odot W_{\\text{up}}x \\right E(x)=WdownSiLU(Wgatex)⊙Wupx
对应的张量形状为:
text
输入 x: 5120
┌─ W_gate:5120 × 1536 ─┐
│ ├─ 逐元素相乘:1536
└─ W_up: 5120 × 1536 ─┘
│
W_down:1536 × 5120
│
输出: 5120
也就是:
text
5120 → 1536 → 1536 → 5120
每个专家都是一个完整的、输入 5120 维、输出 5120 维的小 FFN。
它不是只处理输入向量的 1 / 8 1/8 1/8,也不是只输出结果向量的 1 / 8 1/8 1/8。
3.3. 一个 token 实际怎样经过这 8 个专家?
假设某个 token 的隐藏状态是:
x ∈ R 5120 x\in\mathbb R^{5120} x∈R5120
Router 从 160 个路由专家中选出 6 个,例如:
text
路由专家:3、17、42、81、106、149
那么这个 token 的计算过程是:
text
输入 x:5120 维
│
┌────────────────────┼────────────────────┐
│ │ │
共享专家 1 共享专家 2 Router
5120→1536→5120 5120→1536→5120 │
│ │ 从 160 个专家选 6 个
│ │ │
│ │ 每个都是:
│ │ 5120→1536→5120
│ │ │
▼ ▼ ▼
5120 维输出 5120 维输出 6 个 5120 维输出
│ │ 各自乘以路由权重
└────────────────────┼────────────────────┘
▼
全部相加:5120 维
│
加上残差输入 x
│
最终输出:5120 维
公式就是:
y = x + S 1 ( x ) + S 2 ( x ) + ∑ j = 1 6 g j R j ( x ) y= x+ S_1(x)+S_2(x) + \sum_{j=1}^{6}g_jR_j(x) y=x+S1(x)+S2(x)+j=1∑6gjRj(x)
其中:
- S 1 , S 2 S_1,S_2 S1,S2:两个共享专家;
- R j R_j Rj:本次被选中的第 j j j 个路由专家;
- g j g_j gj:该路由专家的权重;
- x x x:残差连接。
所以最终输出始终是 5120 维,不会变成:
8 × 5120 = 40960 维 8\times5120=40960\text{ 维} 8×5120=40960 维
3.4. 为什么又可以把它看作"临时大 FFN"?
虽然实际不拼接输出,但如果只观察中间隐藏层,8 个专家一共使用了:
8 × 1536 = 12288 8\times1536=12288 8×1536=12288
个中间隐藏单元。
可以把它们在纸上写成:
text
共享专家 1 的中间状态:1536
共享专家 2 的中间状态:1536
路由专家 A 的中间状态:1536
路由专家 B 的中间状态:1536
路由专家 C 的中间状态:1536
路由专家 D 的中间状态:1536
路由专家 E 的中间状态:1536
路由专家 F 的中间状态:1536
────────────────────────────
等效中间宽度: 12288
Dense FFN 可以按中间维度分块。例如一个 SwiGLU FFN:
F ( x ) = W down SiLU ( W gate x ) ⊙ W up x F(x)=W_{\text{down}} \left \\operatorname{SiLU}(W_{\\text{gate}}x)\\odot W_{\\text{up}}x \\right F(x)=WdownSiLU(Wgatex)⊙Wupx
如果把中间维度划成若干块,那么输出可以写成各块之和:
F ( x ) = ∑ j W down ( j ) h j ( x ) F(x)=\sum_j W_{\text{down}}^{(j)}h_j(x) F(x)=j∑Wdown(j)hj(x)
因此,忽略路由权重和额外缩放系数,下面两种写法在代数上具有相似形式:
text
一个宽 FFN:
5120 → 12288 → 5120
8 个窄 FFN 并行:
5120 → 8 × 1536 → 分别投影回 5120 → 求和
DeepSeek-V2 的公开实现配置中,普通稠密 FFN 的中间维度也正是 12288,而单个 MoE 专家的中间维度为 1536。
所以,从单次激活的计算规模看:
2 × 1536 + 6 × 1536 = 12288 \boxed{ 2\times1536+6\times1536=12288 } 2×1536+6×1536=12288
确实可以理解为:
每个 token 临时组成一个中间宽度为 12288 的 FFN。
但这里要注意三点:
- 不是输出拼接,而是各专家投影回 5120 维后求和;
- 其中 2 个专家固定,另外 6 个根据 token 动态选择;
- 这只是当前 token 的等效结构,下一个 token 可能换成另外 6 个路由专家。
3.5. "总容量"和"单次激活容量"的区别
忽略偏置,一个专家的参数量约为:
3 × 5120 × 1536 ≈ 2359 万 3\times5120\times1536 \approx2359万 3×5120×1536≈2359万
每个 token 激活 8 个专家,对应:
8 × 2359 万 ≈ 1.887 亿 8\times2359万 \approx1.887亿 8×2359万≈1.887亿
这与一个 5120 → 12288 → 5120 5120\to12288\to5120 5120→12288→5120 的 SwiGLU 稠密 FFN 参数量基本一致。
但这一层实际保存了 162 个专家:
162 × 2359 万 ≈ 38.2 亿 162\times2359万 \approx38.2亿 162×2359万≈38.2亿
因此,一个 MoE 层可以粗略理解为:
text
候选专家总容量:约 38.2 亿参数
单个 token 使用:约 1.89 亿专家参数
单个 token 等效激活宽度:12288
这些只是专家部分的参数,不包括 Attention、Router、归一化等其他参数。
3.6. 最准确的类比
DeepSeek-V2 准备了 162 个窄 FFN。每个 token 固定使用 2 个共享窄 FFN,再从 160 个路由窄 FFN 中动态选择 6 个。8 个窄 FFN 都处理完整的 5120 维输入,各自输出 5120 维结果,最后通过求和合并。
从数学等效上看,这像是为该 token 临时组装了一个中间宽度为 8 × 1536 = 12288 8\times1536=12288 8×1536=12288 的大 FFN;但实现上不是把 8 块最终输出拼接起来,而是并行计算后加权求和。
因此,它既不是"把一个 FFN 固定切成 8 份",也不是"8 个专家输出 8 块再拼成向量",而是:
固定公共部分 + 动态选择的稀疏部分 \boxed{\text{固定公共部分}+\text{动态选择的稀疏部分}} 固定公共部分+动态选择的稀疏部分
专家并行,在纯计算的算力使用上,比较像 GEMM中的 split-K 算法。实现了一种并行加速,但并不节省,也不浪费算力。
4. 一个选定后的专家的计算过程
接下来,将按照 v2 中数据的典型 size、shape,给出某个已经选定的专家的具体计算过程,并加以说明。
我们假设某个已被选中的路由专家 R 42 R_{42} R42 ,以它为例,完整走一遍计算。矩阵形状使用 DeepSeek-V2 的真实尺寸;为了能手算,假设输入和权重大部分为零。
注意:真实专家的权重矩阵通常是稠密的,这里的大量零值只是为了简化演示,不代表专家内部也是稀疏的。
4.1. 单个专家的计算结构
一个专家接收 5120 维输入,经过 1536 维中间状态,再输出 5120 维向量:
R 42 ( x ) = W d o w n ( SiLU ( W g a t e x ) ⊙ W u p x ) R_{42}(x)=W_{\mathrm{down}} \left( \operatorname{SiLU}(W_{\mathrm{gate}}x) \odot W_{\mathrm{up}}x \right) R42(x)=Wdown(SiLU(Wgatex)⊙Wupx)
其中:
x ∈ R 5120 , W g a t e , W u p ∈ R 1536 × 5120 , W d o w n ∈ R 5120 × 1536 . \begin{aligned} x&\in\mathbb R^{5120},\\ W_{\mathrm{gate}},W_{\mathrm{up}}&\in\mathbb R^{1536\times5120},\\ W_{\mathrm{down}}&\in\mathbb R^{5120\times1536}. \end{aligned} xWgate,WupWdown∈R5120,∈R1536×5120,∈R5120×1536.
⊙ \odot ⊙ 表示逐元素相乘,SiLU 定义为:
SiLU ( z ) = z ⋅ Sigmoid ( z ) = z 1 + e − z \operatorname{SiLU}(z)=z\cdot\operatorname{Sigmoid}(z) =\frac{z}{1+e^{-z}} SiLU(z)=z⋅Sigmoid(z)=1+e−zz
整体流程为:
text
x:5120 维
│
├─ W_gate ───────────────┐
│ 5120 → 1536 │
│ ├─ SiLU(g) ⊙ u:1536 维
└─ W_up ─────────────────┘
5120 → 1536
│
W_down
1536 → 5120
│
专家输出:5120 维
4.2. 假设输入向量
为了避免写出 5120 个数,假设只有前两个元素非零:
x = 1 2 0 ⋮ 0 ∈ R 5120 x= \begin{bmatrix} 1\\ 2\\ 0\\ \vdots\\ 0 \end{bmatrix} \in\mathbb R^{5120} x= 120⋮0 ∈R5120
这个 x x x 是前面 Attention、归一化等计算后,送入当前 MoE 层的隐藏状态。
4.3. 计算门控分支 W g a t e x W_{\mathrm{gate}}x Wgatex
假设 W g a t e W_{\mathrm{gate}} Wgate 的前两行为:
W g a t e = 1 0 0 ⋯ 0 0 1 0 ⋯ 0 0 0 0 ⋯ 0 ⋮ ⋮ ⋮ ⋮ 0 0 0 ⋯ 0 ∈ R 1536 × 5120 W_{\mathrm{gate}}= \begin{bmatrix} 1&0&0&\cdots&0\\ 0&1&0&\cdots&0\\ 0&0&0&\cdots&0\\ \vdots&\vdots&\vdots&&\vdots\\ 0&0&0&\cdots&0 \end{bmatrix} \in\mathbb R^{1536\times5120} Wgate= 100⋮0010⋮0000⋮0⋯⋯⋯⋯000⋮0 ∈R1536×5120
矩阵乘法为:
W g a t e ⏟ 1536 × 5120 x ⏟ 5120 × 1 = g ⏟ 1536 × 1 \underbrace{W_{\mathrm{gate}}}{1536\times5120} \underbrace{x}{5120\times1}= \underbrace{g}_{1536\times1} 1536×5120 Wgate5120×1 x=1536×1 g
得到:
g = W g a t e x = 1 2 0 ⋮ 0 ∈ R 1536 g=W_{\mathrm{gate}}x= \begin{bmatrix} 1\\ 2\\ 0\\ \vdots\\ 0 \end{bmatrix} \in\mathbb R^{1536} g=Wgatex= 120⋮0 ∈R1536
对 g g g 逐元素施加 SiLU:
SiLU ( 1 ) = 1 ⋅ 1 1 + e − 1 ≈ 0.731059 \operatorname{SiLU}(1) =1\cdot\frac1{1+e^{-1}} \approx0.731059 SiLU(1)=1⋅1+e−11≈0.731059
SiLU ( 2 ) = 2 ⋅ 1 1 + e − 2 ≈ 1.761594 \operatorname{SiLU}(2) =2\cdot\frac1{1+e^{-2}} \approx1.761594 SiLU(2)=2⋅1+e−21≈1.761594
所以:
SiLU ( g ) ≈ 0.731059 1.761594 0 ⋮ 0 ∈ R 1536 \operatorname{SiLU}(g)\approx \begin{bmatrix} 0.731059\\ 1.761594\\ 0\\ \vdots\\ 0 \end{bmatrix} \in\mathbb R^{1536} SiLU(g)≈ 0.7310591.7615940⋮0 ∈R1536
4.4. 计算上投影分支 W u p x W_{\mathrm{up}}x Wupx
假设:
W u p = 2 0 0 ⋯ 0 0 − 1 0 ⋯ 0 0 0 0 ⋯ 0 ⋮ ⋮ ⋮ ⋮ 0 0 0 ⋯ 0 ∈ R 1536 × 5120 W_{\mathrm{up}}= \begin{bmatrix} 2&0&0&\cdots&0\\ 0&-1&0&\cdots&0\\ 0&0&0&\cdots&0\\ \vdots&\vdots&\vdots&&\vdots\\ 0&0&0&\cdots&0 \end{bmatrix} \in\mathbb R^{1536\times5120} Wup= 200⋮00−10⋮0000⋮0⋯⋯⋯⋯000⋮0 ∈R1536×5120
于是:
u = W u p x = 2 − 2 0 ⋮ 0 ∈ R 1536 u=W_{\mathrm{up}}x= \begin{bmatrix} 2\\ -2\\ 0\\ \vdots\\ 0 \end{bmatrix} \in\mathbb R^{1536} u=Wupx= 2−20⋮0 ∈R1536
注意: W u p x W_{\mathrm{up}}x Wupx 不经过 SiLU。
4.5. 两个分支逐元素相乘
现在计算:
a = SiLU ( g ) ⊙ u a=\operatorname{SiLU}(g)\odot u a=SiLU(g)⊙u
第一个中间元素:
a 1 = 0.731059 × 2 = 1.462117 a_1=0.731059\times2=1.462117 a1=0.731059×2=1.462117
第二个中间元素:
a 2 = 1.761594 × ( − 2 ) = − 3.523188 a_2=1.761594\times(-2)=-3.523188 a2=1.761594×(−2)=−3.523188
其余元素均为零,因此:
a ≈ 1.462117 − 3.523188 0 ⋮ 0 ∈ R 1536 a\approx \begin{bmatrix} 1.462117\\ -3.523188\\ 0\\ \vdots\\ 0 \end{bmatrix} \in\mathbb R^{1536} a≈ 1.462117−3.5231880⋮0 ∈R1536
这里的 a a a 就是该专家的 1536 维中间激活。
SwiGLU 的意义在于: W g a t e x W_{\mathrm{gate}}x Wgatex 产生的非线性门控会调节 W u p x W_{\mathrm{up}}x Wupx 中每个中间特征的强弱,而不是简单地做一次线性变换。
4.6. 投影回 5120 维
假设 W d o w n W_{\mathrm{down}} Wdown 只有前三行包含非零值:
W d o w n = 1 0 0 ⋯ 0 0 1 0 ⋯ 0 1 2 − 1 2 0 ⋯ 0 0 0 0 ⋯ 0 ⋮ ⋮ ⋮ ⋮ 0 0 0 ⋯ 0 ∈ R 5120 × 1536 W_{\mathrm{down}}= \begin{bmatrix} 1&0&0&\cdots&0\\ 0&1&0&\cdots&0\\ \frac12&-\frac12&0&\cdots&0\\ 0&0&0&\cdots&0\\ \vdots&\vdots&\vdots&&\vdots\\ 0&0&0&\cdots&0 \end{bmatrix} \in\mathbb R^{5120\times1536} Wdown= 10210⋮001−210⋮00000⋮0⋯⋯⋯⋯⋯0000⋮0 ∈R5120×1536
计算:
z = W d o w n a z=W_{\mathrm{down}}a z=Wdowna
矩阵形状为:
W d o w n ⏟ 5120 × 1536 a ⏟ 1536 × 1 = z ⏟ 5120 × 1 \underbrace{W_{\mathrm{down}}}{5120\times1536} \underbrace{a}{1536\times1}= \underbrace{z}_{5120\times1} 5120×1536 Wdown1536×1 a=5120×1 z
前三个输出元素分别为:
z 1 = a 1 = 1.462117 z_1=a_1=1.462117 z1=a1=1.462117
z 2 = a 2 = − 3.523188 z_2=a_2=-3.523188 z2=a2=−3.523188
z 3 = 1 2 a 1 − 1 2 a 2 = 1 2 ( 1.462117 ) − 1 2 ( − 3.523188 ) ≈ 2.492653 z_3=\frac12a_1-\frac12a_2 =\frac12(1.462117)-\frac12(-3.523188) \approx2.492653 z3=21a1−21a2=21(1.462117)−21(−3.523188)≈2.492653
所以该专家的输出为:
R 42 ( x ) = z ≈ 1.462117 − 3.523188 2.492653 0 ⋮ 0 ∈ R 5120 R_{42}(x)=z\approx \begin{bmatrix} 1.462117\\ -3.523188\\ 2.492653\\ 0\\ \vdots\\ 0 \end{bmatrix} \in\mathbb R^{5120} R42(x)=z≈ 1.462117−3.5231882.4926530⋮0 ∈R5120
到这里,一个专家的计算已经完成:
5120 → W g a t e , W u p 1536 → SiLU 门控与逐元素乘法 1536 → W d o w n 5120 \boxed{ 5120 \xrightarrow{W_{\mathrm{gate}},W_{\mathrm{up}}} 1536 \xrightarrow{\operatorname{SiLU}\text{ 门控与逐元素乘法}} 1536 \xrightarrow{W_{\mathrm{down}}} 5120 } 5120Wgate,Wup 1536SiLU 门控与逐元素乘法 1536Wdown 5120
4.7. 再乘以该专家的路由权重
假设 Router 给 R 42 R_{42} R42 的分数为:
g 42 = 0.21 g_{42}=0.21 g42=0.21
那么它对本层输出的贡献是:
g 42 R 42 ( x ) = 0.21 z ≈ 0.307045 − 0.739870 0.523457 0 ⋮ 0 ∈ R 5120 g_{42}R_{42}(x)= 0.21z \approx \begin{bmatrix} 0.307045\\ -0.739870\\ 0.523457\\ 0\\ \vdots\\ 0 \end{bmatrix} \in\mathbb R^{5120} g42R42(x)=0.21z≈ 0.307045−0.7398700.5234570⋮0 ∈R5120
路由权重不是用在专家内部,而是用来缩放专家最终输出。
4.8. 放回整个 MoE 层中
假设另外两个被选中的专家分别是 R 7 R_7 R7 和 R 105 R_{105} R105,则整个 MoE 层的计算形式是:
h ′ = x + S 1 ( x ) ⏟ 共享专家 1 + S 2 ( x ) ⏟ 共享专家 2 + 0.21 R 42 ( x ) ⏟ 刚才计算的专家 + g 7 R 7 ( x ) + g 105 R 105 ( x ) + ⋯ ⏟ 另外 5 个路由专家 \begin{aligned} h' ={}&x +\underbrace{S_1(x)}{\text{共享专家 1}} +\underbrace{S_2(x)}{\text{共享专家 2}}\\ &+\underbrace{0.21R_{42}(x)}{\text{刚才计算的专家}}\\ &+\underbrace{g_7R_7(x)+g{105}R_{105}(x)+\cdots}_{\text{另外 5 个路由专家}} \end{aligned} h′=x+共享专家 1 S1(x)+共享专家 2 S2(x)+刚才计算的专家 0.21R42(x)+另外 5 个路由专家 g7R7(x)+g105R105(x)+⋯
所有加项都是 5120 维向量,因此可以直接相加:
text
残差输入 x: 5120 维
共享专家 1 输出: 5120 维
共享专家 2 输出: 5120 维
路由专家 42 输出 × 0.21: 5120 维
其他 5 个路由专家加权输出: 5120 维
───────────
逐元素相加后的层输出: 5120 维
所以,刚才这个专家并没有产生"最终输出的某一块",而是产生了一个完整的 5120 维候选更新量;乘以路由权重后,与其他专家的更新量一起加到残差通路上。
5. DS-v2 MoE,路由决策的实现
"MoE 中路由决策的实现" 是:
每个 MoE 层都有一个可学习的 Router。它用一个线性矩阵,把当前 token 的 5120 维隐藏状态映射成 160 个专家分数,再经过 Softmax 和 Top-6 选择,决定该 token 发给哪些专家。
路由不是人工指定"数学 token 去数学专家",而是模型在训练中学出来的。
5.1. Router 的输入和参数
设当前 token 在某个 MoE 层的输入为:
x ∈ R 5120 x\in\mathbb R^{5120} x∈R5120
该层 Router 有一个可学习矩阵:
E ∈ R 160 × 5120 E\in\mathbb R^{160\times5120} E∈R160×5120
其中第 i i i 行 e i \mathbf e_i ei 对应第 i i i 个路由专家:
E = e 1 T e 2 T ⋮ e 160 T E= \begin{bmatrix} \mathbf e_1^T\\ \mathbf e_2^T\\ \vdots\\ \mathbf e_{160}^T \end{bmatrix} E= e1Te2T⋮e160T
每个 e i ∈ R 5120 \mathbf e_i\in\mathbb R^{5120} ei∈R5120,可以理解为"专家 i i i 偏好的输入特征方向"。
仅看这个 Router 矩阵,参数量为:
160 × 5120 = 819200 160\times5120=819200 160×5120=819200
即约 82 万个参数,远小于专家本身的参数。
每个 MoE 层都有自己独立的 Router,不是整个模型共用一个。
5.2. 计算 160 个专家的匹配分数
首先计算 logits:
ℓ = E x \ell=Ex ℓ=Ex
矩阵形状为:
E ⏟ 160 × 5120 x ⏟ 5120 × 1 = ℓ ⏟ 160 × 1 \underbrace{E}{160\times5120} \underbrace{x}{5120\times1}= \underbrace{\ell}_{160\times1} 160×5120 E5120×1 x=160×1 ℓ
展开来看:
ℓ i = e i T x \ell_i=\mathbf e_i^Tx ℓi=eiTx
也就是当前 token 的隐藏状态与每个专家向量的点积。
直观上:
- 输入特征越符合专家 i i i 学到的偏好,点积通常越大;
- 点积越大,该专家在 Softmax 后获得的分数通常越高。
然后对 160 个 logits 做 Softmax:
s i = e ℓ i ∑ j = 1 160 e ℓ j s_i=\frac{e^{\ell_i}}{\sum_{j=1}^{160}e^{\ell_j}} si=∑j=1160eℓjeℓi
得到:
s ∈ R 160 , ∑ i = 1 160 s i = 1 s\in\mathbb R^{160},\qquad \sum_{i=1}^{160}s_i=1 s∈R160,i=1∑160si=1
其中 s i s_i si 是 token 对专家 i i i 的亲和度。
5.3. 一个简化的数值例子
为了简化,先看只有 4 个专家的情况。假设:
x = 1 2 x= \begin{bmatrix} 1\\ 2 \end{bmatrix} x=12
4 个专家向量为:
e 1 = 1 0 , e 2 = 0 1 , e 3 = 1 1 , e 4 = − 1 0 \mathbf e_1= \begin{bmatrix}1\\0\end{bmatrix}, \quad \mathbf e_2= \begin{bmatrix}0\\1\end{bmatrix}, \quad \mathbf e_3= \begin{bmatrix}1\\1\end{bmatrix}, \quad \mathbf e_4= \begin{bmatrix}-1\\0\end{bmatrix} e1=10,e2=01,e3=11,e4=−10
分别计算点积:
ℓ 1 = 1 × 1 + 0 × 2 = 1 , ℓ 2 = 0 × 1 + 1 × 2 = 2 , ℓ 3 = 1 × 1 + 1 × 2 = 3 , ℓ 4 = − 1 × 1 + 0 × 2 = − 1. \begin{aligned} \ell_1&=1\times1+0\times2=1,\\ \ell_2&=0\times1+1\times2=2,\\ \ell_3&=1\times1+1\times2=3,\\ \ell_4&=-1\times1+0\times2=-1. \end{aligned} ℓ1ℓ2ℓ3ℓ4=1×1+0×2=1,=0×1+1×2=2,=1×1+1×2=3,=−1×1+0×2=−1.
所以:
ℓ = 1 , 2 , 3 , − 1 \ell=1,2,3,-1 ℓ=1,2,3,−1
Softmax 后约为:
s ≈ 0.0675 , 0.1835 , 0.4984 , 0.0091 s\approx0.0675,0.1835,0.4984,0.0091 s≈0.0675,0.1835,0.4984,0.0091
如果要求 Top-2,就选择:
text
专家 3:权重 0.4984
专家 2:权重 0.1835
该 token 的专家计算结果为:
0.4984 R 3 ( x ) + 0.1835 R 2 ( x ) 0.4984R_3(x)+0.1835R_2(x) 0.4984R3(x)+0.1835R2(x)
专家 1 和专家 4 不执行。
DeepSeek-V2 的实际过程相同,只是规模变为:
text
输入:5120 维
专家:160 个
Router 输出:160 个概率
选择:概率最高的 6 个
例如,假设某个 token 最终得到:
| 专家编号 | Softmax 分数 | 是否选择 |
|---|---|---|
| 42 | 0.12 | 是 |
| 7 | 0.09 | 是 |
| 105 | 0.08 | 是 |
| 23 | 0.05 | 是 |
| 88 | 0.04 | 是 |
| 149 | 0.03 | 是 |
| 其余 154 个专家 | 各自分数 | 否 |
那么路由输出可以表示为:
Top6 = { 42 , 7 , 105 , 23 , 88 , 149 } \operatorname{Top6}= \{42,7,105,23,88,149\} Top6={42,7,105,23,88,149}
这 6 个专家的输出贡献为:
R ( x ) = 0.12 R 42 ( x ) + 0.09 R 7 ( x ) + 0.08 R 105 ( x ) + 0.05 R 23 ( x ) + 0.04 R 88 ( x ) + 0.03 R 149 ( x ) \begin{aligned} R(x)={}&0.12R_{42}(x) +0.09R_7(x) +0.08R_{105}(x)\\ &+0.05R_{23}(x) +0.04R_{88}(x) +0.03R_{149}(x) \end{aligned} R(x)=0.12R42(x)+0.09R7(x)+0.08R105(x)+0.05R23(x)+0.04R88(x)+0.03R149(x)
这里的权重之和为:
0.12 + 0.09 + 0.08 + 0.05 + 0.04 + 0.03 = 0.41 0.12+0.09+0.08+0.05+0.04+0.03=0.41 0.12+0.09+0.08+0.05+0.04+0.03=0.41
按照 DeepSeek-V2 论文的公式,不需要把这 6 个权重重新归一化为总和 1。Softmax 是在全部 160 个专家上计算的,未选中的专家只是不参与专家计算。
最后再加上共享专家和残差:
h ′ = x + S 1 ( x ) + S 2 ( x ) + R ( x ) h'=x+S_1(x)+S_2(x)+R(x) h′=x+S1(x)+S2(x)+R(x)
5.4. 用伪代码表示路由过程
不考虑设备限制时,可以写成:
python
# x: [T, 5120],T 个 token
# E: [160, 5120],Router 参数
# experts: 160 个独立的路由专家
logits = x @ E.T # [T, 160]
scores = softmax(logits, dim=-1) # [T, 160]
# 每个 token 选择分数最高的 6 个专家
top_scores, top_indices = topk(
scores, k=6, dim=-1
)
# top_scores: [T, 6]
# top_indices: [T, 6]
output = zeros([T, 5120])
for token t:
for j in 0..5:
expert_id = top_indices[t, j]
weight = top_scores[t, j]
expert_out = experts[expert_id](x[t]) # [5120]
output[t] += weight * expert_out
# 两个共享专家不参与 Top-6 选择
output += shared_expert_1(x)
output += shared_expert_2(x)
# 残差连接
output += x
真实系统不会简单地逐个 token、逐个专家调用,而是会:
- 根据路由结果,把发送给同一专家的 token 收集起来;
- 每个专家批量处理自己收到的 token;
- 将结果发送回原 token 所在位置;
- 按路由权重加权求和。
例如,一批 token 路由后可能形成:
text
专家 0:收到 135 个 token → 输入形状 [135, 5120]
专家 1:收到 82 个 token → 输入形状 [82, 5120]
专家 2:收到 0 个 token → 本批不计算
......
因此,Router 的输出同时也决定了后续计算的调度方式。
5.5. DeepSeek-V2 还有设备限制
如果完全按照全局 Top-6,6 个专家可能分布在 6 张 GPU 上,通信成本较高。
DeepSeek-V2 训练时把 160 个专家分布在 8 台设备上,并限制每个 token 最多发送到 3 台设备。过程可以简化为:
text
第一步:计算全部 160 个专家的亲和度
↓
第二步:根据设备上的专家分数,选出 3 台目标设备
↓
第三步:只在这 3 台设备的专家候选集合中选择 Top-6
↓
第四步:发送隐藏状态、计算专家、传回并加权合并
所以实际路由不是完全自由的"160 选 6",而是:
先限制设备范围,再在候选专家中 Top-6 \boxed{\text{先限制设备范围,再在候选专家中 Top-6}} 先限制设备范围,再在候选专家中 Top-6
这样每个 token 的通信扇出最多为 3 台设备。
5.6. Top-K 不可微,Router 怎么训练?
"选哪 6 个专家"本身是离散操作,不能直接对专家编号求梯度。但选中后的输出仍然包含可微的路由分数:
h ′ = ⋯ + s i R i ( x ) + ⋯ h'=\cdots+s_iR_i(x)+\cdots h′=⋯+siRi(x)+⋯
假设最终语言模型损失为 L \mathcal L L,则对选中的专家分数有:
∂ L ∂ s i = ( ∂ L ∂ h ′ ) T R i ( x ) \frac{\partial\mathcal L}{\partial s_i}= \left( \frac{\partial\mathcal L}{\partial h'} \right)^T R_i(x) ∂si∂L=(∂h′∂L)TRi(x)
梯度可以继续通过 s i s_i si 传回 Router 矩阵 E E E。
与此同时,负载均衡损失利用所有专家的 Softmax 概率,鼓励 Router 不要把 token 全部分给少数专家。
因此训练信号来自两条路径:
text
语言模型损失
└─ 通过被选专家的加权输出,训练 Router 和专家
负载均衡损失
└─ 通过专家分数和分配统计,避免路由过度集中
专家编号的选择仍然不可微,但选中专家的门控权重是可微的,这就是 Router 能够学习的关键。
5.7. 训练和推理之间的区别
| 阶段 | 路由计算 | 负载均衡损失 | Token 丢弃 |
|---|---|---|---|
| 训练 | Softmax+设备限制+Top-6 | 参与反向传播 | 超载时可能丢弃低分分配 |
| 推理 | 根据隐藏状态计算并选择专家 | 不需要反向传播 | 评估时不丢弃 |
| 不同层 | 使用各层独立 Router | --- | --- |
| 不同 token | 各自独立选择专家 | --- | --- |
总结:
5120 维隐藏状态 → 线性 Router 160 个 logits → Softmax 160 个概率 → 设备限制+Top-6 6 个专家及其权重 \boxed{ 5120\text{ 维隐藏状态} \xrightarrow{\text{线性 Router}} 160\text{ 个 logits} \xrightarrow{\text{Softmax}} 160\text{ 个概率} \xrightarrow{\text{设备限制+Top-6}} 6\text{ 个专家及其权重} } 5120 维隐藏状态线性 Router 160 个 logitsSoftmax 160 个概率设备限制+Top-6 6 个专家及其权重
之后才是"分发 token → 专家计算 → 输出加权求和"。
6. DeepSeek-v2 的 MLA
标准 MHA 写法可以理解为:
K = W K h , V = W V h K = W_K h,\quad V = W_V h K=WKh,V=WVh
其中 W K , W V W_K,W_V WK,WV 都是 [5120, 5120], h h h 是 [5120, T],所以 K , V K,V K,V 都是 [5120, T],T 是已经推理得到的 token 个数。推理时,每层每 token 要缓存 K 和 V 各 5120 维,共 10240 个数/层/token。
MLA 做的事情,本质上是把这两个"完整 K/V"改成了:
先压成一个共享低维潜在向量并缓存,需要时再上投影回 K/V;位置信息单独用一个小向量承载。
6.1. 标准 MHA 的 KV
K = W K h , V = W V h K = W_K h,\qquad V = W_V h K=WKh,V=WVh
缓存的是完整的 K , V K,V K,V。
6.2. MLA 的 KV
MLA 不再直接算完整 K/V 并缓存,而是:
c K V = W D K V h c^{KV} = W^{DKV} h cKV=WDKVh
其中 W D K V W^{DKV} WDKV 是 [512, 5120],所以 c K V c^{KV} cKV 是 [512, T]。这个 c K V c^{KV} cKV 是 K 和 V 共享的低维潜在向量,推理时只缓存它。
然后:
K C = W U K c K V , V = W U V c K V K^C = W^{UK} c^{KV},\qquad V = W^{UV} c^{KV} KC=WUKcKV,V=WUVcKV
其中 W U K , W U V W^{UK},W^{UV} WUK,WUV 是上投影矩阵,把 512 维重新投到每个头的 K/V 维度。DeepSeek-V2 里是 128 头 × 128 维,所以内部 K/V 工作维度是 16384,但缓存里不存这个 16384 维的 K/V ,只存 512 维的 c K V c^{KV} cKV。
位置信息则单独走一条路:
k R = W K R h k^R = W^{KR} h kR=WKRh
k R k^R kR 只有 64 维,且所有头共享,也缓存。
所以 MLA 每层每 token 的 KV Cache 是:
512 + 64 = 576 512 + 64 = 576 512+64=576
而不是标准 MHA 的 2 × 5120 = 10240 2 \times 5120 = 10240 2×5120=10240(按你给的 5120 维 MHA 算),压缩约 17.8 倍;如果按 DeepSeek-V2 内部对照的更宽 MHA 基线算,压缩更夸张。
6.3. 它和标准 MHA 的数学关系
可以粗略看成 MLA 对标准 MHA 的 W K , W V W_K,W_V WK,WV 做了共享低秩分解:
W K ≈ W U K W D K V , W V ≈ W U V W D K V W_K \approx W^{UK} W^{DKV},\qquad W_V \approx W^{UV} W^{DKV} WK≈WUKWDKV,WV≈WUVWDKV
标准 MHA:
K = W K h K = W_K h K=WKh
MLA:
K ≈ W U K ( W D K V h ) = W U K c K V K \approx W^{UK}(W^{DKV}h)=W^{UK}c^{KV} K≈WUK(WDKVh)=WUKcKV
V 同理。关键点是:K 和 V 共用同一个下投影 W D K V W^{DKV} WDKV 和同一个潜在向量 c K V c^{KV} cKV,所以缓存的不再是两套完整 K/V,而是一套低维共享表示。
6.4. 位置编码被拆出去了
标准 MHA 中,K 通常还要加 RoPE。MLA 如果直接对压缩后的 K 加 RoPE,会破坏后续"矩阵吸收"的等价性。所以它把 Key 拆成:
K = K C ; k R K = K\^C; k\^R K=KC;kR
- K C K^C KC:内容部分,由 c K V c^{KV} cKV 上投影得到,不带 RoPE;
- k R k^R kR:位置部分,由 h h h 经过一个小矩阵得到,专门承载 RoPE,所有头共享。
这样既保留了位置信息,又让内容部分可以做低秩压缩和矩阵吸收。
6.5. 推理时还可以"矩阵吸收"
注意力分数里:
q T K C = q T W U K c K V = ( W U K ⊤ q ) T c K V q^T K^C = q^T W^{UK} c^{KV} = (W^{UK\top} q)^T c^{KV} qTKC=qTWUKcKV=(WUK⊤q)TcKV
所以推理时可以把 W U K W^{UK} WUK 吸收到 Query 一侧,直接拿"吸收后的 Query"和缓存的 512 维 c K V c^{KV} cKV 算分数,不必每一步都显式把 c K V c^{KV} cKV 重构成 16384 维的 K 。V 的上投影也可以吸收到输出投影 W O W_O WO 里。
6.6. 总结
标准 MHA 是: h → W K h , W V h h \to W_K h, W_V h h→WKh,WVh,缓存完整 K/V。
MLA 是: h → W D K V h = c K V h \to W^{DKV}h = c^{KV} h→WDKVh=cKV,只缓存低维共享 c K V c^{KV} cKV 和一个 64 维共享 RoPE Key;K/V 变成 c K V c^{KV} cKV 的上投影结果,推理时通过矩阵吸收避免显式重构。
所以它不是"多一个矩阵缩短隐藏表示",而是把 KV 的表示、缓存和计算方式 整体改成了"共享低秩潜在 + 解耦位置 + 矩阵吸收"的结构。
7. 训练 W k W_k Wk 还是训练 W U K W^{UK} WUK、 W D K V W^{DKV} WDKV
直接训练分解后的矩阵 ,而不是先训练完整的 W K , W V W_K, W_V WK,WV 再在部署时做近似分解。
7.1. 训练时:直接学习低秩结构
在 DeepSeek-V2 的 MLA 架构中, W D K V W^{DKV} WDKV、 W U K W^{UK} WUK、 W U V W^{UV} WUV 都是可学习的参数,与模型其他权重一起从零开始随机初始化并联合训练。模型训练时使用的就是下面这套完整的前向公式:
c t K V = W D K V h t c_t^{KV} = W^{DKV} h_t ctKV=WDKVht
k t C = W U K c t K V k_t^C = W^{UK} c_t^{KV} ktC=WUKctKV
v t C = W U V c t K V v_t^C = W^{UV} c_t^{KV} vtC=WUVctKV
其中 W D K V W^{DKV} WDKV 负责将高维隐藏状态 h t h_t ht 压缩到低维潜在空间(DeepSeek-V2 中 d c = 512 d_c=512 dc=512), W U K W^{UK} WUK 和 W U V W^{UV} WUV 则负责从潜在向量重构出每个头的 Key 和 Value。训练过程中并不存在一个完整的、高维的 W K W_K WK 或 W V W_V WV 被学习出来 ;梯度直接通过这条"下投影 → 上投影"的路径回传,同时更新 W D K V W^{DKV} WDKV、 W U K W^{UK} WUK、 W U V W^{UV} WUV。
这种设计意味着 MLA 的低秩瓶颈不是训练后施加的约束,而是模型从一开始就必须适应的固有结构。模型学会的是"如何在一个共享的、512 维的潜在空间里,高效地组织 K 和 V 所需的信息"。
7.2. 推理时:矩阵吸收是部署优化,不是分解
设想的"在推理部署时分解为约等于的矩阵形式",更准确的说法是 "矩阵吸收"(Matrix Absorption),它与训练时的参数分解是两回事。
训练结束后, W U K W^{UK} WUK 和 W U V W^{UV} WUV 已经是训练好的固定权重。推理时,可以利用矩阵乘法的结合律,把它们"折叠"到其他矩阵中,从而避免显式计算高维的 K C K^C KC 和 V C V^C VC:
- Score 计算 :原本是 q ⋅ ( W U K c K V ) q \cdot (W^{UK} c^{KV}) q⋅(WUKcKV),吸收后变成 ( W U K ⊤ q ) ⋅ c K V (W^{UK\top} q) \cdot c^{KV} (WUK⊤q)⋅cKV, W U K W^{UK} WUK 被折叠进了 Query 侧。
- 输出计算 :原本是 attn ⋅ ( W U V c K V ) \text{attn} \cdot (W^{UV} c^{KV}) attn⋅(WUVcKV),吸收后变成 ( attn ⋅ c K V ) ⋅ W U V (\text{attn} \cdot c^{KV}) \cdot W^{UV} (attn⋅cKV)⋅WUV, W U V W^{UV} WUV 被折叠进了输出投影 W O W^O WO 侧。
这种吸收是精确的数学等价变换 ,不引入任何近似误差,目的是让推理时的注意力直接与缓存中的低维 c K V c^{KV} cKV 交互,省去每一步都重构完整 K/V 的开销。它发生在训练完成之后,是对已学得参数的一种部署层面的计算图优化。
7.3. 总结
| 阶段 | 操作对象 | 性质 |
|---|---|---|
| 训练 | W D K V , W U K , W U V W^{DKV}, W^{UK}, W^{UV} WDKV,WUK,WUV 作为独立参数直接联合学习 | 模型架构本身 |
| 推理 | 将训练好的 W U K , W U V W^{UK}, W^{UV} WUK,WUV 通过结合律吸收到其他矩阵 | 部署时的精确优化 |
所以,MLA 的低秩分解根植于训练阶段的参数化方式,而矩阵吸收只是推理时的一种等价变换技巧,两者是"训练时学什么"和"部署时怎么算"的区别。
8. MLA 照进历史

论文 《TransMLA: MLA Is All You Need》 ,核心是提出一个名为 TransMLA 的框架,旨在将现有的、基于GQA(分组查询注意力)的预训练大模型(如LLaMA、Qwen等)低成本地"转换"为基于MLA(多头潜在注意力)的模型,从而在不牺牲性能的前提下,大幅降低推理时的KV Cache开销并提升速度。
8.1. 核心动机:为何要从GQA转向MLA?
论文首先从理论上论证了这次"转换"的价值。
现代大模型的推理瓶颈往往在于KV Cache的通信开销 ,而非纯计算限制。MLA通过低秩矩阵压缩KV Cache,配合 "Absorb"操作,能防止缓存在推理时"膨胀"回原始大小,从而显著加速。
然而,许多模型厂商已在GQA模型上投入巨大,缺乏从头训练MLA模型的动力。论文证明,在相同的KV Cache开销下,MLA的表达能力始终优于GQA。这为"转换"而非"重训"提供了理论依据。
8.2. 关键技术:如何实现GQA到MLA的转换?
将GQA模型转换为MLA的核心障碍在于旋转位置编码(RoPE)。在GQA中,每个注意力头都有各自的RoPE,这会阻碍MLA实现关键的"矩阵吸收"优化。
为此,TransMLA引入了两项创新技术:
- RoRoPE (Rotated RoPE) :这是一种新颖的位置编码处理技术。它通过主成分分析(PCA) ,将分散在各注意力头中的位置信息,集中压缩到少数几个维度上,从而将位置信号与内容信号解耦。
- BKV (Balanced Key-Value) :在压缩过程中,为防止信息损失,BKV会先重新缩放Key和Value子空间,使它们的范数匹配,再进行联合PCA。这能有效恢复两个子空间之间的平衡,显著提升压缩质量。
通过上述技术,转换后的MLA模型能够直接兼容DeepSeek的代码库,从而利用vLLM、SGLang等框架下的深度优化(如FP8量化、多Token预测),获得进一步的推理加速。
8.3. 实验结果:转换效果如何?
论文在LLaMA-2-7B模型上验证了TransMLA的有效性,关键数据如下:
| 指标 | 结果 |
|---|---|
| KV Cache压缩率 | 68.75% 压缩时,在6个基准测试上无需训练 ,性能仅下降 1.65%(作为对比,同期方法MHA2MLA的性能下降了21.85%)。 |
| 极限压缩 | 即使压缩率达到 93%,模型仍能保持有意义的输出。 |
| 微调恢复 | 经过仅60亿Token的微调,模型在多数基准上的性能可基本恢复。 |
| 推理加速 | 在vLLM框架下,于8K上下文长度时,相比原始GQA模型实现了最高 10.6倍 的推理加速。 |
8.4. 总结
这篇论文的定位并非提出一种新的注意力机制,而是为现有的、庞大的GQA模型生态提供了一条低成本、高效率的"升级路径"。它通过TransMLA框架解决了架构转换中的核心技术难题(RoPE兼容性),使得模型厂商能够继承已有的预训练权重,平滑地迁移到MLA架构,从而享受其带来的显存节省和推理加速收益。
9. 先train 后分解 VS. 先分解后train
关于 W_k W_v 与W^{DKV} ,W^{UK} ,W^{UV} ,是先train 后分解,效果更好?还是直接train 分解后的矩阵参数的模型效果更炸裂呢?或者能证明名两者是等价的么?
结论是明确的:直接训练分解后的矩阵,在效果上通常优于"先训练完整矩阵、再事后分解" 。两者也并非数学等价,它们的优化目标和所处的参数空间有着本质区别。
9.1. 根本差异:优化目标不同
"先训练后分解"与"直接训练分解矩阵"的核心分歧在于,模型优化的是什么。
事后分解(如SVD)本质是一种压缩后处理 。它先训练一个完整的、高秩的 (W_K, W_V),然后用SVD将其近似为低秩矩阵的乘积。但SVD最小化的是权重矩阵本身的几何重建误差 (|W - \hat{W}|),而非模型最终的任务损失(如语言建模的交叉熵)。这种目标错配会形成一个性能天花板:即使SVD在几何上是最优的,压缩后的模型也可能因为破坏了与任务相关的关键信息而性能骤降,尤其是在激进压缩时,会导致灾难性的精度损失。
相比之下,直接训练 (W^{DKV}, W^{UK}, W^{UV}) 是一种学习式压缩 。在MLA的预训练中,这三个矩阵作为可学习参数被联合优化。梯度直接通过压缩-解压缩路径回传,模型在训练过程中主动学习如何将信息有效地编码到低维潜在空间 (c^{KV}) 中,并从中重建出对注意力机制最有用的K和V。模型优化的目标与最终任务完全一致,因此能更好地保留任务相关的关键信息。
9.2. 直接训练的优势与权衡
直接训练分解矩阵虽然效果更好,但也并非没有代价。它实际上是在一个受约束的参数空间中优化。
理论上,将权重参数化为低秩乘积((W = UV))会限制模型的表达能力,因为它迫使权重位于一个低秩流形上。有研究指出,从零开始训练这种"瘦"的、预分解的架构,有时会遭遇收敛困难或严重的精度下降,因为受限的参数空间在训练早期可能阻碍模型在复杂的损失 landscape 中进行探索。
不过,这种权衡对于MLA而言是经过设计的、可接受的。DeepSeek-V2的消融实验表明,MLA在匹配或超越MHA质量的同时,实现了大幅压缩。这说明,只要低秩瓶颈((d_c))设置得当,学习到的压缩映射足以保留大部分必要信息。风险在于,如果瓶颈过窄,压缩会变成有损的,导致注意力模式退化。
9.3. 总结
| 维度 | 先训练后分解 (Post-hoc SVD) | 直接训练分解矩阵 (MLA原生训练) |
|---|---|---|
| 优化目标 | 权重重建误差(几何近似) | 任务损失(端到端) |
| 压缩性质 | 事后近似,目标错配 | 学习式压缩,目标一致 |
| 性能表现 | 存在性能天花板,激进压缩易崩溃 | 效果更优,是MLA能达到高性能的设计基础 |
| 优化挑战 | 无(全秩训练) | 参数空间受限,可能影响收敛,但可通过调整瓶颈维度权衡 |
| 等价性 | 不等价 | 不等价 |
因此,DeepSeek-V2的MLA选择直接训练分解矩阵,并非仅仅为了"部署方便",而是因为这是在端到端优化下实现高效、高质量KV Cache压缩的必要设计 。事后分解是一种近似手段,而MLA的低秩结构则是模型固有且被优化的架构属性。
10. MLA 计算流程公式表示
下面给出某一个注意力头 i i i 中,基于 MLA 的自注意力层从输入隐藏表示 h t h_t ht 到该头输出 o t , i o_{t,i} ot,i 的完整计算公式。记号: t t t 为当前 token 位置, j ≤ t j\le t j≤t 为历史位置; d h d_h dh 为每头内容/Value 维度, d h R d_h^R dhR 为解耦 RoPE 维度; d c d_c dc 为 KV 压缩维度, d c ′ d_c' dc′ 为 Query 压缩维度。
1. Query 的低秩压缩与解耦 RoPE
对当前 token h t ∈ R d model h_t\in\mathbb{R}^{d_{\text{model}}} ht∈Rdmodel:
c t Q = W D Q h t ∈ R d c ′ c_t^Q = W^{DQ} h_t \in \mathbb{R}^{d_c'} ctQ=WDQht∈Rdc′
第 i i i 个头的 Query 内容部分:
q t , i C = W i U Q c t Q ∈ R d h q_{t,i}^C = W_i^{UQ} c_t^Q \in \mathbb{R}^{d_h} qt,iC=WiUQctQ∈Rdh
第 i i i 个头的 Query RoPE 部分:
q t , i R = RoPE t ( W i Q R c t Q ) ∈ R d h R q_{t,i}^R = \operatorname{RoPE}_t\left(W_i^{QR} c_t^Q\right) \in \mathbb{R}^{d_h^R} qt,iR=RoPEt(WiQRctQ)∈RdhR
拼接得到该头最终 Query:
q t , i = q t , i C ; q t , i R ∈ R d h + d h R q_{t,i} = \left q_{t,i}\^C ;\\ q_{t,i}\^R \\right \in \mathbb{R}^{d_h+d_h^R} qt,i=qt,iC; qt,iR∈Rdh+dhR
2. Key/Value 的联合低秩压缩
对任意位置 j j j,先计算 K/V 共享的低维潜在向量:
c j K V = W D K V h j ∈ R d c c_j^{KV} = W^{DKV} h_j \in \mathbb{R}^{d_c} cjKV=WDKVhj∈Rdc
第 i i i 个头的 Key 内容部分:
k j , i C = W i U K c j K V ∈ R d h k_{j,i}^C = W_i^{UK} c_j^{KV} \in \mathbb{R}^{d_h} kj,iC=WiUKcjKV∈Rdh
所有头共享的 Key RoPE 部分:
k j R = RoPE j ( W K R h j ) ∈ R d h R k_j^R = \operatorname{RoPE}_j\left(W^{KR} h_j\right) \in \mathbb{R}^{d_h^R} kjR=RoPEj(WKRhj)∈RdhR
拼接得到第 i i i 个头的最终 Key:
k j , i = k j , i C ; k j R ∈ R d h + d h R k_{j,i} = \left k_{j,i}\^C ;\\ k_j\^R \\right \in \mathbb{R}^{d_h+d_h^R} kj,i=kj,iC; kjR∈Rdh+dhR
第 i i i 个头的 Value:
v j , i = W i U V c j K V ∈ R d h v_{j,i} = W_i^{UV} c_j^{KV} \in \mathbb{R}^{d_h} vj,i=WiUVcjKV∈Rdh
注意: k j , i C k_{j,i}^C kj,iC 不带 RoPE;RoPE 只作用于 k j R k_j^R kjR,且 k j R k_j^R kjR 在所有头之间共享。
3. 注意力分数
第 i i i 个头中,当前 token t t t 对历史 token j ≤ t j\le t j≤t 的缩放点积分数为:
s t , j , i = q t , i ⊤ k j , i d h + d h R s_{t,j,i}= \frac{ q_{t,i}^\top k_{j,i} }{ \sqrt{d_h+d_h^R} } st,j,i=dh+dhR qt,i⊤kj,i
展开为内容项与位置项:
s t , j , i = ( q t , i C ) ⊤ k j , i C + ( q t , i R ) ⊤ k j R d h + d h R s_{t,j,i}= \frac{ \left(q_{t,i}^C\right)^\top k_{j,i}^C + \left(q_{t,i}^R\right)^\top k_j^R }{ \sqrt{d_h+d_h^R} } st,j,i=dh+dhR (qt,iC)⊤kj,iC+(qt,iR)⊤kjR
因果掩码下:
s t , j , i = { 上式 , j ≤ t , − ∞ , j > t . s_{t,j,i} = \begin{cases} \text{上式}, & j\le t,\\ -\infty, & j>t. \end{cases} st,j,i={上式,−∞,j≤t,j>t.
4. 注意力权重与单头输出
第 i i i 个头的注意力权重:
α t , j , i = Softmax j ≤ t ( s t , j , i ) = exp ( s t , j , i ) ∑ m = 1 t exp ( s t , m , i ) \alpha_{t,j,i}= \operatorname{Softmax}{j\le t}\left(s{t,j,i}\right)= \frac{ \exp(s_{t,j,i}) }{ \sum_{m=1}^{t}\exp(s_{t,m,i}) } αt,j,i=Softmaxj≤t(st,j,i)=∑m=1texp(st,m,i)exp(st,j,i)
该头输出为对 Value 的加权和:
o t , i = ∑ j = 1 t α t , j , i v j , i ∈ R d h o_{t,i}= \sum_{j=1}^{t} \alpha_{t,j,i}\, v_{j,i} \in \mathbb{R}^{d_h} ot,i=j=1∑tαt,j,ivj,i∈Rdh
5. 多头拼接与输出投影
将所有 n h n_h nh 个头输出拼接:
o t = o t , 1 ; o t , 2 ; ... ; o t , n h ∈ R n h d h o_t = \left o_{t,1}; o_{t,2}; \\dots; o_{t,n_h} \\right \in \mathbb{R}^{n_h d_h} ot=ot,1;ot,2;...;ot,nh∈Rnhdh
最终该层注意力输出:
y t = W O o t y_t = W^O o_t yt=WOot
其中 W O W^O WO 为输出投影矩阵。
6. 推理时实际缓存的内容
推理时并不缓存完整的 k j , i k_{j,i} kj,i 和 v j , i v_{j,i} vj,i,而是缓存:
c j K V ∈ R d c , k j R ∈ R d h R \boxed{ c_j^{KV} \in \mathbb{R}^{d_c}, \qquad k_j^R \in \mathbb{R}^{d_h^R} } cjKV∈Rdc,kjR∈RdhR
即每个 token 每层只缓存一个共享低维潜在向量和一个共享 RoPE Key。需要时可由 c j K V c_j^{KV} cjKV 上投影得到 k j , i C k_{j,i}^C kj,iC 与 v j , i v_{j,i} vj,i。
利用矩阵吸收,内容分数可写成:
( q t , i C ) ⊤ k j , i C = ( q t , i C ) ⊤ W i U K c j K V = ( W i U K ⊤ q t , i C ) ⊤ c j K V \left(q_{t,i}^C\right)^\top k_{j,i}^C= \left(q_{t,i}^C\right)^\top W_i^{UK} c_j^{KV}= \left( W_i^{UK\top} q_{t,i}^C \right)^\top c_j^{KV} (qt,iC)⊤kj,iC=(qt,iC)⊤WiUKcjKV=(WiUK⊤qt,iC)⊤cjKV
因此推理时可直接用"吸收后的 Query"与缓存中的 c j K V c_j^{KV} cjKV 计算内容分数,避免显式重构高维 k j , i C k_{j,i}^C kj,iC。Value 侧同理可把 W i U V W_i^{UV} WiUV 吸收到输出投影中。
7. 单头公式汇总
c t Q = W D Q h t , q t , i C = W i U Q c t Q , q t , i R = RoPE t ( W i Q R c t Q ) , q t , i = q t , i C ; q t , i R , c j K V = W D K V h j , k j , i C = W i U K c j K V , k j R = RoPE j ( W K R h j ) , k j , i = k j , i C ; k j R , v j , i = W i U V c j K V , s t , j , i = q t , i ⊤ k j , i d h + d h R , α t , j , i = Softmax j ≤ t ( s t , j , i ) , o t , i = ∑ j = 1 t α t , j , i v j , i , y t = W O o t , 1 ; ... ; o t , n h . \begin{aligned} c_t^Q &= W^{DQ} h_t,\\ q_{t,i}^C &= W_i^{UQ} c_t^Q,\\ q_{t,i}^R &= \operatorname{RoPE}t(W_i^{QR} c_t^Q),\\ q{t,i} &= q_{t,i}\^C; q_{t,i}\^R,\\4pt c_j^{KV} &= W^{DKV} h_j,\\ k_{j,i}^C &= W_i^{UK} c_j^{KV},\\ k_j^R &= \operatorname{RoPE}j(W^{KR} h_j),\\ k{j,i} &= k_{j,i}\^C; k_j\^R,\\ v_{j,i} &= W_i^{UV} c_j^{KV},\\4pt s_{t,j,i} &= \frac{q_{t,i}^\top k_{j,i}}{\sqrt{d_h+d_h^R}},\\ \alpha_{t,j,i} &= \operatorname{Softmax}{j\le t}(s{t,j,i}),\\ o_{t,i} &= \sum_{j=1}^{t}\alpha_{t,j,i} v_{j,i},\\ y_t &= W^O o_{t,1};\\dots;o_{t,n_h}. \end{aligned} ctQqt,iCqt,iRqt,icjKVkj,iCkjRkj,ivj,ist,j,iαt,j,iot,iyt=WDQht,=WiUQctQ,=RoPEt(WiQRctQ),=qt,iC;qt,iR,=WDKVhj,=WiUKcjKV,=RoPEj(WKRhj),=kj,iC;kjR,=WiUVcjKV,=dh+dhR qt,i⊤kj,i,=Softmaxj≤t(st,j,i),=j=1∑tαt,j,ivj,i,=WOot,1;...;ot,nh.
这就是某一个头中 MLA 自注意力层的完整计算过程。核心结构是:Query 低秩压缩 + Key/Value 联合低秩压缩 + 解耦 RoPE + 因果 Softmax 加权 Value。