下载完一个模型,双击启动,进度条走到 97% 然后报一句 failed to load model。显存不够、格式不对、上下文开太大,都会长成这个样子,而报错文本往往只说"加载失败"。这篇把"跑不动"拆成三层:引擎、格式、量化,再把显存占用拆成四块,最后给一条能一次跑通的最小路径。
1. 三个层次,各自解决一个问题
| 层次 | 回答什么 | 常见选择 | 选错的症状 |
|---|---|---|---|
| 推理引擎 | 谁来加载和执行这个模型 | llama.cpp、Ollama、vLLM、LM Studio | 引擎不支持该架构,直接报错 |
| 模型格式 | 权重怎么存 | GGUF、safetensors、AWQ/GPTQ | 引擎读不了这个后缀 |
| 量化版本 | 权重用几位存 | Q8、Q5、Q4 等 | 能加载但质量明显下降 |
三者必须成套。看到 xxx.Q4_K_M.gguf 就该想到 llama.cpp/Ollama;看到 model-00001-of-00004.safetensors 就该想到 Transformers/vLLM。拿 GGUF 去喂 vLLM,报错通常不解释格式问题。
2. 显存花在哪四块
| 组成 | 大致怎么估 | 随什么变化 |
|---|---|---|
| 权重 | 参数量 × 每参数字节数 | 量化等级、模型大小 |
| KV 缓存 | 层数 × 头数 × 头维 × 2 × 上下文 token 数 × 字节数 | 上下文长度、并发数 |
| 框架开销 | 计算缓冲、图捕获、CUDA context | 引擎实现、批大小 |
| 碎片与预留 | 分配器预留、显存碎片 | 长跑、频繁换上下文 |
粗略换算(按 fp16 核对:每参数 2 字节):
7B × 2B/参数 ≈ 14 GB
13B × 2B ≈ 26 GB
70B × 2B ≈ 140 GB
4bit 量化后 ≈ fp16 的 1/4 左右:7B ≈ 4~5 GB,70B ≈ 35~40 GB
这几个数是量级,不含 KV 缓存与框架开销。实际能不能跑,要在这上面再加 1~3 GB 的余量;长上下文时这个余量会变成十几 GB。
3. 量化等级:省的是显存,付的是质量
| 等级 | 每参数字节(约) | 7B 权重占用(约) | 质量变化 |
|---|---|---|---|
| fp16 | 2.0 | 14 GB | 基准 |
| Q8 | 1.0 | 7 GB | 通常接近无损 |
| Q5 | 0.7 | 5 GB | 多数任务几乎无感 |
| Q4 | 0.55 | 4 GB | 通用任务可用,细节开始丢 |
| Q3 及以下 | ≤0.45 | ≤3 GB | 明显退化,尤其推理与长文 |
"不是越低越省事"的实际含义:Q2/Q3 常常在简单问答上看不出问题,一到需要多步推理、代码生成、长文摘要就崩,而你不会立刻把它归因到量化。判断方法只有一个——同一批固定问题,量化前后各跑一遍,比对输出,而不是凭"感觉还行"。
4. 上下文一长,显存为什么突然爆
KV 缓存随 token 数线性增长,注意力计算随长度平方增长。用户感受到的"突然爆",通常是因为两者叠加:
| 上下文 | KV 缓存(相对) | 注意力量(相对) |
|---|---|---|
| 2K | 1× | 1× |
| 8K | 4× | 16× |
| 32K | 16× | 256× |
# 先确认引擎实际用了多少上下文,别信配置文件
curl -s http://127.0.0.1:11434/api/ps | python3 -m json.tool
很多工具默认上下文只有 2K~4K,你设 32K 时它可能静默截断而不是报错——输出看起来"忘了前面说的话",其实是根本没喂进去。
5. CPU 与 GPU:不是快慢之分,是能不能用
| 维度 | GPU | CPU |
|---|---|---|
| 适合 | 7B 以上、要交互速度 | 小模型、批处理、离线任务 |
| 速度瓶颈 | 显存带宽 | 内存带宽 |
| 量化偏好 | 4~8 bit,需内核支持 | 4 bit 及以下更划算 |
| 典型体验 | 首 token 快,生成流畅 | 首 token 慢,长文等待明显 |
CPU 推理不是"降级方案",在某些批量离线任务里更省事:不需要显存、可以并行多进程、不受驱动限制。GPU 的优势集中在低延迟交互。
6. 第一次跑通的最小路径
# 1) 选一个 7B 级别、Q4 量化的模型,先求跑通不求效果
ollama pull qwen2.5:7b-instruct-q4_K_M
# 2) 起服务并显式指定上下文,别用默认值
OLLAMA_CONTEXT_LENGTH=8192 ollama serve
# 3) 最小验证:一条短提示,看是否有正常输出
ollama run qwen2.5:7b-instruct-q4_K_M "用一句话说明什么是 KV 缓存"
四步原则:小模型 → 合适的量化 → 显式上下文 → 单条验证。跳过任何一步,都会在最后拿到一个语焉不详的"加载失败"。
7. 格式与引擎的匹配关系
| 格式 | 常见引擎 | 备注 |
|---|---|---|
| GGUF | llama.cpp、Ollama、LM Studio | 单文件,含量化,最适合本地起步 |
| safetensors | Transformers、vLLM、TGI | 目录形态,需 config.json |
| GPTQ / AWQ | vLLM、部分专用后端 | GPU 量化,需匹配内核 |
| MLX | Apple Silicon 专用 | 平台锁定 |
# llama.cpp 直接跑 GGUF:-c 上下文,-ngl 卸载到 GPU 的层数
./llama-cli -m model.Q4_K_M.gguf -c 8192 -ngl 99 -p "hello"
-ngl 是关键旋钮:显存不够时把它调小,让部分层留在内存里跑,速度下降但能起来。这就是"offload 的代价"——跨 PCIe 搬运权重,token 生成速度通常掉一个数量级。
8. 速度由什么决定
| 因素 | 影响 | 观察方式 |
|---|---|---|
| 显存带宽 | 生成阶段的主要瓶颈 | 对比同尺寸不同带宽卡 |
| 批大小 | 吞吐上升、单请求延迟上升 | 压测不同并发 |
| 上下文长度 | 越长越慢,且非线性 | 固定 prompt 只改上下文 |
| offload 比例 | 掉速最明显的一项 | -ngl 逐档下调 |
| 量化等级 | 4bit 通常比 fp16 快 | 同 prompt 对比 tokens/s |
生成速度大致由"每 token 要读多少权重"决定,所以带宽比算力更关键;预填充阶段才是算力密集。这也解释了为什么量化能提速:读的字节少了。
9. 本地能做什么、不能做什么
| 任务 | 7B~13B 本地模型 | 说明 |
|---|---|---|
| 分类、打标 | 可用 | 配合少量示例,稳定 |
| 信息抽取 | 可用 | 结构化输出要加约束与校验 |
| 改写、摘要 | 可用 | 短文本效果好,长文要分段 |
| 简单问答 | 可用 | 领域知识靠检索补 |
| 多步推理、复杂代码 | 不可靠 | 容易中途跑偏且不自知 |
| 长文档全局理解 | 不可靠 | 受上下文与注意力衰减限制 |
判断标准不是"它答得像不像",而是错了你能不能发现。抽取类任务有校验,错了能拦住;复杂推理错了往往看起来也很合理。
10. 与 API 调用的取舍
| 维度 | 本地 | 云端 API |
|---|---|---|
| 边际成本 | 电费与硬件折旧 | 按 token 计费 |
| 延迟 | 稳定、无网络抖动 | 受网络与排队影响 |
| 数据 | 不出机器 | 出网 |
| 能力上限 | 受显存约束 | 可用更大模型 |
| 运维 | 驱动、显存、版本全自己扛 | 由服务方承担 |
务实的组合是:高频、简单、格式固定的任务放本地;难任务、低频、需要大模型的上云。而不是二选一。
11. 最小示例:起服务、打一次、换上下文
ollama serve & # 默认监听 11434
curl -s http://127.0.0.1:11434/api/generate -d '{
"model": "qwen2.5:7b-instruct-q4_K_M",
"prompt": "把这句话改写成更简短的技术表述:这个功能主要是用来做数据校验的",
"stream": false,
"options": {"num_ctx": 2048, "temperature": 0.2}
}' | python3 -c "import sys,json;print(json.load(sys.stdin)['response'])"
# 只改上下文,看显存与首 token 延迟的变化
for ctx in 2048 8192 32768; do
/usr/bin/time -f "ctx=$ctx %es" curl -s http://127.0.0.1:11434/api/generate \
-d "{\"model\":\"qwen2.5:7b-instruct-q4_K_M\",\"prompt\":\"hi\",\"stream\":false,\"options\":{\"num_ctx\":$ctx}}" >/dev/null
done
nvidia-smi --query-gpu=memory.used --format=csv
观察点有三个:能不能加载、首 token 多久、显存涨了多少。如果 32K 直接加载失败,说明 KV 缓存把余量吃完了——这时候要么降上下文,要么换更小的量化。
12. 常见坑
| 坑 | 表现 | 修正 |
|---|---|---|
| 显存不足却报"加载失败" | 报错不提 OOM | 看引擎日志与 nvidia-smi,先降 -ngl |
| 上下文设太大 | 加载失败或速度骤降 | 按实际需要设,长文先分块 |
| 模型与引擎不匹配 | 后缀对不上,报无法解析 | 按第 7 节配对 |
| 以为量化无损 | 简单问答正常,复杂任务崩 | 固定题集对比前后输出 |
| 信默认上下文 | 模型"忘事",无任何报错 | 显式设 num_ctx 并记录 |
| 忽略 CPU/GPU 差异 | 换机器后速度差十倍 | 先确认后端与 offload 层数 |
13. 边界
| 场景 | 本地可行 | 该换方案 |
|---|---|---|
| 单机个人助手 | 是 | — |
| 高并发在线服务 | 否 | vLLM 类批处理引擎或云 API |
| 敏感数据不出网 | 是 | — |
| 复杂推理与长文分析 | 否 | 更大模型 |
| 多模态 | 取决于模型 | 专用模型或云服务 |
14. 自测
failed to load model可能对应哪三类原因?先看哪个日志?- 7B 模型 fp16 与 Q4 的权重占用各是多少量级?
- 上下文从 8K 到 32K,KV 缓存和注意力量各涨多少倍?
-ngl调小的代价具体是什么?- 为什么生成阶段带宽比算力更关键?
- 哪一类任务即使本地模型"答得很顺"也不该直接采用?