要点速览
模型量化是把模型权重从 16 比特改成更少的比特保存,文件和内存用量会跟着缩小,代价是多少会有精度损失。GGUF 只是装模型的单一文件格式,Q4_K_M、Q8_0 这些才是文件里权重的量化类型。选法是先用显存算出能放下的最大权重,再在放得下的类型里挑比特数最高的;如果上下文要开很长,就再往下降一级。
模型量化(quantization)是把模型权重改用更少的比特保存:原本每个权重用 16 比特,量化后可能只剩 4 到 8 比特左右。你在下载本地模型时看到的 Q4_K_M、Q8_0、GGUF 这些字符串,讲的都是这件事。这篇会回答三个问题:量化到底改了什么、文件名上的类型怎么读,以及手上的显存放得下哪一级。读完之后,你可以自己用一条算式估出文件大小,并照着流程挑出合适的量化等级。
这篇是原理与判断方法,不教任何工具的安装或下载步骤。文中的数字只有两种来源:官方说明明写的(会标出处),或由文中列出的算式推导出来的(会标「示范计算」与前提)。
模型量化是什么?
大型语言模型(LLM,术语表)的主体是一大堆数字,这些数字叫权重(weights),权重的总数就是参数量。模型原本通常以 bf16 或 float16 保存,每个权重占 2 个字节(byte)。Hugging Face 的官方指南给了一条经验法则:加载一个有 X 十亿参数的模型,bf16 或 float16 精度大约需要 2 × X GB 的显存。显存(VRAM)就是显卡上的内存。
量化做的事,是把这些高精度数字对应到一个小很多的整数集合。Hugging Face 的概念指南举例:int8 只能表示 256 个值,int4 只能表示 16 个值,所以每种量化方法都一定会引入舍入误差(rounding error)。最常见的做法叫仿射量化(affine quantization):取出一个 tensor(权重数组)的最小值和最大值,把整数范围拉伸去对应它。官方的算例是:一个值域从 -1.8 到 2.4 的 tensor 对应到 int8,其中 0.5 会被量化成整数 11,还原时得到约 0.494,误差约 0.006。
缩放比例 S = (最大值 − 最小值) ÷ (整数最大 − 整数最小)
量化:q = round(x ÷ S + Z) 还原:x ≈ S × (q − Z)
x:原本的权重 q:量化后的整数 Z:零点(代表 0.0 的那个整数)
为什么少存几个比特就能更省资源?官方的内存估算很直接:Transformers 总览写到,8B 模型的权重在 bf16 约 16GB,8 比特约 8GB,4 比特约 4 到 5GB。另一个好处在速度面:概念指南说,一次生成一个 token(术语表,模型处理文字的基本单位)时,GPU 大部分时间在读取权重而不是计算,读 4 比特权重搬动的数据量只有 bf16 的四分之一,这个节省有机会超过还原(dequantize)所多花的运算。
量化不是什么
量化不是微调。 微调(fine-tuning)是用新数据继续训练,会改变模型学到的内容。官方对训练后量化(post-training quantization,PTQ)的定义是「量化一个已经训练好的模型,而不改变它学到的东西」。量化只是换一种方式存同样的权重,不会教模型任何新东西。
量化也不是压缩档。 zip 档解开后会一模一样,量化不会。概念指南明讲,还原只能得到原值的近似(approximation),中间的差距就是舍入误差。llama.cpp 的量化工具说明也写了:量化可能引入一些精度损失,通常用困惑度(perplexity,ppl,衡量模型预测文字好坏的指针之一)或 KL 散度(Kullback–Leibler divergence,kld)来衡量。
另外,官方量化工具列出了一个明确的警告:对已经量化过的权重再量化(requantize)可能严重降低品质,建议从 16 比特或 32 比特的版本开始。所以「把别人量化好的 Q4 再转成更小的档」不是好主意。
GGUF 是什么?
GGUF 是 ggml 函数库(GGUF 规格就放在它的仓库里)及以它为基础的运行工具所使用的模型文件格式。ggml 的规格说明把它描述为二进位格式,设计目标是快速加载与保存、容易阅读。模型通常先用 PyTorch 等框架开发,再转成 GGUF 使用。
规格列出的设计目标包括:
- 单一文件:不需要额外的外部文件就能发布与加载。
- 可扩充:添加信息不会破坏既有模型的兼容性。
- 支持
mmap(把文件直接对应进内存的加载方式),加载与保存很快。 - 完整信息:加载模型需要的信息都在文件里,不用用户另外提供。
文件内容由三块组成:标头(header)、每个 tensor 的信息,以及 tensor 数据本身。标头有 GGUF 的魔术数字、版本、tensor 数量与 metadata 键值对的数量。metadata 是键值结构,放模型架构、超参数与描述信息。每个 tensor 的信息则包含名称、维度、**类型(type)**与数据在文件中的位置。
这里有一个重点:量化类型是记录在每个 tensor 上的属性,不是整个文件共用一个开关。规格的类型列表(ggml_type)同时包含 F32、F16、BF16 这些未量化的格式,也包含 Q4_0、Q8_0、Q4_K、Q6_K、IQ4_XS 等量化格式。规格也说,tensor 数据「应该接近或等同原始模型文件的数据,但可能因量化或其他推论优化而不同」,这些差异要记录在 metadata 里;而只要有任何 tensor 被量化,就必须有 general.quantization_version 这个字段。
所以 GGUF 与量化的关系是:GGUF 是容器,量化是里面权重的保存方式。一个 GGUF 文件可以是 F16(未量化),也可以是 Q4_K_M。文件名惯例也反映这点:规格建议用 <BaseName>-<SizeLabel>-...-<Encoding>...gguf 的结构,其中 Encoding 字段就是权重编码方式,范例有 Hermes-2-Pro-Llama-3-8B-F16.gguf 与 Grok-100B-v1.0-Q4_0-00003-of-00009.gguf(最后那串是分成 9 个分片中的第 3 个)。规格也坦白说,这个惯例「不保证能完美解析」。
多模态模型有时还会有额外文件。规格的文件名惯例中,mmproj 前缀代表多模态投影模块(视觉或音频编码器),要搭配主模型一起加载。llama.cpp 的说明提到,这类组件通常保留在 bf16 或 q8 这种高品质格式,因为它们比主模型小很多,用更小的量化省下的速度与内存可以忽略,却可能影响整体品质。
Q4_K_M、Q8_0 的名称怎么读
先讲清楚范围:llama.cpp 与 ggml 的官方说明,并没有一段话逐字解释 Q、K、S、M、L 各代表什么。下面只写能从官方说明直接读出来的部分,不补官方没写的缩写全名。
1. 数字是「约略」的比特数,实际更高。 官方针对 Llama-3.1-8B 列的 bits/weight(每个权重平均比特数)显示:Q4_K_M 是 4.8944,Q8_0 是 8.5008,Q6_K 是 6.5633。官方没有逐一说明这个差距的来源;Hugging Face 的概念指南提到,per-group 量化每一组权重各有一个缩放值,要额外占保存空间,这是常见的原因之一。总之,名字里的 4 不等于真的每个权重 4 比特。
2. 名字里有 _K 的是 k-quants。 llama.cpp 的说明把它们称为 k-quants,并提到 --pure 选项会「禁用 k-quant 混合配置,把所有 tensor 量化成同一种类型」。这句话透露了两件事:k-quant 预设是「混合」的,也就是同一个文件里不同 tensor 可以用不同类型;以及 Q4_K_M 这个名字代表的是整个文件的配置,不是单一种 tensor 类型。这也和规格一致:ggml_type 列表里有 Q4_K,却没有 Q4_K_M;general.file_type 字段的定义是「文件中多数 tensor 的类型」。
3. 同一系列的 S、M、L 比特数递增。 从官方表格可以直接看到:Q3_K_S 是 3.6429、Q3_K_M 是 3.9960、Q3_K_L 是 4.2979;Q4_K_S 是 4.6672、Q4_K_M 是 4.8944;Q5_K_S 是 5.5704、Q5_K_M 是 5.7036。所以同一个系列里,换成后面的字母,文件会更大。至于字母的原文缩写,官方说明没写,这里不补。
4. 名字里有 IQ 的是另一系列。 官方背景链接把它们称为 i-quants,表格里的 IQ1_S 到 IQ4_NL 涵盖较低的比特数(例如 IQ2_M 是 2.9294)。本文不展开它们的内部做法。
5. 没有 K 也没有 IQ 的 Q4_0、Q8_0。 官方说明没有解释它们的内部格式,这里只用数字:Q8_0 在官方表格是 8.5008;Q4_0 不在那张表里,但官方 llama-bench 范例输出里有一个 Q4_0 的 7B 模型(文件 3.56 GiB、6.74 B 参数,下面会用它推导)。
比较表:类型 × 每权重比特数 × 相对 FP16 的大小
下表的数字来自 llama.cpp 量化工具说明针对 Llama-3.1-8B 列出的数据,这是官方对这一个特定模型的数据,不代表你的模型;不同模型的实际比例会有差异。最右栏是用算式推导的。
| 类型 | 每权重比特数 | 文件大小(GiB) | 相对 F16 的大小比例 |
|---|---|---|---|
| F16 | 16.0005 | 14.96 | 100% |
| Q8_0 | 8.5008 | 7.95 | 53.1% |
| Q6_K | 6.5633 | 6.14 | 41.0% |
| Q5_K_M | 5.7036 | 5.33 | 35.6% |
| Q5_K_S | 5.5704 | 5.21 | 34.8% |
| Q4_K_M | 4.8944 | 4.58 | 30.6% |
| Q4_K_S | 4.6672 | 4.36 | 29.2% |
| IQ4_XS | 4.4597 | 4.17 | 27.9% |
| Q3_K_L | 4.2979 | 4.02 | 26.9% |
| Q3_K_M | 3.9960 | 3.74 | 25.0% |
| Q3_K_S | 3.6429 | 3.41 | 22.8% |
| Q2_K | 3.1593 | 2.95 | 19.7% |
| IQ2_M | 2.9294 | 2.74 | 18.3% |
| IQ1_S | 2.0042 | 1.87 | 12.5% |
推导算式(示范计算): 相对 F16 的比例 = 该类型每权重比特数 ÷ F16 的每权重比特数。以 Q4_K_M 为例:4.8944 ÷ 16.0005 ≈ 0.306,也就是约 30.6%。用文件大小算也一样:4.58 ÷ 14.96 ≈ 0.306。
Q4_0 没有出现在这张官方表格里。用 llama-bench 范例反推(示范计算,前提是范例输出中的 3.56 GiB 与 6.74 B 参数指的是同一个文件):3.56 × 2³⁰ × 8 ÷ (6.74 × 10⁹) ≈ 4.54 比特/权重,约为 F16 的 28.4%。这是推导出来的估计值,不是官方给的 bits/weight。
官方对单一模型的困惑度数据
量化工具的类型清单(源代码中的说明文字,即 --help 看到的内容)对部分类型附上了 Llama-3-8B 的文件大小与困惑度增加量。清单只写「+x ppl」,这里理解为相对未量化版本的困惑度增加;数字越小,表示量化带来的增加越少。
| 类型 | 官方标示的大小 | 官方标示的困惑度增加量 |
|---|---|---|
| Q8_0 | 7.96G | +0.0026 |
| Q6_K | 6.14G | +0.0217 |
| Q5_K_M | 5.33G | +0.0569 |
| Q4_K_M | 4.58G | +0.1754 |
| Q4_K_S | 4.37G | +0.2689 |
| Q4_0 | 4.34G | +0.4685 |
| Q3_K_M | 3.74G | +0.6569 |
| Q2_K | 2.96G | +3.5199 |
请把这张表当成「一个模型、一种测量方式」的数据点,不要延伸成类型排名,也不要直接套到别的模型或你的任务上。它能佐证的结论只有两个:同一个模型上,比特数降低时困惑度增加量整体会变大;以及文件大小接近的类型(例如 Q4_K_S 的 4.37G 和 Q4_0 的 4.34G),困惑度增加量并不相同,所以不能只看名字里的数字。官方说明另外提到,困惑度只是衡量方式之一,这个增加量可以搭配合适的 imatrix(importance matrix,重要性矩阵)文件降低;怎么做超出本文范围,请以官方说明为准。
怎么判断该选哪一级
量化等级的选择,本质上是在「放不放得下」与「精度损失」之间取舍。可以照下面的流程走。
第一步:算出权重能用的内存预算。 以显存为例:预算 = 显存总量 − KV 缓存 − 其他运行开销。KV 缓存(key-value cache)是模型生成时保存之前 token 计算结果的暂存区;Hugging Face 的说明指出,预设的缓存会随着生成的进行而成长。所以上下文越长,要留给它的空间越多。这些开销怎么估,请看依显存选模型;显存、内存与硬盘在加载时各自负责什么,则见显存、内存、硬盘的角色。
第二步:反推你能负担的最高比特数。 公式如下:
文件大小(字节)≈ 参数量 × 每权重比特数 ÷ 8
最高可用比特数 ≈ 权重预算(字节)× 8 ÷ 参数量
第三步:在放得下的类型里,挑比特数最高的那个。 比特数越高,越接近原始精度,这是官方文字与上面那张单一模型数据都佐证的方向。
第四步:如果上下文也要开很长,往下降一级。 因为 KV 缓存会吃掉预算,预算变小,最高可用比特数也跟着下降。
示范计算
前提: 假设模型有 80 亿个权重(Llama-3.1-8B 的表格大小可反推约 8.03 B:14.96 × 2³⁰ × 8 ÷ 16.0005 ≈ 8.03 × 10⁹,这里取整数 8 × 10⁹ 方便计算);以下的预算数字纯属假设,不是任何显卡的规格。
- 情境 A:权重预算 6 GiB。 最高可用比特数 = 6 × 2³⁰ × 8 ÷ (8 × 10⁹) ≈ 6.44。上表中不超过 6.44 的类型,比特数最高的是 Q5_K_M(5.7036);Q6_K 是 6.5633,超过了,放不下。
- 情境 B:同一张卡,但要开长上下文,预算缩到 5 GiB。 最高可用比特数 = 5 × 2³⁰ × 8 ÷ (8 × 10⁹) ≈ 5.37。Q5_K_S(5.5704)已经超过,比特数最高的是 Q4_K_M(4.8944)。这就是「往下降一级」的意思:从 Q5 系列降到 Q4 系列。
验证一下算式是否合理:Q4_K_M 的文件大小 ≈ 8.03 × 10⁹ × 4.8944 ÷ 8 ≈ 4.91 × 10⁹ 字节,换算成 GiB 是 4.91 × 10⁹ ÷ 2³⁰ ≈ 4.58,与官方表格的 4.58 GiB 吻合。官方的 Memory/Disk 范例也一致:8B 模型的原始大小 32.1 GB(32 比特)量化成 Q4_K_M 后是 4.9 GB,4.9 ÷ 32.1 ≈ 0.153,而 4.8944 ÷ 32 ≈ 0.153。
品质只能定性说明
关于「Q5 比 Q4 好多少」,官方说明只提供单一模型的困惑度,没有给你的任务的数字,所以这里只能定性说。Hugging Face 的方法比较(Llama 3.1 8B 与 70B;注意这是 Transformers 支持的方法,不是 GGUF 类型)把结果分成三个区间:
| 比特数 | 内存相对 bf16 | 精度(官方描述) |
|---|---|---|
| 8 比特 | 约缩小 2 倍 | 非常接近 bf16 |
| 4 比特 | 约缩小 4 倍 | 高;AWQ 与 GPTQ 常居前,但需要校准 |
| 低于 4 比特 | 缩小超过 4 倍 | 有明显下降,2 比特尤其明显 |
官方还附了一句提醒:在决定之前,请用你自己的任务和硬件做性能与精度测试。这也是本文的建议,因为「能不能用」取决于你要它做什么。Hugging Face 的优化指南也提到,如果显存不是限制,往往不需要考虑量化;但很多显卡不量化就跑不动,这时 4 比特与 8 比特是很实用的工具。也就是说,放得下就不必为了省空间降太多。这个判断的基础是上面的趋势,不是某个类型的保证。
其他种类的量化:不要混在一起
「量化」这个词底下其实有好几种不同的东西,常见的混淆来自把它们当成同一件事。
GGUF 这一系: 先把模型转成 GGUF,再用 llama.cpp 的量化工具转成目标类型;官方流程是两个阶段。Transformers 的比较表把 GGUF 列为「只能加载已量化文件」的类型。要特别注意的是,Transformers 官方说明指出:大多数 GGUF 模型在加载时会被还原成完整精度,所以内存用量和未量化的模型一样;只有特定情况(MPS 加上 kernels 套件,限定部分架构)权重才会维持压缩状态。如果你用的是 llama.cpp 这一系的工具,则是直接以量化类型运行;工具之间的行为不同,请以各自的官方说明为准。
训练后量化函数库: 在 Transformers 官方说明中有一长串,这里只挑几个说明差异。
| 方法 | 官方说明的特性 | 何时量化 |
|---|---|---|
| bitsandbytes | 4 比特或 8 比特;不需要校准数据,并支持微调 | 加载时 |
| GPTQ | 训练后量化,每列权重独立量化以最小化误差;比特数有 2、3、4、8 | 加载时,需要校准数据集 |
| AWQ | 保留少部分对模型表现重要的权重,压缩到 4 比特 | 只能加载已量化的检查点 |
| GGUF | 比特数 1 到 8 | 只能加载已量化文件 |
表中「校准」是什么意思?概念指南说明:有校准数据的方法会拿一小批数据跑过模型,用启动值(activations,模型中间层的输出)来决定每一层怎么量化。这通常在 4 比特以下精度较好,但要花几分钟到几小时。没有校准数据的方法则只靠权重本身算缩放比例,快到可以在加载模型时顺便做。
还有一个不同层次的分法:训练中量化(quantization-aware training,QAT)是在训练时就模拟量化,让模型学会容忍舍入误差,通常在极低比特数下精度最好,但需要训练。本文讲的 GGUF 量化,流程是把已训练好的模型转成 GGUF 再量化,属于训练后量化的做法。
只量化权重,还是连启动值也量化? 概念指南区分了只量化权重(weight-only)与权重加启动值都量化两类。只量化权重的方法省内存,但每次矩阵乘法前要先还原,所以不一定更快;它们在模型受限于内存带宽时最有利。
KV 缓存也可以量化,这是另一件事
KV 缓存(上面提过的暂存区)是跟权重分开的东西,也可以另外量化。llama.cpp 的命令行工具说明列出了 -ctk 与 -ctv 两个选项,分别设置 K 和 V 的数据类型,可选值包括 f32、f16、bf16、q8_0、q4_0、q4_1、iq4_nl、q5_0、q5_1,预设是 f16(选项名称可能变动,以官方说明为准)。这代表:权重用 Q4_K_M,不会自动让 KV 缓存也变小,两者要分开设置。
Transformers 这边也有对应的量化缓存(QuantizedCache),官方的警告是:如果上下文很短,而且显存本来就够用,量化缓存反而可能增加延迟,要在内存效率与延迟之间找平衡。所以长上下文才是它派得上用场的时候,这也呼应了前面「往下降一级」之外的另一条路:与其降权重的等级,也可以评估把 KV 缓存量化。至于哪个选择对你的任务更划算,官方没有给通用答案。
四个常见误解
误解一:比特数越低就一定越快
量化减少要读的数据量,所以常常有帮助:llama.cpp 官方说明写的是「可以加速推论」,用的是「can」。Transformers 的说明则更精确:只量化权重的方法省内存,但不一定更快,因为还原会增加运算。再看 llama.cpp 官方针对 Llama-3.1-8B 的表格(官方没有标明测试硬件,所以只能看趋势):F16 的文字生成速度是表中最低的,量化之后多半变快;但 3 比特的 Q3_K_S 的生成速度,反而低于 4 比特的 Q4_K_S。同一张表里,速度不是比特数的单调函数。这里刻意不抄表中的速度数字,因为它们只属于那次测试的环境,拿来估你的速度只会误导;要怎么正确测速,见Token/s 怎么看。
误解二:量化就是把模型压缩,之后可以还原
不能。量化是有损的,还原得到的是近似值(见上面的定义)。如果你需要最高精度,应该保留 16 比特的版本,量化只是从它产生较小的副本。
误解三:量化一定让模型笨到不能用,或者完全没差
两个极端都没有官方依据。官方的数据指向中间:8 比特非常接近 bf16,4 比特精度高,低于 4 比特有明显下降。单一模型的困惑度增加量,在高比特时很小、比特降低时变大(见前面的表)。至于这些差距在你的任务上算不算「笨」,答案取决于任务,需要自己试。
误解四:文件多大,就只需要多少显存
文件大小只是权重的部分。加载时还要留 KV 缓存(随上下文成长)以及其他运行开销。Hugging Face 的指南也只在「输入少于 1024 个 token」的前提下,才说内存需求主要由加载权重决定。如果你用的是 MoE(混合专家)模型,参数怎么存、怎么算大小,见MoE 混合专家模型;这篇的公式适用于「文件里所有权重」的总数。
实际使用工具时
如果你想看一个本地大模型工具实际怎么用,可以参考Strata 与 PolyStrata 的比较。工具的安装与设置不在本文范围;你在这些工具里看到的模型名称后缀(例如 Q4_K_M),就是这篇讲的东西。
延伸阅读
- 依显存选模型与显存估算方法:先算出你的预算,再回到这篇挑量化等级。
- 显存、内存、硬盘各扮演什么角色:量化后的文件加载时放在哪里。
- MoE 混合专家模型:总参数与实际使用的参数不同,会影响估算。
- Token/s 怎么看与正确的速度测试方法:量化对速度的影响要怎么量。
- Strata 与 PolyStrata 的比较:本地大模型工具范例。
- 术语表:LLM、context window(上下文窗口)、token 等名词的简短定义。
常见问题
Q4_K_M 和 Q8_0 差在哪里?
差在每个权重平均用多少比特保存。llama.cpp 官方针对 Llama-3.1-8B 列出的数字,Q4_K_M 约 4.89 比特、Q8_0 约 8.50 比特,所以 Q8_0 的文件大约是 Q4_K_M 的 1.7 倍。比特数越高通常越接近原始精度,但要放进显存的东西也越多。
GGUF 是量化方法吗?
不是。GGUF 是装模型的单一文件格式,里面有 metadata(模型的描述数据)和每个 tensor(权重数组)的名称、维度与类型。量化类型是记录在每个 tensor 上的属性,所以 GGUF 文件里可以是 Q4_K_M,也可以是未量化的 F16。
量化后模型会变笨吗?
会有精度损失,这是官方说明直接写的,但损失多大要看量化等级、模型与任务。Hugging Face 的 Llama 3.1 比较中,8 比特方法与 bf16 非常接近,低于 4 比特则有明显下降;实际影响请用你自己的任务确认。
比特数越低就一定越快吗?
不一定。量化减少的是要读取的数据量,文字生成常常受限于读取权重的速度,所以通常有帮助;但 Hugging Face 的说明也指出,只量化权重的方法省内存却不一定更快。llama.cpp 官方表格里,3 比特的 Q3_K_S 生成速度还低于 4 比特的 Q4_K_S。
4 比特量化的 8B 模型大概要多少显存?
先用公式估文件大小:参数数量乘以每权重比特数再除以 8。以 llama.cpp 官方列的 Llama-3.1-8B Q4_K_M 为例,文件是 4.58 GiB,这还没算 KV 缓存与其他运行开销,所以显存要留比文件更大的空间。
量化可以让我自己转换任何模型吗?
llama.cpp 官方流程分两步:先把原始模型转成 GGUF,再用量化工具转成目标类型。官方也提醒,对已量化过的权重再次量化可能大幅降低品质,建议从 16 比特或 32 比特的版本开始。
留言
用 GitHub 账号登录就能留言,留言会公开显示在 GitHub Discussions。留言前请先看社区规则。