
你以为“激活参数更少”就一定意味着“更省显存、更好部署”?在 MoE 上,这个直觉往往会把人带沟里。很多团队上来就看激活参数和 FLOPs,却忽略了真正吃掉 GPU 的,是常驻权重、KV 缓存和跨 GPU 的激活搬运。理解这一点,才算真正踏进 MoE 推理工程的门槛。
一个密集型 Transformer 会对每个 token 应用同一个前馈网络(FFN),批处理只是在输入矩阵上扩了行数,权重矩阵本身不变。
MoE 层把这一个 FFN 换成了多个“专家”FFN,由一个路由器为每个 token 打分、挑选要执行的专家。
被选中的少数专家对该 token 做计算,没被选中的专家在这一层就完全闲置。

这样做显著减少了每个 token 实际执行的专家计算量,但路由器在推理时并不知道下一个 token 会选谁,所以服务系统必须让“所有可能被选中的专家”都常驻可用。

以 Qwen3-30B-A3B 为例,模型卡给出的总参数约 305 亿,激活参数约 33 亿,每个 token 从 128 个路由专家里选 8 个。
服务器侧仍然要保证完整模型可用:注意力权重、嵌入、所有专家权重、临时缓冲区,以及不断增长的 KV 缓存。
33 亿激活参数大致刻画了单个 token 的活跃计算路径,却既不等于部署时的显存占用,也不等于专家权重本身的大小。

激活参数更像是“单 token 执行路径的长度”,常驻参数才是“你必须一直塞在 GPU 里的权重体积”。
接下来,我们沿着一个 token 的完整服务路径,看看路由器如何选专家、运行时如何把 token 重新打包成专家批次、本地内核如何执行、激活又是怎么在多块 GPU 间来回穿梭的。
后面还会聊到:专家放置策略、负载不均衡和容量限制、量化对 MoE 的特殊影响,以及如何用测量拆出“算力瓶颈、内存带宽瓶颈、互联瓶颈”各自的位置。

如果你想从代码层彻底理解 MoE,可以配合这篇教程一起看:
从零开始构建基于MoE的Transformer,我们用纯Python和PyTorch讲解了LLaMA 4 →
教程里会一步步实现字符级分词、多头自注意力 + RoPE、稀疏路由的多专家 MLP、RMSNorm、残差与因果掩码,再到训练和生成,把论文里的概念全部落到代码上。
MoE 层结构与 token 路由
路由器如何改写前馈路径
在一个标准 Transformer 层里,先是注意力,再是前馈网络:注意力在不同 token 位置之间混合信息,前馈网络则对每个位置独立做非线性变换。
MoE 层把这一层的单个 FFN 换成了多个“专家 FFN”,每个专家都是一套独立的前馈权重。
注意力部分依旧对所有 token 全量执行,只有前馈这条支路被拆成了“路由 + 多专家”。
所以,稀疏的是专家激活路径,而不是注意力路径——注意力 FLOPs 一点没少。


想象一个只有 4 个专家的玩具 MoE 层:路由器对某个 token 计算出 4 个得分,然后选出得分最高的 2 个专家。
这两个专家分别对同一个 token 向量做前馈变换,层的输出是这两个结果的加权和,权重由路由器给出的路由权重决定。
数学视角:只对选中专家求和
加权专家输出可以写成:

其中:
- x 是 token 的输入向量
- S(x) 是被选中的专家集合
- E_e 表示专家 e,w_e(x) 是该专家的路由权重
- y 是 MoE 层的输出向量
求和只在 S(x) 这个子集上进行,每个专家对同一个输入向量做变换,再用路由权重缩放后相加。

不同架构对 w_e(x) 的归一化方式不一样,有的 softmax,有的会加温度或做截断。

需要注意的是:token→专家的路由发生在每一层 MoE 内部,这和“应用层模型路由”(给整个请求选一个 LLM 副本)完全是两件事。
玩具例子里用的是 top-2 路由,方便讲清楚流程;Qwen3-30B-A3B 则是每个 token 从 128 个专家里选 8 个,模型卡显示总共有 48 层。
有些架构会额外引入“共享专家”,这些专家不看路由得分,对所有 token 都执行一次,比如 DeepSeekMoE 就在路由专家旁边挂了共享专家。
共享专家是“全量执行”,路由专家是“条件执行”,只有模型本身设计了共享专家,这种划分才存在。
所以不要想当然地认为“所有 MoE 模型都有共享专家”,要先看模型结构和服务实现。

