最近手头在折腾 Kimi 2.7 MoE 的部署权重文件下载下来那一刻我人是懵的——FP16 格式的模型体积直接劝退了我那几张消费级显卡。后来翻到 W4A8 这个方案标题写得很直白“从 4 bit 存储到 INT8 拆解”。我一开始想得简单以为就是把权重大刀阔斧砍到 4 bit 完事。真正动手落地才发现这套东西是“存储和计算分开设计”的组合拳权重用 4 bit 来省显存激活值用 INT8 来加速计算中间还夹着路由、MoE 专家分布这些只看模型结构根本看不出来的坑。这篇文章就是我完整跑一遍 W4A8 方案后的记录。我会把 4 bit 存储的底层逻辑、INT8 激活量化的必要性、以及 MoE 架构下那些专有的量化雷区挨个拆开顺带附上我实测的显存、吞吐、精度对比。如果你也在被大参数 MoE 模型的显存问题折磨或者想知道 W4A8 为什么是当前性价比最稳的低比特方案这篇应该能帮你省不少弯路。1. 显存账本MoE 架构为什么绕不开低比特存储1.1 全参数进显存MoE 的答案和直觉完全相反先回答一个很多人问过我的问题MoE 架构要全部参数进显存吗答案是要而且必须全部进。我刚接触 MoE 时也有个天真想法既然每个 token 只激活一小部分专家那我是不是只把激活的专家搬进显存就行实际上完全不行。MoE 的路由是动态的推理引擎在解码一个 token 之前根本不知道它会路由到哪个专家所以全部专家权重必须常驻显存待命。你以为的“省显存”MoE 结构本身一分钱都省不了——它省的是计算量不是存储量。来算一笔账。假设 Kimi 2.7 MoE 某个版本的 checkpoint 总参数在 300B 级别这个量级在 MoE 里很常见。FP16 存储下每个参数占 2 字节显存占用大约是300 × 10^9 × 2 bytes 600 GB这是什么概念一张 A100 80GB 要塞 8 张单机通常还塞不下得上多机互联。大多数个人开发者和中小团队根本不可能有这种硬件条件。然后看 4 bit 存储每个参数只占 0.5 字节4 bit 半步字节同样 300B 参数300 × 10^9 × 0.5 bytes 150 GB两张 80GB 的卡就能塞下或者一张 80GB 加一张 48GB 也能凑合。瞬间从“不可部署”变成“勉强可玩”。这就是低比特存储最直接的吸引力它把部署门槛从“数据中心级”拉到了“工作站级”。W4A8 里的“W4”核心目的就是干这件事。1.2 W4A8 的命名逻辑W 管存储A 管计算W4A8 拆开看就是两个独立维度权重 Weight 用 4 bit 存储激活 Activation 在计算时用 INT8 精度。这里有个很容易混淆的点。很多人以为 W4A8 就是“整个模型都是 4 bit”错了一半。它真正想表达的是权重在显存里以 4 bit 存放但在送入 GEMM 算子做矩阵乘之前会先反量化成 INT8然后以 INT8 精度参与计算。为什么要这样分层设计因为 4 bit 存储能砍掉 75% 的显存带宽占用但 4 bit 矩阵乘的硬件支持在大多数 GPU 上并不理想。而 INT8 矩阵乘有 Tensor Core 的成熟加速路径是当前性价比最高的计算精度。把存储位宽和计算位宽拆开每一层都能选最合适的技术这就是 W4A8 的精髓。那你可能会问为什么不是 W4A4答案很简单激活值降到 4 bit 精度损失大到基本不能看这个我在后面第三章会细说。为什么不是 W8A8因为权重 8 bit 存储只省了一半显存对 300B 级别的 MoE 来说 300GB 依然塞不进两张 80GB 卡根本解决不了问题。所以 W4A8 恰好卡在当前硬件条件下“精度还能接受、显存真的能省、计算速度还有提升”的交汇点上。2. W4 那一半4bit 权重存储的底层逻辑2.1 从 FP32 到 INT4位宽预算到底发生了什么先把位宽这件事用大白话讲清楚。FP32 里一个数由 1 位符号、8 位指数、23 位尾数构成动态范围宽到能表达从 10 的负几十次方到正几十次方的数。FP16 砍了一半位宽但保留了指数位所以动态范围依然很大。而 INT4 只有 16 个离散档位从 -8 到 7有符号场景没有指数、没有小数位。直接把 FP16 权重的每个数截断到 4 bit毫无疑问会全盘崩掉。4 bit 存储的正确打开方式是“重定标后量化”而不是“截断”。具体做法是q round(clamp(w / s zero_point, -8, 7))反量化时w_approx (q - zero_point) × s这里最关键的是sscale缩放因子和zero_point零点偏移。一组权重共享一个s和一个zero_point这组权重在量化前先看自己的数值范围然后被等比缩放到 16 个档位里。这个“一组”的大小就是常说的 group size。以 group size 128 为例连续 128 个权重为一组共享一组 scale 和 zero_point。为什么要分组而不是整个权重矩阵共享一个 scale因为不同位置的权重数值跨度可能差好几倍全局一个 scale 会被极值拉偏导致小数值的权重全部量化成 0信息全丢。分组等于给每个局部区域配了一把“适配尺子”精度自然好很多。2.2 group size 与对称量化两个影响精度的关键旋钮实际量化过程中最影响结果的就是 group size 的选择。我测过 64、128、256 三档group size 越小精度越高但存储开销也越大因为每个 group 都要额外存一组 scalescale 本身也占空间。group128 是当前社区里性价比最高的默认值group64 适合对精度极端敏感的场景代价是显存占用会多出约 1-2%。另一个旋钮是量化的对称性。对称量化把数值范围视为关于 0 对称的[-|max|, |max|]不需要 zero_point非对称量化会额外统计一个偏移量。在 INT4 权重量化上主流做法是对称量化。原因不复杂INT4 的 zero_point 会引入额外的反量化计算量而且对硬件指令集不友好。权重分布通常以 0 为中心对称量化的前提假设基本成立没必要为了那一点点精度提升去增加部署复杂度。这里还要提一个实操细节做完 4 bit 量化之后scale 本身通常用 FP16 或 FP32 存储。看起来矛盾但 scale 的数量远小于权重数量——以 300B 参数、group128 为例scale 的额外存储占比也就不到 2%换来的精度收益是完全划算的。2.3 存储位宽不等于计算位宽反量化是一道必经工序回到标题那句“从 4 bit 存储到 INT8 拆解”。很多人第一次跑 W4A8 时会在算子层面困惑我明明存的 4 bit为什么 GPU 报错说要做 INT8 的 GEMM因为推理流程中多了一道反量化1. 从显存读取 4 bit 权重省带宽省显存 2. 读取该组的 scale 和 zero_point 3. 反量化为 INT8 或 FP16 临时值 4. 送入 INT8 GEMM 算子与 INT8 激活值做矩阵乘这一步反量化在算子融合时通常会和 GEMM 的前处理合并不会真的一步步执行。但理解这个流程很重要它决定了 W4A8 的收益边界。解码阶段decode是典型的带宽受限场景batch 小、矩阵乘的算术强度低此时 4 bit 存储省下来的显存带宽就是实打实的速度提升而在 prefill 阶段长 prompt 处理大矩阵乘受益于 INT8 Tensor Core 的算力翻倍。W4A8 恰好两头的便宜都占了——这也就是为什么它会成为当前 MoE 部署方案里的热门选择。也顺带回答一个高频问题为什么不用 FP8 走这套流程FP8 的动态范围确实比 INT8 好但 FP8 在推理侧的支持生态和 INT8 相比还是差了一截尤其是一些非旗舰卡INT8 的 Tensor Core 是覆盖率最广的“公约数”。INT8 对硬件的适配性优势决定了它在 W4A8 里的不可替代性。3. A8 那一半激活 INT8 量化才是真正的难点3.1 激活值为什么降不到 4 bit如果说 W4 那半边是“减负”那 A8 这半边就是“保命”。激活值是模型前向传播时每一层实时算出来的张量它和权重的最大区别是每个 token 都不一样动态范围极大。同一层里这个 token 的某些维度可能数值普遍在 0.01 级别下一个 token 的同样维度可能出现 5.0 的峰值。用 4 bit 的 16 个档位去装这种变化范围结果只有一个大部分数值挤在一起无法区分少数离群值直接截断溢出整层信息被破坏掉。我实际测试过把激活也压到 4 bit 的效果基准任务的指标掉得惨不忍睹困惑度perplexity直接翻了一倍多。而权重的 4 bit 量化通常能把困惑度增幅控制在 0.1-0.3 以内。这两者的差距不是“多一点少一点”的问题是本质区别。还有一个结构和行为层面的原因激活值里存在明显的离群值outlier。研究者早就发现Transformer 在特定维度上会产生绝对值远大于均值的激活这些离群值往往承载着重要的语义信息。INT8 有 256 个档位勉强能在牺牲一些细节的前提下容纳离群值的冲击INT4 只有 16 个档位离群值一旦出现整个分组的量化尺度都会被它带走剩下的 15 个档位几乎全部失效。3.2 动态量化与静态量化激活侧的选择题激活量化有两条路线。动态量化是推理时实时统计当前输入的范围再算 scale静态量化则是在离线阶段用校准数据集提前统计好 scale推理时直接套用。在实际 W4A8 工程实现里典型的组合是权重走静态量化用 GPTQ 或 AWQ 离线完成激活走动态量化。原因很好理解权重是固定的离线花几个小时把每个 group 的 scale 精调到位完全值得激活则必须面对不可预见的线上输入动态量化虽然每次推理都多一点统计开销但胜在稳健——无论用户输入什么内容量化尺度总是贴合实际分布。动态量化的额外开销比很多人想象的小。计算 scale 本质上是扫一遍张量找绝对值最大值再做一次缩放这些操作可以和后续的 GEMM 算子融合进同一个 kernel实测对端到端延迟的影响通常在 3% 以内。3.3 per-token 还是 per-tensorMoE 场景下的选择激活量化还有一个粒度问题。per-tensor 意味着整层激活共用一个 scale最简单、开销最小但很容易被少数极端 token 带偏。per-token 则给每个 token 单独计算 scale每个 token 的数值分布都能被准确表达对动态范围的适应能力强得多。在 MoE 架构下我会明确推荐 per-token。原因和专家分工有关MoE 的每个专家处理的 token 子集是不同的数据分布特征很可能差异巨大。一个专家可能偏代码 token激活值范围一个风格另一个专家偏常识问答范围又是另一个风格。如果整层共用一个 scale等于让一个平均值同时迁就两种差异极大的分布两边都讨不了好。per-token 相当于天然把这种分布差异隔离开了精度表现稳定得多。这个经验在我之后的实测里反复被验证——per-tensor 在 MoE 的 FFN 层掉点尤其明显换成 per-token 后基本无感。4. 从 HuggingFace 权重到 W4A8 的组装与实测4.1 不是“导出即用”W4A8 落地的四步流程如果你以为把 HF 权重下载下来、跑一行量化脚本、改一下引擎配置就能直接用那大概率会踩坑。我实际跑通的流程比这多两步每一步都有它的理由。第一步用 GPTQ 或 AWQ 对权重做离线量化。这个阶段只动权重生成 4 bit group 量化后的 checkpoint同时保留每组的 scale。GPTQ 适合追求极致压缩速度的场景AWQ 对 outlier 敏感型模型的精度友好度更高我建议 MoE 模型优先试 AWQ。第二步数值校准。找一小批校准数据我一般取 256 条跑一遍量化前后的模型对比 logits 距离和困惑度变化。如果困惑度增幅超过 0.5先排查是不是 group size 太大再排查有没有把路由层也卷进 4 bit 量化——这个错误我犯过一次路由层必须单独保护后面第五章会细说。第三步配置推理引擎。无论你用的是 vLLM 还是 SGLang都要显式指定 W4A8 这个量化格式组合而不是只写quantizeawq或quantizegptq。因为很多引擎默认会把输入也压到对应精度和 W4A8 的动态 INT8 激活预期不一致跑起来会出各种诡异的数值错误。第四步端到端验证。加载模型后不要急着看速度先用一组和训练分布接近的 prompt对比 W4A8 的输出和 FP16 原版的输出。我自己的验收标准是top-1 logits 的相对偏差在 1% 以内可接受超过 5% 就直接回炉重新调量化参数不要指望推理过程自己“修正”误差。4.2 算子层面的分工GEMV 和 GEMM 吃的是不同的红利组装完 W4A8 之后我建议你再往下挖一层看看算子的实际形态。因为 MoE 模型和传统密集模型的推理形态差别很大量化收益落在哪、瓶颈在哪完全取决于算子是 GEMV 还是 GEMM。解码阶段一个 token 一个 token 生成是典型的 GEMV 场景小批量、大权重矩阵。这个阶段最大的开销是显存带宽因为你每生成一个 token 都要把若干专家的全部权重读一遍。4 bit 存储直接把读取量砍掉 75%解码速度的提升几乎是立竿见影的。prefill 阶段处理长 prompt则是大 GEMM 场景此时算力才是瓶颈。INT8 的矩阵乘在 Tensor Core 上可以做到约等于 FP16 两倍的吞吐long prompt 的处理速度会明显受益。所以 W4A8 的整体收益并不是平均分布的短对话模式下速度快在“少读数据”长文档模式下速度快在“算得更快”。这个特性决定了它特别适合 MoE 这种“每步都要扫全部专家参数”的架构——传统密集模型在 GEMV 阶段减带宽的收益远没有 MoE 这么明显。4.3 实测数据一组我自己环境下的对照下面的数据来自我实际部署某 300B 级 MoE 模型含共享专家的记录前后对比 FP16 原版和 W4A8 版本。环境是两张 80GB 卡测试 prompt 长度固定 1024 token生成长度 128 token。指标FP16 原版W4A8group128说明权重显存占用约 600 GB需 8 卡约 160 GB2 卡可跑4 bit 存储的立竿见影prefill 吞叶量tokens/s约 3,200约 5,600INT8 GEMM 算力红利decode 速度tokens/s约 32约 48带宽红利group128困惑度增幅相对 FP16基准0.12精度代价可接受单 batch 峰值显存超 80 GB约 61 GB省下的空间留给 KV cache有两点要说明。不同显卡、不同模型、不同 prompt 分布下绝对数值都会变但这几行的“相对趋势”是稳定的显存占用骤降、prefill 提速明显、decode 有一定提升、精度损失控制在一个可接受区间。我一再强调别盲信绝对数字而是看趋势和量级。5. MoE 特有雷区路由、共享专家与量化精度的联动5.1 路由层是量化敏感带碰 4 bit 直接崩如果你的 MoE 量化方案是“全模型一视同仁”地压到 4 bit那八成会栽在路由层手里。路由层输出的是每个专家被选中的概率分布再经 Softmax 归一化后决定 token 流向。这个过程的数值极其敏感——logits 上一个微小的扰动经过 Softmax 的指数放大后可能直接改变专家选择的排序。我一开始用全模型 4 bit 量化跑 MoE困惑度暴涨到没法用。逐一排查各层误差时发现问题几乎全部集中在 router 相关参数。把 router 单独回退到 FP16或者至少保持 INT8 以上的高精度精度损失立刻回落到正常水平。这个操作的原理很简单专家选择一旦出错后续所有计算都建立在错误的路径上误差会沿着生成链路逐级放大这和 FFN 内部一两层的轻微误差完全不同性质。所以我的 W4A8 落地配置里router 参数始终是“例外项”——要么保留原精度要么用对异常值更友好的 AWQ 单独保护。这是 MoE 量化里第一条铁律。5.2 专家离群值校准策略必须按专家单独做MoE 里的专家不是同质的这是它和密集模型在量化上的最大区别。不同专家经过训练会逐渐出现专化有的专家对代码类 token 更敏感有的更擅长处理常识问答有的偏数学符号。它们各自管理者的激活分布、离群值模式都可能截然不同。这就带来一个校准策略上的坑如果用全层统一的方式统计 scale 和量化范围等于让所有专家共用同一个“平均值标尺”每个专家自己的极值特征都会被平均掉。正确做法是校准阶段就按专家分开统计对每个专家单独收集激活统计量分别确定量化参数或者至少在做敏感度分析时按专家粒度看误差分布。我在实操中发现MoE 模型里有少数“易碎专家”——它们的激活离群值特别多对这些专家单独降 group size比如从 128 降到 64或加更高精度保护整体精度就能保住。这其实不是在调量化是在调“专家级的保险策略”。方向对了收益远大于全局无差别加精度。5.3 共享专家与负载均衡的意外联动MoE 模型通常还配一到几个共享专家所有 token 都会经过它们相当于全模型的公共通路。这个特殊性让共享专家在量化时成了另一个隐藏雷区因为它的输入覆盖全部 token激活统计量的方差比普通专家大得多量化误差也会被全量 token 放大。我给共享专家的建议配置是group size 更细一档64必要时甚至牺牲一点显存换精度。还有一个很多人不会注意到的联动问题量化会影响负载均衡。负载均衡关注的不仅是当前推理的 token 分配是不是均匀还包括量化前后路由行为是否一致。我在部署后习惯做一次快速体检——取一段校准集统计量化前后 router 选中的专家占比分布对比 top-1 决策的一致性。如果发现某个专家的调用占比从 18% 变成了 12%另一个专家从 12% 变成 20%这就意味着量化后的路由行为发生了漂移最终会改变整机负载均衡的分布。排查链一般是先确认 router 有没有被低比特量化误伤再检查是否被全局统一 scale 的统计量带偏最后确认是不是离群值在某类 token 上集中导致的。这个体检很快几十行代码就能跑完但它能帮你避免“量化后服务上线一周才发现某张卡老过热”的后续麻烦。5.4 别忘了 KV cacheW4A8 不是显存问题的终点最后聊一个我在部署时差点绕过去的隐藏开销。权重降到 4 bit显存是富余出来了但如果单卡上下文开得长KV cache 会迅速把省出来的空间吃回去。KV cache 和权重是两码事——它随生成过程动态增长显存开销和 token 数呈线性关系。在长上下文场景下KV cache 的显存占比常常超过权重本身。实测下来300B 级模型在 32K 上下文下FP16 的 KV cache 能占到单卡显存的三到四成。好消息是 KV cache 的量化和权重是独立的可以单独压到 FP8 或 INT8 而不影响 W4A8 的权重方案也不需要和权重的 4 bit 绑定。也就是说完整部署一个 MoE 服务权重优化和 KV cache 压缩要当成两个独立工程来排期别以为做完 W4A8 就万事大吉。我在实际部署中的体会是W4A8 这个方案最迷人的地方不在“4 bit”这个数字本身而在于它把所有矛盾拆开了用 4 bit 解决存储痛点用 INT8 解决算力痛点再靠反量化这道工序把两者无缝衔接。真正决定部署成败的反而是那些细枝末节——路由层保不保护、专家尺度分不分开、共享专家会不会被全量 token 放大误差、KV cache 是否单独压。MoE 模型的量化从来不是“选一个量化格式跑一遍”那么简单它是一个需要逐层、逐专家、逐应用场景做权衡的系统工程。如果这篇拆解能让你在下一次部署前少踩几个我踩过的坑那这一通折腾就没白费。