两个日常场景。其一:在 HuggingFace 搜一个模型,正主仓库是十几个 safetensors 分片加一堆 json;旁边总跟着个「-GGUF」仓库,里面躺着一排单文件——Q4_K_M.gguf(4.08 GB)、Q8_0.gguf(7.16 GB)……其二:ollama run qwen3 一行命令,笔记本就把模型跑起来了。这两件事背后是同一位主角:GGUF。
这篇讲清楚三件事:这个文件里装了什么;Q4_K_M 这串黑话怎么读;以及它和 vLLM、SGLang、Triton 们是什么关系——先剧透:不是竞品,是不同楼层的住户。
一、一只自带说明书的行李箱
GGUF(GPT-Generated Unified Format)是 llama.cpp 作者在 2023 年 8 月定下的模型文件格式(规格见 ggml 仓库,PR #302 定稿),取代此前几度破坏兼容的 GGML/GGJT。设计目标写得很直白:一个文件、自带一切、可以扩展、可以 mmap。
解剖开看是四段结构:
- 文件头:magic 就是
GGUF四个字节,加上版本号(当前是 3)和张量、元数据的数量; - 带类型的 KV 元数据:架构(
general.architecture = "llama")、上下文长度、RoPE 参数……连分词器的整个词表(tokenizer.ggml.tokens)都存在文件里。前代格式的教训正在这里:超参数是一列无类型的裸值,加个字段就得整个换格式;改成带类型的 KV 后,新字段随便加,旧程序照读不误; - 张量目录:每个张量的名字、形状、类型、以及在数据区的偏移;
- 张量数据:按目录偏移直接寻址,默认 32 字节对齐(可用
general.alignment调整)。
对齐加偏移是为了一件事:mmap。操作系统把文件直接映射进虚拟内存,用到哪页读哪页——启动飞快,几个程序加载同一个模型共享一份页缓存,权重还能被 SIMD 指令和 GPU DMA 直接取用,省掉「先整个读进内存再摆好」。
对比一下就明白单文件的价值:HF 原生格式是一家子文件——config.json、tokenizer.json、外加 N 个权重分片,谁都不能丢;GGUF 是一只箱子,拎走就走。另外它是纯数据——和 safetensors 一样,不像 pickle 那样能夹带可执行代码。
二、Q4_K_M 黑话解码
GGUF 文件名里那串后缀是一套量化命名系统。先把最基本的账算明白。
Q4_0:最朴素的块量化。 权重每 32 个分一块,块内共用一个比例尺(scale,用 fp16 存),每个权重就能用一个 4 bit 整数码近似:
算账:32 个 4 bit 码占 16 字节,加 2 字节 scale,共 18 字节——摊到每个权重是 4.5 bit,对比 fp16 的 16 bit,体积缩到 28%。代价是一块共用一个尺,块内细微差异被抹平。
K-quants:给比例尺也做预算。 2023 年年中 llama.cpp 引入 K 系列:256 个权重组成超块,内部再分 8 个 32 权重的子块。每个子块带自己的小比例尺——而这个小尺本身只用 6 bit 存,整个超块再带一对 fp16 总尺。层层转租下来,Q4_K 同样约 4.5 bit/权重,但精度花在了更该花的地方。
后缀 _S / _M / _L 是混搭档位:Q4_K_M(M=medium)会把注意力、输出这类关键张量升格到更高位宽(如 Q6_K),其余用 Q4_K——所以它比纯 4 bit 稍大,也明显更稳,官方文件表直接标注「balanced quality——recommended」。
I-quants:极限压缩要靠校准。 IQ2、IQ3 一族用非线性/向量量化,并依赖 imatrix(重要性矩阵):先拿校准数据跑一遍、找出对输出影响大的权重,量化时优先保它们。没有 imatrix,这一族的质量会明显打折。
这套梯子的真实效果,用同一个 Llama-2-7B 的官方文件大小说话:
规律很清楚:位宽每降一档,体积近乎线性地掉;质量损失却是非线性的——4 bit 一带是公认的甜点区,而 2 bit 一带连官方都标注「significant quality loss」。
三、为什么端侧世界都围着它转
- mmap 的体感:模型「秒开」;关掉再开几乎瞬时(页缓存还热着);两个程序加载同一个模型,内存只占一份;
- 块结构对 CPU 友好:32/256 的块长贴合 SIMD 指令(AVX、NEON)的批处理宽度——这是 llama.cpp 在纯 CPU 上也能像样跑的底气;
- 层卸载:显存装得下几层就搬几层进 GPU(
-ngl N),其余留在内存里由 CPU 算——8GB 显存的机器也能跑 13GB 的模型,拿速度换可行性;
- 生态滚雪球:Ollama、LM Studio、llamafile、koboldcpp……端侧工具几乎全是 llama.cpp 家族,认的都是 GGUF;HuggingFace 原生托管 GGUF,网页上能直接预览文件里的元数据。
四、它和 vLLM、SGLang、Triton 是什么关系
这几个名字经常被放在一起比,其实它们住在不同楼层:
- 权重格式层:GGUF、safetensors——装数据的箱子;
- 内核层:CUDA、Metal,以及 OpenAI 的 Triton——一种写 GPU 算子的语言,vLLM 和 SGLang 的不少内核用它实现;
- 推理引擎层:llama.cpp、vLLM、SGLang、TensorRT-LLM——真正跑 attention 的人;
- 服务平台层:NVIDIA 的 Triton Inference Server——数据中心里管多模型部署的框架。
先把重名解掉:两个 Triton 不是一回事——一个是写核的语言(OpenAI),一个是管服务的平台(NVIDIA),撞名纯属巧合。
真正的选型分野在引擎层,而引擎的分工跟着场景走:
| llama.cpp(+ GGUF) | vLLM / SGLang(+ safetensors 等) | |
|---|---|---|
| 主场 | 端侧、单机、个人使用 | 数据中心、高并发 API 服务 |
| 硬件 | CPU、消费级 GPU、Apple Silicon | 服务器 GPU 集群 |
| 内存招式 | mmap + 量化 + 层卸载 | PagedAttention 把 KV 缓存分页管理(vLLM);RadixAttention 用基数树跨请求复用前缀(SGLang) |
| 吞吐招式 | 单人低延迟 | 连续批处理:请求逐条进出,不等整批凑齐 |
| 量化生态 | GGUF 的 K / I 系列(CPU 友好) | GPTQ、AWQ、FP8 等(GPU 友好) |
那 GGUF 能不能直接进数据中心?vLLM 确实做了 GGUF 加载,但官方文档原话是:「高度实验性且欠优化」,可能与其他特性不兼容。这不是谁不努力——GGUF 的块状布局本来就是为 CPU 的 SIMD 设计的,GPU 引擎要么把它反量化回浮点、要么重排数据,两头不讨好。格式从来不是中立的,它总是偏向自己的主场。
五、写在最后
回头看,GGUF 做对的事情很简单:把「跑一个模型」的门槛降到「下载一个文件」。格式里的每个设计——单文件、带类型的 KV、对齐、mmap——都在为这一个目标服务。
选型也就一句话:自己用、设备五花八门,投 llama.cpp 家族和 GGUF;对外提供高并发服务,投 vLLM / SGLang 和它们的量化生态。两个世界各自繁荣,中间隔着的不是优劣,是场景。
参考
- GGUF 规格(ggml 官方文档)
- llama.cpp quantize 工具说明与量化讨论(k-quants、i-quants)
- TheBloke/Llama-2-7B-GGUF(体积阶梯与官方推荐标注的数据来源)
- vLLM: Efficient Memory Management with PagedAttention(arXiv:2309.06180)
- SGLang: Efficient Execution of Structured Language Model Programs(arXiv:2312.07104,RadixAttention)
- vLLM 的 GGUF 支持(官方文档,标注高度实验性)
- OpenAI Triton、NVIDIA Triton Inference Server