路由器改变的是每个 MoE 层内部的前馈计算路径,批量推理时,服务引擎要把“token→专家”的离散决策,调度成一堆形状各异的专家矩阵。
批次如何变成专家专用矩阵
从 token-专家分配到专家批次
路由器对每个 token 单独做决策,但 GPU 不擅长一次只跑一个小矩阵。
所以运行时要把这些“零散的 token-专家分配”重新打包成更大的专家专用矩阵,才能把 GPU 吃满。
假设有 4 个 token 进入一个 4 专家 MoE 层,使用 top-2 路由,路由结果是:
- T1 → E1(0.7), E3(0.3)
- T2 → E1(0.4), E3(0.6)
- T3 → E1(0.7), E2(0.3)
- T4 → E2(0.8), E4(0.2)
运行时此时手里有 8 条“分配记录”,每条记录都包含:token 向量、选中专家 ID 和路由权重。

接下来运行时按专家分组:

- E1 接收 T1、T2、T3 → 矩阵行数为 3
- E2 接收 T3、T4 → 行数为 2
- E3 接收 T1、T2 → 行数为 2
- E4 接收 T4 → 行数为 1
每个专家对自己的矩阵做一次前馈计算,运行时会在每一行上保留原始 token ID,方便之后把结果拼回去。
然后:
- T1 从 E1 和 E3 各拿到一个输出
- 运行时分别乘以 0.7 和 0.3
- 再相加得到 T1 的最终专家输出
- 其他 token 也是同样的流程
这个“按专家分组”的过程叫 dispatch,“把专家输出按 token 拼回去”的过程叫 combine。
单卡上这两个步骤都在本地完成,多卡时就会牵扯到网络传输。
预填充 vs 解码:专家矩阵大小的波动
专家批次的“大小”数的是分配条数,而不是唯一 token 数。
在 top-2 路由下,每个 token 会贡献两行矩阵;top-8 就是 8 行。
- 预填充(prefill)阶段,一次会处理很多 prompt token,专家矩阵行数通常比较多,GPU 利用率也更好。
- 解码(decode)阶段,每一步只新增一个 token,如果并发不高,专家矩阵行数会很小,光是启动矩阵核的开销就很可观。
高并发时,分配条数变多,可以用 grouped GEMM 把多个专家矩阵打包成一次大调用,既减少 kernel 启动次数,又允许每个专家的行数不同。

有些引擎会把专家矩阵填充到固定行数,方便调度和 kernel 复用,但代价是会对“空行”做无用计算。

预填充和解码的性能都不是常数:prompt 长度、请求数量会直接改变专家矩阵的形状;隐藏层宽度和硬件特性又决定了矩阵乘法本身的效率。

Grouped GEMM 能把已有的专家工作打包起来,减少 kernel 启动次数,但它没法凭空创造工作量——如果某个专家本来就没 token,它还是会闲着。
可以先记住一个认知:路由产生的是“分配记录”,dispatch 把它们变成专家矩阵,combine 再把结果还原到 token 维度。
本地 MoE 执行流水线
路由、置换、专家计算、逆置换
MoE 层在本地执行时,不只是做几次专家矩阵乘法那么简单。

完整流水线大致包括:选择专家、重排 token 行、执行专家 FFN、恢复 token 顺序,每一步都可能成为瓶颈。
路由器先为每个 token 计算所有候选专家的得分,然后做一次 top-k,保留被选中专家的索引和对应权重。
运行时接着要把 token 行重排,让每个专家看到的是一块连续的矩阵,这样矩阵乘法才高效。
这个重排过程叫 permutation:从原始 token 矩阵里按专家顺序读出行,写入新的专家矩阵,同时记录一份“逆映射”,供 combine 阶段用来还原顺序。
在小批量解码场景下,移动这些行的时间有时会和矩阵乘法本身差不多长,说实话,这一点经常被忽略。
Grouped GEMM 会在这一阶段一次性启动多个专家矩阵的计算,允许每个矩阵行数不同,从而减少 kernel 启动次数。
专家 FFN 完成后,运行时用逆映射把结果写回原始 token 顺序,再应用路由权重,对同一个 token ID 的多路专家输出做加权累加。


