「我这台机器能不能跑 32B」是最近被问得最多的问题。答案不取决于参数量上限,而取决于三件更具体的事:显存里还能剩多少给上下文,你能接受每秒几个 token,以及这个任务容不容得下错误。
把这三件事拆开算,选型其实是道算术题,不是信仰题。
先把显存账算清#
权重只是第一笔开销。真正让推理服务起不来的是上下文缓存:每个并发请求都要为它预留一份 KV cache,长度随对话变长而增长。
一个够用的估算:权重占用约等于参数量乘以每参数字节数,量化到 4 bit 时每参数约零点六字节,8 bit 时约一字节,半精度两字节。再留出百分之十五到二十给 KV cache、框架开销和碎片。
| 规模 | 4bit 权重 | 8bit 权重 | 建议显存(4bit + 8k 上下文) |
|---|---|---|---|
| 7B | 约 4.5 GB | 约 7.5 GB | 8 GB 起 |
| 14B | 约 9 GB | 约 15 GB | 16 GB 起 |
| 32B | 约 20 GB | 约 34 GB | 24 GB 起,实际更宽松 |
这些数字是量级估算,不同实现、不同量化格式会有出入,以你实际加载后的显存占用为准。显存刚好卡在阈值上不是好事:一旦有人把上下文拉到三十二 k,或者并发从一涨到四,服务就会开始换页,速度掉一个数量级。
统一内存的机器(比如共享内存的迷你主机)能「跑起来」更大的模型,但 prefill 速度受内存带宽限制,长 prompt 的等待时间会非常难受。能加载不等于能用。
量化的代价落在哪些任务上#
很多人只看困惑度,觉得 4bit 和 8bit 差不多。困惑度对这类损失不敏感,任务准确率才敏感。
经验上,量化损失最先出现在四个地方:需要精确字符操作的任务(改代码、改 JSON)、长上下文里的细节检索、多语言尤其是小语种、以及多步算术。分类、抽取、改写这类任务,4bit 通常够用。
所以选择不是「能 4bit 就 4bit」,而是按任务定:写代码和改配置用 8bit 或更高,做意图分类和字段抽取用 4bit 完全可以。同一个服务里混用两种量化也不是不行,代价是显存要按最大的那份算。
验证方法很朴素:同一个评测集,8bit 跑一遍当基线,4bit 再跑一遍,只看你那几个关键字段的准确率。差距在可接受范围内就用 4bit,不必纠结别人的测试报告——报告里的任务和你的任务不是一回事。
三个规模各自能干的项目#
模型规模和任务难度不是线性关系,但有一条大致可用的分界。
- 7B:意图分类、字段抽取、短文本改写、日志归类、简单的 RAG 问答。适合延迟敏感、量大、错了也不致命的场景。
- 14B:结构化摘要、多轮客服、按模板生成文档、中等难度的代码补全。它是「本地能跑」和「答得像个样子」之间的平衡点。
- 32B:需要读长文档再推理、跨段落比较、复杂指令遵循、代码重构。它对显存和延迟的要求明显更高,通常一台机器只能服务少量并发。
一个直接的判断方法:拿你真实任务的一百条样本,三个规模各跑一遍,人工只看「能不能直接用」。如果 7B 的可用率已经到八成,剩下的两成不值得为 14B 多付一倍的显存。
吞吐比参数更影响体验#
本地部署的体感由两个数字决定:首 token 延迟(prefill 快不快)和生成速度(decode 每秒几个 token)。前者取决于 prompt 长度和算力,后者取决于内存带宽。
量化能显著提升 decode 速度,因为瓶颈在把权重从显存读出来。这也解释了一个常见现象:同样 4bit,14B 的生成速度大约只有 7B 的一半,参数翻倍,带宽压力也翻倍。
"""bench_local.py —— 测本地 OpenAI 兼容端点的首 token 延迟与生成速度
适用于 llama.cpp server、Ollama、vLLM 等暴露 /v1/chat/completions 的实现
运行:python bench_local.py;环境变量 OPENAI_BASE_URL 指向本地端口"""
import os, time
from openai import OpenAI
client = OpenAI(base_url=os.getenv("OPENAI_BASE_URL", "http://127.0.0.1:8080/v1"),
api_key=os.getenv("OPENAI_API_KEY", "not-needed"))
MODEL = os.getenv("MODEL", "local-model")
PROMPT = "用三句话解释什么是 KV cache。"
start = time.perf_counter()
first_token_at = None
chunks = 0
stream = client.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": PROMPT}],
temperature=0.2,
stream=True, # 流式才能量出首 token 延迟
)
for chunk in stream:
delta = chunk.choices[0].delta.content if chunk.choices else None
if not delta:
continue
if first_token_at is None:
first_token_at = time.perf_counter()
chunks += len(delta)
end = time.perf_counter()
print(f"首token={first_token_at - start:.2f}s 总耗时={end - start:.2f}s 字符数={chunks}")
print(f"生成速度≈{chunks / max(end - (first_token_at or start), 1e-6):.1f} 字符/秒")
跑的时候把 prompt 长度也换几档:短问句、两千字文档、八千字文档各来一次。很多「本地模型慢」的印象,其实来自 prefill 而不是生成。
顺带一提,连续批处理(continuous batching)能让多请求共享一次前向计算,吞吐提升很明显。但它也会让首 token 延迟变得不可预测:排在长 prompt 后面的短请求,要等前面算完。对交互式产品,这比平均吞吐更值得盯。
什么时候不该本地跑#
本地部署的隐性成本经常被漏算:卡不是免费的,电费、机位、驱动与框架升级、值班排查都要人。如果只是为了省调用费,先算一下峰值利用率——大多数内部工具的 GPU 空闲率超过七成,租用时长的钱足够买很多次 API 调用。
这几种情况我通常直接建议走云端:并发峰值超过单机能承载、需要频繁切换模型版本做对比、团队没有推理服务运维经验、需要多模态或超长上下文能力。反过来,数据不能出内网、调用量大且稳定、延迟要求苛刻(同机房毫秒级)时,本地才真正划算。
还有一个折中方案:本地跑 7B 做高频小任务,难样本转给云端大模型。分流逻辑按置信度或规则来,成本能压下来一大截,用户也感知不到切换。
结论#
- 先按「权重 + 上下文缓存 + 百分之二十余量」算显存,再决定规模,别反着来。
- 分类抽取用 4bit,代码与结构化输出用 8bit;按任务定精度,不按习惯。
- 7B 覆盖大多数高频小任务,14B 是性价比平衡点,32B 只在你确实需要长文档推理时才值得。
- 用首 token 延迟和每秒 token 数验收,短 prompt 和长 prompt 分别测。
- 并发是隐形成本:每多一个并发,就多一份 KV cache,显存预算要按峰值算。
选型的终点不是「能跑多大的模型」,而是「这个任务用多小的模型就够」。
完