Skip to content

本地部署视觉语言模型的实践记录 ​

把视觉语言模型(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.target
bash
sudo systemctl daemon-reload
sudo systemctl enable --now vllm
sudo journalctl -u vllm -f

nginx 反向代理 ​

把服务挂到子路径,尽量保留 /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;
}

三个容易忽略的点:

  1. proxy_buffering off:不关的话流式响应会被攒成一坨再发,前端看起来像"卡住然后一次性吐完";
  2. client_max_body_size:传图请求体可能几十 MB,默认 1 MB 直接 413;
  3. 超时:长上下文生成几分钟很正常,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

调参顺序建议:

  1. 先定 max_model_len:够用就行,翻倍基本等于 KV Cache 翻倍;
  2. 再定并发:并发数 × 单请求 KV ≈ 显存增量,超过 gpu_memory_utilization 就会开始抢占/排队;
  3. 最后考虑量化:确认掉点可接受再上。

故障排查清单 ​

现象排查方向
启动即 OOMgpu_memory_utilization 太高、max_model_len 太大、模型实际是 FP16 没走量化
请求偶发超时并发打满后排队,看 vLLM 日志里的 running/waiting 队列
图片请求报错框架版本不支持该图片格式,或 limit-mm-per-prompt 太小
流式输出不流nginx 缓冲没关,或中间还有一层 CDN
输出质量突然变差量化配置和模型不匹配(如 AWQ 权重按 GPTQ 加载)

小结 ​

部署这件事,八成时间花在显存和超时上。把"先算显存、再定并发、最后量化"的顺序固定下来, 大多数坑都能提前避开。模型本身的能力边界,还是要靠评测去确认。

本站内容均为个人学习笔记与实践记录,欢迎交流指正。