当前位置: 首页 > news >正文

Qwen3 VL多模态解析

本文作者:kaiyuan
Qwen3 VL多模态解析

想深耕AI Infra领域?欢迎访问InfraTech库!内容涵盖大模型基础、PyTorch/vLLM/SGLang框架入门、性能加速等核心方向,配套50+知识干货及适合初学者的notebook练习:https://github.com/CalvinXKY/InfraTech

本文梳理了Qwen3 VL视觉信息进入语言模型的关键路径:编码、压缩、拼接、跨层注入与位置编码。重点分析数据操作过程,比较图像与视频分支在建模上的差异,并说明DeepStack与RoPE/M-RoPE在多模态对齐中的作用,帮助读者对Qwen3 VL有个整体了解。

1 模型结构概览

Qwen3-VL是Qwen系列新一代视觉-语言模型,面向图文理解、视频理解、多模态推理与智能体交互等场景。 官方形态同时覆盖Dense与MoE,并提供Instruct/Thinking等版本,支持从边缘端到云端的灵活部署。以Qwen3-VL-32B-Instruct和Qwen3-VL-235B-A22B-Instruct为例来了解模型主体结构。

1.1 Qwen3-VL-32B-Instruct

高清图:https://github.com/CalvinXKY/InfraTech/tree/main/models/qwen3_vl

模型结构描述:

  • 类型:视觉-语言指令模型(Instruct)
  • 架构:Qwen3VLForConditionalGeneration
  • 模型类型:qwen3_vl
  • 参数规模:32B(以模型命名为准)
  • 文本上下文长度:262144
  • 文本层数:64
  • 文本隐藏维度:5120
  • 文本FFN中间层维度:25600
  • 文本注意力头数:Q=64,KV=8,head_dim=128
  • 文本词表大小:151936
  • RoPE参数:rope_theta=5000000mrope_section=[24,20,20]
  • 视觉编码器层数:27
  • 视觉隐藏维度:1152
  • 视觉头数:16
  • 视觉FFN中间层维度:4304
  • 图像Patch大小:16,时序Patch大小:2
  • 视觉输出映射维度:5120(与文本隐藏维度对齐)

1.2 Qwen3-VL-235B-A22B-Instruct

高清图:https://github.com/CalvinXKY/InfraTech/tree/main/models/qwen3_vl

模型结构描述:

  • 类型:视觉-语言指令模型(Instruct)
  • 架构:Qwen3VLMoeForConditionalGeneration
  • 模型类型:qwen3_vl_moe
  • 参数规模:235B,激活参数22B(以模型命名为准)
  • 文本上下文长度:262144
  • 文本层数:94
  • 文本隐藏维度:4096
  • 文本FFN中间层维度:12288
  • 文本注意力头数:Q=64,KV=4,head_dim=128
  • 文本词表大小:151936
  • RoPE参数:rope_theta=5000000mrope_section=[24,20,20]
  • MoE配置:专家总数128,每token激活专家数8,moe_intermediate_size=1536
  • 视觉编码器层数:27
  • 视觉隐藏维度:1152
  • 视觉头数:16
  • 视觉FFN中间层维度:4304
  • 图像Patch大小:16,时序Patch大小:2
  • 视觉输出映射维度:4096(与文本隐藏维度对齐)

2 图片到tokens的编码过程

视觉图片类数据在进入语言模型前,需要先转化为tokens序列。不同尺寸的图片经过视觉编码器(Vision Encoder)后,得到的tokens长度通常不同。 在Qwen3VL的技术报告中,有几张示意图展示了常见场景,例如H×W=32×256这种宽高比较大的图片。 这些图片最终会映射为不同长度的tokens。


图像数据如何与tokens序列对应?编码过程一般包含网格分块与压缩两步。网格分块用于重排图像数据,压缩用于降低序列长度。

2.1 图片patch化

下面以Height=800、Width=1440的图片为例,输入形状为[800, 1440, 3]。 在Qwen3VL中,相关patch参数如下:

  • patch_size=16,图片块的高宽;
  • temporal_patch_size=2,时间维度块大小(主要用于视频);
  • in_channels=3,图片通道数;


