专家感知量化:以接近 Q2 的大小实现接近 Q4 的质量?Expert-aware quantisation: near-Q4 quality at near-Q2 size?
针对混合专家模型在特定任务下不同专家重要性不同的问题,提出了一种基于性能分析的量化策略。该方法通过分析找出对特定任务至关重要的“热”专家,并对不活跃的“冷”专家进行极度激进的量化压缩。实验结果表明,这种策略能够在保持接近 Q4 量化精度的同时,实现接近 Q2 量化的体积缩减。这为在本地环境高效运行大型 MoE 模型提供了极具潜力的解决方案。
Martin Alderson
在研究和撰写上一篇关于 KV cache 压缩历史的文章时,我突然想到,尽管在 KV cache 效率方面已经有了大量的实践研究,但实际的模型权重量化仍然相当生硬。
这是合理的——在拥有数以万计 GPU 的大规模场景下,权重本身在很大程度上并不是巨大的效率瓶颈,而且 KV cache 开始占据内存使用的主导地位。
但是,对于我们这些无法拥有装满 HBM 内存仓库的底层平民来说,这是一个问题。本地模型的关键限制(主要)仅仅是如何将权重加载到足够快的设备中。
性能分析
我花了很多时间对应用程序进行性能分析以提升其性能,几个月前,我还构建了一个专门针对 MoE 模型进行同样分析的工具。
这让我陷入了思考。如果我们不是简单地将整个模型量化到某个级别——也就是我刚才提到的生硬做法——而是先对模型进行性能分析,然后针对那组特定任务选择性地量化“冷”专家,结果会怎样?
在这项研究中,我针对 C++ 编程任务对 Qwen3.6 35B-A3B 进行了性能分析。有一个重要的细节值得在一开始就指出:这个特定模型的负载非常均衡,因此在读取代码时,它几乎将工作均匀地分布在各个专家之间(每层的基尼系数接近 0——基本上是均匀的)。这种选择性只有在它生成代码时才会显现出来。
而在生成代码时,它高度集中。在运行少量 C++ 生成提示词后,每层的基尼系数跃升至 0.61——这意味着在代码生成期间,256 个专家中排名前 32 的专家处理了约 50% 的路由,而随机情况下的预期值仅为 12.5%。这种集中性正是我们可以利用的:如果只有一部分专家对该任务真正重要,我们可以将这些专家保持在高精度,并大幅压缩其余部分。
一旦我们获得了这些显示哪些专家是热门(在特定领域大量使用)而哪些是冷门(未使用)的追踪数据,我们就可以进入下一步了。
模型神经外科手术
这花了(Claude Code)相当长的时间——讽刺的是,我怀疑 Fable 可能非常适合执行这类任务。
核心思想是允许 llama.cpp 读取每个专家不同级别的量化,这遇到了不少问题。不过最终,它还是解决了(自主运行了足足 90 分钟!)。
它还编写了一个脚本来获取性能分析数据,并对每个专家进行量化。
结果
以下所有数字均是在 CPU 上测得的困惑度(越低越好)。“读取代码”是一批预留的真实 C++ 源代码;“编写代码”是一组模型自己生成的 C++ 代码。分层模型将 64 个(总共 256 个中的)“热门”专家集保持在高精度,并将其余 192 个“冷门”专家降至 2-bit。
* “编写代码”的评估是由 Q8 模型生成的,因此针对它对 Q8 进行评分会是循环论证——故将其省略。
有几个情况非常显眼。
首先是基准。全尺寸的 Q8(35 GB)在读取 C++ 时的得分为 1.568,而对所有内容进行“生硬的” Q2 量化(13 GB)后,得分跃升至 2.103——体积缩减到不到一半,但质量却大幅下降。(困惑度大致上是“模型对每个 token 的惊讶程度”——因此从 1.57 变成 2.10 意味着模型明显变笨了,虽然没到被脑白质切除的地步,但显然更差了。)
现在来看看实际的实验。我对分层方法进行了两种方式的 A/B 测试:随机选择(作为对照组,随意挑选热点专家)与性能分析选择(保留我们通过性能分析标记为 C++ 热点专家的部分,并大幅压缩冷门专家)。性能分析版本每次都获胜:在两个精度层级和两个评估集上,它取得了四战四捷的成绩。在 Q8 热门 / Q2 冷门(18 GB)的配置下,随机分层得分 1.667,而性能分析版本将向 Q8 靠拢的差距挽回了近一半,达到了 1.620。因此,核心思想是有效的——保护哪些专家很重要,而性能分析能告诉你该保护哪些。
但我必须坦诚地指出一个现实问题:统一的 Q4 表现非常出色。在代码方面,4-bit 几乎是无损的——Q4(20 GB)得分为 1.582,基本上与 Q8 持平。因此,尽管设计得很巧妙,但花哨的 Q8 热门/Q2 冷门模型,实际上并没有击败在相似大小下统一使用 Q4 的做法。
这种方法的优势体现在当你尝试比 Q4 更小的体积时。我构建了一个 Q4 热门 / Q2 冷门的版本——热门专家使用 4-bit,冷门专家使用 2-bit——其大小仅为 14 GB,比直接的 Q2 模型只多出 1 GB。它的阅读得分为 1.635,写作为 1.477——仅凭这额外增加的 1 GB,就挽回了 Q2 和 Q4 之间约 90% 的质量差距。这才是真正的成果:通过将比特数分配给对任务真正重要的专家,从而在接近 Q2 的体积下实现了接近 Q4 的质量。
结论
这绝对还远未达到生产可用的标准,需要由对 llama.cpp 代码库比我精通得多的人来进行大量工作。我仅在 CPU 上进行了运行,速度(非常)缓慢,我的评估集很小,而且毫无疑问,由 Claude 随性生成的实现还可以进一步完善。
关于这一点,还有很多有趣的角度值得继续研究。我在这里尝试了几个分层组合(Q8/Q2 和 Q4/Q2),但完全可以进一步推进——将冷门专家的精度降至 2-bit 以下(IQ1/IQ2),会使模型的体积小于统一 Q2 的大小,同时保持热门专家的精度。你可以想象出一个完整的梯度:最热门的专家保持高精度,然后随着专家热度的降低,逐步采用更激进的量化策略。
还有一个问题是该保留多少个专家的精度。我在这里保护了 256 个专家中的 64 个,事实证明这相当慷慨——慷慨到即使随机挑选这 64 个专家,本身也能挽回 Q2 到 Q4 之间约 80% 的质量差距。这不像乍看之下那么令人惊讶:MoE 层的输出是其激活专家的加权和,因此无论你选择保留哪四分之一的专家,只要保持它们的准确就能稳固结果。性能分析则通过确保实际被激活的专家正是受保护的专家,从而赢得了最后约 10% 的提升。我预期它真正能拉开差距的地方在于小规模的热点集合——如果只保持 16 个专家的精度,随机选择多半会保护到冷门专家并导致崩溃,而性能分析能准确告诉你哪几个才是关键。这就是我接下来要做的实验:它应该能进一步缩小模型体积,并拉大性能分析与随机选择之间的差距,这也正是整套方法体现出其价值的地方。
但最后我认为这是一个非常有趣的方法。如果我们能从真实世界的 llama.cpp 执行中获取大规模的性能分析数据,它可能会带来质量上的巨大飞跃。我可以想象这样一种场景:执行框架检测任务所属的领域,下载针对该特定领域的量化模型,然后通过它运行提示词。这真正利用了存储便宜(相对而言)而 RAM 昂贵这一事实。因此,在磁盘上保存同一模型的一堆不同量化变体是相当可行的。
现有技术
我应该补充一点,在这个领域已经有相当多的现有技术了。我发现的与其最接近的是 DynaExq,它在服务提供期间根据路由追踪记录动态地做非常相似的事情——但我找不到任何人将其作为静态的、基于领域性能分析的量化打包成单个 GGUF 文件来发布。以下是我在做这项工作时阅读的一些链接:
最接近的现有技术(每个专家的精度可变):
基础量化:
现实中的端侧路由:
如果你正在做这类优化,我很乐意听取你的意见——请随时通过我的联系页面与我取得联系。
需要完整排版与评论请前往来源站点阅读。