跳转至

Qwen2.5-VL 使用 vLLM 部署运维实战指南

1. 环境准备

1.1 硬件要求

Qwen2.5-VL-3B-Instruct

  • NVIDIA GPU:至少 12GB 显存
  • 推荐显卡:NVIDIA A10(24GB)/RTX 4070Ti(12GB)/RTX 4080(16GB)
  • 最低支持:NVIDIA RTX 3060 12GB
  • 系统内存:至少 24GB(推荐 32GB)
  • 存储空间:至少 20GB 可用空间(模型文件约 8GB)

Qwen2.5-VL-7B-Instruct

  • NVIDIA GPU:至少 16GB 显存
  • 推荐显卡:NVIDIA A10(24GB)/A4500(20GB)/RTX 4080(16GB)
  • 最低支持:NVIDIA RTX 4080 16GB
  • 系统内存:至少 32GB(推荐 48GB)
  • 存储空间:至少 30GB 可用空间(模型文件约 16GB)

Qwen2.5-VL-72B-Instruct

  • NVIDIA GPU:至少 80GB 显存(单卡部署)或多卡分布式部署
  • 推荐显卡配置:
    • 单卡:NVIDIA A100/H100 80GB
    • 多卡:2-4张 NVIDIA A100 40GB 或 A800 40GB
    • 多卡:2张 NVIDIA A100 80GB 或 H100 80GB
  • 系统内存:至少 128GB(推荐 256GB)
  • 存储空间:至少 200GB 可用空间(模型文件约 150GB)

1.2 软件要求

  • vLLM 版本要求:
  • vLLM >= 0.3.3(推荐 0.3.3)
    • 3B/7B 模型:支持 vLLM 0.2.7 及以上版本
    • 72B 模型:必须使用 vLLM 0.3.3 及以上版本
  • 主要依赖版本:

    • torch >= 2.1.0
    • transformers >= 4.37.0
    • accelerate >= 0.27.0
    • ray >= 2.9.0(用于分布式推理)
  • 操作系统:

  • Ubuntu 20.04/22.04 LTS(推荐)
  • CentOS ⅞(支持)
  • Rocky Linux 8/9(支持)

  • CUDA 要求:

  • CUDA 11.8 及以上(推荐 CUDA 12.1)
  • cuDNN 8.7.0 及以上
  • NVIDIA 驱动版本 >= 525.60.13

  • Python 环境:

  • Python 3.8-3.10(推荐 Python 3.10)
  • pip >= 23.0.1
  • virtualenv >= 20.0.0(推荐使用虚拟环境)

  • 容器环境(推荐):

  • Docker >= 24.0.0
  • NVIDIA Container Toolkit >= 1.13.0
  • docker-compose >= 2.20.0

1.3 网络要求

  • 带宽:建议至少 100Mbps
  • 延迟:建议低于 100ms
  • 公网 IP(如需提供外部服务)
  • 防火墙配置:开放服务端口(默认 8000)

1.4 推荐系统配置

开发测试环境

  • CPU: AMD EPYC 7302 16核 或 Intel Xeon Gold 5315Y
  • 内存: 64GB DDR4
  • 存储: 512GB NVMe SSD
  • GPU: NVIDIA A10 24GB

生产环境

  • CPU: AMD EPYC 7713 64核 或 Intel Xeon Platinum 8358
  • 内存: 256GB DDR4 ECC
  • 存储: 2TB NVMe SSD(RAID 1)
  • GPU: 2-4x NVIDIA A100 40GB 或 2x A100 80GB
  • 网络: 万兆网卡

2. 安装部署

2.1 基础环境安装

# 创建虚拟环境
python -m venv venv
source venv/bin/activate

# 安装基础依赖
pip install torch==2.1.2 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip install vllm==0.3.3 transformers==4.37.2 accelerate==0.27.0

# 如果需要分布式部署,安装 Ray
pip install "ray[default]>=2.9.0"

2.2 Docker 部署(推荐)

  1. 创建 Dockerfile:
FROM nvidia/cuda:11.8.0-devel-ubuntu22.04

WORKDIR /app