Grouped GEMM 本身并不会减少分配条数,它只是把多次小矩阵乘法合并成一次大调用。
融合与激活量化:省的是带宽和启动
核融合会把多个操作合并到一个 kernel 里执行,避免中间激活频繁写回 GPU 全局内存,从而减少内存带宽消耗和 kernel 启动开销。
不过融合通常有不少限制:可能只支持特定的数据类型、批次形状或量化格式,一旦不满足条件,就得退回到更模块化的执行路径。
激活量化是另一种常见优化:
- 可以在 dispatch 之前先把激活量化,减少跨 GPU 传输的字节数
- 也可以先用高精度 dispatch,再在本地量化,节省转换开销

解码阶段的专家矩阵通常很小,这时权重读取、行移动和 kernel 启动都可能成为主导开销;预填充阶段行数多,矩阵乘法效率反而更关键。

融合路径会把这些操作的边界“抹平”,但不会改变专家批次大小,也不会改变路由选择或分配条数。
如果只用一个“MoE 层总耗时”的计时器,很难看出瓶颈到底在路由、置换、专家 GEMM 还是 combine 上,工程上最好把每个阶段单独打点。
模型权重常驻与运行时内存
总参数、激活参数、常驻参数的区别
MoE 部署时,常会看到三种参数计数方式:
- 总参数:描述 checkpoint 的完整大小
- 激活参数:描述单个 token 的执行路径长度
- 常驻参数:描述当前服务系统里“常驻在设备上”的权重
三者完全没必要相等。
某个 token 只会触发少数几个专家,另一个 token 可能触发完全不同的一组专家,但所有这些专家的权重都得事先放在某些设备上。
在“完全常驻”的部署里,所有可选专家都常驻在 GPU 上,注意力、嵌入、归一化和输出层权重也一样一个都不能少,稀疏路由并不会减少这些权重。
以 Qwen3-30B-A3B 为例:总参数约 305 亿,如果用 2 字节/参数存储,光权重就要大约 61 GB 显存。

权重存储的理论下界可以写成:

其中:
- B(weights) 是权重存储的字节数
- P 是总参数个数
- B(stored) 是每个参数占用的字节数
把 P 和 B(stored) 相乘,就能得到权重的理论下界;对 Qwen 来说,这个数字大约是 61 GB,而且这还没算任何运行时开销。
部署时还需要:
- 临时激活和通信缓冲区
- 内存分配器的预留空间
- 存储活跃序列注意力状态的 KV 缓存
这些都不在那 61 GB 的估算里。


所以不要用“33 亿激活参数”去替代“305 亿总参数”估算权重显存,激活参数只告诉你单 token 的计算量,和 checkpoint 的字节数没直接关系。
分片、量化与专家卸载
权重量化会减少 B(stored),比如从 2 字节/参数降到 1 字节/参数,但 P 不变。
分片会把权重分布到多块 GPU 上,同样不改变参数总数,只是把常驻参数拆开存。
稀疏激活减少的是专家计算量,而不是权重存储量,所以很多 MoE 模型依然需要做权重分片。
专家卸载则是另一条路:把部分专家权重放在 CPU 内存里,只有当路由选中这些专家时,才把权重搬到 GPU 上。
这样可以节省 GPU 显存,但会引入额外的传输延迟。
如果某个 token 选中了一个“当前不在 GPU 上”的专家,解码就会被阻塞,缓存可以缓解重复访问的专家,但预取只有在你能大致预测未来路由时才有用。


需要多少块 GPU 没有固定答案:精度、分片策略、预留内存比例、KV 缓存容量都会改变需求。
可以把这部分的核心认知记成一句话:激活参数估的是“算多少”,常驻参数决定“能不能装下”,分片解决“装不下”的问题,但路由激活可能会跨 GPU 乱跑。
专家并行的 dispatch 与 combine
专家并行:权重不动,激活在动
专家并行的思路是:把不同专家分配到不同 GPU 上,让每块 GPU 只存自己负责的那部分专家权重。
假设有 4 个专家分布在 2 块 GPU 上:GPU A 上有 E1 和 E2,GPU B 上有 E3 和 E4。
某个 token 起始在 GPU A 上,路由结果选中了 E1 和 E3。
- 分配到 E1 的那条记录留在 GPU A 本地
- 分配到 E3 的那条记录需要通过网络发到 GPU B
网络消息里会包含 token 向量和一小段元数据:token ID、目标专家 ID、源 GPU ID、路由权重等,目标 GPU 用这些信息在计算完成后把结果正确地发回源 GPU。

