外观
本地部署视觉语言模型的实践记录
把视觉语言模型(VLM)部署在自己的服务器上,难点通常不在"能不能跑",而在 显存够不够、并发扛不扛得住、接口怎么接。这篇记录一次真实部署的完整清单。
先算显存
粗略估算(FP16):
$$ \text{显存} \approx \underbrace{2 \times P}{\text{权重}} + \underbrace{2 \times P \times 0.2}{\text{KV Cache 等}} + \text{图像编码开销} $$
其中 $P$ 是参数量(单位:十亿)。也就是说 7B 模型 FP16 大约 16–18 GB,留出余量按 20 GB 算。 视觉模型还要额外算图像 token:一张 448×448 的图切 patch 后可能产生几百个 token, 多图输入时 KV Cache 会明显上涨。
几条实用结论:
- 24 GB 单卡:7B FP16 勉强,建议 AWQ/GPTQ 4bit;
- 48 GB:13B 4bit 比较舒服,能开 2–4 并发;
- 80 GB:30B 级别 4bit 或 13B FP16 都可以;
- 显存不够时优先降
max_model_len和并发,而不是一味量化——量化掉点比截断上下文更难发现。
选择推理框架
| 框架 | 适合场景 | 备注 |
|---|---|---|
| vLLM | 高并发、OpenAI 兼容接口 | PagedAttention,吞吐好 |
| SGLang | 复杂提示复用、结构化输出 | RadixAttention 对多轮友好 |
| llama.cpp | 单机低显存、CPU/GPU 混合 | GGUF 量化方便,吞吐一般 |
| Transformers | 调试、单条推理 | 最灵活,最慢 |
生产环境我一般用 vLLM,调试阶段用 Transformers 对拍输出,确认量化没有把模型搞坏。
用 vLLM 起一个 OpenAI 兼容服务
bash
python -m vllm.entrypoints.openai.api_server \
--model /models/Qwen2.5-VL-7B-Instruct-AWQ \
--served-model-name vlm \
--quantization awq \
--max-model-len 8192 \
--gpu-memory-utilization 0.90 \
--limit-mm-per-prompt image=4 \
--host 127.0.0.1 \
--port 8000要点:
--served-model-name起一个短名字,客户端就不用写长路径;--gpu-memory-utilization 0.90不要给到 1.0,留一点给 CUDA context;--limit-mm-per-prompt限制单次请求图片数,防止有人一次传 20 张把显存打爆;- 一定绑
127.0.0.1,公网访问交给反向代理处理。
验证:
bash
curl -s http://127.0.0.1:8000/v1/models | jq .
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "vlm",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "这张图里有什么?"},
{"type": "image_url", "image_url": {"url": "https://example.com/demo.jpg"}}
]
}],
"max_tokens": 256
}' | jq -r '.choices[0].message.content'用 systemd 管起来
不要用 nohup 起服务,重启机器就没了。写一个 unit:
ini
# /etc/systemd/system/vllm.service
[Unit]
Description=vLLM OpenAI compatible server
After=network-online.target
[Service]
User=bike
WorkingDirectory=/home/bike
Environment="HF_HOME=/home/bike/.cache/huggingface"
ExecStart=/home/bike/.local/bin/vllm serve /models/Qwen2.5-VL-7B-Instruct-AWQ \
--served-model-name vlm --quantization awq --max-model-len 8192 \
--gpu-memory-utilization 0.90 --host 127.0.0.1 --port 8000
Restart=always
RestartSec=5
LimitNOFILE=65535
[Install]
WantedBy=multi-user.targetbash
sudo systemctl daemon-reload
sudo systemctl enable --now vllm
sudo journalctl -u vllm -fnginx 反向代理
把服务挂到子路径,尽量保留 /v1/ 前缀,客户端就不用改 base_url:
nginx
location /v1/ {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 流式输出必须关缓冲
proxy_buffering off;
proxy_request_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}三个容易忽略的点:
proxy_buffering off:不关的话流式响应会被攒成一坨再发,前端看起来像"卡住然后一次性吐完";client_max_body_size:传图请求体可能几十 MB,默认 1 MB 直接 413;- 超时:长上下文生成几分钟很正常,
proxy_read_timeout默认 60s 会拦腰截断。
压测与调参
用固定长度的请求压一下,看 P99 延迟和显存峰值:
bash
hey -n 200 -c 8 \
-m POST -T application/json \
-d '{"model":"vlm","messages":[{"role":"user","content":"你好"}],"max_tokens":128}' \
http://127.0.0.1:8000/v1/chat/completions调参顺序建议:
- 先定
max_model_len:够用就行,翻倍基本等于 KV Cache 翻倍; - 再定并发:并发数 × 单请求 KV ≈ 显存增量,超过
gpu_memory_utilization就会开始抢占/排队; - 最后考虑量化:确认掉点可接受再上。
故障排查清单
| 现象 | 排查方向 |
|---|---|
| 启动即 OOM | gpu_memory_utilization 太高、max_model_len 太大、模型实际是 FP16 没走量化 |
| 请求偶发超时 | 并发打满后排队,看 vLLM 日志里的 running/waiting 队列 |
| 图片请求报错 | 框架版本不支持该图片格式,或 limit-mm-per-prompt 太小 |
| 流式输出不流 | nginx 缓冲没关,或中间还有一层 CDN |
| 输出质量突然变差 | 量化配置和模型不匹配(如 AWQ 权重按 GPTQ 加载) |
小结
部署这件事,八成时间花在显存和超时上。把"先算显存、再定并发、最后量化"的顺序固定下来, 大多数坑都能提前避开。模型本身的能力边界,还是要靠评测去确认。