Qwen3-VL-8B-Instruct推理测试transformer+sglang双版本
契机
Qwen3-VL-8B发布了,结合官方demo+github仓库issue,在h20显卡服务器上跑了一下,相当于qwen2.5-vl-8b还是有较大的提升。对官方demo进行了加强,输出了推理速度+显存占用+token统计等,跑是跑通了,有点小问题,小问题的处理应该也不麻烦,先记录下来。
说明
- github官方:github.com/QwenLM/Qwen...
- huggingface官方:huggingface.co/Qwen/Qwen3-...
- transformer版本在535驱动+cuda12.1就可以运行,sglang版本需要在cuda12.3下运行
- 测试了transformer版本的图片+视频推理,sglang版本只测试了图片
环境搭建
bash
#driver+cuda略,多显卡遇到cuda识别不了的问题,可以翻我上一篇博客
#模型下载略,可参考我另外一篇《多模态视频大模型Aria在Docker部署》中的下载脚本
#创建conda环境,期间最好有梯子,涉及到github下载源码安装
conda create --name qwen3 python=3.11.14
pip install git+https://github.com/huggingface/transformers
pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple/ && pip install vllm qwen-vl-utils --no-build-isolation -i https://pypi.tuna.tsinghua.edu.cn/simple/
pip install -U flash-attn --no-build-isolation -i https://pypi.tuna.tsinghua.edu.cn/simple/
pip install sglang -i https://pypi.tuna.tsinghua.edu.cn/simple/
#输出我自己conda环境(忽略)
conda env export --no-builds | grep -v "prefix" > qwen3_environment.yml
#可以参考下面我的yml安装
conda env create -f qwen3_environment.yml --name qwen3 # 确保名称与原环境一致
yaml
name: qwen3
channels:
- conda-forge
- https://repo.anaconda.com/pkgs/main
- https://repo.anaconda.com/pkgs/r
dependencies:
- _libgcc_mutex=0.1
- _openmp_mutex=4.5
- bzip2=1.0.8
- ca-certificates=2025.10.5
- conda-pack=0.8.1
- ld_impl_linux-64=2.44
- libexpat=2.7.1
- libffi=3.4.6
- libgcc=15.2.0
- libgcc-ng=15.2.0
- libgomp=15.2.0
- liblzma=5.8.1
- libnsl=2.0.1
- libsqlite=3.50.4
- libuuid=2.41.2
- libxcrypt=4.4.36
- libzlib=1.3.1
- ncurses=6.5
- openssl=3.5.4
- pip=25.2
- python=3.11.14
- readline=8.2
- setuptools=80.9.0
- tk=8.6.13
- wheel=0.45.1
- pip:
- accelerate==1.10.1
- aiohappyeyeballs==2.6.1
- aiohttp==3.13.0
- aiosignal==1.4.0
- airportsdata==20250909
- annotated-types==0.7.0
- anthropic==0.71.0
- anyio==4.11.0
- apache-tvm-ffi==0.1.0b15
- astor==0.8.1
- asttokens==3.0.0
- attrs==25.4.0
- av==16.0.1
- blake3==1.0.8
- blobfile==3.0.0
- build==1.3.0
- cachetools==6.2.1
- cbor2==5.7.0
- certifi==2025.10.5
- cffi==2.0.0
- charset-normalizer==3.4.4
- click==8.2.1
- cloudpickle==3.1.1
- compressed-tensors==0.11.0
- cuda-bindings==13.0.2
- cuda-pathfinder==1.3.1
- cuda-python==13.0.2
- cupy-cuda12x==13.6.0
- datasets==4.2.0
- decorator==5.2.1
- depyf==0.19.0
- dill==0.4.0
- diskcache==5.6.3
- distro==1.9.0
- dnspython==2.8.0
- docstring-parser==0.17.0
- einops==0.8.1
- email-validator==2.3.0
- executing==2.2.1
- fastapi==0.119.0
- fastapi-cli==0.0.13
- fastapi-cloud-cli==0.3.1
- fastrlock==0.8.3
- filelock==3.20.0
- flash-attn==2.8.3
- flashinfer-python==0.4.0
- frozendict==2.4.6
- frozenlist==1.8.0
- fsspec==2025.9.0
- gguf==0.17.1
- grpcio==1.75.1
- grpcio-reflection==1.75.1
- grpcio-tools==1.75.1
- h11==0.16.0
- hf-transfer==0.1.9
- hf-xet==1.1.10
- httpcore==1.0.9
- httptools==0.7.1
- httpx==0.28.1
- huggingface-hub==0.35.3
- idna==3.11
- interegular==0.3.3
- ipython==9.6.0
- ipython-pygments-lexers==1.1.1
- jedi==0.19.2
- jinja2==3.1.6
- jiter==0.11.0
- jsonschema==4.25.1
- jsonschema-specifications==2025.9.1
- lark==1.2.2
- llguidance==0.7.30
- llvmlite==0.44.0
- lm-format-enforcer==0.11.3
- lxml==6.0.2
- markdown-it-py==4.0.0
- markupsafe==3.0.3
- matplotlib-inline==0.1.7
- mdurl==0.1.2
- mistral-common==1.8.5
- modelscope==1.31.0
- mpmath==1.3.0
- msgpack==1.1.2
- msgspec==0.19.0
- multidict==6.7.0
- multiprocess==0.70.16
- nest-asyncio==1.6.0
- networkx==3.5
- ninja==1.13.0
- numba==0.61.2
- numpy==2.2.6
- nvidia-cublas-cu12==12.8.4.1
- nvidia-cuda-cupti-cu12==12.8.90
- nvidia-cuda-nvrtc-cu12==12.8.93
- nvidia-cuda-runtime-cu12==12.8.90
- nvidia-cudnn-cu12==9.10.2.21
- nvidia-cudnn-frontend==1.15.0
- nvidia-cufft-cu12==11.3.3.83
- nvidia-cufile-cu12==1.13.1.3
- nvidia-curand-cu12==10.3.9.90
- nvidia-cusolver-cu12==11.7.3.90
- nvidia-cusparse-cu12==12.5.8.93
- nvidia-cusparselt-cu12==0.7.1
- nvidia-cutlass-dsl==4.2.1
- nvidia-ml-py==13.580.82
- nvidia-nccl-cu12==2.27.3
- nvidia-nvjitlink-cu12==12.8.93
- nvidia-nvshmem-cu12==3.3.20
- nvidia-nvtx-cu12==12.8.90
- openai==1.99.1
- openai-harmony==0.0.4
- opencv-python-headless==4.12.0.88
- orjson==3.11.3
- outlines==0.1.11
- outlines-core==0.1.26
- packaging==25.0
- pandas==2.3.3
- parso==0.8.5
- partial-json-parser==0.2.1.1.post6
- pexpect==4.9.0
- pillow==12.0.0
- prometheus-client==0.23.1
- prometheus-fastapi-instrumentator==7.1.0
- prompt-toolkit==3.0.52
- propcache==0.4.1
- protobuf==6.33.0
- psutil==7.1.0
- ptyprocess==0.7.0
- pure-eval==0.2.3
- py-cpuinfo==9.0.0
- py-spy==0.4.1
- pyarrow==21.0.0
- pybase64==1.4.2
- pycountry==24.6.1
- pycparser==2.23
- pycryptodomex==3.23.0
- pydantic==2.12.2
- pydantic-core==2.41.4
- pydantic-extra-types==2.10.6
- pygments==2.19.2
- pyproject-hooks==1.2.0
- python-dateutil==2.9.0.post0
- python-dotenv==1.1.1
- python-json-logger==4.0.0
- python-multipart==0.0.20
- pytz==2025.2
- pyyaml==6.0.3
- pyzmq==27.1.0
- qwen-vl-utils==0.0.14
- ray==2.50.0
- referencing==0.37.0
- regex==2025.9.18
- requests==2.32.5
- rich==14.2.0
- rich-toolkit==0.15.1
- rignore==0.7.1
- rpds-py==0.27.1
- safetensors==0.6.2
- scipy==1.16.2
- sentencepiece==0.2.1
- sentry-sdk==2.42.0
- setproctitle==1.3.7
- sgl-kernel==0.3.15
- sglang==0.5.3.post3
- shellingham==1.5.4
- six==1.17.0
- sniffio==1.3.1
- soundfile==0.13.1
- soxr==1.0.0
- stack-data==0.6.3
- starlette==0.48.0
- sympy==1.14.0
- tabulate==0.9.0
- tiktoken==0.12.0
- timm==1.0.16
- tokenizers==0.22.1
- torch==2.8.0
- torch-memory-saver==0.0.9rc2
- torchao==0.9.0
- torchaudio==2.8.0
- torchvision==0.23.0
- tqdm==4.67.1
- traitlets==5.14.3
- transformers==4.57.1
- triton==3.4.0
- typer==0.19.2
- typer-slim==0.19.2
- typing-extensions==4.15.0
- typing-inspection==0.4.2
- tzdata==2025.2
- urllib3==2.5.0
- uvicorn==0.37.0
- uvloop==0.22.1
- vllm==0.11.0
- watchfiles==1.1.1
- wcwidth==0.2.14
- websockets==15.0.1
- xformers==0.0.32.post1
- xgrammar==0.1.25
- xxhash==3.6.0
- yarl==1.22.0
transformer推理
图片推理
python
from transformers import Qwen3VLForConditionalGeneration, AutoProcessor
import torch
import time # 用于计时
# 加载模型和处理器
model = Qwen3VLForConditionalGeneration.from_pretrained(
"/xxx/Qwen3-VL-8B-Instruct",
dtype=torch.bfloat16,
attn_implementation="flash_attention_2",
device_map="auto",
)
processor = AutoProcessor.from_pretrained("/xxx/Qwen3-VL-8B-Instruct")
# 输入消息
messages = [
{
"role": "user",
"content": [
{
"type": "image",
"image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg",
},
{"type": "text", "text": "描述图片"},
],
}
]
# 处理输入(转为模型可接受的格式)
inputs = processor.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_dict=True,
return_tensors="pt"
)
inputs = inputs.to(model.device)
# 计算输入token数量(batch_size=1,取第一个样本的长度)
input_token_count = inputs.input_ids.size(1)
print(f"输入token数量: {input_token_count}")
# 热身推理(避免首次推理包含编译等额外耗时)
with torch.no_grad():
model.generate(** inputs, max_new_tokens=10)
# 重置CUDA显存统计(确保测量的是推理阶段的显存)
torch.cuda.reset_peak_memory_stats()
# 记录推理开始时间
start_time = time.time()
# 执行推理
with torch.no_grad():
generated_ids = model.generate(**inputs, max_new_tokens=128)
# 记录推理结束时间
end_time = time.time()
inference_duration = end_time - start_time # 推理耗时(秒)
# 计算输出token数量(去掉输入部分后的生成token长度)
generated_ids_trimmed = [
out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
]
output_token_count = len(generated_ids_trimmed[0]) # batch_size=1,取第一个样本
print(f"输出token数量: {output_token_count}")
# 计算推理速度(token/秒)
inference_speed = output_token_count / inference_duration if inference_duration > 0 else 0
print(f"推理时间: {inference_duration:.4f}秒")
print(f"推理速度: {inference_speed:.2f} token/秒")
# 获取最大显存占用(仅GPU有效,单位:GB)
if torch.cuda.is_available():
max_memory_allocated = torch.cuda.max_memory_allocated() / (1024**3) # 转为GB
max_memory_reserved = torch.cuda.max_memory_reserved() / (1024**3)
print(f"最大显存占用(已分配): {max_memory_allocated:.2f} GB")
print(f"最大显存占用(已预留): {max_memory_reserved:.2f} GB")
else:
print("未使用GPU,无法统计显存占用")
# 打印模型输出
output_text = processor.batch_decode(
generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
)
print("\n模型输出:", output_text[0])
输出如下:
输入token数量: 2764 输出token数量: 128 推理时间: 5.6309秒 推理速度: 22.73 token/秒 最大显存占用(已分配): 17.18 GB 最大显存占用(已预留): 17.38 GB
模型输出: 这是一张充满温馨与宁静氛围的海滩照片,捕捉了人与宠物之间亲密互动的瞬间。
画面主体是一位年轻女性和一只金毛犬,他们正坐在沙滩上。女性侧身对着镜头,面带灿烂的笑容,她身穿一件蓝白相间的格子衬衫和深色裤子,左手腕上戴着一块白色手表。她正与狗狗互动,右手伸出,似乎在给狗狗一个高五或是在奖励它,左手则拿着一小块食物。
金毛犬温顺地坐在她面前,前爪抬起,与女性的手相触,姿态乖巧。它身上
可以看到输出是不完整的,视频版本的一样,但是在sglang版本里面输出是完整的
视频推理
python
from transformers import Qwen3VLForConditionalGeneration, AutoProcessor
import torch
import time # 用于计时
# 加载模型和处理器
model = Qwen3VLForConditionalGeneration.from_pretrained(
"/xxx/Qwen3-VL-8B-Instruct",
dtype=torch.bfloat16,
attn_implementation="flash_attention_2",
device_map="auto",
)
processor = AutoProcessor.from_pretrained("/xxx/Qwen3-VL-8B-Instruct")
# 输入消息
messages = [
{
"role": "user",
"content": [
{
"type": "video",
"video": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-VL/space_woaudio.mp4",
},
{"type": "text", "text": "描述视频"},
],
}
]
# 处理输入(转为模型可接受的格式)
inputs = processor.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_dict=True,
return_tensors="pt"
)
inputs = inputs.to(model.device)
# 计算输入token数量(batch_size=1,取第一个样本的长度)
input_token_count = inputs.input_ids.size(1)
print(f"输入token数量: {input_token_count}")
# 热身推理(避免首次推理包含编译等额外耗时)
with torch.no_grad():
model.generate(** inputs, max_new_tokens=10)
# 重置CUDA显存统计(确保测量的是推理阶段的显存)
torch.cuda.reset_peak_memory_stats()
# 记录推理开始时间
start_time = time.time()
# 执行推理
with torch.no_grad():
generated_ids = model.generate(**inputs, max_new_tokens=128)
# 记录推理结束时间
end_time = time.time()
inference_duration = end_time - start_time # 推理耗时(秒)
# 计算输出token数量(去掉输入部分后的生成token长度)
generated_ids_trimmed = [
out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
]
output_token_count = len(generated_ids_trimmed[0]) # batch_size=1,取第一个样本
print(f"输出token数量: {output_token_count}")
# 计算推理速度(token/秒)
inference_speed = output_token_count / inference_duration if inference_duration > 0 else 0
print(f"推理时间: {inference_duration:.4f}秒")
print(f"推理速度: {inference_speed:.2f} token/秒")
# 获取最大显存占用(仅GPU有效,单位:GB)
if torch.cuda.is_available():
max_memory_allocated = torch.cuda.max_memory_allocated() / (1024**3) # 转为GB
max_memory_reserved = torch.cuda.max_memory_reserved() / (1024**3)
print(f"最大显存占用(已分配): {max_memory_allocated:.2f} GB")
print(f"最大显存占用(已预留): {max_memory_reserved:.2f} GB")
else:
print("未使用GPU,无法统计显存占用")
# 打印模型输出
output_text = processor.batch_decode(
generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
)
print("\n模型输出:", output_text[0])
输出如下:
输入token数量: 13428 输出token数量: 128 推理时间: 7.6084秒 推理速度: 16.82 token/秒 最大显存占用(已分配): 20.18 GB 最大显存占用(已预留): 21.05 GB
模型输出: 视频开始于一个控制室,一名穿着深色Polo衫和卡其色裤子的男子站在一个大型屏幕前,屏幕上显示着地球的图像和一些数据。他正在向观众讲解,手势生动,似乎在介绍某个项目或任务。背景中可以看到多个显示屏和控制面板,显示出这是一个高科技的环境。
接下来,画面切换到国际空间站(ISS)的外部图像,显示了其复杂的结构和太阳能电池板。随后,镜头转到空间站内部,两名宇航员站在一个充满设备和仪器的房间里,他们穿着宇航服,手持麦克风,似乎在
sglang版本
python
#期间解决的error-1:升级cuda-12.3
Exception: Capture cuda graph failed: Assertion error (/sgl-kernel/build/_deps/repo-deepgemm-src/csrc/apis/../jit_kernels/impls/../../jit/compiler.hpp:142): (major > 12 or (major == 12 and minor >= 3)) and "NVCC version should be >= 12.3"
#期间解决的error-2:修改事件循环
RuntimeError: There is no current event loop in thread 'MainThread'.
#期间解决的error-3:设置单张显卡+参数
ValueError: For qwen3-vl-fp8 models, please make sure (text_config.moe_intermediate_size=768 // (self.tp_size=8 // self.moe_ep_size=1)) % weight_block_size_n=128 == 0. You can fix this by using arguments such as `--tp-size 8 --ep-size 8`
python
import os
import time
import requests
import torch
from PIL import Image
from sglang import Engine
from qwen_vl_utils import process_vision_info
from transformers import AutoProcessor
# 指定使用0号GPU(需在导入torch前设置)
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
if __name__ == "__main__":
checkpoint_path = "/mnt/tos-tommi-algorithm/lzy_workspace/Qwen3-VL-8B-Instruct"
processor = AutoProcessor.from_pretrained(checkpoint_path)
tokenizer = processor.tokenizer # 提取tokenizer
# 图像URL
image_url = "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"
# 构建对话消息
messages = [
{
"role": "user",
"content": [
{"type": "image", "image": image_url},
{"type": "text", "text": "描述图片"},
],
}
]
# 生成对话模板文本
text = processor.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True
)
# 处理图像信息
image_inputs, _ = process_vision_info(messages, image_patch_size=processor.image_processor.patch_size)
# 初始化引擎(加载模型前清理显存)
torch.cuda.empty_cache()
llm = Engine(
model_path=checkpoint_path,
enable_multimodal=True,
mem_fraction_static=0.7,
tp_size=1,
ep_size=1,
attention_backend="fa3"
)
# 初始化事件循环
import asyncio
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# --------------------------
# 计算输入token数量(含图像token)
# --------------------------
response_img = requests.get(image_url, stream=True)
image = Image.open(response_img.raw)
width, height = image.size
patch_size = processor.image_processor.patch_size
patch_size = patch_size[0] if isinstance(patch_size, tuple) else patch_size
image_token_count = (height // patch_size) * (width // patch_size)
text_encoded = tokenizer.encode(text, add_special_tokens=False)
text_token_count_with_placeholder = len(text_encoded)
input_token_count = (text_token_count_with_placeholder - 1) + image_token_count
# --------------------------
# 执行推理并记录时间
# --------------------------
start_time = time.time()
sampling_params = {"max_new_tokens": 1024}
response = llm.generate(prompt=text, image_data=image_inputs, sampling_params=sampling_params)
end_time = time.time()
inference_time = end_time - start_time
# --------------------------
# 计算输出token数量及推理速度
# --------------------------
output_text = response['text']
output_token_count = len(tokenizer.encode(output_text, add_special_tokens=False))
inference_speed = output_token_count / inference_time # tokens per second
# --------------------------
# 精确获取最大显存占用(关键修改)
# --------------------------
# 获取当前设备完整显存统计
mem_stats = torch.cuda.memory_stats()
# 最大已分配显存(GB):模型张量实际占用
max_allocated = torch.cuda.max_memory_allocated() / (1024 **3)
# 最大预留显存(GB):框架为模型预留的总显存峰值
max_reserved = torch.cuda.max_memory_reserved() / (1024** 3)
# 当前活跃显存(GB)
active = torch.cuda.memory_allocated() / (1024 **3)
# --------------------------
# 打印所有统计信息
# --------------------------
print(f"=== 推理性能统计 ===")
print(f"推理时间: {inference_time:.2f}秒")
print(f"输入token数: {input_token_count} (文本token + 图像patch token)")
print(f"输出token数: {output_token_count}")
print(f"推理速度: {inference_speed:.2f} tokens/秒")
print(f"=== 显存占用(GB) ===")
print(f"最大已分配显存: {max_allocated:.2f}")
print(f"最大预留显存(推荐参考): {max_reserved:.2f}")
print(f"当前活跃显存: {active:.2f}")
print(f"\n生成文本: {output_text}")
输出如下:
=== 推理性能统计 === 推理时间: 5.97秒 输入token数: 10892 (文本token + 图像patch token) 输出token数: 349 推理速度: 58.45 tokens/秒 === 显存占用(GB) === 最大已分配显存: 0.24 最大预留显存(推荐参考): 0.31 当前活跃显存: 0.00
生成文本: 这是一张充满温馨与宁静氛围的海滩照片,捕捉了一个女孩与她的狗在日落时分互动的幸福瞬间。
-
主体人物与宠物:
- 一位年轻女子坐在沙滩上,身体微微前倾,面带灿烂的笑容,眼神温柔地注视着她面前的狗狗。她留着棕色长发,身穿一件蓝黑相间的格子衬衫和深色裤子,左手腕上戴着一块白色手表,右手正拿着一小块食物。
- 一只金黄色的拉布拉多犬(或类似品种)坐在她对面,前爪抬起,似乎正在与女孩击掌或请求食物,姿态乖巧而专注。狗狗身上戴着一条带有彩色小花图案的蓝色胸背带,红色的牵引绳放在旁边的沙滩上。
-
场景与环境:
- 场景位于一片广阔的沙滩上,背景是波光粼粼的大海,近处有一道正在破碎的海浪,远处海天相接处泛着温暖的金色光芒。
- 画面整体沐浴在柔和的金色夕阳中,光线从右后方照射过来,为女子的发丝和画面边缘镀上了一层金边,营造出温暖、梦幻的氛围。天空大部分是明亮的白色,与海面的金色形成对比。
-
构图与氛围:
- 构图将人与狗置于画面中央偏右的位置,前景是细腻的沙滩纹理,富有层次感。
- 整体色调偏暖,以金色和蓝色为主,强调了夕阳下的宁静与喜悦,传达出人与宠物之间深厚的情感联系和共享美好时光的幸福感。
显存这里统计有问题,我实际在nvidia-smi看到大约在71912MiB左右
总结
- sglang需要12.3的cuda
- transformer输出不完整,sglang完整
- sglang没咋研究过,感觉显存占用太大了,应该有参数可以压
写到最后
