MoE推理计算全解析:从路由机制到vLLM显存优化实践

发布时间:2026/11/3 23:38:17

MoE推理计算全解析:从路由机制到vLLM显存优化实践 1. 从一次显存告警说起MoE到底在算什么第一次认真拆MoE的计算过程是因为线上一个Qwen3-30B-A3B的推理服务突然开始频繁触发显存告警。模型权重文件明明只有60GB出头按FP16算两张80G的卡绰绰有余但实际跑起来KV Cache一涨显存就顶到天花板。当时我盯着vLLM的日志看了半天才反应过来问题不在权重本身而在于我对MoE的“激活参数”和“总参数”这两个概念的理解一直停留在纸面上。MoEMixture of Experts混合专家。这个词在AI Infra圈子里这两年出现的频率高得离谱从DeepSeek-V3到Qwen3-MoE再到各种开源MoE变体几乎每个做大模型部署的人都会碰到。但很多人对它的理解停留在“稀疏激活”“只算一部分参数”这种模糊层面真到了要调vLLM参数、算显存预算、排查负载均衡问题的时候就发现脑子里那点概念完全不够用。这篇笔记想做的事情很具体把MoE从输入到输出的完整计算过程拆开讲清楚一个token进来之后它到底经过了哪些矩阵乘法、哪些参数被激活了、哪些没被激活、显存里到底驻留了什么、vLLM这类推理引擎在处理MoE时又做了哪些特殊处理。适合正在做MoE模型部署、想搞明白vLLM里MoE相关配置项含义、或者单纯想补AI Infra底层知识的人。不需要你之前写过CUDA Kernel但最好对Transformer的基本结构有个概念。2. MoE的整体设计思路为什么要把FFN拆成多个专家2.1 从Dense到Sparse一个参数量与计算量的解耦问题标准Transformer的FFN层结构很朴素两个线性层夹一个激活函数中间维度通常是隐藏维度的4倍。比如hidden_size是4096那FFN的中间层就是16384参数量大约是2 × 4096 × 16384 ≈ 1.34亿。每一层都这样堆几十层参数量就上去了。这个结构的问题在于参数量和计算量是绑死的。你想让模型容量更大就得加参数加参数就意味着每个token都要过更多的矩阵乘法计算量线性增长。训练成本、推理延迟全都跟着涨。MoE的思路是把这个绑定拆开。具体做法是不再用一个大的FFN而是准备N个小的FFN每个叫一个“专家”Expert。然后加一个路由网络Router/Gate对每个token只选其中Top-k个专家来计算其余专家不参与。这样一来总参数量可以是N个小FFN之和看起来很大但每个token实际只经过k个小FFN计算量只和k有关。这就是所谓的“稀疏激活”——参数总量大但激活的参数少。拿Qwen3-30B-A3B举例总参数30B但每个token只激活约3B参数。显存里要装下30B的权重但计算量只相当于一个3B的Dense模型。这就是为什么它能在消费级多卡上跑出不错的吞吐。2.2 路由机制Token怎么知道该找哪个专家路由是MoE的核心。最简单的路由是一个线性层把token的hidden state乘一个gate矩阵得到N个分数然后取Top-k。具体来说假设hidden_size为H专家数为Ngate矩阵W_g的形状是[H, N]。对一个token的hidden state h形状[H]计算logits h W_g # 形状 [N]然后对这N个logits做Softmax得到每个专家的权重。取Top-k个最大的用它们的权重对对应专家的输出做加权求和。这里有个细节Top-k之后通常会重新归一化。因为Softmax是在全部N个专家上做的取Top-k之后权重和不为1需要再除以这k个权重之和。这个操作在代码里就是一行除法但对数值稳定性有影响后面讲实现的时候会展开。2.3 为什么是SwiGLUMoE专家内部的激活函数选择MoE的每个专家本身就是一个FFN。那这个FFN用什么激活函数现在主流的选择是SwiGLU。SwiGLU的结构是输入分别过两个线性层一个做门控gate一个做值value然后门控过SiLU激活后和值逐元素相乘再过一个输出线性层。用公式表示gate SiLU(x W_gate) value x W_up output (gate * value) W_down相比ReLU或GELUSwiGLU多了一个门控路径参数量多了50%左右因为多了一个W_gate但效果通常更好。在MoE场景下每个专家都比较小SwiGLU带来的额外参数可以接受而且它能让每个专家的表达能力更强。这里要注意一个实现细节很多MoE实现会把W_gate和W_up合并成一个矩阵来做因为它们是并行计算的合并后一次矩阵乘法就能出结果减少Kernel launch开销。vLLM的MoE实现里就有这种融合。3. MoE计算过程逐步拆解一个Token的完整旅程3.1 第一步Router计算与Top-k选择假设我们有一个batch的tokenhidden state形状是[B, H]B是token数H是隐藏维度。Router的计算过程# gate_weight: [H, N] # hidden_states: [B, H] router_logits hidden_states gate_weight # [B, N] routing_weights softmax(router_logits, dim-1) # [B, N] topk_weights, topk_indices topk(routing_weights, kK, dim-1) # [B, K] topk_weights topk_weights / topk_weights.sum(dim-1, keepdimTrue) # 重新归一化这段代码看起来简单但有几个坑。第一个坑是Softmax的精度。如果router_logits的数值范围很大Softmax之后可能出现极端值导致某些专家的权重接近0或1。这在训练时会导致梯度问题推理时则可能让路由过于集中。实践中通常会在Softmax之前对logits做一定的缩放或者用温度系数调节。第二个坑是Top-k的平局问题。当多个专家的分数完全相同时Top-k的选择可能不稳定导致同一个token在不同batch里被路由到不同专家。这在推理时通常不是大问题但在需要确定性输出的场景下要注意。第三个坑是重新归一化的数值稳定性。如果Top-k的权重之和非常小比如所有选中的专家权重都接近0除法可能产生很大的值。实际实现里通常会加一个极小值eps来避免除零。3.2 第二步Token分发与专家计算拿到topk_indices之后需要把每个token送到对应的专家那里去计算。这里有两种实现方式一种是“token-centric”遍历每个token对它选中的k个专家分别计算然后加权求和。这种方式逻辑简单但效率低因为每个专家的计算是独立的无法利用矩阵乘法的并行性。另一种是“expert-centric”先按专家把token分组同一个专家的所有token放在一起做一次大的矩阵乘法。这种方式效率高因为可以充分利用GPU的并行能力。vLLM和大多数高性能MoE实现都采用这种方式。具体流程是根据topk_indices统计每个专家被分配到了哪些token对每个专家收集所有分配给它的token的hidden state组成一个矩阵对这个矩阵做一次FFN计算SwiGLU把计算结果按原来的token顺序散射回去用topk_weights做加权求和这个过程涉及到两次数据重排一次是gather按专家收集token一次是scatter把结果放回原位。这两次重排在GPU上是有开销的尤其是当token分布不均匀时某些专家分到的token特别多某些特别少会导致负载不均衡。3.3 第三步加权求和与输出所有专家的输出散射回去之后每个token有k个输出向量来自k个专家。用topk_weights加权求和得到最终的FFN输出。然后这个输出会经过残差连接进入下一层。这里有一个重要的点MoE层通常只替换Transformer中的FFN部分Attention部分还是Dense的。也就是说每个token在Attention层还是要和所有其他token交互只是在FFN层才走稀疏激活。这也是为什么MoE模型的总计算量虽然比同等参数量的Dense模型小但并没有小到“只算3B”的程度——Attention的计算量还在。4. 显存里到底装了什么MoE的显存占用分析4.1 权重显存所有专家都要驻留这是很多人容易误解的地方。MoE的“稀疏激活”指的是计算时只激活部分专家但所有专家的权重都需要加载到显存里。因为Router是动态选择的你无法提前知道哪个token会选哪个专家所以所有专家都必须随时可用。拿Qwen3-30B-A3B来说总参数30BFP16精度下权重占用约60GB。这60GB必须全部在显存里不管当前batch的token实际激活了哪些专家。这就是为什么MoE模型对显存的要求并没有比同等参数量的Dense模型低——省的是计算不是显存。那有没有办法只加载部分专家有但那是另一套思路叫“专家卸载”Expert Offloading把不常用的专家放在CPU内存或SSD上需要时再加载。这种方式会引入额外的传输延迟适合显存极度受限的场景但吞吐会明显下降。4.2 KV CacheMoE和Dense没有本质区别KV Cache的大小取决于Attention层的配置层数、头数、头维度、序列长度、batch size。MoE模型和Dense模型在Attention部分的结构通常是一样的所以KV Cache的占用也遵循同样的计算公式。但MoE模型通常层数更多、hidden_size更大所以KV Cache的绝对值可能比小Dense模型大。在vLLM里KV Cache的显存分配是通过gpu_memory_utilization参数控制的它会先加载权重然后把剩余显存的指定比例分配给KV Cache。4.3 激活显存中间结果的开销除了权重和KV Cache推理过程中还会产生中间激活值。MoE的激活值包括Router的logits、Top-k的权重和索引、每个专家的输入输出矩阵、gather/scatter的临时缓冲区等。这些激活值的大小和batch size、序列长度、专家数、Top-k值都有关系。在长序列或大batch场景下激活显存可能相当可观。vLLM通过PagedAttention和连续批处理来优化这部分开销但MoE特有的gather/scatter操作还是会带来额外的内存占用。5. vLLM中的MoE实现从配置到Kernel5.1 vLLM的MoE相关配置项在vLLM里部署MoE模型有几个配置项需要特别关注配置项含义调优建议tensor_parallel_size张量并行度MoE模型通常需要较大的TP因为权重总量大expert_parallel_size专家并行度把不同专家放到不同GPU上减少单卡权重压力gpu_memory_utilization显存利用率MoE模型建议设高一些比如0.9因为权重大max_model_len最大序列长度直接影响KV Cache大小MoE模型要留足显存enable_expert_parallel是否启用专家并行多卡部署MoE时建议开启专家并行是MoE特有的并行方式。假设有8个专家4张GPU可以每张GPU放2个专家。每个token的Router计算在所有GPU上复制一份因为gate矩阵很小然后根据Top-k结果把token发送到对应专家所在的GPU上计算最后再把结果收集回来。这种方式可以显著减少单卡的权重显存占用但引入了GPU间的通信开销。5.2 vLLM的MoE KernelFused MoEvLLM的MoE实现核心是一个叫FusedMoE的模块。它把Router计算、Top-k选择、token分发、专家FFN、加权求和这几个步骤融合在一起尽量减少Kernel launch次数和内存访问。具体来说FusedMoE的forward过程大致是计算router_logits一次矩阵乘法Top-k选择一个小的排序操作根据topk_indices做token permutationgather对permuted的token做分组GEMM每个专家一次反permutationscatter加权求和其中第4步的分组GEMM是性能关键。因为不同专家分到的token数不同GEMM的M维度是动态的这会导致Kernel的负载不均衡。vLLM通过一些技巧来缓解这个问题比如把多个小GEMM合并成一个大GEMM或者用专门的Grouped GEMM Kernel。5.3 负载均衡MoE的“木桶效应”MoE推理中最头疼的问题之一就是负载不均衡。理想情况下每个专家分到的token数应该差不多这样GPU的利用率最高。但实际上Router的选择往往是不均匀的——某些专家可能被大量token选中某些专家可能几乎没人选。这种不均衡会导致两个问题一是计算时间被最忙的专家拖长其他专家算完了要等二是显存占用不均忙的专家需要更多的临时缓冲区。在训练时通常会在损失函数里加一个负载均衡损失Load Balancing Loss鼓励Router把token均匀分配给各个专家。但在推理时这个损失不起作用只能靠Router本身学到的分布。vLLM在推理时对负载均衡的处理主要是通过Expert Parallel和合理的专家放置策略。如果某些专家特别忙可以考虑把它们放到独立的GPU上避免和其他专家争抢资源。6. 实操中踩过的坑与排查技巧6.1 显存够但跑不起来激活显存的隐形开销前面提到过MoE的权重显存和Dense模型一样大。但实际部署时我发现即使权重能装下模型还是可能OOM。原因往往在激活显存上。MoE的gather/scatter操作会产生临时缓冲区这些缓冲区的大小和batch size、序列长度、Top-k值成正比。在长序列场景下这些缓冲区可能占用几个GB的显存。vLLM的日志里会打印显存分配的详细信息如果看到“activation memory”或“temporary buffer”占用异常高就要考虑减小batch size或序列长度。6.2 吞吐上不去专家并行的通信瓶颈用Expert Parallel部署MoE时吞吐可能反而不如单卡。这是因为token在不同GPU之间传输需要通信如果通信量大于计算节省的时间就得不偿失。判断是否遇到通信瓶颈可以看GPU的利用率。如果计算Kernel的利用率不高但GPU之间的NVLink或PCIe带宽跑满了那就是通信瓶颈。解决办法包括减少Expert Parallel的度、增大batch size来摊薄通信开销、或者用更快的互联比如NVLink。6.3 输出质量下降Router的数值精度问题有一次部署一个MoE模型发现输出质量明显不如预期。排查后发现是Router的Softmax精度问题。在某些实现里Router的logits计算用了FP16Softmax之后权重精度不够导致Top-k选择不稳定。解决办法是把Router的计算提升到FP32。虽然这会增加一点计算开销但对输出质量的提升很明显。vLLM里可以通过设置dtype参数来控制或者在模型配置里指定Router的精度。6.4 常见问题速查表问题现象可能原因排查方向解决方法显存OOM激活显存过大查看vLLM日志的显存分配减小batch size或max_model_len吞吐低专家并行通信瓶颈检查GPU利用率和互联带宽降低EP度或增大batch输出质量差Router精度不足检查Router计算精度提升Router到FP32负载不均Router分布偏斜统计各专家的token数调整专家放置或加均衡损失Kernel报错Top-k索引越界检查topk_indices范围确认专家数和Top-k配置7. 几个容易被忽略的细节7.1 Top-k的k值选择k值不是越大越好。k越大每个token激活的专家越多计算量越大但模型容量也越大。通常k取1或2。k1时计算量最小但路由的容错性差k2时计算量翻倍但输出更稳定。Qwen3-MoE用的是k8对于某些配置DeepSeek-V3用的是k8具体要看模型设计。7.2 专家数量的影响专家数N越多总参数量越大但每个token的计算量不变因为k不变。N的选择要在模型容量和显存之间权衡。N太大权重显存吃不消N太小模型容量不够。7.3 Shared Expert的设计有些MoE模型会加一个“共享专家”Shared Expert所有token都会经过它不参与路由。这个共享专家通常是一个较大的FFN用来捕捉通用知识而路由专家则专注于特定领域。DeepSeek-V3就用了这种设计。共享专家的计算量是固定的不随k变化所以会增加一些基础计算开销但能提升模型效果。7.4 推理时的Batch Size选择MoE推理的batch size选择比Dense模型更敏感。batch太小专家利用率低很多专家分不到tokenbatch太大激活显存和KV Cache压力大。实践中需要根据具体模型和硬件做benchmark找到吞吐和延迟的平衡点。8. 从计算图看MoE一张图理解数据流如果把MoE的计算过程画成数据流图大致是这样的输入hidden_states → Router线性层Softmax→ Top-k选择 → 得到topk_weights和topk_indices → 按专家分组gather→ 每个专家做SwiGLU FFN → 结果散射回原位scatter→ 用topk_weights加权求和 → 输出这个数据流里Router和Top-k是轻量级的计算量很小gather/scatter是数据移动不涉及计算但耗时专家FFN是计算主体但因为是分组GEMM效率取决于分组是否均匀。理解这个数据流对调优很重要。比如如果发现Router耗时占比高可能是N太大导致Softmax和Top-k的开销上来了如果gather/scatter耗时高可能是token分布太不均匀如果专家FFN耗时高那就要看GEMM的效率和负载均衡。9. 实际部署中的参数计算示例假设要部署一个MoE模型配置如下总参数30B专家数64Top-k8hidden_size2048层数48FP16精度。权重显存30B × 2 bytes 60GB。KV Cache假设max_model_len8192batch_size32头数32头维度128层数48。每层每token的KV Cache是2 × 32 × 128 × 2 bytes 16KB。48层就是768KB per token。8192 token × 32 batch 262144 token总KV Cache约192GB。这个数字很大实际部署时需要减小batch或序列长度。激活显存Router的logits是[B, 64]B是当前batch的token数。假设B4096logits就是4096 × 64 × 2 bytes 512KB。Top-k的权重和索引类似量级。gather/scatter的缓冲区取决于每个专家分到的token数最坏情况下一个专家分到所有token缓冲区就是4096 × 2048 × 2 bytes 16MB。这些加起来在百MB级别相比权重和KV Cache不算大但在显存紧张时也要考虑。这个计算示例说明MoE部署的显存瓶颈主要在权重和KV Cache上激活显存是次要的但不可忽略。10. 我个人的一些经验体会搞MoE推理这段时间最大的感受是不要被“稀疏激活”这个词迷惑。稀疏的是计算不是显存。显存预算要按总参数算不能按激活参数算。很多人第一次部署MoE时都会在这个点上栽跟头。另一个体会是vLLM的MoE实现虽然做了很多优化但Expert Parallel的通信开销在中小规模部署里往往得不偿失。如果单卡或双卡能装下权重优先考虑不用EP用TP就够了。EP适合大规模部署比如8卡以上这时候通信开销能被计算节省摊薄。最后分享一个小技巧调MoE模型时先把batch size设小确认模型能正常输出再逐步加大batch看吞吐和显存的变化。MoE的显存曲线不是线性的batch大到一定程度后激活显存会突然涨得很快找到那个拐点很重要。
延伸阅读