每个 GPU 会把本地和远程收到的分配按专家分组,对每个活跃的本地专家执行一次矩阵乘法,然后把结果发回源 GPU。
源 GPU 再用之前保存的 ID 恢复 token 顺序,应用路由权重并累加,就完成了 combine。

多 GPU 时,每块 GPU 可能要同时向多个对等 GPU 发送分配,形成一种“全互联”的通信模式,combine 阶段则在计算后把专家结果再发回源头。
专家权重通常不会在网络上来回搬,网络传输的是 token 激活和元数据,填充和临时缓冲会额外增加一些字节。
激活传输量的上界估算
激活传输的上界可以粗略估成:


其中:
- B(dispatch) 是激活传输的字节数
- T 是进入该 MoE 层的 token 数
- K 是每个 token 选中的专家数
- H 是隐藏层宽度
- b 是每个激活值的字节数
这个乘积相当于“每条分配都带着一个完整的隐藏向量”时的上界,还没考虑任何局部性优化。
留在同一 GPU 的分配不会消耗网络带宽,实际流量取决于“远程分配”的数量,元数据和填充会再多出一截,combine 阶段的返回流量也要算进去。
这个估算只覆盖激活负载,不含协议开销,也没法直接推断延迟,要结合拓扑和消息大小做实测。
每一层 MoE 都会重复这样的交换,哪怕单次延迟很小,累计起来也会很可观,独立的注意力计算可以部分掩盖这段延迟。
DeepEP 这类库会为“大批量”和“小批量”通信分别提供不同路径:大批量追求吞吐,小批量追求低延迟。
需要提醒一点:核带宽和端到端服务速度不是一回事,一个完整请求还包括注意力、调度、采样、缓存管理以及所有非 MoE 层。

网络拓扑与专家放置
NVLink、InfiniBand 与“谁离谁更近”
不同 GPU 之间的链路成本差异很大:
- 同一服务器内可能通过 NVLink 或 NVSwitch 互联
- 跨服务器则通常走 InfiniBand 或高速以太网
跨服务器传输的延迟和带宽成本往往远高于机内传输。
专家放置策略决定了每个专家常驻在哪块 GPU 上,也就决定了路由时跨慢链路的频率。
常见的一种布局是:尽量把“高频被选中的专家”放在同一个 NVLink 域内,减少跨节点流量。
另一种布局则会把不同层分布到不同服务器上,具体边界要结合模型结构和集群拓扑来选。

放置改变的是“物理目标 GPU”,不会改变路由器的输出,只要逻辑专家 ID 不变,模型行为就保持一致。

高频专家可以被“搬近” token 的源 GPU,运行时也可以为某些逻辑专家创建多个物理副本,进一步减少跨节点流量。
节点限制路由与放置优化的边界
有些模型在训练阶段就引入了“节点限制路由”,比如 DeepSeek-V3:每个 token 只能访问某几个节点上的专家,这个约束是写进训练架构里的。
如果在训练之后再强行加类似限制,就会改变被选中的专家集合,模型质量也会跟着变,这已经不再是“纯工程优化”。
放置优化需要两类信息:
- 路由轨迹:每个专家在真实流量下的工作量和共现关系
- 拓扑测量:不同 GPU 之间的通信成本
只看“专家热度”是不够的:把所有热门专家堆在一起,可能会让某台服务器过载;把专家均匀摊开,又会增加跨服务器流量。
复制专家只有在 GPU 内存还有富余时才有意义,否则会直接挤占 KV 缓存空间。
按通信域(机内、跨机、跨机架等)分别测量字节数和时间,比只看集群总流量要有用得多。

张量、专家与数据并行执行布局
三种并行方式怎么拆工作
MoE 部署里常见的三种并行布局:
- 张量并行(TP):把单个大矩阵拆成多块,每块 GPU 只算一部分
- 专家并行(EP):把不同专家分配给不同 GPU
- 数据并行(DP):把不同请求分配给不同副本
张量并行会把权重矩阵按列或按行分片,每块 GPU 计算部分结果,再通过 all-reduce 或 all-gather 把结果拼起来。

