pydata: Huiming's learning notes

Keep Looking, Don't Settle

DeepSeek V3 MoE

MLA plot

1. Deepseek MoE 的结构

DeepSeek-V3 的 MoE 架构延续并优化了其特有的 DeepSeekMoE 设计,并引入了 无辅助损失的负载均衡(Auxiliary-loss-free Load Balancing 策略。

1. 整体结构公式

对于 Token \(t\) 层的输入向量 \(\mathbf{u}_t\)(即 Attention 层的输出),MoE 层的输出 \(\mathbf{h}_t^{\prime}\) 定义为:

$$\mathbf{h}_t^{\prime} = \mathbf{u}_t + \text{MoE}(\mathbf{u}_t)$$

其中 \(\text{MoE}(\mathbf{u}_t)\)共享专家(Shared Experts路由专家(Routed Experts 两部分组成:

$$\text{MoE}(\mathbf{u}_t) = \sum_{i=1}^{N_s} \text{FFN}_i^{(s)}(\mathbf{u}_t) + \sum_{i=1}^{N_r} g_{i,t} \text{FFN}_{i}^{r}(\mathbf{u}_t)$$
  • \(N_s\):共享专家的数量。
  • \(N_r\):路由专家的数量, \(=256\)
  • \(g_{i,t}\):第 \(i\) 个激活专家的门控增益(Gating Value)。只有\(K_r\)个激活专家。
  • \(\text{FFN}(\cdot)\):标准的 Feed-Forward Network。
  • \(K_r\):每个 token 激活的路由专家数量(Top-K)。

2. 专家内部结构 (FFN)

DeepSeek-V3 采用 SwiGLU 作为专家内部的激活函数。对于任意专家 \(i\),其计算公式为:

$$ \text{FFN}(\mathbf{u}) =\mathbf{W}_{down} \left(\text{SiLU} \left(\mathbf{W}_{up} \mathbf{u}\right) \odot ( \mathbf{W}_{gate} \mathbf{u} ) \right) \in \mathbb{R}^{d} = \mathbb{R}^{7168} $$

维度定义: * 输入向量 \(\mathbf{u} \in \mathbb{R}^{d}\),其中 \(d = 7168\)。 * \(\mathbf{W}_{up}, \mathbf{W}_{gate} \in \mathbb{R}^{d_{inter} \times d}\):升维矩阵。 * 对于路由专家,\(d_{inter} = 2048\) which is the moe_intermediate_size。 * 对于共享专家,\(d_{inter} = 2048\)(V3 中共享专家维度与路由专家一致,但始终激活)。 * \(\mathbf{W}_{down} \in \mathbb{R}^{d \times d_{inter}}\):降维矩阵。

这里 \(\odot\) 是两个维度为2048的向量的逐个元素相乘。

3. 路由与门控机制 (Routing & Gating)

DeepSeek-V3 采用了 无辅助损失(Aux-loss-free) 的负载均衡方案。

(1) 计算亲和力分数 (Affinity Score)

对于 token \(t\),计算其对第 \(i\) 个路由专家的亲和力:

$$s_{i,t} = \text{Sigmoid}(\mathbf{u}_t^\top \mathbf{e}_i)$$

其中,\(\mathbf{e}_i \in \mathbb{R}^{d}\) 是第 \(i\) 个专家的质心向量(Centroid)。

对所有的专家,他就等价于跟一个 \(\mathbf{u}_t\) 跟一个 Centroid 矩阵 \(\mathbf{W}_{route}\)相乘,\([\mathbf{s}_{1,t}; \mathbf{s}_{2,t}; \cdots ; \mathbf{s}_{N_r,t}] = \mathbf{u}_t \mathbf{W}_{route}\)

这里 \(\mathbf{W}_{route} \in \mathbb{R}^{d \times N_r} = \mathbb{R}^{7168 \times 256}\).

(2) 引入偏置项 (Load Balancing Bias)

为了实现负载均衡而不损害性能,模型在推理和训练中维护一个动态偏置 \(b_i\)

$$s'_{i,t} = s_{i,t} + b_i$$

* 如果专家 \(i\) 过载,算法会减小 \(b_i\); * 如果专家 \(i\) 空闲,算法会增大 \(b_i\)

(3) Top-K 选择与归一化

选择 \(s'_{i,t}\) 最高的 \(K_r = 8\) 个专家。最终的门控值 \(g_{i,t}\) 是通过对原始 \(s_{i,t}\) 进行归一化得到的:

$$g_{i,t} = \frac{s_{i,t}}{\sum_{j \in \text{TopK}} s_{j,t}}$$

注意:计算 \(g_{i,t}\) 时使用的是不含偏置的 \(s_{i,t}\),以保证数学上的纯净性。

4. 关键维度与参数汇总

根据官方参数设置,DeepSeek-V3 的 MoE 维度如下:

参数项 符号 数值 备注
模型隐藏层维度    \(d\) 7168 输入/输出向量维度
路由专家总数    \(N_r\) 256 总共有 256 个可选专家
激活专家数    \(K_r\) 8 每个 token 选 8 个专家
共享专家数    \(N_s\) 1 始终激活的专家
专家中间层维度       \(d_{inter}\)     2048 SwiGLU 升维后的维度
专家质心矩阵    \(\mathbf{E}\) \(256 \times 7168\)     用于计算路由分数
激活参数量    - 37B 每次推理实际调用的参数
总参数量    - 671B 包含所有专家矩阵

5. 总结:DeepSeekMoE 的精髓

  1. 细粒度专家(Fine-Grained Experts):\(d_{inter}\) 设为 2048(远小于传统 MoE 的 8192 或更高),通过增加专家数量 \(N_r\) 来提高知识的覆盖面和专业化程度。
  2. 共享专家(Shared Experts): 通过 \(N_s\) 捕获公共知识,减少路由专家之间的信息冗余。
  3. Aux-loss-free 策略: 传统的辅助损失(如 Switch Transformer 的 load balance loss)会约束路由分布,导致性能下降。DeepSeek-V3 通过调节偏置 \(b_i\) 来平衡流量,使得 Loss 专注于预测准确性,从而在 671B 规模下依然保持极高的性能。

2. DeepSeek MoE 的参数数量

1. FFN 变换矩阵的参数量 (单层)

对于每一个专家(无论是共享还是路由),它都包含三个主要的变换矩阵(基于 SwiGLU 结构): * \(\mathbf{W}_{up}\): \(d_{inter} \times d = 2048 \times 7168\) * \(\mathbf{W}_{gate}\): \(d_{inter} \times d = 2048 \times 7168\) * \(\mathbf{W}_{down}\): \(d \times d_{inter} = 7168 \times 2048\)

根据 DeepSeek-V3 的参数设置: * \(d = 7168\) * \(d_{inter} = 2048\) * 共享专家数 \(N_s = 1\) * 路由专家总数 \(N_r = 256\)

单个专家的 FFN 参数量为:

$$(7168 \times 2048) + (7168 \times 2048) + (2048 \times 7168) = 3 \times (7168 \times 2048) \approx 44.04 \text{ M}$$

所有专家(1 个共享 + 256 个路由)的 FFN 总参数量为:

$$3 \times (7168 \times 2048) \times (1 + 256) \approx 11.32 \text{ B (每层)}$$

2. 路由中心(Router Centroids)(单层)

除了 FFN 矩阵,MoE 层还有一个关键组件:Router。 它需要一套参数来决定每个 token 该去哪个专家。DeepSeek 使用的是向量内积来计算亲和力,因此每个路由专家都有一个对应的“质心”向量 \(\mathbf{e}_i\)。对所有的256个路由专家,就是上面的 \(\mathbf{W}_{route}\)

  • \(\mathbf{W}_{route}\)路由参数量 = 专家数 \(\times\) 模型维度 = \(256 \times 7168 \approx 1.83 \text{ M (每层)}\)

虽然这部分参数相比 FFN 矩阵非常小,但在严谨的参数统计中是独立存在的。

3. 完整的 MoE 层参数公式(单层)

完整 MoE 层的总参数量(不含 Attention 部分),公式应该是:

$$\text{Params}_{\text{MoE}} = \underbrace{3 \times (d \times d_{inter}) \times (N_s + N_r)}_{\text{FFN 变换矩阵}} + \underbrace{(N_r \times d)}_{\text{Router 质心}}$$

代入数值:

$$\text{Params}_{\text{MoE}} \approx 11,318,341,632 + 1,835,008 \approx 11.32 \text{ B}$$

4. MoE 总参数量

DeepSeek-V3 一共有 58 层 的MoE。简单相乘,得到 MoE的总参数为:\(58 \text{ 层} \times 11.32 \text{ B/层} \approx 656.6 \text{ B}\)

DeepSeek 官方公布的总参数量是 671B,可以看出模型的主要参数都是在MoE部分。 余下来的差别来自于: 1. MLA(注意力机制)层也会占用一部分参数。 2. Embedding 层(约 0.9B)和输出层。 3. 实际上,DeepSeek 在 \(671\)B 的总规模下,对某些维度进行了微调,以确保模型在 37B 激活参数的情况下达到最优性能平衡。