MoE 模型上线后输出质量莫名下滑?可能是 Capacity Factor 导致 Token 被静默丢弃
要理解这个问题,首先得看 MoE 的运行机制。在推理过程中,路由(Router)负责将 Token 分配给最合适的专家(Expert)。为了在 TPU 或 XLA 等硬件上实现高效的批处理矩阵乘法,系统需要保证静态形状(Static Shape)。因此,每个专家都被分配了一个固定大小的缓冲区。
这里涉及到一个核心的计算公式:capacity = capacity_factor * tokens * top_k / num_experts。
如果 capacity_factor 被设置为 1.0,意味着每个专家只能接收刚好平均分配的 Token 数量。但 MoE 的天性就是“强者恒强”,路由分配绝不可能绝对均匀。一旦某个专家在当前 Batch 中被选中的 Token 数量超过了缓冲区上限,超出部分会被直接丢弃。
这种丢弃是“静默”的。被丢弃的 Token 不会触发报错,而是直接跳过该层的前馈网络(FFN),仅依靠残差连接(Residual Connection)将信息传递到下一层。这意味着你的模型在某些层实际上变成了“空跑”,导致表达能力骤降。这就解释了为什么 MoE 推理会出现一种“玄学”现象:同一个句子,如果放在 Batch 的前面可能被正常处理,但如果放在后面,由于缓冲区已满,它可能被丢弃,导致最终输出结果不一致。
在实际生产环境的优化中,针对这种丢弃问题通常有三条路径。
最直接的手段是调高 capacity_factor。例如将其从 1.0 提升至 1.2 或 1.5,给专家留出更多的冗余空间。虽然这会直接增加显存占用,但能迅速降低 Token 丢弃率,恢复模型质量。
进阶方案是放弃固定缓冲区的设计,切换到 Dropless 的块稀疏内核。比如采用 MegaBlocks 风格的 Grouped GEMM,通过动态形状处理彻底消除固定容量的限制,让所有 Token 都能被处理,但这对推理框架的底层实现要求较高。
从架构层面看,可以参考 DeepSeek-V3 的做法,采用无辅助损失的平衡机制(per-expert routing bias)。通过在路由阶段引入专家偏置,在不牺牲模型质量的前提下,强行维持负载均衡,从而在低容量因子下也能保证极低的丢弃率。
最后给一个部署建议:在监控面板中必须加入每层、每个专家的 Token 丢弃率(Drop Rate)指标。如果你不记录这个数字,你永远无法感知模型是否在高并发压力下悄悄“偷懒”,而只能在用户反馈质量下降后才开始排查。