VLLM加速推理qwen3-vl-8b完成图生文工作本地实现记录
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引擎配置
参考官方配置指南: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}")
更多推荐




所有评论(0)