专家并行则是把完整的专家(或专家分片)分布到不同 GPU 上,token 激活根据路由结果在这些 GPU 间移动。
数据并行会复制一整套模型权重,把不同请求分配到不同副本上,有些引擎会在 DP 组内复制注意力组件,而把专家分布在更大的 EP 组里。
真实部署通常会混合使用这些布局:注意力层和专家层甚至可以用完全不同的 GPU 分组方式,一个标签很难概括整个拓扑。
vLLM 的一个混合示例
举个 vLLM 文档里的例子:

- 张量并行大小 TP = 2
- 数据并行大小 DP = 4
注意力层在 4 个 DP 组中分别运行,每个组内有 2 块 GPU 做张量并行。
专家层则把所有 8 块 GPU 组成一个大的 EP 组,统一做专家并行。

在这个配置下:
- TP=2,DP=4 产生 4 个注意力组,每组 2 个 rank
- 专家层用全部 8 个 rank 组成一个 EP 组

不同引擎对通信组的划分方式不完全一样,但大体思路类似:
- 张量并行在矩阵内部通信
- 专家并行在路由激活上通信
- 数据并行复制组件,占用更多内存
低并发时,专家并行的通信效率会变差,弱互联拓扑下尤为明显;张量并行在集体通信性能不佳时也会被拖慢。

专家负载不均衡与 rank 级尾延迟
一个热门专家拖慢一整层
路由器几乎不可能把工作完美均匀地分配给所有专家。
假设有一个专家接收了整层一半的分配,那它所在的 GPU 上矩阵就会比其他 GPU 大得多。
其他 GPU 可能很快就算完了,接下来只能干等这个“热门专家”所在的 GPU 把活干完。
MoE 层的延迟由最慢的那块 GPU 决定,这就是 rank 级尾延迟。

训练阶段可以通过损失项鼓励路由更均衡,但线上真实流量往往和训练数据分布差别很大:语言、任务类型、prompt 模板都会影响专家选择。
专家不均衡是“单个模型副本内部”的问题,而请求不均衡是“不同副本之间”的问题,两者需要完全不同的测量和治理手段。

日常的平均直方图很容易掩盖掉“偶发的极慢一步”,要想看清楚尾延迟,需要按“每一步、每块 GPU”去打点。
放置与复制:不改路由,只改物理位置
可以记录每一步的:
- 分配条数
- dispatch 时间
- 专家执行时间
- combine 时间
- GPU 空闲时间
这些指标能帮你定位尾延迟到底卡在 dispatch、专家计算,还是 combine 上。
放置优化可以把“经常一起被选中的专家”拆开,分散到不同 GPU 上,或者把热点专家迁移到负载更轻的 GPU 上,路由决策本身不变。
复制则是为某个逻辑专家创建多个物理副本,路由仍然只看到一个逻辑专家,运行时在多个副本之间做负载均衡。

复制会占用更多 GPU 内存,直接挤压 KV 缓存空间;频繁移动专家也会消耗带宽,而且很多流量峰值在你刚重平衡完就已经过去了。

有些运行时会根据近期负载自动重平衡专家,并支持冗余副本,但前提是有足够内存,且负载模式相对稳定。
👉 重平衡改变的是专家的物理位置,不改变逻辑专家的选择和输出,这一点对“模型正确性”非常关键。
推理优化与模型正确性边界
哪些优化“只改执行”,哪些会“改结果”
MoE 优化大致可以分成两类:
- 只改变执行方式,不改变逻辑计算内容
- 直接改变数值或计算选择
融合、grouped GEMM、专家放置、通信重叠等,都属于第一类:它们保持专家集合和路由权重不变,只是换了一种更高效的执行方式,可能会有微小的浮点差异,但逻辑上是等价的。
量化则会改变权重存储或激活传输的数值精度,减少内存和网络流量的同时,引入量化误差,需要在目标负载上做质量评估。
减少 top-k 则更激进:
- 直接减少被执行的专家数量
- 移除部分专家的贡献
- 重新归一化剩余权重


