qwen3_vl_mm_kwargs_analysis
Qwen3-VL mm_kwargs 多模态输入分析报告
分析文件:
vllm/model_executor/models/qwen3_vl.py
核心结论
mm_kwargs 不是单个视觉输入(单张图片或单个视频)的表示,而是请求级别(request-level)的聚合结构,包含该请求中所有图像和/或视频的数据。
mm_kwargs 在 Qwen3-VL 中有两种截然不同的含义,出现在不同的调用层次:
- 处理时(Processing-time)mm_kwargs:用户传入的”控制参数”,指定如何对媒体数据进行预处理(分辨率、帧率等)。
- 前向推理时(Forward-time)mm_kwargs:经过处理后传入模型 forward 的”张量数据”,包含该请求所有图像/视频的 pixel values 和 grid 信息。
一、处理时 mm_kwargs(用户侧配置参数)
出现在 _get_vision_info、_call_hf_processor、get_max_video_tokens 等处理阶段,由用户在发起请求时传入,用于覆盖处理器的默认配置。
1.1 图像输入
| 参数键 | 类型 | 说明 |
|---|---|---|
size |
dict |
覆盖 image_processor 的 size,格式为 {"shortest_edge": int, "longest_edge": int} |
min_pixels |
int |
覆盖最小像素数,等效于 size["shortest_edge"] |
max_pixels |
int |
覆盖最大像素数,等效于 size["longest_edge"] |
代码依据(_get_vision_info 第 884–891 行):
1 | mm_kwargs = self.ctx.get_merged_mm_kwargs(mm_kwargs) |
1.2 视频输入(在图像参数基础上额外支持)
| 参数键 | 类型 | 说明 |
|---|---|---|
fps |
float |
视频采样帧率(与 num_frames 互斥) |
num_frames |
int |
显式指定采样帧数(与 fps 互斥) |
do_sample_frames |
bool |
是否在 HF Processor 内部采样帧;False 表示帧已由 vLLM 预采样 |
temporal_patch_size |
int |
覆盖时间维度 patch 大小 |
注意:fps 和 num_frames 互斥。当指定 num_frames 时,代码会强制将 fps 设为 None(第 1240–1241 行):
1 | if "num_frames" in video_mm_kwargs and "fps" not in video_mm_kwargs: |
二、前向推理时 mm_kwargs(模型输入张量)
出现在模型的 forward、embed_multimodal、encoder_cudagraph_forward 等方法中,是经过完整预处理后的张量字典,直接输入视觉编码器。
关键特性:这是请求级别的聚合结构,而非单个媒体项的结构。
2.1 仅有图像时(N 张图片,N ≥ 1)
所有图片的 patch 在 dim=0 顺序拼接成一个张量,image_grid_thw 有每张图片一行:
1 | { |
具体示例(3 张图片):
1 | # 图 1: 448×448 → grid=[1,32,32] → 1024 patches |
模型通过 image_grid_thw 计算每张图的 patch 数,再用 split() 拆分(第 1975–1978 行):
1 | # Split concatenated embeddings for each image item. |
2.2 仅有视频时(N 个视频,N ≥ 1)
1 | { |
具体示例(2 个视频):
1 | # 视频 1: 8帧, 224×224 → T=4, H=16, W=16 → 1024 patches,4 个时间戳 |
2.3 同一请求中既有图像又有视频时(M 张图片 + N 个视频)
图像字段和视频字段同时存在,共 5 个 key,互不干扰:
1 | { |
这一结构在 _call_hf_processor(第 1302–1320 行)中明确构建:视频和图像分开处理后合并进同一个 BatchFeature:
1 | video_outputs = dict( |
2.4 预计算 Embedding 路径(可选替代形式)
若用户预先提供 embedding,可用 image_embeds/video_embeds 替换对应的 pixel_values:
1 | # 图像 embedding 替代形式 |
三、各字段的拼接规则
由 _create_qwen2vl_field_factory(qwen2_vl.py 第 723–760 行)定义,描述各字段如何存储以及如何按媒体条目拆分:
| 字段 | 所属模态 | 存储类型 | 含义 |
|---|---|---|---|
pixel_values |
image | flat_from_sizes |
所有图片 patch 在 dim=0 拼接;按 T_i*H_i*W_i 切分还原 |
image_embeds |
image | flat_from_sizes |
所有图片 embedding 拼接;按 T_i*H_i*W_i // merge^2 切分 |
image_grid_thw |
image | batched(CPU) |
每张图一行 [T,H,W],stack 成 [N_img, 3] |
pixel_values_videos |
video | flat_from_sizes |
所有视频 patch 在 dim=0 拼接;按 T_j*H_j*W_j 切分还原 |
video_embeds |
video | flat_from_sizes |
所有视频 embedding 拼接 |
video_grid_thw |
video | batched(CPU) |
每个视频一行 [T,H,W],stack 成 [N_vid, 3] |
timestamps |
video | batched(CPU) |
每个视频一个时间戳列表,外层 list 长度 = N_vid |
四、Encoder CUDA Graph 场景(仅图像)
CUDA Graph 路径只处理图像(视频走 eager 路径),mm_kwargs 结构与”仅图像”情形相同(第 1860–1863 行):
1 | mm_kwargs = { |
相关方法利用 image_grid_thw 做 per-image 计算:
1 | # 图像数量(第 1769 行) |
五、关键维度说明
模型默认配置(来自 Qwen3VLVisionConfig):
patch_size = 14temporal_patch_size = 2spatial_merge_size = 2
图像(448×448):
1 | grid_thw = [1, 32, 32] |
视频(224×224,8帧):
1 | padded_frames = round_up(8, 2) = 8 |
六、数据流总结
1 | 用户请求(含 M 张图片 + N 个视频) |
生成日期:2026-04-13
分析文件:vllm/model_executor/models/qwen3_vl.py(共 2750 行)