【大模型】跟着张量走一遍 VLM:图像如何变成 LLM 的 token?|多模态大模型专栏③
2026/7/9 6:48:47 网站建设 项目流程

跟着张量走一遍 VLM:图像如何变成 LLM 的 token?|多模态大模型专栏③

一句话讲透本篇:借助一个零依赖的小 demo,跟随一个 batch 的张量从头走到尾,看清「图像 → patch → ViT → Projector → 拼进文本流 → LLM」每一步的 shape 变化。走完会发现:VLM 中真正"新"的组件,仅有一个 Projector。


写在前面

前两篇 我们建立了两件事:① VLM 的三组件架构;② CLIP 如何训练出那双"眼睛"。

但很多读者反馈:架构图和损失函数都懂了,可一张图到底是怎么一步步变成 LLM 能处理的 token 序列的,脑子里还是糊的。

这很正常——因为中间隔着太多 shape 变换,光看公式记不住。本篇的解法很直接:写一个玩具 VLM,打印每一个中间张量的 shape,跟着数据走一遍。

  • 零依赖:纯 Python,不用 torch/numpy,复制就能跑
  • 玩具维度:8×8 的小图、4 个 patch、d_v=8d_h=12——以clarity > realism为原则,便于清晰追踪每一步 shape
  • NLP 视角:每个步骤都标注它在 NLP 里的等价物

本文代码对应code/01_vision/04_walkthrough_demo.pypython 04_walkthrough_demo.py即可运行。本文展示的所有输出都是真实运行结果


一、实验设置:一个极简的玩具 VLM

我们手搓一个最小可运行的 VLM 前向过程。超参数故意设得很小:

H=W=8# 8x8 的小图P=4# patch 大小 4x4 -> 一共 4 个 patchC=3# RGB 三通道d_v=8# 视觉隐藏维数d_h=12# LLM 隐藏维数N_visual=(H//P)*(W//P)# = 4 个视觉 token

模拟的 prompt 是:<bos> <image> What is in this image ?(8 个文本 token,其中<image>是占位符)。

整个流程分 7 个 Stage,下面逐个走。


二、Stage A-B:图像 → patch(CV 的 “tokenization”)

Stage A:图像就是一个张量

image shape=(8, 8, 3) = 8x8x3 grid of RGB pixel values in [0,1] ↩ NLP 对照:raw text string(还没分词,是连续的字符/像素)

图像在模型眼里就是(H, W, C)的数值网格。对应到 NLP,这就是"还没分词的原始文本"。

Stage B:切 patch = 分词(核心类比)

patches shape=(4, 48) N = (H/P)*(W/P) = (8/4)*(8/4) = 4 patches; each patch flattened to P*P*C = 48 floats ↩ NLP 对照:BPE 分词 → token ids(把连续图像切成离散 patch = 切词)

这是整篇最关键的一步。把 8×8 的图按 4×4 切块,得到(8/4)×(8/4) = 4个 patch,每个 patch 摊平成4×4×3 = 48维向量。

记住这个类比,它会贯穿整个 VLM:

CV(视觉)NLP(文本)
连续像素图像原始文本字符串
切 patchBPE 分词
每个 patch 摊平成向量每个 token 的 id
patch 数 N = (H/P)(W/P)序列长度 L

💡 真实 VLM 用 224×224 的图、P=14,会得到(224/14)² = 256个 patch。高分辨率(如 448)就是 1024 个——这就是第一篇说的"一张图比一段文本还长"的来源。


三、Stage C-D:patch embedding + ViT(和 BERT 几乎一样)

Stage C:patch → embedding(查表)

patch_embeddings shape=(4, 8) (N=4, d_v=8) — like token embeddings, but from images ↩ NLP 对照:nn.Embedding(ids) → token_emb (L, d),查表把离散 id 变成稠密向量 + [CLS] token + pos shape=(5, 8) (1 + N) = 5 tokens; ViT now sees a 'sentence' of patches ↩ NLP 对照:BERT 的 [CLS] + 位置编码,机制完全一样

