优化一:混合精度,好钢用在刀刃上优化二:两级分组 + 双重量化写在最后

想在自己的电脑上跑大模型,第一道坎就是显存。一个 7B 的模型原封不动塞进内存,十几个 G 起步,普通机器直接劝退。这时候 llama.cpp 就派上用场了——它靠一手量化优化,硬是把模型"瘦身"到能跑的程度。Ollama 这类工具之所以好用,底层站着的就是它。

量化原理本身不难理解,就是把模型里的浮点权重从 FP16 压到 INT4,用精度换体积。但 llama.cpp 的厉害之处在于,它不光会压,还压得有讲究。今天就把它的两个核心优化拆开看看:混合精度和双重量化。

llama.cpp 里的量化类型五花八门,有 I-Quants、K-Quants 两大类,K-Quants 下面又分 Q4_K_M、Q5_K_M 等等。挑个最有代表性的说,就是 Q4_K_M——用得最多,官方也默认推荐。

这三个字母各代表啥?Q4 表示用 4 位来量化权重,K 表示用的是 K-Quant 优化算法,M 则是"中等平衡"的意思。说白了,它是在精度和体积之间找了个性价比最高的档位。

大模型是一层一层摞起来的,Transformer 架构里,注意力层这种关键部分,重要性跟普通层不是一个量级。如果所有层统统用 INT4 一刀切,那就是吃大锅饭——不管重不重要,待遇一样。

混合精度的思路很简单:大部分层用 4 位,但给注意力层这类重要层的关键张量多分点精度,比如 5 位或 6 位。

有人可能要问,张量是啥?简单说,数值是标量,一维数组是向量,二维数组是矩阵,这些统称张量。模型权重本质上就是一堆张量,量化就是对它们逐块压缩。

这个策略翻译成人话就是:重要的地方多给点预算,不重要的地方能省则省。模型瘦了身,精度损失还控制在可接受范围内。

这部分才是真正的硬核操作。

量化不是把整个权重矩阵一股脑压成 INT4,而是分组进行的。llama.cpp 的做法是搞了两级分组:

打开网易新闻 查看精彩图片

第一级,把权重分成一个个超级块,每个超级块含 256 个权重参数。第二级,每个超级块内部再分成 8 个子块,每个子块 32 个权重参数。

为什么不直接按 32 个一组分一次就完事?这就得说到量化的"密钥"问题了。

量化本质上有点像加密:把 A 转成 B,用的时候再转回来。转回来靠的是啥?缩放因子(scale)和偏移量(min),这就是量化的"密钥",得跟着模型一起存。

算笔账就明白了:假设 512 个权重参数,按 32 个一组,一共 16 组。每组存两个 FP16 的密钥,光密钥就得 512 位。而所有权重按 INT4 量化,本身也就 512 位——好家伙,密钥的体积跟权重一样大,模型直接又胖回去了。

怎么办?聪明人一眼就看出路子了:把密钥也量化了呗。这就是双重量化——子块的密钥不直接用 FP16 存,而是压成 6-bit 整数。但问题来了:量化密钥也得有"密钥的密钥"才能还原,放哪?

放上一级。没有上一级?那就创造上一级——两级分组就是这么来的。

于是每个超级块额外存两个高精度 FP16 的"密钥的密钥",用来还原子块那些 6-bit 的密钥。再算一笔账:512 个权重,按 256 个一组分两个超级块,超级块存 2×2 个 FP16 共 64 位;16 个子块的密钥用 6-bit 存,6×2×16=192 位。加起来 256 位,对比一级分组 512 位的消耗,正好省一半。

子块分组小(32 个),量化粒度细,精度保住了;双重量化又把密钥的体积膨胀压了下去。这就是 llama.cpp 的平衡艺术——在精度和体积之间,抠出那个性价比的天花板。

回头看看,llama.cpp 的优化思路其实很朴素:不搞花活,就是算账。哪里重要就给哪里多投点精度,元数据太占地方就给它也瘦个身。整套设计环环相扣,不得不服。

你在本地部署大模型的时候用过 Q4_K_M 吗?还是喜欢别的量化档位?评论区聊聊你的实测体验。