qwen3-vl-8b是阿里新开源的视觉模型,其并不是moe类型的模型,为了 支持多模态输入,在本地部署上其有一些官方特定的接口要求,因此需要学习一下。从模型官方接口定义到VLLM参数配置,带你详细学习如何利用VLLM实现qwen3-vl-8b本地推理完成图生文任务。

多模态数据格式处理


https://bgithub.xyz/QwenLM/Qwen3-VL

参考模型官网vllm本地多模态图像推理代码总结以下核心要点:

首先创建属于模型的多模态输入处理器autoprocessor,然后用prepare_inputs_for_vllm函数,解析图像、视频等数据为vllm支持的格式传递给vllm。

process_vision_info 是 qwen_vl_utils 库中专门为 Qwen-VL 系列模型设计的工具函数,其函数定义本身就规定了返回三个元素,分别对应图像数据、视频数据和视觉处理的元数据参数。

prepare_inputs_for_vllm函数,将用户输入的对话消息(包含文本、图像 / 视频)预处理为 vllm 能识别的格式,同时确保视觉信息(图像 / 视频)与模型的视觉编码器兼容。将原始的messages(包含文本和视觉信息)转换为 vllm 推理所需的结构化输入,具体包括:

  • 文本部分:转换为模型可理解的对话格式(带角色标识和生成提示);

  • 视觉部分:提取并处理图像 / 视频数据,生成模型视觉编码器可接收的格式;

  • 元数据:提取视觉信息的辅助参数(如尺寸、帧率),辅助模型理解视觉内容。

  checkpoint_path = "Qwen/Qwen3-VL-235B-A22B-Instruct-FP8"
    processor = AutoProcessor.from_pretrained(checkpoint_path)
    inputs = [prepare_inputs_for_vllm(message, processor) for message in [messages]]
def prepare_inputs_for_vllm(messages, processor):
    text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    image_inputs, video_inputs, video_kwargs = process_vision_info(
        messages,
        image_patch_size=processor.image_processor.patch_size,
        return_video_kwargs=True,
        return_video_metadata=True
    )#返回图像数据、视频数据和视觉处理的元数据参数
    print(f"视觉信息处理参数: {video_kwargs}")

    mm_data = {}
    if image_inputs is not None:
        mm_data['image'] = image_inputs
    if video_inputs is not None:
        mm_data['video'] = video_inputs

    return {
        'prompt': text,
        'multi_modal_data': mm_data,
        'mm_processor_kwargs': video_kwargs
    }

vllm引擎配置

Qwen3-VL 使用指南 - vLLM 配方

参考官方配置指南:vllm版本需要大于0.11。安装vllm 0.11.0版本。

官方的适用于图片推理的相关参数配置:

vllm serve Qwen/Qwen3-VL-235B-A22B-Instruct-FP8 \  # 启动vllm服务,指定模型路径(Hugging Face Hub或本地路径)
  --tensor-parallel-size 4 \  # 张量并行度:将模型拆分到4张GPU上运行(需至少4张GPU)
  --limit-mm-per-prompt.video 0 \  # 限制每个请求的视频数量为0(仅处理图片,不处理视频)
  --async-scheduling \  # 启用异步调度:提高并发请求处理效率,适合服务场景
  --gpu-memory-utilization 0.95 \  # GPU显存利用率:使用每张卡95%的显存(充分利用资源,需确保无其他进程占用)
  --max-num-seqs 128  # 最大并发序列数:同时处理的最大请求数(根据GPU能力调整,避免显存溢出)

vllm参数实际控制:

    llm = LLM(
    model=checkpoint_path,
    tensor_parallel_size=2,  # 必须:张量两张卡并行
    gpu_memory_utilization=0.92,  # 建议保留:充分利用显存
    max_num_batched_tokens=5000,  # 可选保留:限制批处理规模
    max_model_len=5000,  # 必须:控制KV缓存需求,控制序列长度
)

这两个参数我有点分不清楚,问了d老师:

       max_model_len = 输入token数上限 + 输出token数上限。max_model_len 定义了模型能处理的最大序列长度(输入的 prompt token + 生成的输出 token 之和)。这个长度直接限制了 KV 缓存的 “最大容量”:

  • 假设 max_model_len=4096,意味着模型最多处理 4096 个 token(比如 2000 个输入 token + 2096 个输出 token)。
  • 此时 KV 缓存需要预留出 “存储 4096 个 token 对应的 Key 和 Value” 的显存空间(无论实际生成多少 token,都会按这个最大值预留基础空间)。
  • 如果 max_model_len 设得太大(远超实际需求),KV 缓存会占用大量冗余显存,导致 “明明任务不需要这么长,却浪费了显存”,甚至直接爆存。如果 max_model_len 设得太小(小于实际序列长度),模型会无法完整处理输入 / 生成内容,导致截断或报错。
  • max_model_len:可以理解为 “单个快递盒的最大容量”。每个样本(比如 “一张图片 + 文字指令 + 生成的总结”)就像一个快递盒,这个参数规定了这个盒子能装下的 “东西总量”(token 数)。如果东西太多超过盒子容量,就装不下(模型会报错或截断内容)。
  • max_num_batched_tokens:可以理解为 “快递车的最大载重”。一次运输的所有快递盒(所有样本)加起来的总重量(总 token 数)不能超过这个值。如果超重,车拉不动(显存爆存)。max_num_batched_tokens 必须 ≥单个盒子的容量(即max_model_len)。比如,如果想一次处理 2 张图片(2 个样本),那么总重量就是 2 个盒子的容量之和(2000×2=4000),这时候max_num_batched_tokens就要设为≥4000(比如 4000)。max_num_batched_tokens 和 max_model_len 的 “倍数关系” 并不直接等于 “默认处理的图片数量”,因为每个样本的实际 token 数可能不同,vllm 会根据样本的实际大小动态调整批次中的样本数量。