每个 48 维的 patch,经一层 Linear 映射成d_v=8维的 embedding——nn.Embedding把 token id 变成向量是同一回事,只是一个用查表、一个用线性投影。然后像 BERT 一样:前面拼一个[CLS],加上位置编码,序列从 4 变成 5。

Stage D:ViT = Transformer(没有新东西)

vision_features shape=(5, 8) shape unchanged: (1+N, d_v). All tokens have mixed information. ↩ NLP 对照:Transformer encoder —— 和 BERT/GPT 主体一模一样,没有新东西 Attention matrix (5x5, which token attends to which): CLS -> [0.19 0.16 0.21 0.24 0.19] p0 -> [0.20 0.19 0.22 0.19 0.19] p1 -> [0.20 0.20 0.19 0.22 0.20] p2 -> [0.32 0.16 0.17 0.20 0.15] p3 -> [0.27 0.19 0.19 0.18 0.17]

ViT 跑了一个 Transformer block,shape 不变(还是(5, 8)),但每个 token 已经"混合"了其他 token 的信息。看到这个 attention 矩阵,NLP 工程师应该很有亲切感——它和 BERT 的自注意力完全一样,只是这里的"词"是图像 patch。比如p2行对CLS的注意力最高(0.32),说明第 2 号 patch 的信息在往 CLS 汇聚。

结论:到这一步为止,整个过程没有任何超出 Transformer 范畴的新东西。


四、Stage E:Projector(VLM 唯一的"新组件")

patch_features (drop CLS) shape=(4, 8) visual_embeddings shape=(4, 12) (N, d_h) = (4, 12). Now LLM-shaped, ready to splice. ↩ NLP 对照:NLP 没有这一步 —— 这是 VLM 独有的'翻译层',把视觉空间对齐到 LLM 空间

先丢掉[CLS](LLaVA 只保留 patch token),然后过一个2 层 MLP Projector,把d_v=8维翻译成d_h=12维——也就是 LLM 的隐藏维数。

这是整个 VLM 流程里唯一 NLP 不存在的组件,也是第一篇说的"VLM 区别于纯 LLM 的灵魂"。它的作用就是当翻译官:SigLIP 说的"视觉语"是 8 维,LLM 听懂的"语言"是 12 维,Projector 负责对齐。

🔧动手建议:将d_h改为 4096(接近真实 LLM 的 hidden_size),可发现 Projector 的参数量主要由d_v × d_h决定——这正是 Projector 通常仅有数百万至数千万参数、远小于 LLM 的原因。


五、Stage F-G:拼进文本流 + loss mask

Stage F:splice(把视觉 token 缝进文本)

text_embeddings (pre-splice) shape=(8, 12) (L_text=8, d_h=12). The <image> slot is a placeholder. full_sequence (post-splice) shape=(11, 12) (L_text - 1 + N) = 7 + 4 = 11 tokens ↩ NLP 对照:把 prompt 里的 <image> 占位符,原地替换成 N 个真正的视觉 token

这是 VLM 最巧妙的设计:prompt 里原本有个<image>占位符(1 个 token),现在把它原地替换成 4 个真正的视觉 token。序列从 8 变成8 - 1 + 4 = 11

替换后,视觉 token 和文本 token 就平权地住在同一条序列里了。

Stage G:LLM 看到的样子 + loss mask

Token layout (T = text token, V = visual token): T V V V V T T T T T T The LLM runs causal self-attention over this 11-token sequence. Loss is computed only on assistant tokens (appended after this in real training). Visual tokens NEVER contribute to loss — they are inputs, not targets. ↩ NLP 对照:label mask:只在 assistant 文本 token 上算 loss,视觉 token 永远 mask 掉

LLM 看到的就是这条T V V V V T T T T T T的序列(1 个<bos>+ 4 个视觉 token + 6 个文本 token)。它在上面跑 causal attention,和纯文本 LLM 没有任何区别。

