大模型量化从0到1(九):用 llama.cpp 把模型转成 GGUF 并跑本地推理 上一篇把 GGUF 这个打包盒子从里到外解剖清楚了。这一篇动手,把盒子从制作到使用完整走一遍:用 llama.cpp 把一个 HuggingFace 模型转成 GGUF、量化成你要的档位、然后在本地(CPU 或 GPU)真正跑起来。目录一、动手前先看清整条流水线二、编译安装 llama.cpp三、第一步:把模型转成 GGUFFP16四、第二步:量化成低比特档位五、第三步:本地跑推理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.cppllama.cpp 是个 C 项目,要先把它编译出来。2.1 拉代码gitclone https://github.com/ggml-org/llama.cppcdllama.cpp2.2 编译现在 llama.cpp 用 CMake 编译。分两种情况:只用 CPU最简单,任何机器都行:cmake-Bbuild cmake--buildbuild--configRelease-j想用 NVIDIA GPU 加速有独显再选这个:cmake-Bbuild-DGGML_CUDAON cmake--buildbuild--configRelease-jMac 用户注意:Apple SiliconM 系列芯片会自动启用 Metal 加速,直接用 CPU 那套编译命令即可,它会自动用上 GPU。这是 Mac 跑大模型的一大优势。编译完,可执行文件在build/bin/目录下,关键的几个:llama-cli命令行推理工具llama-quantize量化工具llama-server起一个本地 API 服务2.3 装 Python 依赖转换脚本要用转换脚本是 Python 写的,装下依赖:pipinstall-rrequirements.txt三、第一步:把模型转成 GGUFFP16环境好了,开始第一步——转换。3.1 准备原始模型先把要转的 HuggingFace 模型下到本地。假设你已经下好了,放在./models/Qwen2.5-1.5B-Instruct/目录里面是 safetensors、config.json、tokenizer 等文件。3.2 跑转换脚本llama.cpp 自带一个convert_hf_to_gguf.py脚本,专门干这个:python convert_hf_to_gguf.py ./models/Qwen2.5-1.5B-Instruct/\--outfile./models/qwen2.5-1.5b-f16.gguf\--outtypef16参数解释:第一个参数:原始 HF 模型的目录。--outfile:输出的 GGUF 文件路径。--outtype f16:输出 FP16 精度。这一步先不量化,只换格式,所以用 f16 保留原精度。跑完你会得到一个qwen2.5-1.5b-f16.gguf。这时候它的体积和原模型 FP16 差不多1.5B 模型约 3GB,因为还没量化,只是把多文件打包成了单文件。3.3 验证转换结果用上一篇学的方法,读一下这个 GGUF 的信息,确认转换成功:fromggufimportGGUFReader readerGGUFReader(./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工具:./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_M4bit、K-quant、Medium 档,是最常用的通用档位)。跑完你会得到一个q4_k_m.gguf,体积从约 3GB 降到 1GB 出头。这一步很快,通常几十秒到几分钟,因为它不需要校准数据、不跑复杂算法,就是按 K-quant 方案分块压缩回顾上一篇。4.2 常用量化档位速览Q4_K_M只是众多档位之一。这些档位的完整选择逻辑是下一篇的主题,这里先给个够用的速查:档位大致比特特点适用Q8_08接近无损,体积大内存充足、要高质量Q6_K6.5高质量,体积中等质量优先Q5_K_M5.5质量和体积平衡推荐的高质量档Q4_K_M4.5通用甜点,最常用大多数人的默认选择Q4_K_S4比 M 更小,质量略降内存紧张Q3_K_M3.5明显压缩,质量下降极限压缩Q2_K2.5最激进,质量损失大只在别无选择时一个经验法则:先无脑选 Q4_K_M,它是绝大多数场景的最佳平衡点。内存够、想要更好质量就往上走 Q5_K_M、Q6_K;内存实在紧张再往下退 Q4_K_S、Q3_K_M。4.3 一次转多个档位前面说过,同一个 FP16 GGUF 可以量化出多个版本。比如你想同时准备一个高质量和一个小体积的:# 高质量档./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 对比一下各档位的体积importos 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,}forname,pathinfiles.items():ifos.path.exists(path):sizeos.path.getsize(path)/1024**2print(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直接对话:./build/bin/llama-cli\-m./models/qwen2.5-1.5b-q4_k_m.gguf\-p请用通俗的语言解释什么是模型量化。\-n256参数:-m:模型文件路径。-p:提示词prompt。-n:生成多少个 token。回车,你会看到模型在你自己的机器上一个字一个字地往外蹦答案。如果你是纯 CPU 机器,这一刻意义重大——没有任何显卡,你也跑起了大模型。5.2 交互式对话模式想连续多轮对话,加-cnvconversation参数,进入聊天模式:./build/bin/llama-cli\-m./models/qwen2.5-1.5b-q4_k_m.gguf\-cnv这样就能像用 ChatGPT 一样和它连续对话,它会记住上下文。5.3 用 GPU 加速如果你编译时开了 CUDA或在 Mac 上有 Metal,可以把模型的一部分或全部层放到 GPU 上,用-nglnumber of GPU layers参数:./build/bin/llama-cli\-m./models/qwen2.5-1.5b-q4_k_m.gguf\-p介绍一下你自己。\-n256\-ngl99# 把最多 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起个服务:./build/bin/llama-server\-m./models/qwen2.5-1.5b-q4_k_m.gguf\--host127.0.0.1\--port11434\-ngl99起来之后,它提供一个兼容 OpenAI 格式的 API。你可以用任何 OpenAI 客户端连它,把本地模型当成私有的 ChatGPT 用。地址以终端实际输出的为准。六、用 Python 调用与效果评测命令行跑通了,实际项目里我们通常用 Python 调用。有两种常见方式。6.1 方式一:通过 llama-server 的 API 调先按上面把llama-server起起来,然后用标准的 OpenAI 客户端连它:fromopenaiimportOpenAI# 连本地的 llama-server地址以你启动时终端输出的为准clientOpenAI(base_urlhttp://127.0.0.1:11434/v1,api_keynot-needed,# 本地服务不需要真 key)responseclient.chat.completions.create(modellocal-model,# 名字随便填,本地就一个模型messages[{role:user,content:用三句话说说 GGUF 是什么。}],max_tokens256,)print(response.choices[0].message.content)这种方式的好处是:你的代码用的是标准 OpenAI 接口,以后想换成真的云端模型,改个base_url就行,代码不用动。6.2 方式二:用 llama-cpp-python 直接加载不想起服务,想在 Python 里直接加载 GGUF?用llama-cpp-python:pipinstallllama-cpp-pythonfromllama_cppimportLlama# 直接加载 GGUF 文件llmLlama(model_path./models/qwen2.5-1.5b-q4_k_m.gguf,n_ctx2048,# 上下文长度n_gpu_layers99,# GPU 层数,纯 CPU 就设 0verboseFalse,)# 对话式调用outputllm.create_chat_completion(messages[{role:user,content:什么是模型量化简短回答。}],max_tokens200,)print(output[choices][0][message][content])这种方式把 GGUF 直接嵌进你的 Python 程序,不用单独起服务,适合做成一个独立应用。6.3 简单评测:不同档位的速度和质量我们对比一下不同量化档位的推理速度,感受档位的取舍:fromllama_cppimportLlamaimporttimedefbench_gguf(model_path,prompt,n_tokens128):llmLlama(model_pathmodel_path,n_ctx2048,n_gpu_layers0,verboseFalse)# 纯 CPU 测starttime.time()outllm(prompt,max_tokensn_tokens,echoFalse)elapsedtime.time()-start genout[usage][completion_tokens]print(f{model_path.split(/)[-1]:30s}f生成{gen}tokens, 用时{elapsed:.1f}s, f速度{gen/elapsed:.1f}tokens/s)prompt解释一下什么是大语言模型。forpathin[./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_CUDAON或 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_CUDAON是 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 到底怎么精确区分、面对一堆档位到底该怎么科学地选,还没讲透。下一篇就专门解决这个高频困惑,给你一套清晰的选档决策方法。