先说清楚一个前提:本文的实操命令以 llama.cpp 工具链为主线,因为分片、量化、mmap 加载这些底层动作都由它完成;Llama-modchip 这类封装工具本质上是在外面套一层配置管理与启动编排,具体字段名以仓库 README 和 --help 为准。两边的参数可以一一对照,文末给了映射关系。这样无论你装的是哪一层,照着做都能跑通。
这篇能做出什么
一台 64GB 内存、没有独立显卡的普通机器,能完整加载并推理一个总参数 284B 的 MoE 大模型,输出连贯的中文,速度落在"能用但不快"的区间——适合离线批处理、夜间任务、文档摘要、代码审查这类不追求交互延迟的场景。
关键认知有两条,理解了就不会走弯路:
第一条:284B 是总参数,不是每 token 的计算量。 MoE(混合专家)模型每个 token 只激活一小部分专家,真正参与矩阵乘法的参数量远小于 284B。所以无显卡机器的算力其实是够的——你不会因为 CPU 太慢而跑不动。
第二条:真正的瓶颈是内存容量和内存带宽。 284B 参数按 FP16 存要 568GB,按 Q4 量化也要 150GB 上下,都远超 64GB。所以必须做两件事:把权重量化到 2 bit 附近,再靠 mmap 让操作系统按需把权重页换进来。CPU 推理的速度大致等于"内存带宽 ÷ 每 token 需要读取的字节数",而 MoE 的稀疏激活刚好把后一项压了下来。
下面按顺序走一遍。
前置条件清单
动手前逐项核对,缺一项都会在中途卡住:
- 内存:物理内存 64GB,
free -g看到的 available 至少 58GB。如果系统里跑着数据库、浏览器、IDE,先关掉。 - 存储:NVMe SSD,剩余空间 ≥ 200GB。原始 GGUF、量化中间产物、最终分片三份会同时存在。机械硬盘不是不能跑,但会慢到失去意义。
- 系统:Linux 发行版(Ubuntu/Debian 系最省事)。macOS 也能跑,但要注意内存压力;Windows 建议直接用 WSL2 并给它分配足够内存。
- 工具链:
git、cmake(3.20 以上)、支持 C++17 的编译器、python3与pip。 - 网络:能访问模型托管平台,下载量在几十 GB 量级,建议挂夜间。
- 时间:首次跑通预留 3~5 小时,其中大部分是下载和量化。
一行命令检查环境:
```bash
free -g && nproc && lscpu | grep -E "Model name|Core|Thread|Socket" && df -h ~
```
记下三个数字:物理核心数(不是逻辑线程数)、可用内存、家目录剩余空间。后面调参全靠它们。
第一步:把工具链编译出来
不要用系统包管理器里的旧版本,直接源码编译,这样能开本机指令集优化。版本以官方文档当前版本为准,不用纠结具体 commit。
```bash
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_NATIVE=ON
cmake --build build --config Release -j$(nproc)
```
GGML_NATIVE=ON 会让编译器针对你这颗 CPU 生成 AVX2/AVX512 指令,这是无显卡场景下少数几个免费的性能提升之一。
编译完成后确认二进制在:
```bash
ls build/bin/ | grep llama
```
你应该能看到 llama-cli、llama-quantize、llama-gguf-split、llama-server 这几个。如果你的版本里量化工具叫 quantize 而不是 llama-quantize,名字不同而已,功能一样。
如果你用的是 Llama-modchip 这类封装,这一步换成按它的 README 安装即可,它内部链接的依然是同一套 ggml 后端。
第二步:拿到模型文件
两条路,强烈建议走第一条。
路线 A:直接下载已经量化好的 GGUF。 在模型托管平台搜索模型名 + GGUF,找带 Q2_K、IQ2_XXS、IQ1_S 字样的仓库。省掉几小时的转换和量化时间,也省掉几百 GB 的中间文件。下载时用 huggingface-cli download 或 hf download(命令名以官方文档当前版本为准),记得加 --local-dir 指定目录,别让它堆在缓存里。
路线 B:自己从原始权重转换再量化。 只有当找不到合适量化版本,或者你需要特定的量化组合时才走这条。流程是:原始 safetensors → convert_hf_to_gguf.py 转 FP16 GGUF → llama-quantize 量化。中间会需要一个 500GB 以上的临时空间,普通机器容易卡在磁盘上。
```bash
python3 convert_hf_to_gguf.py /path/to/model --outfile model-f16.gguf --outtype f16
```
这一步很吃内存,建议在别的机器上做完再拷过来,或者干脆放弃改走路线 A。
第三步:选量化等级
这是全流程里最需要判断力的一步。下表是按 284B 参数量估算的体积,"每参比特"是近似值,实际以你下载到的文件大小为准:
| 量化等级 | 每参比特(约) | 权重体积(约) | 64GB 内存下的表现 |
|---|---|---|---|
| FP16 | 16 | 570 GB | 不可行 |
| Q8_0 | 8.5 | 300 GB | 不可行 |
| Q4_K_M | 4.8 | 170 GB | 严重依赖换页,慢 |
| Q3_K_M | 3.9 | 140 GB | 勉强,长时间跑会抖 |
| Q2_K | 3.0 | 105 GB | 可跑,SSD 必须够快 |
| IQ2_XXS | 2.1 | 75 GB | 推荐起点 |
| IQ1_S / IQ1_M | 约 1.6 | 57 GB | 能全塞进内存,质量下降明显 |
判断逻辑很简单:权重体积 ≤ 可用内存的 0.9 倍时,模型能常驻,速度稳定;超过这个线,就要靠 mmap 从 SSD 换页,速度取决于你的 SSD 随机读性能。
284B 这个量级,想完全常驻 64GB 内存,只有 IQ1 系列做得到。IQ2_XXS 落在 75GB 左右,会有一部分权重反复换入换出,但 MoE 的稀疏性帮了大忙——每个 token 只碰少数专家,被换入换出的页其实是集中的一小撮,热起来之后就稳了。
实操建议:先用 IQ2_XXS 跑通,确认输出质量能接受;如果嫌慢或发现抖得厉害,降到 IQ1_M 换稳定性;如果嫌质量差,升到 Q2_K 并接受更慢的速度。
量化命令(路线 B 才需要):
```bash
./build/bin/llama-quantize \
--imatrix imatrix.gguf \
model-f16.gguf \
model-IQ2_XXS.gguf \
IQ2_XXS
```
--imatrix 是重要性矩阵文件,用校准语料统计出来的,能显著改善低比特量化的质量。如果没有现成的 imatrix,去掉这个参数也能跑,但 2 bit 附近的质量差距会比较明显。
第四步:分片
单文件超过 50GB 在传输和加载上都容易出问题,分成 20GB 左右的几片更稳。
```bash
./build/bin/llama-gguf-split \
--split \
--split-max-size 20G \
model-IQ2_XXS.gguf \
./shards/model-IQ2_XXS
```
产物形如 model-IQ2_XXS-00001-of-00004.gguf。推理时只需要指定第一个分片,程序会自动按序号把其余分片找齐。这一点容易踩坑:如果你手动把分片改名、或者只拷了其中几个,加载会直接失败。
```bash
ls -lh ./shards/
```
核对两件事:文件数量对不对,每个文件大小是否齐全(末尾那个通常会小一些,正常)。下载中断是最常见的分片缺失原因,用 sha256sum 对照仓库给的校验值过一遍。
第五步:第一次启动
先用最短的上下文、最小的输出长度验证能跑通,不要一上来就压满参数。
```bash
./build/bin/llama-cli \
-m ./shards/model-IQ2_XXS-00001-of-00004.gguf \
-c 2048 \
-n 128 \
-t 16 \
-p "用三句话解释什么是混合专家模型。" \
--no-warmup
```
参数含义:
-c 2048:上下文长度。KV cache 的内存占用和它成正比,先小后大。-n 128:最多生成 128 个 token,够验证了。-t 16:线程数。填物理核心数,不要填逻辑线程数。 超线程在这里是负收益,两个线程抢一个物理核的访存单元,反而更慢。--no-warmup:跳过预热。第一次跑想快点看到结果就加,但正式用的时候要去掉。
第一次运行会明显慢,因为操作系统在把权重页从 SSD 读进 page cache。盯着另一个终端看:
```bash
vmstat 1
```
关注 si(swap in)和 so(swap out)两列。如果是 0,说明内存够用,慢只是因为首次读盘;如果持续非零,说明物理内存已经不够,系统在往 swap 里倒,这时候速度会掉到不可接受,需要降量化等级或关掉其他进程。
第六步:内存与吞吐调优
跑通之后开始调。按收益从高到低排:
1. 降低 swap 倾向,把内存留给模型页
```bash
sudo sysctl vm.swappiness=10
sudo sysctl vm.vfs_cache_pressure=50
```
这两条让内核尽量保留文件页缓存,而不是急着回收。模型权重就是文件页,保住它们等于保住速度。写进 /etc/sysctl.d/ 可以持久化。
2. 分情况决定 mmap 还是 mlock
- 权重能装进内存(IQ1 系列):用
--mlock锁住,防止被换出。注意--mlock需要ulimit -l足够大,先执行ulimit -l unlimited。 - 权重装不进(IQ2/Q2):保持默认的 mmap,让内核自己调度。不要加
--no-mmap,那会一次性把全部权重读进内存,直接 OOM。
3. 上下文长度和 KV cache 量化
上下文是最容易被忽视的内存黑洞。-c 8192 相比 -c 2048,KV cache 会涨四倍。开 KV 量化能省一半以上:
```bash
./build/bin/llama-cli \
-m ./shards/model-IQ2_XXS-00001-of-00004.gguf \
-c 8192 \
-n 512 \
-t 16 \
--cache-type-k q8_0 \
--cache-type-v q8_0 \
-p "你的提示词"
```
q8_0 的 KV 量化对输出质量影响很小,是性价比很高的一档。如果你的版本支持 -fa(Flash Attention),在长上下文下也打开,能进一步压 KV 内存。
4. 调 batch 大小控制峰值内存
```bash
-b 512 -ub 128
```
-ub(ubatch)直接决定单次前向计算的峰值内存。内存紧张时把 -ub 降到 64 或 32,能明显减少瞬时占用,代价是速度略降。
5. 线程绑核(多路 CPU 才需要)
单路机器上 -t 设成物理核心数就够了。如果是双路服务器,跨 NUMA 节点访存会拖慢,用 numactl --cpunodebind=0 --membind=0 把进程和一个内存节点绑在一起。
6. 预热
正式使用前先发一个几百 token 的请求,把热专家页拉进内存。之后的速度会稳定很多。
常见坑与排错
进程被 Killed,没有任何报错
系统 OOM killer 干的。查日志确认:
```bash
dmesg | tail -20 | grep -i "killed process"
```
对策:降量化等级、减小 -c、减小 -ub、关掉其他进程。
速度慢到几秒一个字
先分清是"首次读盘慢"还是"持续在换页"。用 vmstat 1 看 si/so:持续非零就是内存不够,在跟 SSD 做拉锯。用 iostat -x 1 看磁盘 %util,长期接近 100% 说明瓶颈在盘。
加载时报 "failed to load model" 或 tensor 数量对不上
九成是分片不全。检查文件数、文件大小、校验值。另外确认你只传了第一个分片路径,且分片文件名保持原样(序号不能被改)。
输出重复、跑题、突然夹杂乱码
量化太狠了。2 bit 以下对小模型或者非 MoE 结构伤害很大。换高一档,或者补上 imatrix 重新量化。
线程开满反而变慢
把 -t 从逻辑线程数改成物理核心数。用 lscpu 看 Core(s) per socket 和 Thread(s) per core,两者相乘是逻辑数,-t 填的是前者乘 socket 数。
内存明明没用满,速度却上不去
mmap 是惰性加载,free 里显示的 used 低是正常的。真正要看的是 free 的 buff/cache 列——模型页都在那里。如果 buff/cache 也很低,说明内核在频繁回收,回头检查 swappiness 和 vfs_cache_pressure。
Llama-modchip 与原生参数的对照
封装工具省掉的是记忆成本,底下的动作还是这些。对照关系大致是:
| 你要做的事 | 原生做法 |
|---|---|
| 指定模型 | -m 指向第一个分片 |
| 上下文长度 | -c |
| 线程数 | -t |
| KV 量化 | --cache-type-k / --cache-type-v |
| 锁内存 | --mlock(配合 ulimit -l) |
| batch 控制 | -b / -ub |
| 服务模式 | llama-server --host 0.0.0.0 --port 8080 -m ... |
封装工具的配置文件字段名以它自己的 README 为准,但只要你理解了上面每个旋钮在控制什么,迁移过去就是改个键名的事。
下一步建议
接成服务。 用 llama-server 起一个 OpenAI 兼容接口,之后所有现成的客户端、脚本、Agent 框架都能直接指过来,不用改代码:
```bash
./build/bin/llama-server \
-m ./shards/model-IQ2_XXS-00001-of-00004.gguf \
-c 8192 -t 16 \
--cache-type-k q8_0 --cache-type-v q8_0 \
--host 0.0.0.0 --port 8080
```
量化等级做实测评。 别凭感觉选。准备 30~50 条你真实业务里的问题,在 IQ1_M、IQ2_XXS、Q2_K 三档上各跑一遍,人工看输出质量。很多时候 IQ2_XXS 就够用,没必要为了心理安慰多花一倍时间。
把速度预期管理好。 无显卡跑 284B 是"能跑"而不是"好用"。交互式对话会让人着急,但批处理场景完全够——一晚上处理几千份文档,这个吞吐是划算的。
考虑混合方案。 如果之后手头有一张显存不大的卡,可以把注意力层和一部分专家放 GPU,其余留 CPU,用 -ngl 和 MoE 卸载参数控制分层。原理和现在这套 mmap 换页是一回事,只是把"慢速存储"从 SSD 换成了"较慢但快于 SSD 的内存"。
最后一条,也是最重要的:先把 vmstat 和 iostat 看懂。 无显卡大模型推理的调优,说到底就是内存和磁盘之间的调度问题。能把这两组指标读明白,换任何模型、任何工具,你都能自己找到瓶颈在哪。
