MoE推理工程,清晰解析

你以为“激活参数更少”就一定意味着“更省显存、更好部署”?在 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 路径图。