假设模型原本每个 token 选 8 个专家,运行时只执行其中 4 个,再用 4 个专家的得分做归一化,输出的专家函数集合和组合尺度都会发生变化。
论文《Training-Free Halving of Activated Experts》专门研究了这一点:在 Qwen3.6-35B-A3B 上,直接把 8 个专家减到 4 个,MMLU 掉了 4.65 分;如果用更大的参考集做归一化,质量损失可以压到 0.35 分左右,同时把专家计算量减半。
同样的方法在 Qwen3.5-397B-A17B 上,从 10 个专家减到 5 个,MMLU 损失约 0.55 分,作者还发现困惑度和任务准确率偏好的设置并不完全一致。
这些结果都高度依赖具体 checkpoint 和评估集,不能直接当成通用规则照搬,我也不太确定在所有中文业务场景下会有同样的结论。
专家并行只改变“在哪块 GPU 上算”,减少 top-k 则改变“算哪些专家”,两者在模型正确性上完全不是一类优化。
容量限制与丢弃规则的风险
有些训练系统会在训练时对每个专家的 token 数做容量限制,超出的 token 会被丢弃或重路由。
部分推理运行时也会实现类似的容量限制,用来避免某个专家在极端情况下被打爆。

MoE 推理本身不会“自动丢弃忙碌专家的 token”,任何容量或丢弃规则都属于特定模型或运行时的行为,要先确认实现细节。
一个比较稳妥的顺序是:
- 先做不改变计算内容的优化(融合、grouped GEMM、放置、通信重叠)
- 再尝试量化,做性能 + 质量双重测试
- 最后才考虑 top-k 变更,并做输出对比和任务级评估
基准设计与瓶颈诊断
有效基准:一次只改一个变量
一个靠谱的 MoE 基准,应该尽量做到“每次只改变一个服务决策”。
保持 checkpoint、精度、prompt、输出长度限制和请求到达模式不变,逐步调整并行布局或 kernel 路径,才能看清楚每个改动的真实影响。
可以从最小可行布局开始,先在单机内测清楚,再逐步扩展到跨服务器的专家并行。
内存容量可能会限制某些配置能否加载模型,后端实现也可能不支持某些 kernel 或通信布局,这些都要在实验设计里提前考虑。
测试时要覆盖:

- 低并发和高并发
- 短 prompt 和长 prompt
这样才能区分“小矩阵解码阶段”和“prompt 密集预填充阶段”的表现差异。
指标与分解:从请求到 GPU
建议同时记录:
- 预填充吞吐和输出吞吐
- 首 token 延迟和平均每 token 延迟
- token 间延迟分布和 95/99 分位请求延迟
GPU 级别的指标可以解释这些请求级指标背后的原因:
- 各通信域的网络字节数
- 每块 GPU 的分配条数
- 专家执行时间和空闲时间
- 路由、置换、dispatch、GEMM、combine、逆置换的分阶段耗时
- 常驻权重占用和剩余 KV 缓存容量


不同阶段的指标含义也不一样:
- dispatch 时间高,往往指向拓扑、局部性或消息大小问题
- 某块 GPU 持续偏慢,可能是路由偏斜或专家放置不佳

有些诊断表会把常见症状和初步测量对应起来,但硬件拓扑和后端行为的差异很大,实际排查时还得结合自己的集群特性。
如果看到“内存占用很高,但专家计算时间不长”,往往说明权重容量或 KV 缓存成了瓶颈,可以通过查看剩余 KV 容量来确认。
增加并发有时能改善矩阵大小和 GPU 利用率,但也可能进一步压缩 KV 缓存空间,这类 trade-off 需要在基准里显式测出来。

