系列第 9 篇 · 本地部署
你以为本地部署就是ollama run qwen2.5一行命令的事?我在一台32 核、无独立显卡的机器上,用纯 NumPy 推理引擎把 Qwen2.5-0.5B 和 1.5B 真跑了一遍:0.5B 解码 12.1 tok/s、首 token 2.35s;1.5B 直接掉到 4.74 tok/s。更扎心的是——int4 量化为了把权重压到 247MB,质量却肉眼崩坏,top-1 一致率只剩 27.8%。
本篇不讲「4090 多香」,只讲没有显卡时本地部署的真实水位,以及量化这道坎到底卡在哪。
实测环境(先说清楚能不能复现):Intel/AMD 32 核 CPU、无 GPU、纯 NumPy 2.5.1 / OpenBLAS、OMP_NUM_THREADS=8;模型 Qwen2.5-0.5B-Instruct / 1.5B-Instruct。本文 GPU/4090 相关数字来自公开 benchmark,非本人实测,已逐处标注。数据基于本人环境,仅供参考。
一、解码速度:CPU 的真实水位
我把两次解码取中位数,结果如下(decode 指逐 token 生成速度,TTFT 指首 token 延迟,即预填整个 prompt 的耗时):
| 模型 | 量化 | 解码 tok/s | 首 token (TTFT) | 权重体积 (bf16) |
|---|---|---|---|---|
| Qwen2.5-0.5B | bf16 | 12.1 | 2.35 s | 988 MB |
| Qwen2.5-1.5B | bf16 | 4.74 | 5.91 s | 3.09 GB |
反直觉点 1:1.5B 参数只有 0.5B 的 3 倍,解码速度却掉了2.55 倍(12.1→4.74)。本地推理不是线性缩放,注意力与词表投影(15 万维)是固定大头,模型越大越吃亏。
反直觉点 2:12 tok/s 听起来能聊,但人类阅读舒适区在 30+ tok/s 以上。CPU 上 0.5B 勉强能「慢慢唠」,1.5B 已经接近「边想边等」的体验下限。
二、预填远比解码快:长 prompt 不亏
用批量矩阵前向(真实的 serving 做法)测预填速度,明显快于逐 token 解码:
| 上下文长度 | 0.5B 预填 (tok/s) | 1.5B 预填 (tok/s) |
|---|---|---|
| 128 | 254 | 86.7 |
| 256 | 271 | 101 |
| 512 | 237 | 98.4 |
| 1024 | 219 | 99.4 |
结论:预填速度是解码的 ~20 倍。所以「长 system prompt + 长文档」主要吃首 token 延迟,真正拖慢体感的是生成阶段,不是输入长度。
三、量化的真相:省的是显存,崩的是质量
权重体积的硬账(按参数量算,bf16=2 字节):
| 量化 | 权重体积 | 与 bf16 的 KL | top-1 一致率 | 实测观感 |
|---|---|---|---|---|
| bf16 | 988 MB | — | — | 正常 |
| int8 | 494 MB | 0.013 | 83.3% | 几乎无差 |
| int4 | 247 MB | 1.15 | 27.8% | 明显复读/跑题 |
我跑了一段固定中文让模型续写:int4 的 0.5B 直接开始复读「大模型的本地计算。大模型的本地计算……」。
诚实标注:我的纯 NumPy 引擎量化只是把权重按行缩放后仍在 fp32 计算,所以这里测的是「质量损失」,不是「提速」。真实提速要靠 llama.cpp 的整数矩阵内核——公开数据里 int4 在 CPU 上约2–4×加速。但质量损失是真实的:小模型上 int4 几乎不可用。
四、映射到显卡:你能跑多大
把本人实测和公开 benchmark 拼成一张选型表(GPU 数字非本人实测):
| 硬件 | 显存 | 能跑(量化后) | 参考 tok/s(公开) |
|---|---|---|---|
| 本机 CPU | — | 0.5B / 1.5B bf16 | 12.1 / 4.74(本人实测) |
| RTX 3060 | 12 GB | 7B int4 (~3.5GB) | ~30–40 |
| RTX 4090 | 24 GB | 7B fp16 / 14B int4 / 32B int4 | 70–100 |
关键判断:6G 卡(如 3060 的某些阉割版)连 7B fp16(~14GB)都塞不下,必须 int4;24G 的 4090 才能舒服地跑 7B fp16 或 14B int4。
五、三个血泪坑
- 坑 1:本机 torch / llama.cpp 直接 segfault,GPU 路径全废。教训:开搞前先
python -c "import torch; print(torch.cuda.is_available())"探环境,别一上来就写 CUDA 代码。 - 坑 2:小模型 + int4 = 不可用。0.5B 上 int4 的 top-1 一致率掉到 27.8%,生成直接复读。底线:小模型至少 int8,大模型才上 int4。
- 坑 3:Windows CUDA 版本地狱(公开常见坑,非本人实测但要提醒):CUDA / cuDNN / 驱动三角版本锁死,装错一个直接报 ABI 错。能用 Ollama / LM Studio 的预编译包就别自己编。
六、今天就能动手的 3 件事
- 测你自己的水位:装好 Ollama 后跑
ollama run qwen2.5:0.5b,看解码 tok/s 是多少,对照本文 CPU 的 12.1,算算你的显卡快了几倍。 - 量化先定底线:小模型从 int8 起步;用一段固定文本续写,算 KL / top-1 一致率验证质量没崩再上生产。
- 先探环境再选型:
python -c "import torch; print(torch.cuda.is_available())一行定生死——能 GPU 就 GPU,不能就走 CPU 推理引擎(如本文的纯 NumPy 兜底)。
交付物:① 上面的「机型-模型匹配表」可直接当选型清单;② 本地部署一键脚本骨架——ollama pull qwen2.5:1.5b(GPU 机)或本文的纯 NumPy 引擎(无 GPU 兜底)。
下篇我们聊「上线前最后一道关」:提示注入攻防——我用 15 条真实攻击把自家 AI 应用的防线捅了一遍。
合规声明:本文解码/预填/量化质量数据均为本人在上述 CPU 环境实跑得出;GPU/4090 等数字为公开 benchmark,非本人实测,仅供参考。