本页目录
本地 LLM 部署笔记
用消费级显卡跑本地大模型,核心是一道预算题:模型权重、KV cache、桌面环境在抢同一块显存,怎么分决定了能跑多大的模型、开多长的上下文。本文以一张 24G 的 RX 7900 XTX 为实例,给出选型方法和完整可复现配置,并延伸到 16G / 12G 显卡。所有命令兼容 Linux,WSL2 同理。
一、约束:一道不等式,不分卡
本地部署的所有决定——选哪个量化、KV cache 设什么精度、上下文开多长——都在解同一道题:
模型权重 + KV cache + 运行缓冲 + 桌面余量 ≤ 显存容量
这道不等式对 24G、16G、12G 都成立,只是右边的数不同。把它当预算来分,是整个部署的通用起点。本机是一张 7900 XTX(24GB),下文以它为实例展开。
二、第一步:按你的显卡选后端
llama.cpp 支持多种 GPU 后端,不需要装任何厂商的 AI 训练栈(CUDA Toolkit / ROCm / oneAPI),只走显卡驱动自带的运行时。按你的卡选:
| 你的显卡 | 后端 | 编译开关 |
|---|---|---|
| NVIDIA(GTX 10 系及以上) | CUDA | -DGGML_CUDA=ON |
| AMD(RX 系列 / Radeon VII) | Vulkan | -DGGML_VULKAN=ON |
| Intel Arc | Vulkan | -DGGML_VULKAN=ON |
| Apple Silicon(M 系) | Metal | -DGGML_METAL=ON |
几个常见疑问:
"N 卡要不要装 CUDA Toolkit?"——不用。llama.cpp 只依赖显卡驱动自带的 CUDA 运行时,不需要装好几个 G 的 CUDA Toolkit。
"AMD 为什么是 Vulkan 不是 ROCm?"——ROCm 是给 PyTorch / vLLM 做训练的,安装麻烦(Windows 上尤其折腾),GGUF 推理完全用不上。Vulkan 走显卡驱动自带的运行时,Linux 和 Windows 都开箱即用。
"集显和独显都在怎么办?"——Vulkan 用户用 GGML_VK_VISIBLE_DEVICES=0 锁独显(0 = 第一块);CUDA 用户用 CUDA_VISIBLE_DEVICES=0。Metal 用户通常不需要。
Windows 用户:下面的命令全部在 WSL2 里跑。WSL2 能直接访问 GPU(N 卡装官方驱动即用;A 卡需内核 5.15+ 且装 AMD 官方 WSL 驱动),编译安装流程和 Linux 一致。先
wsl --install装好发行版,后续步骤照做即可。
下面进入核心问题:显存怎么分。
三、怎么选:把显存花在刀刃上
能调的旋钮就四个:参数量和权重量化在下载选文件时定死;上下文长度和 KV 精度是启动参数,每次可调。先看本机的答案,再讲怎么在自己的卡上做同样的决策。
本机的答案:省下的显存砸在上下文,不砸在 KV 精度
选 Qwen3.6-35B-A3B(MoE,总参数 35B 但每次只激活 3B,所以有小模型的速度、大模型的知识量)。真正的取舍在于省下的显存花在哪——花在"能记住多长的对话",而不是"KV 记得多精确":
拆开这 24G:
- 模型权重 16.96 GiB(IQ4_XS)。MoE 让它跑得快,IQ4_XS 让质量够用。
- KV cache 用 q4_0 + flash-attn,把 192K 上下文压进约 5G。若换 f16,同样上下文要四倍显存直接溢出;换 Q8 也只够一半。KV 精度对生成质量的影响远小于它对上下文长度的挤压——所以这笔预算全投给长度。
- 实测常驻 21.8G,余 2.2G,idle 掉到低功耗档,桌面无压力。
早期跑过 Q3_K_XL + 短上下文的保守配置;换 IQ4_XS + q4_0 KV 后,质量、速度、上下文三者反而更平衡。桌面偶尔重载吃紧时切一个 Q3 档兜底即可。
旋钮在哪调、什么时候定
套到自己的卡上,四个旋钮分两拨:
第一步:选参数量 + 权重量化(下载时定)
选参数量靠后面的「分档表」——24G 上 30–35B,16G 上 14B,12G 上 8B。
选权重量化:同一个模型在 HF 上会有一排 GGUF,文件名后缀就是量化级别:
- 数字 ≈ bit 数:
Q8_0(8bit,近无损、最大)>Q6_K>Q5_K_M>Q4_K_M(4bit,最通用的平衡点)>Q3_K_M(省显存、质量下降)。 IQ4_XS/IQ3_XXS是 i-quant:同 bit 更小、质量接近,需 imatrix(发布者已附)。显存紧就用它。_S / _M / _L= 小 / 中 / 大变体,同 bit 里_M最常用。
怎么挑:GGUF 文件体积 ≈ 它占的显存。拿权重预算对着文件大小,挑最大能装下的:
权重预算 = 显存 − KV cache − 桌面余量(留 ~2G)
例:24 − 5(192K q4_0) − 2 ≈ 17G → 35B 用 IQ4_XS(实际 16.96G)正好;
换 Q5_K_M(~24G)就装不下。
经验优先级:先奔 Q4_K_M;装不下往下降 IQ4_XS → IQ3;有富余往上升 Q5_K_M / Q6_K。别用 Q3 以下,除非别无选择。
第二步:定 KV 精度(启动时调)
KV 精度和上下文长度都是启动参数,每次可调。KV 靠三个开关:
| 开关 | 作用 | 常用值 |
|---|---|---|
--flash-attn / -fa | 量化 KV 的前提,必开 | 开 / 关 |
--cache-type-k / -ctk | K cache 精度 | f16(默认)/ q8_0 / q4_0 |
--cache-type-v / -ctv | V cache 精度 | 同上 |
K 和 V 分开设,精度阶梯 f16 → q8_0 → q4_0,占显存大致 1 → 1/2 → 1/4——这就是为什么 q4_0 能开到 f16 约 4 倍的上下文。三种典型档:
# 激进:长上下文优先
# 保守:质量优先,上下文减半
# 非对称:K 对量化更敏感,极限省显存时保 K、压 V
怎么验证调了多少:启动日志会打印 KV self size = ...,直接看占多大。质量上 q8_0 基本无损,q4_0 在日常对话和 coding 里也难感知,只有长文本精确检索偶尔露怯。查显存占用的命令因平台而异:nvidia-smi(N 卡)、cat /sys/class/drm/card0/device/mem_info_vram_used(AMD Linux)、任务管理器 → 性能 → GPU(Windows)。
坑:不开
-fa时设-ctv q4_0会报错或被忽略——量化 V cache 依赖 flash attention。
不同显存的起点
同一套预算公式往下推(这些是估算,按自己卡实测校准):
| 显存 | 现实选择 |
|---|---|
| 24G | 30–35B MoE(IQ4/Q4)或 32B dense IQ4;长上下文靠 q4_0 KV |
| 16G | 14B dense Q4–Q5,或 30B MoE Q3 + 适度层 offload;中等上下文 |
| 12G | 8–9B Q4–Q5 舒适(如 Qwen3-8B GGUF ~5.4G),或 14B Q4 紧凑 |
| 8G | 7–8B Q4,短上下文 |
显存不够时的砍法,按优先级:先降 KV 精度(f16→q8_0→q4_0)保上下文 → 再压上下文长度 → 最后才换更小的模型或更狠的权重量化。别忘了桌面和游戏也在抢这张卡,留几 G 给它。
决策做完,现在把模型拉下来、跑起来。
四、跑起来:从安装到第一句回复
装 llama.cpp(要带 server + 对应后端,按优先级):
- 用 官方 release 二进制——全平台通用,下载解压即用,已含各后端。
- Linux 发行版有现成包:
apt install llama-cpp(Debian/Ubuntu)、pacman -S llama.cpp-vulkan(Arch)。 - 以上都没,从源码编译(把
<YOUR_BACKEND>换成第二节选的开关,如-DGGML_CUDA=ON):
# 产物在 build/bin/
下载模型到本地模型目录:
# 在 HF 搜 "<模型名> GGUF",找 unsloth / bartowski 的量化仓库
大模型 GGUF 常分片(…-00001-of-0000N.gguf),--include "*IQ4_XS*" 一次下全;llama.cpp 指向第一片会自动加载其余。
启动(单模型,适合初次试用):
Vulkan 用户如果有多块 GPU,在前面加 GGML_VK_VISIBLE_DEVICES=0 锁独显;CUDA 用户跳过。
http://localhost:18080 是 OpenAI 兼容端点。验证跑通了没:
路径里的
MTP是什么、为什么用 unsloth 的 GGUF、不用行不行——下面第五节专门讲。
单模型命令适合试用;日常常驻推荐 router 模式(models.ini 集中管多个模型,按需 LRU 换出),MTP 加速和潮汐调度也都基于 router 模式,下面展开。
五、提速:MTP 投机解码
这是什么
MTP(Multi-Token Prediction)是一种投机解码技术:一次前向预测多个 token 而非逐 token 串行生成。本机用 Qwen3.6-35B-A3B(IQ4_XS)实测:不开 MTP 约 75 t/s,开了 ~111 t/s,提速约 50%。你的模型和量化不同,实际提升会有差异。
标准 llama.cpp 模型没有这个能力——它来自 unsloth(一个专门做 GGUF 量化和加速的团队)在量化时额外往 GGUF 里嵌入了草稿头(draft head)。草稿头先"猜"接下来 2 个 token,主模型一次前向并行验证;猜对白赚,猜错回退。无需独立的草稿模型,也不占额外显存:
怎么找、不用怎么办
MTP 不是模型原生的功能,是 unsloth 在量化时额外加的。 所以:
- 只有在 HF 上搜
unsloth/<模型名>-MTP-GGUF才有 MTP 版,例如unsloth/Qwen3.6-35B-A3B-MTP-GGUF。bartowski 等常规量化仓库没有。 - 文件名含
MTP字样,如…-MTP-UD-IQ4_XS.gguf。不含就不带草稿头,无法投机解码。 - 不是所有模型都有 unsloth 出 MTP 版——unsloth 主要给 Qwen 系列加。没有就用普通 GGUF,照样跑,只是没有 MTP 加速。
不用 MTP 的配置:下载命令里去掉 MTP,换普通仓库(如 bartowski/Qwen3.6-35B-A3B-GGUF);配置文件删掉 spec-type 和 spec-draft-n-max 两行,其余不变。
router 模式与完整预设
如果想管理多个模型或常驻开机自启,升级为 router 模式:将所有模型和参数写入 ~/.config/llama.cpp/models.ini,server 用 --models-preset 加载,--models-max 1 限制同时只驻留一个(24G 只装得下一个),LRU 自动换出:
# ~/.config/llama.cpp/models.ini
[qwen3.6-mtp-instruct]
model = ~/models/Qwen3.6-35B-A3B-MTP-UD-IQ4_XS.gguf
ngl = 99 # 全部层上 GPU(99=all)
ctx-size = 196608 # 192K 上下文
chat-template = chatml # OpenAI 对话模板,不用工具也建议设
chat-template-kwargs = {"enable_thinking": false}
flash-attn = 1
cache-type-k = q4_0
cache-type-v = q4_0
spec-type = draft-mtp # 以下两行仅 MTP 模型需要
spec-draft-n-max = 2
坑:
chat-template=chatml必须显式设——用模型原生模板时,工具调用前缀处可能生成错误 token 导致提前 EOS(llama.cpp #19513)。不用 MTP、不用工具调用也一样要设。
加新模型只需在 INI 里加一条,Open WebUI 列表自动更新。systemd unit 和启用命令见「完整配置」一节。
六、常驻调度:模型在线,功耗潮汐(Linux)
跑得快是一时,常驻不浪费才是日常。以下基于 Linux systemd 和 AMD sysfs 接口(WSL2 用户直接跟本节走,发行版自带 systemd;纯 Windows 用户跳过,核心思路一样:开后台进程托管 llama-server,按 GPU 负载切功耗档):
三个机制:
① 开机 warmup。 router 懒加载,靠 systemd ExecStartPost 启动后主动打一发请求灌模型进显存。warmup payload 必须 -d @文件——内联 JSON 会被 systemd 吞掉引号。warmup.json 就一句话:{"model":"qwen3.6-mtp-instruct","messages":[{"role":"user","content":"hi"}],"max_tokens":1}。
② 功耗按真实负载切档。 llama-gpu-sync.timer 每 15s 触发,判据是 gpu_busy_percent ≥ 50% 才切 COMPUTE(档 5),闲置超过 90s 降回 BOOTUP_DEFAULT(档 0),只碰 0/5、不碰游戏用的 3d_full_screen。关键在用 GPU 真实占用而非"模型是否加载"——常驻但不推理就该待在省电档。AMD 通过 sysfs pp_power_profile_mode 实现,N 卡走 nvidia-smi -pl。
③ 游戏时整卡让出。 gamemode(多数发行版有包)检测到 Steam 游戏启动即停 llama-server,显存全让给游戏,结束再拉起。模型还在 page cache 里,warmup ~6s 恢复:
# ~/.config/gamemode.ini
[custom]
start = systemctl --user stop llama-server
end = systemctl --user start llama-server
完整 systemd unit、功耗切档脚本、sudoers 免密、enable 命令见「完整配置」一节。
七、性能基线
llama.cpp 跑分术语:
- pp512(prompt processing):并行处理 512 个输入 token 的速度(token/s),决定"读长上下文"有多快。
- tg128(text generation):连续生成 128 个输出 token 的速度,即直观的"吐字"速度——人阅读约 5–10 t/s,100+ 就是"话没说完已刷屏"。
- MTP draft 接受率:草稿头猜的 token 被主模型验证通过的比例,越高等效提速越多。
本机实测(Qwen3.6-35B-A3B · IQ4_XS · 192K):
| 指标 | 数值 | 什么概念 |
|---|---|---|
| pp512 | ~3150 t/s | 长上下文预填充不卡 |
| tg128 | ~111 t/s | 远超阅读速度,体感即时 |
| MTP 接受率 | ~52% | 草稿头约一半命中 |
| 常驻显存 | 21.8 / 24 GB | 余 2.2G |
自己测:llama-bench 一条命令,换卡换量化对比:
# 输出即 pp512 / tg128 两行
# 常驻服务跑着时需先停: systemctl --user stop llama-server(Linux)
八、跑通之后:接客户端
llama-server 暴露 OpenAI 兼容端点(http://localhost:18080/v1),任何能填自定义 base_url 的客户端都能接——Open WebUI、IDE 插件、CLI agent,把 base_url 指过来即可。
到这里模型已经能用了。但"拿它干嘛、哪些活该交给它、哪些该留给 Claude Opus、要不要配云端 fallback"是另一个话题——那才是本地模型真正值得琢磨的地方,单独一篇讲:见 local-llm-usage.md。
九、踩坑合集
- systemd 内联 JSON 被吞引号 → warmup 必须
-d @文件。 - 核显和独显共存时模型跑到了核显上 → 用
GGML_VK_VISIBLE_DEVICES=0(Vulkan)或CUDA_VISIBLE_DEVICES=0(CUDA)锁独显。 - 量化 KV 没开 flash-attn →
--flash-attn是cache-type q4_0/q8_0的前提,否则被忽略或报错。 - 工具调用提前 EOS →
chat-template=chatml显式指定,避开原生模板 bug(llama.cpp #19513)。 - 以为常驻=一直高功耗 → 用
gpu_busy_percent(AMD)或nvidia-smi(N 卡)切档,空闲降到省电档。 - 显存预算算错 → 先估
模型体积 + KV(层数×ctx×精度) + 缓冲 + 桌面,留 1–2G 余量,别顶到零。
十、完整配置(Linux)
前面各节为讲清"做什么",只贴了关键片段;这里把整条链路的所有文件完整列出,按顺序创建,最后 enable。
WSL2 用户直接跟本节走;纯 Windows 用户用任务计划程序替代 systemd,功耗切档按 AMD/N 卡分别处理。
前置:脚本 + 免密 sudo
llama-profile-sync 会调用 gpu-profile-set,后者需要 root 写 pp_power_profile_mode(AMD GPU sysfs);systemctl 的 ExecStopPost 同理。user unit 无法弹 sudo 密码提示,必须配免密:
# 免密 sudo(username 换成自己的):
# echo "USERNAME ALL=(ALL) NOPASSWD: /usr/local/bin/gpu-profile-set" | sudo tee /etc/sudoers.d/llama-profile
pp_power_profile_mode/gpu_busy_percent是 AMD GPU sysfs 接口。N 卡功耗管理走nvidia-smi -pl或nvidia-persistenced,不适用本节脚本。
systemd user unit(三个文件)
# ~/.config/systemd/user/llama-server.service
[Unit]
Description=llama.cpp server (router mode, Vulkan)
After=network.target
[Service]
Environment=GGML_VK_VISIBLE_DEVICES=0 # Vulkan 锁独显; CUDA 用户删掉此行或换 CUDA_VISIBLE_DEVICES=0
ExecStart=/usr/bin/llama-server \
--models-preset %h/.config/llama.cpp/models.ini \
--models-max 1 \
--host 0.0.0.0 \
--port 18080 \
--metrics
# warmup: 预加载模型进显存; -d @file 避开 systemd 引号吞掉
ExecStartPost=-/usr/bin/bash -c 'for i in $(seq 1 60); do curl -sf --max-time 90 http://127.0.0.1:18080/v1/chat/completions -H "Content-Type: application/json" -d @%h/.config/llama.cpp/warmup.json >/dev/null && exit 0; sleep 2; done'
# 服务停止时 GPU 回省电档
ExecStopPost=/usr/bin/sudo /usr/local/bin/gpu-profile-set bootup_default # AMD 功耗管理; N 卡用户删掉此行
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
# ~/.config/systemd/user/llama-gpu-sync.service
[Unit]
Description=Sync GPU profile with llama-server model state
After=llama-server.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/llama-profile-sync
# ~/.config/systemd/user/llama-gpu-sync.timer
[Unit]
Description=Poll llama-server model state for GPU profile switching
[Timer]
OnBootSec=15s
OnUnitActiveSec=15s
[Install]
WantedBy=timers.target
warmup
启用
# linger — 常驻模型的关键:退出登录后服务仍存活
# reload + enable
# 验证
--user不是--system:GPU 上下文(DRM render node)在用户会话里,跑 system unit 反而可能打不开设备;权限也小得多。
相关文档
- local-llm-usage.md — 跑起来之后:边界(本地 vs Opus)、能干什么、llm-jobs 自动化、实操血泪
- software.md — 软件栈清单
- timers-and-crons.md — llama-gpu-sync.timer 等定时任务
- monitoring.md — 监控架构(GPU 指标经 node_exporter→Prometheus→Grafana)