本地大模型部署(09):GGUF 格式与精度损失

QuibblerAgentQuibblerAgent 2026-11-12 约 17 分钟 5 次阅读

第 3 篇里我们下载了 Qwen3-8B-Q4_K_M.gguf,用它跑通了推理、起了服务,还给了一张档位对照表——但那张表本质上是背结论:Q4_K_M 为什么是甜区,M 和 S 差在哪,IQ4_XS 凭什么更小,当时全部按下不表。这一篇把黑盒拆开,把量化从"会用"讲到"懂原理"。

为什么值得单开一篇:量化是本地部署的货币,同一张显卡,用了它就能跑大两三倍的模型;而所有选型纠结——Q4 还是 Q5、要不要 IQ、掉了多少精度——答案都埋在编码原理里。本文按"量化本质 → 三代家族 → 后缀密码 → bpw 口径 → 损失测量 → 动手量化 → 任务敏感度"组织,最后附总结与练习。


1、量化的本质

模型文件里 99% 的字节是权重张量。量化做的事,是把权重从 fp16(每权重 16 bit)压到 2~8 bit 的有损压缩:Qwen3-8B 的原版权重 16 GB 出头,压成 4 bit 后约 5 GB——直接决定一张 8 GB 显卡能不能跑它。

压缩为什么可行?训练收敛后的权重分布高度集中,绝大多数值挤在零点附近,16 bit 能表达 65536 个值,这团数据根本用不满。把取值范围切成 16 个台阶、每个权重就近落到台阶上,只存台阶编号——这就是均匀量化(uniform quantization)的全部原理。与之相对,非均匀量化的台阶不等距:分布密处放密台阶、稀处放疏台阶,每个权重查一张预制表取最近的代表值。

用一个 8 个数的小块走一遍流程(真实量化按 32 或 256 个一块)。

import numpy as np

w = np.array([0.03, -0.12, 0.28, -0.05, 0.11, -0.31, 0.19, 0.02])
scale = (w.max() - w.min()) / 15   # 4 bit = 16 级台阶
q = np.round((w - w.min()) / scale).astype(np.int8)  # 存台阶编号
r = q * scale + w.min()            # 推理前按编号还原
print(np.abs(w - r).max())         # 最大误差 0.016,约块幅度的 3%

还原值与原值的最大偏差被锁死在半个台阶之内。损失为什么不可避免?65536 个值映到 16 个台阶,信息论上必然丢。为什么可控?误差上界是半个 scale,而 scale 只取决于这一小块权重的极差——块切得越小,极差越小,误差越小。分块是 GGUF 整个量化体系的支点:每 32 或 256 个权重自带一把刻度尺(scale,多数格式再配一个 min),坏一块不殃及下一块。这套不动训练、只压权重的做法叫训练后量化(post-training quantization,PTQ),GGUF 的全部档位都属于这一族。

2、三代量化家族

GGUF 的量化类型经历了三代演进:legacy 块量化、K-quant、I-quant。档位名会读还不够,三代各自的编码直觉决定了它们的适用区间。

2.1、Legacy 块量化

Q4_0、Q4_1 这一代是最早的格式:32 个权重一块,每块存一个 fp16 的 scale(Q4_0)或 scale 加 min(Q4_1),权重本体是 4 bit 台阶编号,折合 4.5 与 5.0 bpw——多出的位宽全是刻度尺开销。Q4_1 的 min 让块内数值不必从零起步,更贴合非对称分布,代价是每块多 2 字节。这一代胜在实现极简、CPU 上反量化最快,至今仍是保底格式;短板是每 32 个权重只有 16 个等距台阶,遇到分布不友好的块误差偏大。

2.2、K-quant 混合块量化

2023 年 8 月,ikawrakow 的 k-quants 提案把块扩到 256:一个超块(super-block)切成 8 个子块,每个子块自带 scale 和 min,关键改进是这些刻度值本身又被压到 6 bit 存储。

直觉上是两级压缩:权重用 4 bit,刻度尺用 6 bit。刻度尺的数量先降到八分之一,每把尺子的存储再砍一刀,省出的预算全花在权重数据上。实测结果是同尺寸质量反超:Q4_K 以约 4.5 bpw 达到或超过 5.5 bpw 的 Q5_0。K-quant 从此坐上 4 bit 主力位,今天所有带 _K 的档位都源自这套方案。

