DeepSeekMoE:细粒度专家分割 + 共享专家隔离 + Sigmoid Top-K 路由 + 无辅助损失负载均衡。
MoELayer
01 Syntax
02 Methods
| Name | Overloads | Summary |
|---|---|---|
| .ctor | 1 | |
| RegisterParameters | 1 | 把本层参数登记进参数集。 |
| Forward | 1 | 前向:h = Σ 共享专家(u) + Σ_{Top-K} g_i · 路由专家_i(u)(不含残差)。 |
| ApplyNodeLimitedRouting | 1 | 把 -inf 写入"不在候选节点内"的专家,实现节点受限路由。 |
| BuildBuckets | 1 | 把每个 token 命中的专家整理成"每个专家对应一批 token 行下标"。 |
| GateWeightOf | 1 | 查某个 token 在某个专家上的门控权重(同一 token 的 topK 内专家互不重复)。 |
| Backward | 1 | 反向传播:返回对输入的梯度,并累加路由器与全部专家的参数梯度。 |
| UpdateBalanceBias | 1 | 按 b_k ← b_k + ε_k · (L − A_k)(ε_k = u / |L − A_k|)更新选择偏置。 |
| ResetLoadStatistics | 1 | 清空累积的负载统计(不影响 MoELayer.BalanceBias)。 |
| LifetimeLoad | 1 | 累计至今各路由专家的负载占比(用于在训练结束时展示"负载是否被拉平")。 |
| ResetLifetimeLoad | 1 | 把历史负载统计清零。 |
| ZeroGradients | 1 | 清零路由器与全部专家的梯度累加器。 |
| MakeTrainingStep | 1 | 按 AdamW 规则更新路由器与全部专家。 |
03 Properties
| Name | Overloads | Summary |
|---|---|---|
| LastCache | 1 | 最近一次 MoELayer.Forward()) 的中间量缓存。 |
| LastRouteInfo | 1 | 最近一次 MoELayer.Forward()) 的路由统计。 |
| NumRoutedExperts | 1 | 路由专家个数。 |
| NumSharedExperts | 1 | 共享专家个数。 |
| TopK | 1 | 每个 token 激活的路由专家数。 |
| TotalParameters | 1 | 本层的总参数(全部专家 + 路由器),也就是"知识容量"。 |
| ActiveParametersPerToken | 1 | 单个 token 实际激活的参数个数。 |
04 Fields
| Name | Overloads | Summary |
|---|---|---|
| _experts | 1 | 路由专家(细粒度小专家)。 |
| _sharedExperts | 1 | 共享专家(每个 token 无条件激活)。 |
| Wr | 2 | 路由器权重,形状 [dModel, nRoutedExperts]。 |
| BalanceBias | 2 | 路由专家的动态选择偏置 b_i(长度 nRoutedExperts)。 |
| _loadCounts | 1 | 自上次偏置更新以来累积的专家命中次数。 |
| _loadTotal | 1 | 自上次偏置更新以来累积的"总分配次数"(token 数 × topK)。 |
| _lifetimeCounts | 1 | 路由专家被选中的历史总次数(用于展示负载是否趋于均匀)。 |
05 Members
Int32, Int32, Int32, Int32, Int32, Int32, Int32, Double)| Name | Type | Description |
|---|---|---|
dModel | Int32 | 输入输出宽度 |
expertHidden | Int32 | 单个专家的中间层宽度(细粒度即该值较小) |
nRoutedExperts | Int32 | 路由专家个数(细粒度分割后的专家数 mN) |
topK | Int32 | 每个 token 激活的路由专家数 mK |
nSharedExperts | Int32 | 共享专家个数 K_s;传 0 表示不使用共享专家 |
nodeGroups | Int32 | 把路由专家划分成的节点组数;<= 1 表示不做节点受限路由 |
maxNodesPerToken | Int32 | 每个 token 最多使用的节点数;<= 0 表示不限制 |
biasUpdateRate | Double | 负载均衡偏置的更新步长 u |
把本层参数登记进参数集。
Tensor)前向:h = Σ 共享专家(u) + Σ_{Top-K} g_i · 路由专家_i(u)(不含残差)。
| Name | Type | Description |
|---|---|---|
x | Tensor | 输入 |
Double(), Int32, Int32())把 -inf 写入"不在候选节点内"的专家,实现节点受限路由。
Int32, Int32())把每个 token 命中的专家整理成"每个专家对应一批 token 行下标"。
Int32(), Double(), Int32, Int32)查某个 token 在某个专家上的门控权重(同一 token 的 topK 内专家互不重复)。
Tensor)反向传播:返回对输入的梯度,并累加路由器与全部专家的参数梯度。
| Name | Type | Description |
|---|---|---|
forwardCache | Cache | 与该次前向对应的缓存快照 |
dOut | Tensor | 对前向输出的梯度(形状同输入) |
按 b_k ← b_k + ε_k · (L − A_k)(ε_k = u / |L − A_k|)更新选择偏置。
该式等价于 b_k ← b_k + u · sign(L − A_k):负载过重的专家被调低偏置、 过轻的被调高,且因为使用固定步长 u 做"符号更新",一步即可逼近均衡, 同时完全不影响梯度方向(偏置不参与反向传播)。 应在每个训练步结束时调用一次。
更新前的最大负载比(1.0 表示完全均匀),便于观测收敛过程。
清空累积的负载统计(不影响 MoELayer.BalanceBias)。
累计至今各路由专家的负载占比(用于在训练结束时展示"负载是否被拉平")。
把历史负载统计清零。
清零路由器与全部专家的梯度累加器。
Double, Int32)按 AdamW 规则更新路由器与全部专家。
最近一次 MoELayer.Forward() 的中间量缓存。
最近一次 MoELayer.Forward() 的路由统计。
路由专家个数。
每个 token 激活的路由专家数。
本层的总参数(全部专家 + 路由器),也就是"知识容量"。
单个 token 实际激活的参数个数。
共享专家无条件激活,路由专家只激活 Top-K 个 —— 它的量与 MoELayer.TotalParameters 的比值就是本层的"激活率"。
路由专家(细粒度小专家)。
路由器权重,形状 [dModel, nRoutedExperts]。
路由专家的动态选择偏置 b_i(长度 nRoutedExperts)。
它不是神经网络参数:不接收梯度、不被优化器更新,只由 MoELayer.UpdateBalanceBias() 按负载统计直接调整。
自上次偏置更新以来累积的专家命中次数。
自上次偏置更新以来累积的"总分配次数"(token 数 × topK)。
路由专家被选中的历史总次数(用于展示负载是否趋于均匀)。