更多相关文章

2026/11/3 23:38:17

移动云Clawdbot:分布式爬虫管理平台的核心技术与应用

1. 项目背景与技术定位 Clawdbot作为新一代分布式爬虫管理平台,其核心价值在于将复杂的网络数据采集任务标准化、自动化。移动云此次全面上线该服务,标志着云计算平台在数据采集领域的基础设施建设进入新阶段。这个"一键部署"功能背后&#xf…

2026/11/3 23:38:17

看懂电器符号:待机、IP防护与双回字形的安全边界

搬家那次,我在纸箱堆里翻出一台用了好几年的旧风扇,面板上除了三个按键,还印着一竖一圆圈、两个同心方框、一串不认识的小三角。按键我倒认得,那几个图形却让我愣了半天——明明是自己的东西,却像在看一门外语。后来做…

2026/11/3 23:38:17

Tigshop开源商城AWS S3存储对接技术解析

1. 项目背景与核心价值Tigshop作为一款面向跨境电商领域的JAVA开源商城系统,其最新发布的AWS S3存储对接功能解决了跨境业务中最为头疼的静态资源管理问题。在跨境电商的实际运营中,商品图片、视频、多语言文案等静态资源的存储往往面临三大挑战&#xf…

2026/11/4 1:18:35

构建智能法律助手:Hermes Agent法律文档分析与建议

