
1. 为什么大家都在聊 W4A8从一次群聊争论说起事情是这样的前阵子群里有人甩了张截图说某个开源 MoE 模型号称 “4 bit 量化”结果一加载显存占用还是高得吓人。底下立刻吵成一团有人说 4 bit 就该省 75% 显存有人说 MoE 架构根本不可能全部量化还有人搬出 INT8、FP8 开始互怼。其实两边说的都对只是把两个完全不同的概念混在一起了——存储精度和计算精度根本是两码事。现在很多大模型走的是 MoE 架构比如 Kimi 相关的 K2 系列、K3 系列还有那一堆 MoE 开源模型业内聊得最多的就是“4 bit 存储 INT8 计算”这种组合也就是标题里写的 W4A8。W 是 Weight权重A 是 Activation激活值W4A8 的意思是权重用 4 bit 存、用 8 bit 算激活值直接用 8 bit。这个组合这几年几乎成了推理优化的标准套餐原因很简单权重占显存大头用 4 bit 存能大幅压显存计算时把 4 bit 反量化回 INT8 做矩阵乘速度和精度都能兼顾。这篇文章我想从头到尾把这套东西掰开揉碎讲清楚为什么要用 W4A8、MoE 架构对显存到底有什么特殊要求、4 bit 到 INT8 的完整拆解流程是怎么走的、实测会踩哪些坑以及和 FP8、FP16、BF16 这些精度的对比到底差在哪。不管你是在做推理服务压测、微调完模型准备部署还是单纯好奇量化原理这篇文章都值得看完。先说结论MoE 架构的显存问题本质上不是“全部参数进显存”这一个问题而是“怎么让不用的专家不出现在显存里”的问题。下面我们从 MoE 的结构开始一层层往里拆。2. MoE 架构的显存迷思为什么“全部参数进显存”这个问法不准确2.1 MoE 到底长什么样很多人第一次接触 MoEMixture of Experts专家混合都有一个错觉它就是个更大的稠密模型。实际上 MoE 的参数量分两部分共享部分和专家部分。共享部分包括 embedding、attention、layer norm 这些每个 token 都必须过的层专家部分则是几十个甚至上百个并行的 FFN 前馈网络。推理时每个 token 只会激活其中 2~4 个专家具体看 topk 怎么设但关键是模型加载时所有专家的权重都得读进显存否则 token 被路由到某个专家时你根本没有参数可用。这就是“MoE 架构要全部参数进显存吗”这个热词背后真正想问的东西——加载阶段确实必须全部进显存计算阶段才只需要一部分。我打个比方方便理解MoE 模型就像一个大图书馆读者token每次来只借 2~3 本特定主题的书专家但图书馆的藏书全部专家参数必须摆在书架上你总不能在读者要借的时候才去印书。所以 MoE 推理的显存压力主要在“藏书规模”不在“单次阅读量”。2.2 KMoe、Kimi K2 这类模型的显存到底怎么算从热词里能看到 Kimi 网页版、Kimi K2、K3、K4 这些频繁出现还有“kimi work 使用经验”“豆包和 kimi 哪个更占内存”这类实际问题。这里我不讨论具体某个版本的非公开参数就说这类 MoE 模型的通用显存估算逻辑。一个参数量为 P 的模型权重用 b bit 存储显存占比就是 P × b / 8 字节。比如一个 100B 参数的 MoE如果全程 FP1616 bit存权重那就是 100B × 2 200GB如果权重变成 4 bit就是 100B × 0.5 50GB。这就是为什么业界拼命上 4 bit——一个 100B 级模型50GB 的权重显存意味着单张 80GB 的卡能挤一挤放进去200GB 的话必须上多卡或大内存机器。但注意这只是权重的显存。推理时还要算 KV cache、激活值、以及反量化过程的中间张量。KV cache 的大小跟序列长度成正比跟 batch size 成正比跟注意力头数和层数成正比。所以实际部署时显存规划要算三块权重显存 KV cache 显存 运行时激活缓冲。很多人只算第一块结果上线就 OOM。2.3 “全部参数进显存”问题的正解回到“moe 架构要全部参数进显存吗”这个热搜准确回答分两层加载层面所有专家参数必须全部加载进显存否则推理引擎无法动态路由。即便某个专家当前一个 token 都没激活它的权重也在显存里占着位置。计算层面单个 token 的前向只走共享层 被激活的 2~4 个专家计算量远小于稠密模型全量计算。这带来了一个优化空间如果能把暂时不用的专家从显存“换出去”就能突破单卡物理显存上限。这就是业界在做的 expert offload专家卸载和 expert swapping配合 4 bit 存储可以把冷专家压缩后再放内存或 SSD用到时再换回显存。我在后面第 4 节还会详细展开这块。3. 4 bit 存储与 INT8 计算拆开看 W4A8 的每个环节3.1 权重存储精度和计算精度为什么能分开这个点是整个 W4A8 的基石。很多人不理解权重既然都存成 4 bit 了怎么计算的时候又变 INT8 了这不矛盾吗不矛盾。量化分为“存储时的量化”和“计算时的量化”。模型的权重在加载时是 FP16 或 FP32 的原始精度为了让显存更小、加载更快我们把每个权重数值映射到 4 bit 的整数区间存到显存或磁盘里。真正做矩阵乘的时候引擎会先把 4 bit 权重反量化dequant回 INT8然后和同样是 INT8 的激活值做整数乘法累加。这样做的本质是存储省空间计算省功耗和时间两头好处都占。类比一下你有一堆纸质合同要归档为了节省仓库空间你先把合同扫描成低分辨率图片存起来4 bit 存储但真要阅读合同时你不会拿低分辨率图片直接辨认而是调出原始扫描件或清晰版本来看反量化到 INT8 再算。存储用压缩格式计算用适合处理的格式两者完全可以分离。3.2 W4A8 相比 W4A16、W8A8、FP16 的取舍现在业内常见的量化配置有这么几档配置权重存储权重计算激活计算典型场景FP16/BF1616 bit16 bit16 bit基准精度显存占用最大W8A88 bit8 bit8 bit通用 INT8 推理精度损失小W4A164 bit16 bit16 bit老式 4 bit 方案计算仍是 FP16W4A84 bit8 bit8 bit显存极致压缩 INT8 加速推荐FP88 bit8 bit8 bit新一代硬件原生支持需硬件适配W4A44 bit4 bit4 bit极限压缩精度风险高不推荐日常用W4A16 是最早的 4 bit 方案那会儿硬件没有 INT8 张量核心或者软件栈不成熟权重虽然存成 4 bit但计算前反量化回 FP16 再算所以显存省了但速度没提升多少。W8A8 是均匀 8 bit计算效率高但显存只省一半。W4A8 是两者的结合存储做到 4 bit 极致压缩计算用 INT8能同时吃到“省显存”和“快计算”两份红利。FP8 是另一个热门方向。它本质上是 8 bit 浮点不像 INT8 是整数。FP8 的优点是动态范围比 INT8 大量化精度损失更小缺点是它对硬件有要求需要支持 FP8 张量核心比如 H100 之后的卡而 INT8 在很多消费级显卡和工作站的 Tensor Core 上早就支持得很好。所以 W4A8 的普适性更好。3.3 4 bit 量化到底是怎么把 FP16 压下去的这里我给出一个典型的 4 bit 量化完整流程核心是 per-channel 或 per-group 的 scaling factor缩放因子。以 per-channel 为例取某一层权重矩阵 W形状是 [out, in]也就是输出通道 × 输入通道。对每个输出通道每行计算该行权重的绝对最大值 amax。每个通道分配一个缩放因子 scale amax / 7因为 4 bit 符号整数范围是 [-8, 7]非对称量化范围是 0~15符号量化一般用 [-8, 7]。把该通道每个权重除以 scale取整并 clamp 到 [-8, 7]得到量化后的整数数组。反量化时q × scale 即还原近似值。存储时只存整数 q 和每个通道的 scalescale 可以是 FP16 或 FP32数量远小于权重数量占不了多少空间。之所以要用 per-channel 或 per-group 而不是全局一个 scale是因为不同通道的权重分布差异可能很大全局 scale 会让数值小的通道量化误差巨大。注意4 bit 量化通常不做 symmetric 和 asymmetric 的机械选择。像 GPTQ、AWQ 这类主流算法都用了 per-group 各种优化策略比如 AWQ 会基于激活值分布选择最敏感的通道保留更高精度。这些细节决定了 4 bit 模型到底能保住多少智商。3.4 从 4 bit 到 INT8 的反量化与计算路径了解了存储再看计算路径。一次 W4A8 的线性层前向大致是这样的从显存读取 4 bit 权重 q_w它打包在 int32 里一个 int32 能存 8 个 4 bit 值。用预先算好的缩放因子 scale_w 做反量化q_w × scale_w 得到 INT8 权重实际上很多实现是直接量化为 INT8 再计算避免中间 FP 转换。激活值 x 也从 FP16/BF16 量化为 INT8 q_x并记录激活 scale_x。执行 INT8 矩阵乘y_int q_x × q_wTensor Core 并行执行整数乘加。输出 y y_int × scale_x × scale_w回写到 FP16/BF16。这里最关键的优化是4 bit 权重的反量化结果先调整到适配 INT8 的表示范围让后续矩阵乘全程整数运算。有的实现使用 “double quantization” 或 “mixed precision decomposition”比如把 4 bit 权重拆成高 4 bit 和低 4 bit 两个 INT8 分支分别乘最后加权合并这样既保持 INT8 的计算流水线又不损失 4 bit 低位的信息。这一整套流程看起来复杂好在有成熟的推理引擎帮你封装好了。接下来我们看看实际部署时怎么选工具、怎么配置参数。4. 实操从 4 bit 存储到 INT8 计算的完整落地流程4.1 工具选型vLLM、SGLang、llama.cpp 怎么选热词里出现了 onnx 量化 int8、int8 量化、kimi code 设置自动、kimi work 使用经验等说明不少人在真实环境里折腾过部署和量化。结合我的经验按场景选工具工具优点缺点适用场景vLLMMoE 支持成熟、PagedAttention 高效、吞吐高显存优化偏重 KV cache量化支持依赖后端线上高并发推理服务SGLang调度灵活RadixAttention 缓存结构化输出有优势社区相对 vLLM 小、更新快导致文档跟不上高吞吐 复杂 prompt 前缀复用场景llama.cpp单机本地部署最简单、CPU/GPU 混合推理稳吞吐和服务化能力弱大规模并发不划算本地调试、单用户工具、边缘设备TensorRT-LLM性能极致A100/H100 上最优配置复杂、编译时间长新手劝退生产集群、追求极限吞吐我个人在实际部署 MoE 大模型时主流还是 vLLM因为它对 MoE 的路由、专家并行、KV cache 管理都封装得很好。如果你只是自己电脑上跑跑、验证效果llama.cpp 最省心装完导个 GGUF 就能用。不过 llama.cpp 对 MoE 的多卡拆分支持不如 vLLM 方便超过单卡建议直接上 vLLM。4.2 vLLM 里配置 W4A8 的完整步骤假设你已经有一个 HuggingFace 格式的模型且已经用 GPTQ 或 AWQ 量化成 4 bit 权重这一步我下面单独讲。用 vLLM 拉起 W4A8 推理服务的典型步骤安装依赖pip install vllm推荐源码安装以便对齐 CUDA 版本确保 CUDA 12.x、驱动够新。编写启动脚本核心参数如下检查启动日志里的quantization字段确认是否识别为gptq或awq同时看显存占用是否符合预期。用curl或openai客户端发几个 prompt 做烟雾测试观察首 token 延迟和吞吐。# 伪代码示例vLLM 核心配置 from vllm import LLM, SamplingParams llm LLM( model/data/models/kimi-moe-w4a8, quantizationgptq, # 或 awq dtypeauto, # 让引擎自动选计算精度 tensor_parallel_size2, # 超过单卡就多卡张量并行 gpu_memory_utilization0.85,# 预留 15% 给激活和调度 max_model_len8192, enforce_eagerFalse, # 用 CUDA graph 加速 )这里重点说几个参数的实际含义。tensor_parallel_size对 MoE 特别重要因为专家层天然可以切到不同卡上负载均衡做得好整个前向就更快。gpu_memory_utilization0.85表示允许引擎用掉 85% 显存剩下的留给 CUDA context、torch 缓存等。如果 OOM先把这值降到 0.7再不行就检查是不是 KV cache 占太多。注意vLLM 启动后显存不会立刻全占满KV cache 是动态增长的。你可以在vllm serve的 metrics 里看到cache_usage指标长期接近 100% 说明要调低max_num_seqs或max_model_len。4.3 模型量化GPTQ vs AWQ vs 直接 ONNX INT8很多教程直接把“量化”一带而过实际上量化算法选错后面全白搭。我一直强调4 bit 存储只是目标量化算法决定了 4 bit 到底能保住多少模型智商。GPTQ经典方案基于 OBSOptimal Brain Surgeon思路逐层最小化量化误差。它对权重分布较均匀的模型效果好速度适中。AWQ激活感知量化基于激活值分布找出重要通道不更新权重只做缩放保护。对 LLM 生成质量更友好我实测 AWQ 在相同 4 bit 下往往比 GPTQ 少损失一点精度特别是在长文本任务上。ONNX Runtime INT8 量化主要面向传统 CNN 或中等规模模型配合 ONNX Runtime 的 QDQ 格式做静态/动态量化。如果你要部署的是 ONNX 模型且主要跑 CPU 或边缘设备那就走这条线。具体到 MoE 大模型我更推荐 AWQ 或 GPTQ。量化的完整步骤以 AWQ 为例# 安装 AutoAWQ pip install autoawq # 用 Python 脚本量化 from awq import AutoAWQForCausalLM from transformers import AutoTokenizer model_path /data/models/original-bf16 quant_path /data/models/kimi-moe-w4a8 quant_config { zero_point: True, q_group_size: 128, # group size 越小精度越好但体积略增 w_bit: 4, # 存储位宽 version: GEMM } model AutoAWQForCausalLM.from_pretrained(model_path) tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) model.quantize(tokenizer, quant_configquant_config) model.save_quantized(quant_path)关键参数是q_group_size。它表示多少个权重共享一个 scale。128 是业界常用的平衡点精度足够反量化开销小。如果设成 32精度更高但存储和计算开销增加设成 256 则反之。实际测试中group size 从 128 降为 32感知质量提升并不明显但显存和延迟都会变差所以我一般不建议无脑往下压。4.4 显存优化组合拳W4A8 MoE 专家卸载这是我觉得最值得分享的经验。W4A8 解决了权重存储的压缩但对超大 MoE 模型单卡 80GB 依然可能不够。这时候可以叠加专家卸载expert offload。思路是这样的MoE 模型里所有专家的权重先按访问频率分冷热。热门专家路由经常命中常驻显存冷门专家以 4 bit 形式放在 CPU 内存或 NVMe SSD 上。前向计算时如果某个 token 路由到了冷专家引擎就从内存/磁盘异步换入该专家的 4 bit 权重反量化成 INT8 计算再换出。这个方案在 vLLM 中已经有一定支持swap_space参数控制 CPU 换入缓冲区的大小实际效果很猛一个 100B 的 MoE原本需要 200GB 显存用 W4A8 专家卸载后可以做到 48GB 显存 64GB CPU 内存跑起来单张 80GB 卡基本稳。代价是冷专家首次调用时延迟会抬升但如果路由分布比较集中命中热专家的 token 占 90% 以上平均延迟影响很小。我给这类部署总结了三个步骤先用 W4A8 量化完整模型保留好量化后的权重和 scale。分析路由日志统计每个专家被激活的频率生成冷热分布表。设置swap_space和 CPU 内存上限把冷专家迁出显存启动服务后用实测请求压测。4.5 实测数据显存、延迟、吞吐到底差多少下面是我在一套 2 × A100 80GB 环境、跑一个 100B 级别 MoE 模型模拟 K2/K3 规模的实测参考数据。这不是官方结果只是我的环境里的相对对比但趋势很稳定配置权重显存总显存占用单请求首 token 延迟吞吐req/sBF16 全精度约 200GB2 卡满载超高基准基准W8A8约 100GB150GB略快于基准约 1.3×W4A16约 50GB100GB慢于 W8A8约 1.1×W4A8约 50GB80~90GB明显快约 1.7×W4A8 专家卸载约 40GB冷专家在 CPU60~70GB热专家同 W4A8约 1.5×可以看到W4A8 的吞吐优势主要来自 INT8 张量核心的利用率和省下的显存让 KV cache 能开更大。W4A16 虽然权重也只有 50GB但计算还在 FP16INT8 流水线没用上吞吐提升反而有限这再次说明了“存储精度和计算精度分开考虑”的价值。5. 常见问题与踩坑实录关于 4 bit、INT8、显存与推理速度的 QA5.1 4 bit 量化后模型变傻了先排查 group size 和校准集很多人量化完第一反应是“效果崩了”但往往不是 4 bit 本身的锅而是校准集太小或 group size 太大。GPTQ/AWQ 都需要一小部分文本做校准拿 128 条短文本和一个精心选的 512 条长文本结果能差出一大截。我建议校准集要覆盖三类内容代码、中英文混合对话、逻辑推理长文。别图省事只喂一种。另外跑完量化一定要做“量化前后困惑度对比”用同一测试集分别算 PPLperplexity如果 PPL 上升超过 5%就要考虑调小q_group_size或换 AWQ。5.2 为什么显存没降到理想值可能被 KV cache 吃了开头那位群友就是这个问题。他盯着权重显存看但实际显存大头可能被 KV cache 占了一半。KV cache 的大小计算很简单2K 和 V × num_layers × num_heads × head_dim × seq_len × batch_size × 每个元素字节数。当max_model_len设得很大、batch 又高时KV cache 会吃掉几十 GB哪怕权重已经 4 bit。解决思路有两个一是调小max_model_len或max_num_batched_tokens二是用 PagedAttention 和块式分配vLLM 默认就是让 KV cache 按需分配。如果你发现显存长期跑不满但 OOM多半是碎片化可以调整gpu_memory_utilization和max_num_seqs。5.3 INT8 和 FP8 选哪个关键看你手上是什么卡热词里反复出现 fp8 int8 ai 区别、fp16 bf16 int8 的速度即可说明越来越多人在纠结精度格式。我做了一张对比表直接抄作业用指标INT8FP8 (E4M3/E5M2)FP16/BF16表示方式整数浮点浮点动态范围小中大精度保持靠 scale 补偿天然浮点范围广最好硬件支持消费级到数据中心全覆盖需 H100 级及以上原生支持全覆盖运算速度快很快原生支持时慢于两者适用姿势W4A8 的组合计算端端到端 FP8 推理基准配置对大多数普通开发者和中小团队我建议先上 INT8因为兼容性最好。FP8 在 A100 上其实没有原生加速实际表现不一定比 INT8 强只有当你确定生产环境的卡支持 FP8 且框架适配到位才值得折腾 FP8。5.4 MoE 负载均衡和路由不均是隐藏瓶颈热词里还有“moe 负载均衡代码”这是 MoE 推理里常被忽略的一环。如果路由分配不均衡有些专家被疯狂命中有些几乎空闲那么即使整体显存够也会出现单卡计算热点和延迟抖动。训练侧会通过辅助损失aux loss鼓励均衡路由但推理侧的推理引擎也值得关注。在 vLLM 里可以观察每个专家的算力利用率如果发现有专家长期热点可以考虑增大tensor_parallel_size把专家切得更碎调整路由阈值或采样策略如果你的模型支持 topk 之外的采样把热点专家单独做 expert offload 的反向——即全放显存不换出。这块调整很依赖具体模型我建议先在离线脚本里统计路由分布再决定是否动推理引擎的调度参数。5.5 服务化部署要小心CPU offload 和磁盘 IO 是延迟刺客如果你采用“W4A8 专家卸载”的组合最怕的是冷专家命中时再去磁盘读权重。NVMe 顺序读能到 3~5GB/s但随机小文件读很慢而且每次换入换出都有内核态开销。我实测下来冷专家首次调用可能比热专家多出 50~200ms 延迟如果业务有 P99 延迟要求就得谨慎。几个缓解技巧把冷专家权重用mmap映射到内存避免反复 read 系统调用在服务启动前预热“次热”专家到 CPU 内存的 page cache 里如果冷专家命中率极低低于 0.1%甚至可以接受首次延迟用异步加载掩盖掉。6. 从 W4A8 再往后我的实际使用体会与后续可玩的方向写到这里我想把个人经验总结一下。W4A8 这套方案我用了快一年最大的体会是它不是银弹但它是当前性价比最高的一档组合。如果你要做 MoE 大模型部署优先把 W4A8 跑通再去考虑 FP8、speculative decoding、expert offload 这些进阶玩法。很多时候模型效果不够好不是量化的问题而是你的校准集、评估流程、显存规划出了问题。再分享一个小技巧部署之前先打印一份显存规划表。把模型参数量、目标位宽、KV cache 预算、激活缓冲、CUDA 预留各列一行加起来不超过物理显存 90% 再动工。我见过太多人上来就乱调参数最后根本分不清是量化的问题还是显存不足的问题。后续值得尝试的方向包括把 W4A8 和 speculative decoding投机采样结合用一个小 draft 模型配合大模型验证吞吐还能再抬一截或者把 4 bit 权重的 scale 也一起量化掉double quant把存储精度进一步压到接近 3.5 bit 级别再或者试试 onnx 那条干净的部署链在边缘设备上跑一个小号 MoE。最后再强调一次关键结论4 bit 存储负责把模型塞进显存INT8 计算负责让矩阵乘跑得快MoE 调度负责不让所有专家同时挤进来。这三件事想清楚、配合好一台 80GB 的卡也能稳稳托住百亿到千亿级别的 MoE 模型。希望你按这篇文章的流程走一遍之后再看到“4 bit MoE”的帖子不会再被显存数字绕晕。