# code

QwenImage2.1结构解析

📅 2026-09-28 ✍️ Layla

总体结构

image-20261001220513812

结构上,主要就是四大组件:

  • text_encoder(Qwen3-VL)
  • transformer(单流DiT)
  • vae_decoder
  • vae_encoder(只有输入参考图的时候才生效)

1. Qwen3-VL

image-20260928200702495

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 直接在同一条流里互相交流。

“单流”是什么意思?

多模态扩散模型通常有几种组织方式。

  1. Cross-Attention DiT
图像 token ── Self-Attention
     │
     └── Cross-Attention ── 文字 token

图像 token 必须通过一个专门的 Cross-Attention 模块读取文字。

  1. 双流 DiT
文字流 → 文字 Transformer ──┐
                           ├── Joint Attention
图像流 → 图像 Transformer ──┘

文字和图像有两套独立的主干,到特定位置再交互。

  1. Qwen-Image-2.1 单流 DiT
[文字条件] [参考图latent] [目标图latent]
                 ↓
        同一条 Self-Attention
                 ↓
        32 层统一 Transformer

所以这里没有单独的文本 Transformer,也没有单独的 Cross-Attention。目标图 token 可以直接查看文字 token 和参考图 token。

整体结构:

image-20261001113419960

输出的噪声再继续通过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 的表示本来就只包含左边的内容。

举个例子:

85533ec19e3ae6c6053da91865404311

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。

image-20261001213826193

内部结构:

                    通道数            分辨率
  浓缩信息  ─────▶   64       ×      64×64
     │
  ① 升维           1152      ×      64×64      ← 先变"宽",准备开始画
     │
  ② 理解一轮       1152      ×      64×64      ← 先想清楚整体是什么
     │
  ③ 放大第一级      576      ×     128×128
     │
  ④ 放大第二级      288      ×     256×256
     │
  ⑤ 放大第三级      144      ×     512×512
     │
  ⑥ 放大第四级       4       ×    1024×1024    ← 最后直接输出 RGBA

注意,输出通道之所以是4维,是因为需要支持透明图,也就是RGBA。

# 研究讨论 / Discussions