构建智能法律助手:Hermes Agent法律文档分析与建议 在数字化时代,法律文档的处理与分析往往耗费大量人力与时间。Hermes Agent作为一款强大的AI智能助手,凭借其先进的文档处理能力和灵活的工具集成,为法律从业者提供了高效、精准…

2026/11/4 1:18:35

免装客户端的网盘直链下载:一份用户脚本 LinkSwift 的使用与选路

免装客户端的网盘直链下载:一份用户脚本 LinkSwift 的使用与选路 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云…

2026/11/4 1:18:35

AutoHotkey 用 DllCall 隐藏鼠标光标的3种玩法

AutoHotkey 用 DllCall 隐藏鼠标光标的3种玩法 【免费下载链接】AutoHotkey AutoHotkey - macro-creation and automation-oriented scripting utility for Windows. 项目地址: https://gitcode.com/gh_mirrors/au/AutoHotkey AutoHotkey 是 Windows 上免费开源的自动化…

2026/11/4 1:13:32

阅读APP书源一次配好:26个实测书源,3步导入

阅读APP书源一次配好:26个实测书源,3步导入 【免费下载链接】Yuedu 📚「阅读」自用书源分享 项目地址: https://gitcode.com/gh_mirrors/yu/Yuedu 阅读APP本身没有书籍内容,阅读APP书源才是让它干活的关键:靠书…

2026/11/3 7:47:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/3 14:24:53

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/4 0:03:20

AI辅助技术栈评估:提升研发决策效率60%

1. 项目背景与核心价值技术决策一直是研发团队面临的重大挑战。随着技术生态的快速演进,技术栈的选择、维护和迭代变得愈发复杂。我们团队在过去两年里,先后经历了三次重大技术栈迁移,每次都需要投入大量人力进行技术评估和风险分析。直到去年…

2026/11/4 0:03:20

从生成Token到直接决策:Jev如何重塑AI Agent工程实践

1. 从"生成文字"到"拍板决策":Jev 到底在解决什么问题大多数人第一次接触大模型,脑子里建立的模型是"输入问题、输出文字"。你问它今天天气怎么样,它给你一段描述;你让它写个周报,它给你…

2026/11/4 0:03:20

32G统一内存跑大模型:内存账、TPS实测与端云分流

想在一台 32G 统一内存的小主机上把大模型跑成"日常可用",这件事我前后折腾了小半年。标题里说的这台 Mac mini M6,是我们这次讨论的目标平台,具体到手规格以实物为准;但下面这套推算方法和实测思路,对任何统…

2026/11/3 7:47:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/3 14:24:53

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