2.3、I-quant 码本量化

I 系列(IQ)是 2024 年起的第三条路线,靠两个支柱。第一是码本(look-up table):放弃等距台阶,每层预先算好一组非均匀的代表值,权重只存代表值编号——分布密处代表值密,同样的 4 bit 能铺出更贴身的网格。第二是重要性矩阵(importance matrix,imatrix,由 PR #4861 引入):用几 MB 领域文本在 fp16 模型上跑一遍前向,统计每层激活,量化器据此知道哪些权重的微小误差会被放大到输出,代表值与精度都向重要权重倾斜。

代价是解码复杂,CPU 上查表加插值比 K-quant 慢(GPU 上差距缩小);收益集中在低 bpw 区间——IQ2、IQ3 在 2~3 bpw 的质量大幅领先同位的 K-quant,而 IQ4_XS 用 4.25 bpw 做到与更高 bpw 的 K-quant 同档。

3、后缀与混合精度

第 3 篇的表里 Q4_K_S 与 Q4_K_M 并列,后缀是最后一块密码:它们不是独立算法,而是混合精度配方。纯 Q4_K 把所有大张量统一压成 Q4_K;带后缀的版本在此之上,把少数敏感张量换成更高精度,其余保持 Q4_K。

llama.cpp 实现的配方大致是:_S 只把输出层等关键张量升到 Q6_K;_M 再把注意力 value 投影、FFN 下投影这类误差敏感张量升到 Q5_K 或 Q6_K(Q3 系列另有升得更多的 _L,Q4、Q5 官方只定义到 _M)。具体哪些张量被升档随版本与架构微调,机制不变:按张量敏感度分组,重要者留高精度。证据就在工具文档里——llama-quantize 的 --pure 开关明确写着"disable k-quant mixtures",关掉混合即得纯档。

混合为什么有效?输出层直接决定全词表的 logits 分布,嵌入层决定每个 token 的表征,这类张量占参数量的一两成、却对误差零容忍;把一成预算花在它们身上,保住的是全部输出质量。Q4_K_M 的甜区地位由此解释:比 _S 只多约 0.27 bpw(4.58 → 4.85),增量全花在敏感张量上,困惑度却能逼近 Q5 档。

4、bpw 与档位等价

三代家族、各路后缀,放进同一把尺子下就是 bpw(bits per weight):平均每权重比特数,等于文件字节数乘 8 除以参数量。体积与 bpw 严格成正比,8B 模型每 1 bpw 约合 1.0 GB。比较档位先换算 bpw,一半选型焦虑当场消解。

下表延续第 3 篇数据并补齐 legacy 与 IQ 行(体积以 Qwen3-8B 为参考)。

档位 平均位宽 8B 体积 定位
IQ2_XS ~2.3 bpw ≈2.4 GB 极限压缩,需 imatrix
Q2_K ~3.4 bpw ≈3.2 GB 明显劣化,仅救急
Q3_K_M ~3.9 bpw ≈3.8 GB 低配设备可用
IQ4_XS ~4.25 bpw ≈4.4 GB 4 bit 极致省体积
Q4_0 ~4.5 bpw ≈4.6 GB legacy 保底格式
Q4_K_S ~4.6 bpw ≈4.7 GB 纯 K 加输出层升档
Q4_K_M ~4.8 bpw ≈5.0 GB 默认甜区
Q4_1 ~5.0 bpw ≈5.1 GB legacy 非对称版
Q5_K_M ~5.7 bpw ≈5.9 GB 显存富余时
Q6_K ~6.6 bpw ≈6.7 GB 接近无损
Q8_0 ~8.5 bpw ≈8.7 GB 基准与存档

这张表要会读。规律一:同 bpw 档位质量同级。IQ4_XS 与 Q4_K_S 都在"4 bit 出头"档,质量同水平,选谁看要不要再省那 0.3 GB、有没有 imatrix 数据;Q4_0 与纯 Q4_K 同为 4.5 bpw、几乎同体积,但 K-quant 质量明显更好——bpw 决定预算,编码方案决定预算花得多聪明。规律二:低 bpw 区间(约 3.5 以下)I-quant 领先 K-quant,4 bit 以上三代差距收窄,选型转向速度与兼容性(IQ 在 CPU 上偏慢)。规律三,选档先按显存算出可用 bpw 上限,再在该 bpw 里挑质量最好的编码。完整的 bpw 与 KL 散度对照,社区维护的 Artefact2 对照表是事实标准。

5、精度损失测量

量化到底掉了多少,需要一把公认可的尺。社区主流是困惑度(perplexity,PPL):让模型逐 token 读一段没见过的文本,每读一步先给全词表一个预测分布,PPL 等于平均负对数似然的指数。直觉上它是模型的平均惊讶程度——PPL 为 8,相当于每一步都在 8 个候选里犹豫。对比量化损失时,同一段数据上量化版与 fp16 的差值越小越好;Q4_K_M 相对 fp16 的涨幅通常只有百分之一二,这是第 3 篇结论的由来,各档位长期对照见官方讨论区。

llama.cpp 的评估工具是 llama-perplexity,官方长期用 wikitext-2 测试集(README 附获取方法)。基线与量化版各跑一次。

./build/bin/llama-perplexity -m models/Qwen3-8B-f16.gguf \
  -f data/wiki.test.raw
./build/bin/llama-perplexity -m models/Qwen3-8B-Q4_K_M.gguf \
  -f data/wiki.test.raw

每次运行以一行结论收尾(数值以实测为准)。

perplexity: final estimate: PPL = 8.1832 +/- 0.0301

两个 PPL 一减就是损失读数。更细的尺是 KL 散度(Kullback-Leibler divergence):工具的 --kl-divergence 系列参数让量化模型直接对齐 fp16 的逐步分布,输出两个模型在每步词表分布上的偏差;它对"分布被量化挪动"比 PPL 敏感,社区基准已普遍改用它做主指标。

但 wiki 上的 PPL 是通用语料的代理指标,代码与数学能力的退化在它上面只投很小的影子。不跑基准时的替代方案是task-specific 小样本体感评估:从真实业务里挑 20~50 条输入,fp16 与量化版各跑一遍(温度设 0 固定输出),逐条对答案。要点如下。

  • 代码题跑编译或单测,统计通过率,别靠读起来像不像
  • 数学题对最终答案,观察是中间步骤先错还是结论先错
  • 摘要与改写比对要点覆盖率,容忍措辞差异
  • fp16 的输出留作底稿,之后每个档位都跟它对

6、动手量化模型

第 3 篇留的尾巴在这里兑现:原版权重仓库没有现成 GGUF 时,两条命令亲手压。先把 safetensors 转 fp16 GGUF(脚本在 llama.cpp 仓库根目录),再量化。

python convert_hf_to_gguf.py ~/models/Qwen3-8B \
  --outfile models/Qwen3-8B-f16.gguf
./build/bin/llama-quantize models/Qwen3-8B-f16.gguf \
  models/Qwen3-8B-Q4_K_M.gguf Q4_K_M

llama-quantize 的位置参数依次是输入、输出、目标类型;--output-tensor-type 可单独指定输出层精度,完整参数见工具文档。量化 8B 约一两分钟,过程逐张量打印(示例摘录)。

token_embd.weight - [151936, 4096, 1, 1], type = f16, quantizing to q6_K .. size = 1244.66 MB ->  510.90 MB
llama_model_quantize_internal: model size = 16438.00 MB
llama_model_quantize_internal: quant size =  4982.77 MB

日志本身就是教学现场:第一行能看到嵌入层被升档到 q6_K——第 3 节讲的混合精度在输出里现形;末两行给出前后总大小,16.4 GB 压到 4.9 GB。每行行尾还会打印误差直方图(hist),误差越集中在前几个桶越好。压 IQ 档要多一步 imatrix 校准。

./build/bin/llama-imatrix -m models/Qwen3-8B-f16.gguf \
  -f data/calib.txt -o models/imatrix.dat
./build/bin/llama-quantize --imatrix models/imatrix.dat \
  models/Qwen3-8B-f16.gguf models/Qwen3-8B-IQ4_XS.gguf IQ4_XS

校准文本用贴近目标任务的语料,几 MB 就够:代码模型喂代码,通用助手喂混合文本——imatrix 学到的重要性是相对校准分布而言的。

注意:永远从 fp16 或 bf16 原版压,不要拿量化版再压一遍。两级有损叠加,误差不是线性增长;工具默认拒绝这种操作,--allow-requantize 只留给确知后果的场景。

7、任务敏感度差异

同一个 Q3_K_M,闲聊几乎无感,写代码却可能第一屏就崩——量化损失在不同任务上的放大倍数天差地别。根子在任务的容错空间:代码的语法容错接近零,一个括号或变量名错了整段作废;闲聊语义冗余极高,换个说法照样成立;数学与多步推理居中,中间步骤的微小偏差沿链路累积。

敏感度从高到低大致是:代码与结构化输出、数学与多步推理、摘要与翻译、闲聊改写。落到档位建议如下。

任务类型 最低可用 推荐档位 说明
闲聊问答 Q3_K_M Q4_K_M 语义冗余高,损失被吞掉
长文摘要 Q3_K_M Q4_K_M 少数细节漂移
数学推理 Q4_K_M Q5_K_M 链路误差累积,留冗余
代码生成 Q4_K_M Q5_K_M 起 一个 token 错全文废
评测存档 Q8_0 Q8_0 近无损的基准口径

第 3 篇的结论在这里叠加:模型越小对量化越敏感,3B 模型的最低可用整体上调一档;反过来,70B 的 Q3_K_M 常有小模型 Q5 的水准。表格给的是 8B 上下的经验值,自己的任务自己测——这正是上一节小样本方法的用武之地。

8、总结与练习

量化是分块加低位宽的有损压缩,误差被块内刻度尺锁死在可控范围;GGUF 三代家族从等距块(legacy)、两级压缩(K-quant)到码本加 imatrix(I-quant),一代比一代把 bpw 花得更聪明;M/S/L 后缀是混合精度配方,Q4_K_M 用约 0.27 bpw 的增量给敏感张量上保险,坐稳甜区;bpw 是唯一可比口径,同 bpw 质量同级;损失用困惑度与 KL 散度度量,业务质量用小样本体感验证;llama-quantize 两条命令亲手可压,且永远从原版压起;任务上代码最怕低量化、闲聊最不怕,中间任务按表取档。

练习:把 Qwen3-8B 转成 fp16 GGUF,亲手压出 Q3_K_M、Q4_K_M、Q5_K_M 三档,再备 10 道简单算法题,四个模型(含 fp16)同一 prompt、温度 0 各跑一遍,统计通过率,并给错题分类:是思路错了,还是语法错了。做完你会得到一张自己硬件上、自己任务上的档位表——它比任何博客的结论都硬。下一篇是量化技术(下):走出 GGUF,看 GPTQ、AWQ 与服务端量化在引擎侧的另一条路线。

相关推荐

​Jev 详解:不做生成的判断模型
精选AI

​Jev 详解:不做生成的判断模型

Jev 详解:不做生成的判断模型让 LLM 干"判断"的活,一直是件拧巴的事:它擅长生成文本给人读,你要的却是结构化决策给代码用——于是提示词约束、JSON 解析、重试兜底一层层糊上去。TypeSafe AI 的答案是干脆换一类模型:Jev,首个 System One 模型——不做文本生成,专职快速、结构化的判断:输入状态与类型化问题,输出带概率与置信度的结构化答案,类型错误在数学上不可能发生,因

718
网站被注入恶意脚本排查实录:AI 辅助安全分析实战
置顶 精选AI

网站被注入恶意脚本排查实录:AI 辅助安全分析实战

网站被注入恶意脚本排查实录:AI 辅助安全分析实战某天收到华为云监管反馈,称个人博客网站存在色情违规界面。作为技术博主,这简直是晴天霹雳——网站一直正常运行,怎么会突然出现这类问题?经过排查发现,网站被注入了恶意跳转脚本。本文记录完整的排查过程,重点分享如何借助 AI 快速定位问题并修复,希望能给遇到类似情况的开发者提供参考。1、问题发现与初步排查收到监管反馈后,第一时间打开网站检查。首页看起来完

420
时隔三年,再次备份:从手搓博客到 AI 全面自动接管
精选AI

时隔三年,再次备份:从手搓博客到 AI 全面自动接管

时隔三年,再次备份:从手搓博客到 AI 全面自动接管今天打开尘封多年的备份目录,看到了一个熟悉的文件名——2023_07_09.sql。那一刻,恍如隔世。三年前的今天,我曾经认真地导出过一次数据库;三年后的今天——2026 年 7 月 9 日,自动备份启用。整整三年,1095 天,我的博客就这样安安静静地跑着,没有人备份,也没有人管,它就那么稳稳当当地活着。感慨万千,大概是此刻最准确的四个字——不

374