对图片进行网格化处理(时间维度会补齐到2,再除以temporal_patch_size):

  • grid_t = 2⁄2 = 1
  • grid_h = 800⁄16 = 50
  • grid_w = 1440⁄16 = 90

进入VisionEncoder前的序列长度:
L_pre = grid_t * grid_h * grid_w = 15090 = 4500

每个patch向量维度:
D = 3216*16 = 1536

得到数据:

  • pixel_values 形状:[1, 4500, 1536]
  • image_grid_thw:[1, 50, 90]


最后一个维度的变化与映射方式相关:进入VisionEncoder后会先经过PatchEmbed处理,末端再经过Merger映射运算。

2.2 PatchEmbed/Merger处理

最后一个维度的变化与映射方式相关:进入VisionEncoder后会先经过PatchEmbed处理,末端再经过Merger映射运算。


PatchEmbed处理

PatchEmbed包含一个3D卷积映射(Conv3d),将维度从1536映射到hidden_size,因此pixel_values形状变为[1, 4500, 1152]。

示例代码如下:

classQwen3VLVisionPatchEmbed(nn.Module):def__init__(self,config)->None:super().__init__()self.patch_size=config.patch_size self.temporal_patch_size=config.temporal_patch_size self.in_channels=config.in_channels self.embed_dim=config.hidden_size kernel_size=[self.temporal_patch_size,self.patch_size,self.patch_size]self.proj=nn.Conv3d(self.in_channels,self.embed_dim,kernel_size=kernel_size,stride=kernel_size,bias=True)defforward(self,hidden_states:torch.Tensor)->torch.Tensor:target_dtype=self.proj.weight.dtype hidden_states=hidden_states.view(-1,self.in_channels,self.temporal_patch_size,self.patch_size,self.patch_size)hidden_states=self.proj(hidden_states.to(dtype=target_dtype)).view(-1,self.embed_dim)returnhidden_states

Merger处理

Merger计算中会压缩序列长度。相关配置:

  • spatial_merge_size=2,视觉压缩(融合)尺寸。
  • out_hidden_size=5120,语言模型的hidden维度大小。模型规模不同时,该参数会变化。
classQwen3VLVisionPatchMerger(nn.Module):def__init__(self,config:Qwen3VLVisionConfig,use_postshuffle_norm=False)->None:super().__init__()self.hidden_size=config.hidden_size*(config.spatial_merge_size**2)self.use_postshuffle_norm=use_postshuffle_norm self.norm=nn.LayerNorm(self.hidden_sizeifuse_postshuffle_normelseconfig.hidden_size,eps=1e-6)self.linear_fc1=nn.Linear(self.hidden_size,self.hidden_size)self.act_fn=nn.GELU()self.linear_fc2=nn.Linear(self.hidden_size,config.out_hidden_size)defforward(self,x:torch.Tensor)->torch.Tensor:x=self.norm(x.view(-1,self.hidden_size)ifself.use_postshuffle_normelsex).view(-1,self.hidden_size)x=self.linear_fc2(self.act_fn(self.linear_fc1(x)))returnx

在最后的Merger计算时(self.use_postshuffle_norm=True),输入x会先调整形状:x.view(-1, self.hidden_size),其中:

self.hidden_size = config.hidden_size * (spatial_merge_size**2)。