但有一个训练时的关键细节:loss 只在 assistant 回复的文本 token 上算,视觉 token 永远被 mask 掉、不参与 loss——因为它们是"输入"不是"目标"。这个 label mask 是 VLM 训练最容易踩坑的地方(参见code/03_sft/sft_mask_walkthrough.py)。


六、核心一图:NLP 路径 vs VLM 路径

把上面 7 步串起来,就是这张对照图(demo 末尾的真实输出):

【NLP 路径】 text --BPE--> ids (L,) --Embedding--> token_emb (L, d) --Transformer--> hidden (L, d) --> LLM 【VLM 路径】 image (8,8,3) --patch 切分--> patches (4, 48) ◀ 相当于 BPE 分词 --Linear--> patch_emb (4, 8) ◀ 相当于 Embedding --+CLS +pos--> (5, 8) --ViT--> vis_feat (5, 8) ◀ Transformer,无新东西 --Projector--> visual_emb (4, 12) ◀ VLM 独有!翻译到 LLM 维度 --splice--> full_seq (11, 12) ◀ 视觉 + 文本拼成一条序列 --> LLM(和 NLP 的 LLM 是同一个) 一句话:LLM 完全没变,只是 'token 从哪来' 换了—— NLP 用 BPE 切词,VLM 用 patch 切图 + Projector 把图翻译成 LLM 的语言。

patch 切分 = 分词

Linear = Embedding

+CLS +pos

ViT = Transformer

Projector · VLM 独有

splice 拼进文本

image (8,8,3)

patches (4, 48)

patch_emb (4, 8)

(5, 8)

vis_feat (5, 8)

visual_emb (4, 12)

full_seq (11, 12)

LLM · 与 NLP 同一个


七、从这次 walkthrough 里带走的 5 个洞察

  1. 切 patch 就是 CV 的分词——一旦想通这一点,VLM 的恐惧感就少了一半。
  2. ViT 没有任何新东西——它就是 BERT,把 patch 当 token 而已。懂 Transformer 就懂 ViT。
  3. Projector 是 VLM 唯一的新组件——一个把视觉维度翻译到 LLM 维度的 MLP,参数量不大,却是 Stage 1 训练的唯一主角。
  4. splice 后视觉 token 与文本 token 平权——(11, 12)里没人知道哪 4 个是图、哪 7 个是字,LLM 一视同仁。
  5. 视觉 token 不参与 loss——它们是输入不是目标,label mask 必须正确处理,否则训练会崩。

八、动手验证(强烈建议)

这篇的价值在于"自己跑一遍"。打开code/01_vision/04_walkthrough_demo.py,做三个小实验:

  1. H = W = 16:patch 数从 4 变成 16,亲眼验证"高分辨率 → 视觉 token 变多"这条因果链。
  2. P = 2:patch 更细,token 更多——理解 patch 大小如何权衡粒度与开销。
  3. image_slot_idx:把视觉 token 拼到 prompt 的不同位置,理解 splice 的灵活性。

📎 本篇涉及代码

文件说明
code/01_vision/04_walkthrough_demo.py零依赖端到端 walkthrough(本文增强版:每步含「NLP 对照」+ 末尾路径对照图)

小结

Stage做了什么NLP 等价
A图像 = (H,W,C) 张量raw text
B切 patchBPE 分词
Cpatch embedding + CLS + posEmbedding + 位置编码
DViTTransformer(无新东西)
EProjector 维度翻译NLP 无(VLM 独有)
Fsplice 拼进文本流替换<image>占位符
GLLM attention + loss masklabel mask

下一篇我们进入对齐机制:从 BLIP-2 的 Q-Former 到 LLaVA 的 MLP Projector,再到 Qwen-VL 的 M-RoPE,搞懂视觉 token 如何获得 2D 位置感知、动态分辨率怎么工作。关注专栏,更新第一时间通知。

专栏:《从 NLP 到 VLM:多模态大模型研发实战》
上一篇:② 手写 CLIP | 本篇:③ 张量级 walkthrough | 下一篇:④ 对齐机制(上)· Projector

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询