Qwen 本地部署总是起不来 / 是显存不够还是量化选错了?

文章导读
Qwen 本地起不来,很少是单纯“显存不够”或单纯“量化选错”,更常见的是两件事叠在一起:模型本身放不下,同时又挑了一个当前后端不认的量化格式,于是加载阶段直接中断。判断顺序建议固定下来:先量实际可用显存,再按参数量和量化位数算一遍权重占用,接着选一个后端支持的量化版本,最后用启动日志把失败缩到具体环节,而不是反复换版本试运气。
📋 目录
  1. 先量出当前可用显存与已被占用的显存
  2. 按参数量和量化位数估算权重占用
  3. 选一种能落地的量化格式再启动
  4. 读启动日志定位卡在哪一步
  5. 用一条最短请求验证服务真的可用
A A

Qwen 本地起不来,很少是单纯“显存不够”或单纯“量化选错”,更常见的是两件事叠在一起:模型本身放不下,同时又挑了一个当前后端不认的量化格式,于是加载阶段直接中断。判断顺序建议固定下来:先量实际可用显存,再按参数量和量化位数算一遍权重占用,接着选一个后端支持的量化版本,最后用启动日志把失败缩到具体环节,而不是反复换版本试运气。

先分清“放不下”和“配不对”。显存不足通常发生在权重加载之后的缓存分配阶段,日志里能看到 KV cache、max_model_len 相关内容;量化格式不被后端支持,则更早在加载权重时就报错。先跑一次显存查看命令,再按公式估算权重占用,基本能确定该降量化位数、换后端还是缩上下文。边界是:不同后端、不同量化实现的占用并不一致,最终仍要结合自己机器上的实际日志确认。

先量出当前可用显存与已被占用的显存

不要凭显卡型号猜,直接在机器上读一次当前状态:

nvidia-smi
nvidia-smi `--query-gpu`=index,name,memory.total,memory.used,memory.free `--format`=csv

解读方式:memory.total 是这张卡的物理显存总量;memory.used 里既包含图形界面、别的推理进程、上一次没退干净的残留进程占用的部分,也包含你刚启动的进程已经吃掉的显存;memory.free 才是此刻能分配给新模型的余量。注意 free 并不是“全部都能给模型”,驱动和显示服务本身也会占一部分,实际可分配上限通常比 free 略小。

多卡机器先看清楚哪几张卡是空的,用 CUDA_VISIBLE_DEVICES 指定卡号,别让进程跑到一张已被占满的卡上。如果发现 used 很高但你认为没跑模型,先用进程列表确认是不是上一次启动没退干净的进程还在占着。

按参数量和量化位数估算权重占用

估算公式很直接:权重占用 ≈ 参数量 × 每参数字节数。每参数字节数大致是:FP16/BF16 约 2,INT8 约 1,4bit 约 0.5~0.6(量化会带来 scale、zero-point 之类的小额额外开销)。

算一遍通用例子:一个 7B 模型,FP16 权重约 14GB,INT8 约 7GB,4bit 约 3.5~4GB。这只是权重本身,还没算 KV cache 和运行时开销,所以实际显存需求通常要在这个数之上再加一截。

上下文长度和批大小的作用方向要单独记住:上下文长度翻倍,KV cache 大致跟着翻倍;并发数或批大小增加,KV cache 也按倍数往上走。因此“模型刚好放得下”的时候,把上下文开得很大同样会 OOM,而且失败点往往在权重加载完成之后的缓存分配阶段,这一点和权重放不下要区分开。

参数量量化位数权重占用方向起步建议
小参数量(1.5B~3B 级)FP16约参数量 ×2,较小显存也可能放下先按后端默认上下文启动
7B 级FP16约参数量 ×2,需要较宽裕的显存显存紧张就直接降量化位数
7B 级INT8约参数量 ×1中等显存可试,必须给 KV cache 留余量
7B 级4bit约参数量 ×0.5~0.6显存吃紧时的常见起点
更大参数量4bit / INT8按公式同比放大,单卡常常放不下考虑多卡拆分、CPU 卸载或换更小参数量