所以,压缩后的序列计算方式:
L_final = L_pre / ((spatial_merge_size**2) = 4500 / 4 = 1125

得到输出的序列长度与嵌入维度的shape=[1125, 5120]

3 视频数据的编码过程

视频数据与图片数据的主要差异在于增加了时间维度。视频会先抽帧降低数据量,再进行时序patch划分。 以Qwen3VL示意图中的视频为例:分辨率H=448、W=736,时长8秒。


默认参数配置:

  • fps=2
  • temporal_patch_size=2
  • patch_size=16
  • merge_size=2

计算过程:

  • 采样帧数:num_frames ≈ 8 * 2 = 16
  • 时间网格:grid_t = 16 / 2 = 8

空间网格:

  • grid_h = 448 / 16 = 28
  • grid_w = 736 / 16 = 46

得到数据:

  • pixel_values 形状:[1, 10304, 1536]
  • image_grid_thw:[[8, 28, 46]]
  • L_pre = grid_t * grid_h * grid_w = 10304

与图片流程相同,视频也需要经过PatchEmbed和Merger处理,最终得到:
shape=[2576, 5120]

4 数据的拼接

文本、图片、视频tokens最终会一起送入LLM处理,因此需要拼接。其做法是:先在输入构造阶段通过placeholder预留图片/视频tokens位置,再在视觉分支计算完成后回填拼接。


其中,图片/视频数据会引入一些额外tokens。继续以上面的视频为例,上一步得到的视觉序列长度为2576。实际构造输入时,每个grid_t都会拼接一段:

每一段分为四块:

  1. ”<{curr_time:.1f} seconds>“(文本,会被分词成若干 token)时间戳文本
  2. vision_start_token
  3. 一段视频placeholder,本例中长度为:2576/8 = 322
  4. vision_end_token

所以总长度上,除了2576个视觉占位外,还会额外增加:vision_start/end共2*grid_t=16个特殊token,以及8段时间戳文本(会进一步分词)。

5 DeepStack操作

DeepStack原理:不把所有视觉token都“横向串成一长串”塞进LLM输入,而是把额外的高分辨率视觉token按层“纵向堆叠”, 分配到LLM的多个早期/中期层里,用残差方式逐层注入,从而在不显著增加上下文长度的前提下,让模型有效处理更多视觉细节。 DeepStack在保留更多视觉信息的同时,能够有效控制上下文长度。

Qwen3VL使用了DeepStack技术:在vision encoder中,从指定层(deepstack_visual_indexes)取出hidden_states,并分别经过deepstack_merger, 将空间merge后的视觉特征整理到可注入语言模型的维度。Qwen3VL配置中指定了[8, 16, 24]层。


操作过程:

  1. vision decoder中间层特征(多尺度/不同语义深度)先各自经过merger,得到deepstack_features;
  2. 这些特征后续会在文本模型前几层按视觉位置加回(_deepstack_process)。

注意:VisionDecoder最后一层的Merger与DeepStack分支中的Merger存在差异:最后一层Merger的LayerNorm位置不同,且需要进行尺寸压缩。

6 位置编码的处理

在Qwen3VL中,位置编码采用RoPE(Rotary Position Embedding),但视觉与文本在位置ID构造上存在差异: 视觉分支通常使用2D/3D坐标构造位置;在多模态统一建模时,会通过M-RoPE(Multimodal Rotary Position Embedding)把t/h/w信息映射到不同通道分组。 其核心差异主要体现在嵌入维度(head_dim)分量的分配方式与位置索引的组织方式上。

6.1 视觉塔中的2D编码


对图片数据而言,每个token有二维索引(h, w),对应视觉塔中的row/col;若head_dim=128,其分频逻辑是:

  • 128先分为64对二维向量构成独立的平面;
  • 前32对的频率分量由row索引决定,后32对的频率分量由col索引决定。


更具体一点,每个维度的归属如下:

索引区间内容
emb[0:32]row的32个频率分量
emb[32:64]col的32个频率分量
emb[64:96]再次row(与0:32相同)
emb[96:128]再次col(与32:64相同)

然后cos = emb.cos(),sin = emb.sin(),形状都是(seq_len, 128)。规律:

  • 前32个旋转对(覆盖q[0:32]与q[64:96])由行坐标r rr决定相位。
  • 后32个旋转对(覆盖q[32:64]与q[96:128])由列坐标c cc决定相位。

其中r rrc cc由token在图片中的行列位置决定。row与col共用freqs;max_len通常取max(R,C),其中R、C分别为row与col方向的最大索引。

6.2 文本模型的M-RoPE

视觉3D位置编码是在2D位置编码的基础上,进一步引入了时间维度。在M-RoPE中,有三个独立的角度:

  • 时间维度角度:angle_t = t * theta_i
  • 高度维度角度:angle_h = h * theta_i
  • 宽度维度角度:angle_w = w * theta_i

Qwen3 VL采用了Interleaved-MRoPE,其核心思想如下:

在1D RoPE中,嵌入层分成了d/2组,每一对连续元素(x[2j], x[2j+1])视为一个二维子空间,j = 0, 1, …, d/2 - 1。

确定第j jj个子空间应使用t、h、w中的哪一个轴进行旋转采用轮询(Round‑Robin)分配:
axis ( j ) = j m o d 3 \text{axis}(j) = j \bmod 3axis(j)=jmod3

其中定义映射:0→t,1→h,2→w。因此:

  • 当j % 3 == 0时:该子空间用t轴的旋转角度angle = t * θ_j
  • 当j % 3 == 1时:该子空间用h轴的旋转角度angle = h * θ_j
  • 当j % 3 == 2时:该子空间用w轴的旋转角度angle = w * θ_j

这种方式将时间、高度、宽度三个维度的信息交错融入整个特征空间,从而使多模态模型能更全面、更高效地理解数据中的复杂时空结构。

位置编码知识可参考:《彻底搞懂RoPE计算原理:从1D到3D》
相关文章:《VLM视觉-语言融合流程解析(Kimi K2.5/VL)》


参考文章:

  • https://github.com/huggingface/transformers/blob/main/src/transformers/models/qwen3_vl/
  • Qwen3-VL Technical Report:https://arxiv.org/pdf/2511.21631
  • https://arxiv.org/pdf/2406.04334

InfraTech申明:未经允许不得转载!

http://www.jsqmd.com/news/1175705/

相关文章:

  • 2026年装修避坑指南:西安本地新房装修哪家好,整装交付不踩坑 - 子柔传媒
  • 零基础学 Python 第 4 章 | for 循环与 range():让程序帮你重复干活
  • MVP变换
  • 1. 生产级 Agent 完整实现流程2. Agent 状态机与工作流引擎3.长对话上下文管理与压缩4. Agent 流式响应与中断控制
  • 2026年7月最新乌鲁木齐格拉苏蒂官方售后客户服务热线与维修网点地址汇总 - 亨得利钟表维修中心
  • 2026年奎屯正规优选GEO优化公司优势及选择推荐
  • 亨得利官方名表服务中心|全新维修地址及官方热线权威信息通告(2026年7月最新) - 亨得利官方博客
  • 5分钟搞定AI多语言本地化:LocalAI让你的模型说中文、日文、法文...
  • 2026年7月最新扬州伯爵官方售后客户服务电话及线下网点地址 - 亨得利钟表维修中心
  • PrimoToon高级渲染技术:光照模型、法线映射与边缘检测详解
  • 深度剖析LLMLingua三版本架构:大模型提示压缩的3大技术突破与5倍效率提升
  • 舒缓刺痛干痒面膜哪家好用:蜜妙诗缓解不适 - MXyuyu
  • 第36篇: 数据结构核心:栈与队列深度解析
  • CCManager与Devcontainer无缝集成:打造安全隔离的AI开发环境
  • 2026平洲高端翡翠门店综合实力排行名单汇总 - 互联网科技品牌测评
  • C++笔记之手写一个queue、手写一个vector、手写一个SharedPtr
  • 【AI大模型进阶】不要只会Chat:命令行里使用大模型,才是极客的浪漫
  • 深圳江诗丹顿回收价格查询与各大平台实测排行(2026年7月最新) - 诚收名表回收平台
  • 逐帧动画的 CSS 实现与 JavaScript 控制的性能对比分析
  • Vagas安全与隐私保护终极指南:如何确保职位信息安全
  • 先看节点覆盖,再看空间落地:西安豪美装修工艺/施工工艺 - 小随科技
  • 别傻了!Tkinter Python教程:GUI不是程序的命,但学它你才懂窗口的灵魂
  • 2026 年现阶段宁陵靠谱的拦污栅工厂格局重塑与选型新思路,它烂在暗处,整个水电站却因几根铁条濒临停摆 - 企业信息推荐【官方】
  • VRCX:终极VRChat社交管理助手,让虚拟社交变得如此简单
  • 再见Fable 5,OpenAI出手了!GPT-5.6真香!
  • 2026年7月最新天津真力时官方售后客服中心地址电话及服务网点分布 - 亨得利官方服务中心
  • 十、电商数仓用户行为采集平台
  • 如何用CastXML将C/C++代码转换为可分析的XML结构?
  • STM32L021与ADS122U04高精度低功耗数据采集方案
  • 如何通过CC Switch深度链接协议快速配置AI助手?终极指南