QwenImage2.1结构解析
总体结构

结构上,主要就是四大组件:
- text_encoder(Qwen3-VL)
- transformer(单流DiT)
- vae_decoder
- vae_encoder(只有输入参考图的时候才生效)
1. Qwen3-VL

1.1. 视觉编码器ViT
1.1.1. 把图片切成小块
ViT的作用是把图片切成小块并理解画面。图片不会直接整张送进模型,而是先切成 16×16 的小方块。
比如一张 512×512 的图片:512 ÷ 16 = 32
因此横向 32 块、纵向 32 块,总共 32 × 32 = 1024 个图像小块
可以把每个向量理解成一张“小便签”:
这里是红色布料
这里是猫耳朵
这里是沙滩纹理
这里有一段文字
1.1.2. 告诉模型每个小块在哪里
模型不仅要知道小块“是什么”,还要知道它在图片中的位置。
Qwen3-VL 使用两套位置编码:
- 可学习位置编码:先准备一个较大的标准位置网格,再根据图片尺寸插值到当前图片。
- 空间 RoPE:分别编码小块的行位置和列位置
简单理解:
“红色布料” + “第 3 行、第 8 列”
这样模型才能理解上下、左右、遮挡和物体位置。
1.1.3. 27层ViT
视觉编码器共有 27 层。每一层主要做两件事:
视觉自注意力
↓
视觉 FFN / MLP
↓
残差连接
视觉自注意力是双向的:
- 图片里的每个小块都可以查看其他小块。
- 左上角可以参考右下角。
- 猫耳朵可以参考猫脸、身体和背景。
这和生成文字时“只能看前面”的因果注意力不同。
1.1.4 合并相邻的小块
经过 27 层后,模型会把空间上相邻的 2×2 个小块合并:
[小块1 小块2]
[小块3 小块4]
↓
1 个图像 token
一个 1152 维小块,四个拼起来是1152 × 4 = 4608
然后通过一个 MLP,把 4608 维压回语言模型使用的 4096 维。
所以:
1 个图像 token
= 原图中的 2×2 个 16×16 小块
= 32×32 像素区域
1.2. 文字Embedding
这里的Embedding和一般LLM中的embedding层是一样的。
文字首先被 Tokenizer 分成 token:
“一只戴红色帽子的猫”
可能变成类似:
[一只] [戴] [红色] [帽子] [的] [猫]
每个 token 会转换成一个 4096 维向量:
Token ID → 151936×4096 的词表 Embedding
最终语言模型的输入就像一列排好队的积木:
文字token 文字token 图像token 图像token 文字token ...
语言模型共有 36 层,隐藏维也是 4096。
1.3. DeepStack
DeepStack的作用是把视觉细节送进语言模型。普通的多模态模型可能只把最终图像结果送进语言模型。这样一些细节可能在传递过程中丢失。
普通做法通常是:
27层视觉编码器
↓
只取最后一层特征
↓
送入语言模型
Qwen3-VL 会在视觉编码器的第 8、16、24 层分别保存一次图像特征:
视觉层 8 → 浅层特征:边缘、颜色、纹理
视觉层 16 → 中层特征:部件、文字、局部形状
视觉层 24 → 深层特征:物体语义、场景关系
然后把这三组特征注入语言模型最前面的三层:
语言第 1 层 ← 视觉第 8 层
语言第 2 层 ← 视觉第 16 层
语言第 3 层 ← 视觉第 24 层
好处是:
- 浅层视觉细节不会被过早压缩。
- 小文字、纹理、人脸、产品细节更容易保留。
- 深层视觉语义也能参与语言推理。
2. 单流DiT
DiT部分是Qwen-Image-2.1的主干部分。
2.1. 整体结构
单流 DiT 把文字条件、参考图 latent 和待生成图 latent 排成一条序列,然后用 32 层 Transformer 统一处理。它不像传统模型那样分成“文字 Cross-Attention”和“图像 Self-Attention”,而是所有 token 直接在同一条流里互相交流。
“单流”是什么意思?
多模态扩散模型通常有几种组织方式。
- Cross-Attention DiT
图像 token ── Self-Attention
│
└── Cross-Attention ── 文字 token
图像 token 必须通过一个专门的 Cross-Attention 模块读取文字。
- 双流 DiT
文字流 → 文字 Transformer ──┐
├── Joint Attention
图像流 → 图像 Transformer ──┘
文字和图像有两套独立的主干,到特定位置再交互。
- Qwen-Image-2.1 单流 DiT
[文字条件] [参考图latent] [目标图latent]
↓
同一条 Self-Attention
↓
32 层统一 Transformer
所以这里没有单独的文本 Transformer,也没有单独的 Cross-Attention。目标图 token 可以直接查看文字 token 和参考图 token。
整体结构:

输出的噪声再继续通过schduler去噪。这一部分实际上用的是flow matching,之后有空可能会写一篇博客专门解释一下flow matching的去噪推理过程。
2.2. prefix kv cache与block-causal mask
joint 序列: [ 文本 T1 T2 ] [ 条件图 A (4096) ] [ 文本 T3 ] [ 目标图 Z (4096) ]
└────────────── prefix:内容与去噪步无关 ──────────────┘ └ 每步都变 ┘
prefix = 文本 token + 条件图 latent。它的特点是从第 1 步起就固定不变,所以能缓存。目标图那段每步都在变。
也就是说,前缀部分是不变的,可以缓存的。每步循环去噪的过程中之后目标图的token在变。
接下来解释block-causal mask。
首先要明确,这里的block指的是一张图的 token 块。block-causal的规则很简单:
allowed = (q_idx >= kv_idx) | same_image_block
-
块与块之间是全因果的:每个 token 能看到它前面的一切,看不到后面的。
-
块内部是双向的:同一张图内部的 token 互相都能看见。
-
文本比较特殊:文本 token 只能看到自己之前的文本(严格因果),因为它来自一个因果 LLM,第 i 个 token 的表示本来就只包含左边的内容。
举个例子:

t是文本token,A是条件图token,Z是目标图token。
为什么文本必须严格因果?
文本token之间是有先后次序的。如果让 DiT 里的 token i 去看 token j>i,等于让”刚读完第二个字的人”引用”读到第十个字才知道的信息”。这两个状态的信息集本来就不同,混在一起是信息错配——模型得额外学会补偿这个错配。
为什么图像block内部必须双向?
因为图像token之间是没有先后次序的。去噪是全局一致性问题,不是逐 token 生成。DiT 每一步要一次性预测整张图的 velocity,不是自回归吐 token。每个 patch 必须看到其他所有 patch 当前的估计,才能决定自己”该往哪个方向去噪”。
3. VAE Decoder
编码器把 1024×1024 的图压成 64×64 的浓缩信息,解码器的工作就是把这个浓缩信息重新”画”回 1024×1024。

内部结构:
通道数 分辨率
浓缩信息 ─────▶ 64 × 64×64
│
① 升维 1152 × 64×64 ← 先变"宽",准备开始画
│
② 理解一轮 1152 × 64×64 ← 先想清楚整体是什么
│
③ 放大第一级 576 × 128×128
│
④ 放大第二级 288 × 256×256
│
⑤ 放大第三级 144 × 512×512
│
⑥ 放大第四级 4 × 1024×1024 ← 最后直接输出 RGBA
注意,输出通道之所以是4维,是因为需要支持透明图,也就是RGBA。