选一种能落地的量化格式再启动

位数取舍的方向:位数越低,文件和显存占用越小,但精度损失出现的概率越高,对格式约束、代码生成、长链推理这类任务更明显。8bit 通常更接近未量化输出,代价是占用偏大;4bit 体积明显缩小,多数对话场景可用,但要接受一定的质量波动;更低的位数只在显存实在不够时再考虑。

比位数更容易踩的坑是格式和后端不匹配:GGUF 系(llama.cpp、Ollama 一类)读 GGUF 文件;AWQ、GPTQ 这类权重量化通常配 vLLM、TGI 之类后端。把 GGUF 塞进只认 AWQ/GPTQ 的后端,报错会出现在加载阶段。所以先确认后端支持哪种格式,再决定下载哪个文件。

Qwen 本地部署总是起不来 / 是显存不够还是量化选错了?

llama.cpp 一类后端的启动骨架:

./llama-server \
  -m /path/to/qwen-<参数量>-<量化档位>.gguf \
  -c <上下文长度> \
  -ngl <放到 GPU 的层数> \
  -b <批大小> \
  `--host` 0.0.0.0 `--port` 8080

vLLM 一类后端的启动骨架:

python -m vllm.entrypoints.openai.api_server \
  `--model` <本地权重目录或模型标识> \
  `--quantization` <awq|gptq|None> \
  `--max-model-len` <上下文长度> \
  `--gpu-memory-utilization` <0.8~0.9> \
  `--max-num-seqs` <并发上限>

需要替换的参数就这几个:模型文件或目录路径、量化类型、上下文长度、并发上限。其中 -ngl 决定多少层放到 GPU、多少层留在 CPU,层数越少显存越省但速度越慢;gpu-memory-utilization 是给权重加 KV cache 的总量比例上限,调低它等于给缓存留余地,但调得太低可能直接启动失败。

读启动日志定位卡在哪一步

日志要按阶段看,三个阶段的表现和处理动作不一样:

  • 下载权重阶段失败。典型表现是 404、连接中断、校验不匹配、No space left on device 之类。处理动作:确认模型路径确实存在、磁盘剩余空间够、文件名与后端期望的一致;网络不稳就先把权重下到本地,再把参数指向本地目录。
  • 加载权重阶段失败。典型表现是 unknown model architecture、unsupported quantization、tensor shape mismatch、CUDA error,或者在 loading weights 这一步就 OOM。处理动作:大概率是量化格式和后端不匹配,或权重本身超出显存;换后端或换更低位数、更小参数量的量化文件。
  • 分配缓存阶段失败。权重已经加载完,日志走到 KV cache、max_model_len、gpu_memory_utilization 相关位置才 OOM。处理动作:缩短上下文长度、降低并发、调低显存占用比例,或减少放到 GPU 上的层数。

把这三类对上去,基本就能判断是显存问题还是量化问题,而不是笼统地“起不来”。

用一条最短请求验证服务真的可用

进程没报错退出,不等于接口通了。启动完成后单独发一次最小请求,把“模型没起来”和“接口没通”分开:

curl http://127.0.0.1:8080/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"<服务里注册的模型名>","messages":[{"role":"user","content":"ping"}],"max_tokens":16}'

期望返回是一段 JSON,其中 choices 数组里有 message.content 字段,内容可能很短甚至只是几个字,但结构完整、HTTP 状态正常。

返回异常时按层往回查:curl 直接连接被拒,说明服务进程没起来或端口没在监听,回上一节看启动日志;返回 404 或 model not found,通常是 model 字段和服务注册的名字不一致,改成本地实际加载的模型名;返回 200 但内容为空或明显错乱,多半是对话模板没匹配对,检查后端是否用了与 Qwen 对应的 chat template;请求一直卡住直到超时,偏向缓存或排队问题,回去缩短上下文、降低并发再试。