sampling_params = SamplingParams(
    temperature=0,  # 确定性输出,保证结果稳定准确
    max_tokens=1024,  # 足够覆盖多数图片总结需求
    top_k=-1,  # 不限制候选词,避免遗漏专业信息
    top_p=0.9  # 可选:进一步过滤低概率词,减少冗余
)

VL模型 message格式理解

message中type 字段是 Qwen-VL 等多模态模型(VL 模型)强制要求的输入格式参数,用于明确指定 content 中每个元素的类型,模型会根据 type 解析对应内容:

  • type: "text":表示该元素是文本(如指令、对话内容);
  • type: "image":表示该元素是图像(需提供图片路径或二进制数据);
  • type: "video":表示该元素是视频(需提供视频路径或帧数据)。

模型在预处理时,会通过 type 判断如何处理对应内容(比如对 image 进行分块编码,对 video 进行分帧处理,对 text 进行分词)。如果缺少 type 或使用未定义的类型(如 type: "file"),模型会无法解析输入,导致报错或生成错误结果。

如果在单个样本中同时包含图片和视频(即同一个 messages 里既有 type: "image" 又有 type: "video"),如果想同时结合图片和视频内容进行总结,messages应该这样设置:

messages = [
    {
        "role": "user",
        "content": [
            {"type": "image", "image": "1.jpg"},  # 图片
            {"type": "video", "video": "1.mp4"},  # 视频(新增)
            {"type": "text", "text": "结合图片和视频,总结两者的核心内容。"}  # 对应指令
        ],
    }
]

如果在单个样本中同时包含图片和视频(即同一个 messages 里既有 type: "image" 又有 type: "video"),如果只希望图片总结,messages应该这样设置:

messages = [
    {
        "role": "user",
        "content": [
            {"type": "image", "image": "path/to/image.jpg"},  # 只保留图片
            {"type": "text", "text": "总结图片中的文字内容。"}  # 对应指令
        ],
    }
]

完整代码

# -*- coding: utf-8 -*-
import torch
import os
from qwen_vl_utils import process_vision_info
from transformers import AutoProcessor
from vllm import LLM, SamplingParams
os.environ['VLLM_WORKER_MULTIPROC_METHOD'] = 'spawn'

def prepare_inputs_for_vllm(messages, processor):
    text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    image_inputs, video_inputs, video_kwargs = process_vision_info(
        messages,
        image_patch_size=processor.image_processor.patch_size,
        return_video_kwargs=True,
        return_video_metadata=True
    )#返回图像数据、视频数据和视觉处理的元数据参数
    print(f"视觉信息处理参数: {video_kwargs}")

    mm_data = {}
    if image_inputs is not None:
        mm_data['image'] = image_inputs
    if video_inputs is not None:
        mm_data['video'] = video_inputs

    return {
        'prompt': text,
        'multi_modal_data': mm_data,
        'mm_processor_kwargs': video_kwargs
    }


if __name__ == '__main__':
    messages = [
        {
            "role": "user",
            "content": [
                {
                    "type": "image",  
                    "image": "your/path",
                },
                {"type": "text", "text": "请对图片的核心内容进行总结."},
            ],
        }
    ]

    checkpoint_path = "Qwen3-VL/Qwen/Qwen3-VL-8B-Instruct"
    processor = AutoProcessor.from_pretrained(checkpoint_path)
    inputs = [prepare_inputs_for_vllm(messages, processor)]

    llm = LLM(
    model=checkpoint_path,
    tensor_parallel_size=2,  # 必须:两张卡并行
    gpu_memory_utilization=0.95,  # 建议保留:充分利用显存
    max_num_batched_tokens=4000,  # 可选保留:限制批处理规模
    max_model_len=2000,  # 必须:控制KV缓存需求
    #device="cuda"  # 强制指定使用 CUDA 设备,解决设备字符串为空的问题
)

    sampling_params = SamplingParams(
    temperature=0,  # 确定性输出,保证结果稳定准确
    max_tokens=1024,  # 足够覆盖多数图片总结需求
    top_k=-1,  # 不限制候选词,避免遗漏专业信息
    top_p=0.9  # 可选:进一步过滤低概率词,减少冗余
)
    # 打印输入信息
    for i, input_ in enumerate(inputs):
        print()
        print('=' * 40)
        print(f"输入[{i}]的prompt: {input_['prompt']!r}")
    print('\n' + '>' * 40)

    # 生成结果
    outputs = llm.generate(inputs, sampling_params=sampling_params)
    for i, output in enumerate(outputs):
        generated_text = output.outputs[0].text
        print()
        print('=' * 40)
        print(f"图片中的文字内容: {generated_text!r}")

Logo

ModelScope旨在打造下一代开源的模型即服务共享平台,为泛AI开发者提供灵活、易用、低成本的一站式模型服务产品,让模型应用更简单!

更多推荐