工程清单与实践路径
Qwen3-30B-A3B 这样的 MoE 模型,一边有 305 亿总参数决定权重存储体积,一边有 33 亿激活参数刻画单 token 的执行路径长度。
MoE 服务链路上会依次经历:路由、token 置换、dispatch、分组专家计算、combine 和逆置换,多 GPU 部署还会叠加通信域划分、专家放置和 rank 级负载不均衡。
一个相对稳妥的验证顺序是:
- 先确认权重和 KV 缓存的容量边界
- 再拆解 MoE 路径的每个阶段,测清楚分配偏斜和各通信域的字节数
- 最后在同一条请求轨迹上,对比不同放置、kernel、量化或 top-k 策略的影响
放置和 kernel 调整主要针对运行时开销;量化会改变数值精度;top-k 变更则直接改变执行的专家集合。
把这些实验拆开做,才能把性能和模型质量的变化归因到具体改动上。
从工程视角看,MoE 推理诊断更容易被拆成三块:
- 存储哪些权重、放在哪
- 如何执行被选中的专家
- 如何在 GPU 间移动路由激活
在动服务布局之前,把这三块分别测清楚,会省掉后面很多“盲调”的时间。
想把这些概念和代码打通,可以参考:
从零开始构建基于MoE的Transformer,我们用纯Python和PyTorch讲解了LLaMA 4 →
教程会从字符级分词、多头自注意力 + RoPE,到稀疏路由多专家 MLP、RMSNorm、残差与因果掩码,再到训练和生成,把 MoE 的每个环节都写成可运行的 PyTorch 代码。
如果你打算在生产环境里上 MoE,这套判断和拆解方法很值得反复翻出来用一用。
遇到新的模型、新的集群拓扑,先别急着“拍脑袋调参数”,按这条路径走一遍,往往比问十个同事都更靠谱。
常见问题
Q:MoE 模型的“激活参数”和“总参数”到底有什么用,部署时该看哪个?
A:部署规划时,优先看“总参数”和每参数字节数,用来估算权重显存下界;“激活参数”主要用来估算单 token 的计算量。原因在于,总参数直接决定 checkpoint 的大小和常驻权重体积,而激活参数只描述一条典型执行路径会触发多少参数,与需要加载多少权重没有一一对应关系。实操建议是:先用总参数 × 每参数字节数估算权重显存,再加上 KV 缓存和临时缓冲的预留,最后再用激活参数和并发度估算算力需求和吞吐上限。
Q:MoE 解码阶段 GPU 利用率很低,应该优先从哪里优化?
A:解码阶段专家矩阵通常很小,GPU 利用率低往往是 kernel 启动、行移动和权重读取占了大头。原因在于,每步只新增一个 token,top-k 又把它拆成多条分配,导致专家批次行数有限。可操作建议包括:提高并发度让更多序列同时解码;启用 grouped GEMM 减少小矩阵 kernel 启动次数;检查是否可以使用融合 kernel 降低内存访问开销;在网络条件允许时,适度调整专家放置,减少跨节点 dispatch 带来的额外延迟。
Q:怎么判断 MoE 部署的瓶颈是算力、显存还是网络?
A:可以通过分阶段打点和资源监控来拆分。直接看 GPU 利用率和显存占用,只能给出模糊印象;更有效的做法是分别记录路由、置换、dispatch、专家 GEMM、combine 的耗时,以及各通信域的网络字节数和带宽利用率。如果专家 GEMM 时间长且 GPU 利用率高,多半是算力瓶颈;如果显存接近上限而专家计算时间不长,可能是权重或 KV 缓存容量限制;如果 dispatch/combine 时间占比高且跨节点流量大,则是网络瓶颈。建议在同一条请求轨迹上,只改一个变量(比如并行布局),对比这些指标的变化来定位问题。
Q:减少 MoE 的 top-k(比如从 8 个专家减到 4 个)会带来多大质量损失?
A:质量损失大小和具体模型、任务、归一化策略强相关,不能简单套一个固定数字。已有研究在 Qwen3.6-35B-A3B 上发现,直接从 8 个专家减到 4 个,MMLU 约掉 4.65 分;如果用更大的参考集做归一化,损失可降到 0.35 分左右,同时把专家计算量减半。实操建议是:把 top-k 调整视为“模型变更”,在目标业务的真实负载和评测集上做 A/B,对比任务指标、困惑度和用户反馈,不要只看单一基准;同时保留一套“原始 top-k”配置作为回退方案。
Q:专家负载严重不均衡时,应该优先用放置优化还是复制专家?
A:优先尝试放置优化,在不增加权重副本的前提下,通过调整专家与 GPU 的映射来缓解热点。原因是复制专家会额外占用显存,直接挤压 KV 缓存空间,还会增加同步和重平衡的复杂度。放置优化可以根据路由轨迹,把经常一起被选中的专家拆开,或把热点专家迁移到负载更轻的 GPU 上。只有在放置优化空间不大、且 GPU 显存仍有富余时,再考虑为极少数热点专家增加物理副本,并配合监控观察尾延迟是否明显改善。最后,记得定期复盘路由分布,避免在流量模式变化后仍沿用过时的放置方案。
写到这里,我自己的感觉是:MoE 真正难的不是某个单点技巧,而是把“算力、显存、网络、路由行为”这几块拼成一张清晰的地图。你可以从下一次排查延迟抖动开始,试着按文中的拆解方式画一张属于自己集群的 MoE 路径图。


