上一篇把 GGUF 这个"打包盒子"从里到外解剖清楚了。这一篇动手,把盒子从制作到使用完整走一遍:用 llama.cpp 把一个 HuggingFace 模型转成 GGUF、量化成你要的档位、然后在本地(CPU 或 GPU)真正跑起来。
目录
- 一、动手前先看清整条流水线
- 二、编译安装 llama.cpp
- 三、第一步:把模型转成 GGUF(FP16)
- 四、第二步:量化成低比特档位
- 五、第三步:本地跑推理(CPU 与 GPU)
- 六、用 Python 调用与效果评测
- 七、常见误区与避坑
- 八、小结
一、动手前先看清整条流水线
先把整个流程在脑子里过一遍,免得操作时迷路。从一个 HuggingFace 模型到能在本地跑的 GGUF,要走三步:
HuggingFace 模型(FP16,多文件)
│
│ ① 转换脚本 convert
▼
GGUF 文件(FP16,单文件,还没压缩)
│
│ ② 量化 llama-quantize
▼
GGUF 文件(Q4_K_M 等低比特,体积大幅缩小)
│
│ ③ 推理 llama-cli / llama-server
▼
本地跑起来,CPU 或 GPU 都行
三步各干一件事:
- 转换:把 HF 格式(一堆 safetensors + 配置 + 分词器)打包成一个 FP16 的 GGUF。这一步只是"换包装",还没量化,文件依然很大。
- 量化:把 FP16 的 GGUF 压成低比特(比如 Q4_K_M),体积砍到四分之一左右。这才是省显存/内存的关键一步。
- 推理:用 llama.cpp 的工具加载量化后的 GGUF,在本地生成文本。
有个新手常踩的点先说清:为什么要先转成 FP16 GGUF、再量化,不能一步到位? 因为转换和量化是两个独立的工具、两件事。转换负责"格式对齐",量化负责"精度压缩"。分开也灵活------同一个 FP16 GGUF,你可以量化出好几个不同档位的版本。
二、编译安装 llama.cpp
llama.cpp 是个 C++ 项目,要先把它编译出来。
2.1 拉代码
bash
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
2.2 编译
现在 llama.cpp 用 CMake 编译。分两种情况:
只用 CPU(最简单,任何机器都行):
bash
cmake -B build
cmake --build build --config Release -j
想用 NVIDIA GPU 加速(有独显再选这个):
bash
cmake -B build -DGGML_CUDA=ON
cmake --build build --config Release -j
Mac 用户注意:Apple Silicon(M 系列芯片)会自动启用 Metal 加速,直接用 CPU 那套编译命令即可,它会自动用上 GPU。这是 Mac 跑大模型的一大优势。
编译完,可执行文件在 build/bin/ 目录下,关键的几个:
llama-cli:命令行推理工具llama-quantize:量化工具llama-server:起一个本地 API 服务
2.3 装 Python 依赖(转换脚本要用)
转换脚本是 Python 写的,装下依赖:
bash
pip install -r requirements.txt
三、第一步:把模型转成 GGUF(FP16)
环境好了,开始第一步------转换。
3.1 准备原始模型
先把要转的 HuggingFace 模型下到本地。假设你已经下好了,放在 ./models/Qwen2.5-1.5B-Instruct/ 目录(里面是 safetensors、config.json、tokenizer 等文件)。
3.2 跑转换脚本
llama.cpp 自带一个 convert_hf_to_gguf.py 脚本,专门干这个:
bash
python convert_hf_to_gguf.py ./models/Qwen2.5-1.5B-Instruct/ \
--outfile ./models/qwen2.5-1.5b-f16.gguf \
--outtype f16
参数解释:
- 第一个参数:原始 HF 模型的目录。
--outfile:输出的 GGUF 文件路径。--outtype f16:输出 FP16 精度。这一步先不量化,只换格式,所以用 f16 保留原精度。
跑完你会得到一个 qwen2.5-1.5b-f16.gguf。这时候它的体积和原模型 FP16 差不多(1.5B 模型约 3GB),因为还没量化,只是把多文件打包成了单文件。
3.3 验证转换结果
用上一篇学的方法,读一下这个 GGUF 的信息,确认转换成功:
python
from gguf import GGUFReader
reader = GGUFReader("./models/qwen2.5-1.5b-f16.gguf")
print(f"架构: {reader.fields.get('general.architecture')}")
print(f"张量总数: {len(reader.tensors)}")
print(f"第一个张量类型: {reader.tensors[0].tensor_type.name}") # 应该是 F16
看到张量类型是 F16、架构正确、张量数量合理,就说明转换成功了。现在盒子造好了,但还是"大盒子",接下来压缩它。
四、第二步:量化成低比特档位
这一步把 FP16 的 GGUF 压成低比特,是省资源的关键。
4.1 最基本的量化命令
用 llama-quantize 工具:
bash
./build/bin/llama-quantize \
./models/qwen2.5-1.5b-f16.gguf \
./models/qwen2.5-1.5b-q4_k_m.gguf \
Q4_K_M
三个参数:
- 输入:刚才转出来的 FP16 GGUF。
- 输出:量化后的 GGUF 路径。
- 量化档位:这里用
Q4_K_M(4bit、K-quant、Medium 档,是最常用的通用档位)。
跑完你会得到一个 q4_k_m.gguf,体积从约 3GB 降到 1GB 出头。这一步很快,通常几十秒到几分钟,因为它不需要校准数据、不跑复杂算法,就是按 K-quant 方案分块压缩(回顾上一篇)。
4.2 常用量化档位速览
Q4_K_M 只是众多档位之一。这些档位的完整选择逻辑是下一篇的主题,这里先给个够用的速查:
| 档位 | 大致比特 | 特点 | 适用 |
|---|---|---|---|
| Q8_0 | 8 | 接近无损,体积大 | 内存充足、要高质量 |
| Q6_K | 6.5 | 高质量,体积中等 | 质量优先 |
| Q5_K_M | 5.5 | 质量和体积平衡 | 推荐的高质量档 |
| Q4_K_M | 4.5 | 通用甜点,最常用 | 大多数人的默认选择 |
| Q4_K_S | 4 | 比 M 更小,质量略降 | 内存紧张 |
| Q3_K_M | 3.5 | 明显压缩,质量下降 | 极限压缩 |
| Q2_K | 2.5 | 最激进,质量损失大 | 只在别无选择时 |
一个经验法则:先无脑选 Q4_K_M,它是绝大多数场景的最佳平衡点。 内存够、想要更好质量就往上走 Q5_K_M、Q6_K;内存实在紧张再往下退 Q4_K_S、Q3_K_M。
4.3 一次转多个档位
前面说过,同一个 FP16 GGUF 可以量化出多个版本。比如你想同时准备一个高质量和一个小体积的:
bash
# 高质量档
./build/bin/llama-quantize ./models/qwen2.5-1.5b-f16.gguf \
./models/qwen2.5-1.5b-q5_k_m.gguf Q5_K_M
# 通用甜点档
./build/bin/llama-quantize ./models/qwen2.5-1.5b-f16.gguf \
./models/qwen2.5-1.5b-q4_k_m.gguf Q4_K_M
# 小体积档
./build/bin/llama-quantize ./models/qwen2.5-1.5b-f16.gguf \
./models/qwen2.5-1.5b-q3_k_m.gguf Q3_K_M
三个文件体积依次递减,你可以按不同设备分别部署。这就是"先转 FP16 再量化"这个两步走的灵活性。
4.4 对比一下各档位的体积
python
import os
files = {
"FP16": "./models/qwen2.5-1.5b-f16.gguf",
"Q5_K_M": "./models/qwen2.5-1.5b-q5_k_m.gguf",
"Q4_K_M": "./models/qwen2.5-1.5b-q4_k_m.gguf",
"Q3_K_M": "./models/qwen2.5-1.5b-q3_k_m.gguf",
}
for name, path in files.items():
if os.path.exists(path):
size = os.path.getsize(path) / 1024**2
print(f"{name:8s}: {size:7.0f} MB")
典型输出:
FP16 : 2980 MB
Q5_K_M : 1150 MB
Q4_K_M : 980 MB
Q3_K_M : 790 MB
体积随档位阶梯下降,你按设备内存挑一个就行。
五、第三步:本地跑推理(CPU 与 GPU)
模型量化好了,终于到最激动人心的一步------在本地跑起来。
5.1 命令行一问一答
最简单的方式,用 llama-cli 直接对话:
bash
./build/bin/llama-cli \
-m ./models/qwen2.5-1.5b-q4_k_m.gguf \
-p "请用通俗的语言解释什么是模型量化。" \
-n 256
参数:
-m:模型文件路径。-p:提示词(prompt)。-n:生成多少个 token。
回车,你会看到模型在你自己的机器上一个字一个字地往外蹦答案。如果你是纯 CPU 机器,这一刻意义重大------没有任何显卡,你也跑起了大模型。
5.2 交互式对话模式
想连续多轮对话,加 -cnv(conversation)参数,进入聊天模式:
bash
./build/bin/llama-cli \
-m ./models/qwen2.5-1.5b-q4_k_m.gguf \
-cnv
这样就能像用 ChatGPT 一样和它连续对话,它会记住上下文。
5.3 用 GPU 加速
如果你编译时开了 CUDA(或在 Mac 上有 Metal),可以把模型的一部分或全部层放到 GPU 上,用 -ngl(number of GPU layers)参数:
bash
./build/bin/llama-cli \
-m ./models/qwen2.5-1.5b-q4_k_m.gguf \
-p "介绍一下你自己。" \
-n 256 \
-ngl 99 # 把最多 99 层放 GPU(够多就是全放)
-ngl 的用法很灵活:
-ngl 0:全用 CPU。-ngl 99:尽量全放 GPU(层数不够 99 就是全放)。-ngl 20:放 20 层到 GPU,其余留 CPU------这是显存不够时的杀手锏:显存装不下整个模型,就放一部分到 GPU、剩下的 CPU 扛,虽然比全 GPU 慢,但能跑起来。
这个"部分层 offload 到 GPU"的能力,正是 llama.cpp 灵活性的体现------它让你在有限显存下也能跑更大的模型。
5.4 起一个本地 API 服务
想让别的程序调用你的本地模型?用 llama-server 起个服务:
bash
./build/bin/llama-server \
-m ./models/qwen2.5-1.5b-q4_k_m.gguf \
--host 127.0.0.1 \
--port 11434 \
-ngl 99
起来之后,它提供一个兼容 OpenAI 格式的 API。你可以用任何 OpenAI 客户端连它,把本地模型当成私有的 ChatGPT 用。地址以终端实际输出的为准。
六、用 Python 调用与效果评测
命令行跑通了,实际项目里我们通常用 Python 调用。有两种常见方式。
6.1 方式一:通过 llama-server 的 API 调
先按上面把 llama-server 起起来,然后用标准的 OpenAI 客户端连它:
python
from openai import OpenAI
# 连本地的 llama-server(地址以你启动时终端输出的为准)
client = OpenAI(
base_url="http://127.0.0.1:11434/v1",
api_key="not-needed", # 本地服务不需要真 key
)
response = client.chat.completions.create(
model="local-model", # 名字随便填,本地就一个模型
messages=[
{"role": "user", "content": "用三句话说说 GGUF 是什么。"}
],
max_tokens=256,
)
print(response.choices[0].message.content)
这种方式的好处是:你的代码用的是标准 OpenAI 接口,以后想换成真的云端模型,改个 base_url 就行,代码不用动。
6.2 方式二:用 llama-cpp-python 直接加载
不想起服务,想在 Python 里直接加载 GGUF?用 llama-cpp-python:
bash
pip install llama-cpp-python
python
from llama_cpp import Llama
# 直接加载 GGUF 文件
llm = Llama(
model_path="./models/qwen2.5-1.5b-q4_k_m.gguf",
n_ctx=2048, # 上下文长度
n_gpu_layers=99, # GPU 层数,纯 CPU 就设 0
verbose=False,
)
# 对话式调用
output = llm.create_chat_completion(
messages=[{"role": "user", "content": "什么是模型量化?简短回答。"}],
max_tokens=200,
)
print(output["choices"][0]["message"]["content"])
这种方式把 GGUF 直接嵌进你的 Python 程序,不用单独起服务,适合做成一个独立应用。
6.3 简单评测:不同档位的速度和质量
我们对比一下不同量化档位的推理速度,感受档位的取舍:
python
from llama_cpp import Llama
import time
def bench_gguf(model_path, prompt, n_tokens=128):
llm = Llama(model_path=model_path, n_ctx=2048, n_gpu_layers=0, verbose=False) # 纯 CPU 测
start = time.time()
out = llm(prompt, max_tokens=n_tokens, echo=False)
elapsed = time.time() - start
gen = out["usage"]["completion_tokens"]
print(f"{model_path.split('/')[-1]:30s} "
f"生成 {gen} tokens, 用时 {elapsed:.1f}s, "
f"速度 {gen/elapsed:.1f} tokens/s")
prompt = "解释一下什么是大语言模型。"
for path in [
"./models/qwen2.5-1.5b-q3_k_m.gguf",
"./models/qwen2.5-1.5b-q4_k_m.gguf",
"./models/qwen2.5-1.5b-q5_k_m.gguf",
]:
bench_gguf(path, prompt)
典型输出(纯 CPU,速度随机器不同差异很大):
qwen2.5-1.5b-q3_k_m.gguf 生成 128 tokens, 用时 6.2s, 速度 20.6 tokens/s
qwen2.5-1.5b-q4_k_m.gguf 生成 128 tokens, 用时 6.8s, 速度 18.8 tokens/s
qwen2.5-1.5b-q5_k_m.gguf 生成 128 tokens, 用时 7.9s, 速度 16.2 tokens/s
看出规律了:档位越低(比特越少),推理越快、体积越小,但质量也越低。 Q3 最快最小但质量最差,Q5 慢一点大一点但质量最好,Q4_K_M 卡在中间------这就是它成为默认推荐的原因。速度和质量怎么权衡,取决于你的机器和需求,下一篇会把这套档位选择逻辑彻底讲透。
七、常见误区与避坑
误区 1:直接拿量化脚本一步到位,跳过 FP16 转换。
流程是两步:先 convert 转成 FP16 GGUF,再 llama-quantize 量化。转换负责格式、量化负责压缩,是两件事。虽然有些脚本能合并,但理解成两步不容易出错,也更灵活(一个 FP16 源可以量化出多档)。
误区 2:编译时没开 CUDA,却期待 GPU 加速。
-ngl 参数只有在编译时开了 -DGGML_CUDA=ON(或 Mac 的 Metal)才有效。纯 CPU 编译的版本,设再大的 -ngl 也用不上 GPU。想用 GPU,编译那步别忘了加开关。
误区 3:-ngl 设得越大越好。
不一定。-ngl 是放多少层到 GPU,受显存限制。显存不够却设太大,会爆显存导致报错或退回 CPU。显存装不下整个模型时,要试探着调 -ngl,找到显存刚好够用的层数,让 GPU 和 CPU 分工。
误区 4:模型转换报错"不支持的架构"。
和 autoawq 类似,convert_hf_to_gguf.py 也只支持它认识的架构。全新或冷门架构可能还没适配,需要等 llama.cpp 更新,或自己写转换逻辑。转之前确认架构在支持列表里。
误区 5:量化档位随便选,下个最小的图省空间。
省了空间可能赔了质量。Q2_K 这种极限档质量损失很大,除非设备实在扛不住,否则别用。Q4_K_M 是公认的甜点,先从它开始,有明确理由再调整。
误区 6:以为 GGUF 一定比 GPU 上的 AWQ/GPTQ 慢,所以看不上。
场景不同别硬比。在纯 CPU 或 Mac 上,GGUF 是你唯一实用的选择,AWQ/GPTQ 根本跑不起来。在有好显卡的服务端,那俩确实更快。工具是给场景服务的,本地就该用 GGUF。
八、小结
这一篇我们用 llama.cpp 把 GGUF 从制作到使用完整跑通了,拎几个要点:
- 整条流水线三步:convert 转成 FP16 GGUF → llama-quantize 量化成低比特 → llama-cli/server 本地推理。转换管格式、量化管压缩,分开更灵活。
- 编译分 CPU 版和 GPU 版 :
cmake -B build是 CPU 版,加-DGGML_CUDA=ON是 CUDA 版,Mac 自动用 Metal。 - 量化档位先选 Q4_K_M------通用甜点。内存够往上(Q5_K_M、Q6_K),内存紧往下(Q4_K_S、Q3_K_M)。同一个 FP16 源可量化出多档。
- 推理靠
-ngl调 GPU/CPU 分工:0 全 CPU、99 尽量全 GPU、中间值让显存不够时也能跑,这是 llama.cpp 的杀手锏。 - Python 调用两种方式:连 llama-server 的 OpenAI 兼容 API(易迁移),或用 llama-cpp-python 直接加载 GGUF(易打包)。
- 实测看到档位越低越快越小但质量越低,Q4_K_M 卡在最佳平衡点。
到这里,你已经能把任意一个(受支持的)模型,亲手转成 GGUF、量化、然后在自己的电脑上跑起来了------不管有没有显卡。这是本地大模型自由的关键一步。不过我们一直在说"Q4_K_M 是甜点""按内存选档位",可这些 Q4_K_M、Q5_K_S 到底怎么精确区分、面对一堆档位到底该怎么科学地选,还没讲透。下一篇就专门解决这个高频困惑,给你一套清晰的选档决策方法。