# 安装系统依赖
RUN apt-get update && apt-get install -y \
    python3.10 \
    python3-pip \
    git \
    && rm -rf /var/lib/apt/lists/*

# 安装 Python 依赖
RUN pip3 install --no-cache-dir \
    torch==2.1.2 \
    vllm==0.3.3 \
    transformers==4.37.2 \
    accelerate==0.27.0 \
    sentencepiece \
    pillow \
    ray>=2.9.0

# 设置环境变量
ENV CUDA_VISIBLE_DEVICES=all

# 下载模型(可选,也可以通过挂载卷的方式加载模型)
RUN pip3 install modelscope
RUN python3 -c "from modelscope import snapshot_download; snapshot_download('qwen/Qwen2.5-VL')"

EXPOSE 8000

CMD ["python3", "-m", "vllm.entrypoints.openai.api_server", \
     "--host", "0.0.0.0", \
     "--port", "8000", \
     "--model", "qwen/Qwen2.5-VL", \
     "--trust-remote-code"]
  1. 创建 docker-compose.yml:
version: '3'
services:
  qwen-vl-vllm:
    build: .
    runtime: nvidia
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
    ports:
      - "8000:8000"
    volumes:
      - ./models:/app/models  # 如果使用本地模型
      - ./images:/app/images  # 用于存放测试图片
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
  1. 启动服务:
docker-compose up -d

2.3 使用本地模型

如果您已经将模型下载到本地,可以通过以下方式使用:

  1. 直接使用本地路径:
# 启动服务时指定本地模型路径
python -m vllm.entrypoints.openai.api_server \
    --model /path/to/local/Qwen2.5-VL \
    --trust-remote-code \
    --host 0.0.0.0 \
    --port 8000
  1. Docker 部署时挂载本地模型:
version: '3'
services:
  qwen-vl-vllm:
    build: .
    runtime: nvidia
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
      # 使用本地模型
      - VLLM_MODEL_PATH=/app/models/Qwen2.5-VL
      # 如果使用 ModelScope,设置缓存目录
      - MODELSCOPE_CACHE=/app/models
    ports:
      - "8000:8000"
    volumes:
      # 挂载本地模型目录
      - /path/to/local/models:/app/models
    command: >
      python3 -m vllm.entrypoints.openai.api_server
      --model /app/models/Qwen2.5-VL
      --trust-remote-code
      --host 0.0.0.0
      --port 8000
  1. 使用环境变量配置:
# 设置模型路径环境变量
export VLLM_MODEL_PATH=/path/to/local/Qwen2.5-VL
# 如果使用 ModelScope,设置缓存目录
export MODELSCOPE_CACHE=/path/to/models/cache

# 启动服务
python -m vllm.entrypoints.openai.api_server \
    --model $VLLM_MODEL_PATH \
    --trust-remote-code \
    --host 0.0.0.0 \
    --port 8000
  1. 本地模型目录结构示例:
/path/to/local/Qwen2.5-VL/
├── config.json
├── configuration.json
├── generation_config.json
├── model.safetensors
├── pytorch_model.bin
├── special_tokens_map.json
├── tokenizer.json
└── tokenizer_config.json

注意事项: 1. 确保模型文件权限正确(读取权限) 2. Docker 部署时注意挂载目录的权限 3. 建议使用绝对路径避免路径解析问题 4. 如果模型文件较大,确保磁盘空间充足

2.4 直接部署

# 下载模型
pip install modelscope
python -c "from modelscope import snapshot_download; snapshot_download('qwen/Qwen2.5-VL')"

# 启动服务
python -m vllm.entrypoints.openai.api_server \
    --model qwen/Qwen2.5-VL \
    --trust-remote-code \
    --host 0.0.0.0 \
    --port 8000

3. 性能优化

3.1 显存优化

# 启动时添加显存优化参数
python -m vllm.entrypoints.openai.api_server \
    --model qwen/Qwen2.5-VL \
    --trust-remote-code \
    --gpu-memory-utilization 0.85 \
    --max-num-batched-tokens 4096 \
    --max-num-seqs 256

3.2 多 GPU 部署

# 使用张量并行
python -m vllm.entrypoints.openai.api_server \
    --model qwen/Qwen2.5-VL \
    --trust-remote-code \
    --tensor-parallel-size 2

3.3 量化部署

# 使用 AWQ 量化
python -m vllm.entrypoints.openai.api_server \
    --model qwen/Qwen2.5-VL \
    --trust-remote-code \
    --quantization awq \
    --dtype float16

4. API 调用示例

4.1 Python 示例

from openai import OpenAI
import base64
import json

def encode_image(image_path):
    """将图片转换为 base64 编码"""
    with open(image_path, "rb") as image_file:
        return base64.b64encode(image_file.read()).decode('utf-8')

# 初始化客户端
client = OpenAI(
    base_url="http://localhost:8000/v1",  # vLLM API 地址
    api_key="dummy"  # vLLM 不需要实际的 API key
)

# 1. 基础图文理解示例
def basic_vision_chat(image_path, question):
    base64_image = encode_image(image_path)
    completion = client.chat.completions.create(
        model="qwen2.5-vl",  # 或 "qwen2.5-vl-chat"
        messages=[
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": question},
                    {
                        "type": "image_url",
                        "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}
                    }
                ]
            }
        ],
        temperature=0.7,
        max_tokens=2048
    )
    return completion.choices[0].message.content

# 2. 多轮对话示例
def multi_turn_chat(image_path):
    base64_image = encode_image(image_path)
    messages = [
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "这张图片里有什么?"},
                {
                    "type": "image_url",
                    "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}
                }
            ]
        },
        {
            "role": "assistant",
            "content": "这是一张..."  # 助手的回复
        },
        {
            "role": "user",
            "content": "请详细描述图中的..."  # 用户的后续提问
        }
    ]

    completion = client.chat.completions.create(
        model="qwen2.5-vl",
        messages=messages,
        temperature=0.7,
        max_tokens=2048
    )
    return completion.choices[0].message.content

# 3. 批量处理示例
def batch_process_images(image_paths, question):
    responses = []
    for image_path in image_paths:
        base64_image = encode_image(image_path)
        completion = client.chat.completions.create(
            model="qwen2.5-vl",
            messages=[
                {
                    "role": "user",
                    "content": [
                        {"type": "text", "text": question},
                        {
                            "type": "image_url",
                            "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}
                        }
                    ]
                }
            ],
            temperature=0.7,
            max_tokens=1024
        )
        responses.append({
            'image': image_path,
            'response': completion.choices[0].message.content
        })
    return responses

# 4. 流式输出示例
def stream_chat(image_path, question):
    base64_image = encode_image(image_path)
    completion = client.chat.completions.create(
        model="qwen2.5-vl",
        messages=[
            {
                "role": "user",
                "content": [
                    {"type": "text", "text": question},
                    {
                        "type": "image_url",
                        "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}
                    }
                ]
            }
        ],
        temperature=0.7,
        max_tokens=2048,
        stream=True  # 启用流式输出
    )

    for chunk in completion:
        if chunk.choices[0].delta.content is not None:
            print(chunk.choices[0].delta.content, end="", flush=True)

# 使用示例
if __name__ == "__main__":
    # 基础图文理解
    response = basic_vision_chat("path/to/image.jpg", "这张图片里有什么?")
    print("基础图文理解结果:", response)

    # 多轮对话
    response = multi_turn_chat("path/to/image.jpg")
    print("多轮对话结果:", response)

    # 批量处理
    image_paths = ["image1.jpg", "image2.jpg", "image3.jpg"]
    responses = batch_process_images(image_paths, "描述这张图片")
    print("批量处理结果:", json.dumps(responses, ensure_ascii=False, indent=2))

    # 流式输出
    print("流式输出结果:")
    stream_chat("path/to/image.jpg", "详细描述这张图片")

4.2 CURL 示例

# 1. 基础图文理解请求
curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen2.5-vl",
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "text",
            "text": "这张图片里有什么?"
          },
          {
            "type": "image_url",
            "image_url": {
              "url": "data:image/jpeg;base64,..."
            }
          }
        ]
      }
    ],
    "temperature": 0.7,
    "max_tokens": 2048
  }'

# 2. 流式输出请求
curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen2.5-vl",
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "text",
            "text": "这张图片里有什么?"
          },
          {
            "type": "image_url",
            "image_url": {
              "url": "data:image/jpeg;base64,..."
            }
          }
        ]
      }
    ],
    "temperature": 0.7,
    "max_tokens": 2048,
    "stream": true
  }'

4.3 注意事项

  1. 图片要求:
  2. 支持格式:JPEG、PNG、GIF(静态)
  3. 建议分辨率:小于 2048x2048
  4. 文件大小:建议小于 5MB

  5. API 限制:

  6. 并发请求数:默认最大 256
  7. 超时时间:默认 300 秒
  8. 令牌数限制:默认最大 2048

  9. 错误处理:

  10. 图片格式错误:检查图片格式和大小
  11. 模型加载失败:检查模型路径和显存
  12. 超时错误:调整超时参数或减少处理内容

  13. 性能优化:

  14. 使用批量处理提高吞吐量
  15. 启用流式输出提升用户体验
  16. 合理设置并发数和超时时间
回到页面顶部