跳到主要内容

打开那两个编码器 — token、图块、每块怎么互相看

这一章讲三件事: 一句话和一张图进去之后各自被切成了什么; 那些块怎么互相看(这套机制的名字叫注意力); 以及一堆块最后怎么被汇成第 01 章那一串数。 它在全书链条里的位置是「补地基」:第 13 章整章的关键动作, 就是把本章第 5 节那一层里的两个零件删掉——不先讲清这一层,那一章会变成天书。 另外,「ViT-B/16」「RN50」这种记法后面每一章都在用,这里一次讲完。

1. 顶层全景:两条一模一样的流水线

第 01 章说过,CLIP 有两个部件,各自把输入压成一串数。拆开看,两边的工序完全同构:

文字这边 图片这边
───────────────────────────────── ─────────────────────────────────
「a photo of a corgi」 一张 224×224 的照片
│ 切块 │ 切块
▼ ▼
6 个 token,前后加两个特殊标记 196 个 16×16 的小方块
│ 每块换成一串数 │ 每块换成一串数
▼ ▼
一条 77 个位子的序列 一条 196 个位子的序列
│ │
│ 每块去看别的块(注意力),反复几十层 │
▼ ▼
指定一个「代表」把整条汇成一串数 同样指定一个「代表」
│ │
▼ ▼
一串数 ──────────→ 比夹角 ←────────────── 一串数

图说:两边的差别只在「怎么切」和「代表是谁」。中间那一大段是同一套东西。
**这条流水线就是本章的主走查**,输入沿用第 01 章那张柯基照片和那句话。

2. 文字这边:先切成 token

先看现象:模型认不认得「corgi」这个词?

不一定认得整个词,但一定认得它的碎片。

模型不是拿一本词典去查词,而是先把句子切成一串小块,每块叫一个 token。 一个 token 可能是一个完整的词,也可能只是词的一小截(像英文里的 pre-、-ing 这种词头词尾)。

CLIP 的切法有三个写死的数1:

是什么参照物
49152它认得的块一共这么多种,这张清单叫词表英语常用词大约几万个;词表比它还多,因为里面还有大量词的碎片和标点
77一句话最多占 77 个位子,不够的补空、超了截断「a photo of a corgi」这种模板句只占十来个,离上限很远
512每个 token 被换成 512 个数就是第 01 章说的那种「一串数」,只不过这里是每个 token 各有一串

为什么要固定成 77 个位子? 因为要成批处理:一次塞几百句话进去一起算, 长度不一样就没法码成一个整齐的方阵。这是工程约束,不是语言学决定。

走查第 ① 步:那句话被切开

输入:「a photo of a corgi」
切成:[SOS] a photo of a corgi [EOS] ← 前后两个特殊标记
└ 起 ┘ └ 止 ┘
占掉:8 个位子,剩下 69 个位子空着
每块:换成 512 个数

图说:[SOS] 和 [EOS] 是「句子从这里开始 / 到这里结束」两个记号,不对应任何真实的词。
**这个切法是为演示编的**——corgi 究竟被切成一块还是两块,取决于 CLIP 自己那份 49152 条的清单。

注意最后那个 [EOS]:它就是文字这边的「代表」。 整句话的那串数,取的正是 [EOS] 这个位置上的输出,再过两道收尾工序: 先做一次归一化(把这串数缩放到一个稳定的范围里,免得数值忽大忽小), 再做一次线性变换(乘一个训练好的数表,把它换成最终要用的那种形状)1。 (为什么可以拿句尾当代表,下面第 5 节和图片那边一起讲。)

3. 图片这边:也切,切成方块

先看现象:图片没有「词」,怎么切?

ViT 的做法简单到有点粗暴:拿刀把图切成大小相等的小方块,每一块当一个 token。

书里对这件事的说法是,ViT「把图块当成序列来处理」—— 这正是它和上一代做法的分界线2

走查第 ② 步:一张 224 的图变成 196 块

输入:224×224 的柯基照片
切法:每块 16×16 像素
每边:224 ÷ 16 = 14 块
一共:14 × 14 = 196 块
每块:换成一串数,和文字那边一样

图说:196 这个数是按 224 和 16 算出来的(我们的算术,书里没直接给这个数)。
为什么这个数要记住:第 05 章讲「视觉 token 太多会把模型撑爆」时,数的就是它。

「ViT-B/16」这个记法,就是在说切法

这个记法后面每一章都在用,现在一次讲清3:

记法拆开看后果
ViT-B/16ViT + B(Base,尺码)+ 16(图块边长 16 像素)224 的图切成 196 块
ViT-B/32同样尺码,但每块 32 像素每边 7 块,一共 49 块——只有前者的四分之一
RN50 / RN101另一族主干(下面第 6 节讲),50 层 / 101 层——
RN50×16RN50 的加大版,书里说它的计算量是 16 倍——

块切得越小,块数越多,看得越细,也越慢。 这条取舍在后面反复出现。

4. 每块去看别的块:注意力

先看问题:一个 16×16 的小方块,本身几乎没有意义

柯基照片切出来的 196 块里,有一块可能全是草地,有一块可能只有半只耳朵。 光看那一块,谁也说不出它是什么。

要判断「这块是狗身上的」,必须让它去看别的块: 看到旁边有鼻子、有眼睛,这一块才算得上「狗脸的一部分」。

机制:每块生成三样东西,拿两样打分、取走第三样

这就是注意力。 书里给的是一个公式,拆开只有四步4:

① 每一块各自生成三样东西(各乘一个训练出来的矩阵,相当于三种不同的加工):
query(问题):我在找什么样的邻居
key (标签):我是什么样的块
value(货物):我身上带着什么信息

② 打分:拿我的 query 去和每一块的 key 相乘 —— 分数高 = 那一块正是我要找的

③ 摊平:把这一排分数除以一个固定的数、再摊成加起来等于 1
(和第 01 章最后那一步是同一个动作)

④ 取货:按这排比例,把所有块的 value 加权加起来,变成我这一块的新表示

图说:第 ③ 步除的那个固定的数,书里写作 1/√d,作用是别让分数太大以致摊平后非 0 即 1。

「query / key / value」这三个名字必须留下——你在任何一份讲这类模型的材料、 任何一段相关代码里都会撞见 q、k、v 这三个字母。而第 13 章要删掉的,正是其中的前两个。

为什么这一招有效: 每一块的新表示里,已经掺进了它自己挑出来的那些块的信息。 反复做几十层之后,「草地那块」和「狗脸那块」就带上了完全不同的上下文。

边界在哪: 每一块都要和每一块打一次分,块数翻倍,这一步的开销就翻四倍。 所以「把图切得更细」是平方级涨价的——这笔账第 04 章会正面算。

走查第 ③ 步:处理某一块时,它给别的块打了什么分

正在处理:第 84 块(狗脸中央)
它的 query 去和 196 个 key 各打一次分,摊平之后:

第 84 块(自己) 0.31
第 83 块(狗鼻子) 0.22
第 85 块(狗眼睛) 0.18
第 12 块(左上角草地) 0.02
…… 其余 192 块合计 0.27

然后按这排比例,把 196 块的 value 加权加起来 → 第 84 块的新表示

图说:**这几个分数是为演示编的,不是真实数值。**
真实模型里这一排分数分布得很散,而且同一层里有好几组这样的分数同时在算
(**一组叫一个「头」**,每组各看各的,最后拼起来——书里写作「多头」)。

5. 一堆块怎么汇成一串数:两种「代表」

这一节是全章最要紧的一节,因为第 13 章拆的就是这里。

先看问题

到上一步为止,196 块各自有一串数。可第 01 章要的是整张图一串数这一步不是自动发生的,得专门指定一个「代表」。 书里的两族主干,做法完全不同。

做法一(ViT 版):多加一个不对应任何像素的块

在 196 块之外,再加一个特殊的块,叫 [CLS]。 它不来自图片的任何位置,从头到尾只干一件事:跟着大家一起过注意力,把信息收集到自己身上。 最后取它的输出,就是整张图的那串数5

文字那边的 [EOS] 是同一个套路——句尾那个记号本身没有意思, 但它一路看完了整句话,所以拿它当代表。

做法二(ResNet 版):注意力池化

这一层是第 13 章的手术台,所以要讲得细一点。 书里的描述是6:

① 先把 196 块的表示求个平均,得到一个「全图平均特征」
② 拿这个平均特征当 query —— 全场只有这一个 query
③ 每个位置照常出自己的 key 和 value
④ 打分、摊平、按比例把各位置的 value 加权求和
⑤ 结果再过一个线性层 ℱ,输出整张图的那串数

图说:和第 4 节那套注意力比,唯一的差别是「只有一个 query,而且它是算出来的、不是某一块的」。
书里管这一层叫「全局注意力池化」。

书里在这里埋了一个观察,它就是第 13 章整章的种子:

每个位置的 value 单独过一遍那个线性层 ℱ,得到的东西本身就已经是「这个位置的语义响应」了—— 因为最后的输出无非是这些东西的加权和6

换句话说:整张图的答案是各位置答案的平均;那各位置的答案其实一直都在,只是被平均掉了。 第 13 章要做的事,就是把「求平均」这一步拿掉,直接把各位置的答案留下来—— 删掉 query 和 key 两个部件,分数从 8.3 跳到 18.5。

这两种做法之间只差两点,书里说得很明确: ViT 那一层的全局 query 来自 [CLS] 而不是平均, 而且它多一条把输入直接加到输出上的连线6所以同一套手术两边都能做。

走查第 ④ 步:回到第 01 章那串数

196 块的表示

├─ ViT 版:取 [CLS] 那一块的输出
└─ ResNet 版:平均当 query,做一次注意力池化,再过线性层

512 个数 ←—— 这就是第 01 章走查第 ① 步的输出,两章在这里接上

图说:主走查到此闭合。文字那边同理,取 [EOS] 位置的输出。

6. 两种主干:一条从 2012 年走到 2020 年的线

「主干」指的是负责把图片变成特征的那个大网络。 这本书里两种主干一直并存, 所有实验表都要标是哪一种,所以这条线要交代清楚。

书里给的顺序是四步7:

时间顺序是什么它解决了什么
ImageNet一个超大的图像数据集与比赛提供了统一的赛场,后面这些突破都是在它上面刷出来的
AlexNet第一个在这个赛场上大胜的卷积网络(CNN)证明了「让机器自己从数据里学特征」这条路走得通
ResNet加了一种「把输入直接抄一份加到输出上」的连线解决了网络一深就训不动的老问题,于是可以堆到几十上百层
ViT把图切成块当序列,全靠注意力就是本章第 3、4 节讲的那套

卷积是什么: 拿一个小窗口(比如 3×3)在图上一格一格滑过去,每次只看窗口里那一小片, 把结果记下来;下一层再在结果上滑一遍。一层只看得到局部,层数堆多了才看得到全局。 这正是它和注意力的根本差别:注意力第一层就能让任意两块直接打分。

为什么两种并存到今天: CLIP 官方就同时放出了两族——ResNet 那族(RN50、RN101、RN50×16) 和 ViT 那族(ViT-B/32、ViT-B/16)。后面每一章都在这两族里挑,而且挑哪一族对结果影响很大:

主干同一个任务上的零样本准确率
RN5060.33
RN10162.53
ViT-B/3263.80
ViT-B/1668.73(比 RN50 高 8.40)
RN50×1670.94

(这一组数出自原书第 7 章的一张表,量的是同一个千类分类任务8。)

所以后面看到任何一个分数,第一件事是问:这是哪个主干上的? 第 10 章那篇论文用的甚至不是 CLIP 的主干,而是另一个数据集上预训练的 ViT—— 那一章的绝对值和别章根本不能比,我们会在那里当场写明。

7. 作者的判断与证据

书里给了证据的:

说法证据
每个位置的 value 过完线性层就已经是该位置的语义响应原书第 11 章据此做了实验:删掉 query 和 key,分割分数从 8.3 变 18.5,翻了一倍多
ViT 层与注意力池化层只差两点(全局 query 来源、有无残差)原书第 11 章据此把同一套改动搬到 ViT 上,同样有效
主干越大、换成注意力那一族,效果越好原书第 7 章那张五主干对照表,五个数单调上升

属于作者的推断、书里没给证据的:

  • 「我们相信这是可能的,因为 ℱ(v) 已经捕捉到了局部语义」—— 原书第 11 章原文用的是「we believe」,这是一个假设,先信了再去试, 验证是靠后面的实验结果反推的,不是先证明再动手。

8. 边界与局限

① 书里没有讲注意力为什么有效。 它给了公式,没给直觉,更没有讨论为什么这套机制 比卷积更适合做这件事。本章第 4 节的四步拆解、「query 是问题、key 是标签、value 是货物」 这套说法,是我们为了讲清楚而补的,不是原书的写法。

② 「多头」这件事书里只写了缩写。 原书第 8 章的公式里出现了「多头自注意力」, 但全书没有一处解释为什么要分成多组。我们在第 4 节走查的图说里给了一句话, 再深就超出这本书的范围了。

③ 图块数与序列长度的代价,本章只点了一句。 「块数翻倍、开销翻四倍」这件事 是这条技术路线最硬的约束之一,书里把它放在原书第 1 章的难题清单里,我们放到第 04 章讲。

④ CNN 那一族的内部结构本章只讲到「滑窗」这一层。 残差、池化、下采样倍数这些 在第 13 章才成为承重概念(那一章会讲「ResNet 把图缩小 32 倍、ViT 只缩小 16 倍, 对逐像素任务很不利」),到那里再展开。

9. 可带走的

  1. 两边的流水线同构:切块 → 每块变成一串数 → 块与块互相看 → 指定一个代表汇总;
  2. 文字切成 token,CLIP 认得 49152 种,一句话固定占 77 个位子,每块换成 512 个数;
  3. 图片切成方块,224 的图按 16 像素切就是 196 块,每块当一个 token;
  4. 「ViT-B/16」= 尺码 B + 图块边长 16;B/32 只有 49 块,快四倍但看得糙;
  5. 注意力 = 每块拿自己的 query 和别人的 key 打分,按分数取走别人的 value;
  6. 块数翻倍、这一步开销翻四倍——「看得更细」永远是平方级涨价 (块数翻 n 倍,开销翻 n² 倍);
  7. 一堆块汇成一串数不是自动的:ViT 版加一个 [CLS] 块当代表,ResNet 版做一次注意力池化;
  8. 注意力池化里藏着一个关键事实:各位置的 value 过完线性层本来就是该位置的答案, 只是被平均掉了——第 13 章整章就靠这一句;
  9. 两族主干一直并存,同一个任务上 RN50 是 60.33、ViT-B/16 是 68.73; 看到任何一个分数,先问是哪个主干;
  10. 文字那边的代表是 [EOS],句尾那个记号本身没意思,但它一路看完了整句话。

10. 原文地图

主题原书章原文位置
词表 49152、77 个位子、512 维、[EOS] 当代表5. Differentiable Prompt Learningtext/26-ch05-5-differentiable-prompt-learning-for-vision-lang.txt:85(搜「49,152」)
ViT 把图块当序列1. Foundations of VLMstext/07-ch01-1-foundations-of-vision-language-models-concepts.txt:57(搜「treating image patches as sequences」)
ViT-B/16 与 B/32 的记法、RN50×167. Learning Efficient Feature Adapterstext/37-ch07-7-learning-efficient-feature-adapters-for-vision.txt:399(搜「patch size 32 × 32」)
五种主干的零样本准确率7. Learning Efficient Feature Adapterstext/37-ch07-7-learning-efficient-feature-adapters-for-vision.txt:421(搜「68.73」)
query/key/value 的定义与公式8. Efficient Tuning with Neural Prompt Searchtext/38-ch08-8-efficient-tuning-of-vision-foundation-models-w.txt:75(搜「queries Q」)
[CLS] 块与图块嵌入拼在一起8. Efficient Tuning with Neural Prompt Searchtext/38-ch08-8-efficient-tuning-of-vision-foundation-models-w.txt:101(搜「class token [CLS]」)
注意力池化:平均当 query、value 过线性层11. Unlocking CLIP for Zero-Shot Dense Segmentationtext/41-ch11-11-unlocking-clip-for-zero-shot-dense-segmentati.txt:95(搜「globally average-pooled feature works as the query」)
ViT 层与注意力池化只差两点11. Unlocking CLIP for Zero-Shot Dense Segmentationtext/41-ch11-11-unlocking-clip-for-zero-shot-dense-segmentati.txt:109(搜「the only two differences」)
ImageNet → AlexNet → ResNet → ViT1. Foundations of VLMstext/07-ch01-1-foundations-of-vision-language-models-concepts.txt:57(搜「vanishing gradient」)

Footnotes

  1. 出处:「5. Differentiable Prompt Learning for Vision-Language Models」第 85 段(text/26-ch05-5-differentiable-prompt-learning-for-vision-lang.txt:85,搜「49,152」)。原文的完整链条是:每个 token(含标点)先被转成一种叫 BPE 的编码,也就是一个唯一的数字编号;为了成批处理,每条序列前后加 [SOS] 和 [EOS] 并固定成 77 长度;这些编号再被换成 512 维的词嵌入向量喂进 Transformer;最后取 [EOS] 位置的特征,做层归一化并过一个线性投影层。「英语常用词几万个」这个参照物是补充(不在书里,来自通用知识)。 2

  2. 出处:「1. Foundations of Vision-Language Models: Concepts and Roadmap」第 57 段(text/07-ch01-1-foundations-of-vision-language-models-concepts.txt:57,搜「treating image patches as sequences」)。原文说 ViT「把图块当作序列来处理,并以完全基于自注意力的方式学习表示」,重新定义了这个领域。224÷16=14、14×14=196 这两步是我们的算术,书里没有直接给 196 这个数。

  3. 出处:「7. Learning Efficient Feature Adapters for Vision-Language Models」第 399 段(text/37-ch07-7-learning-efficient-feature-adapters-for-vision.txt:399,搜「patch size 32 × 32」)。这是表 7.3 的表头说明:ViT-B/32 与 ViT-B/16 分别指图块边长 32×32 与 16×16 的 ViT-Base,RN50×16 指计算量为 ResNet-50 十六倍的版本。「B 是尺码(Base)」是补充(不在书里,来自通用知识)。

  4. 出处:「8. Efficient Tuning of Vision Foundation Models with Neural Prompt Search」第 75 段(text/38-ch08-8-efficient-tuning-of-vision-foundation-models-w.txt:75,搜「queries Q」)。原文给的是公式:输入序列经三个投影矩阵得到 Q、K、V,再按 softmax(QKᵀ/√d)V 做加权求和,其中 1/√d 是缩放因子。书里只给公式、不给直觉;把 query/key/value 讲成「问题 / 标签 / 货物」是我们为讲清楚而补的说法(补充,不在书里)。

  5. 出处:「8. Efficient Tuning of Vision Foundation Models with Neural Prompt Search」第 101 段(text/38-ch08-8-efficient-tuning-of-vision-foundation-models-w.txt:101,搜「class token [CLS]」)。原文是在讲 VPT 这种做法时顺带交代的:一个 ViT 块的输入由 [CLS] 与各图块嵌入组成。「[CLS] 的输出被当作整张图的表示」这一点书里没有单独一句话说明,是从第 11 章那段对照(见下一条脚注)反推出来的。

  6. 出处:「11. Unlocking CLIP for Zero-Shot Dense Segmentation」第 95 段(text/41-ch11-11-unlocking-clip-for-zero-shot-dense-segmentati.txt:95,搜「globally average-pooled feature works as the query」)与第 109 段(text/41-ch11-11-unlocking-clip-for-zero-shot-dense-segmentati.txt:109,搜「the only two differences」)。第 95 段给出注意力池化的公式,并说「我们相信这是可能的,因为每个空间位置上算出来的 ℱ(v) 已经捕捉到了与文本嵌入很好对应的局部语义响应」——注意原文用的是「we believe」,这是假设不是结论。第 109 段说明 ViT 那一层与它只差两点:全局 query 来自 [CLS] 而不是各位置平均;以及多一条残差连接。 2 3

  7. 出处:「1. Foundations of Vision-Language Models: Concepts and Roadmap」第 57 段(text/07-ch01-1-foundations-of-vision-language-models-concepts.txt:57,搜「vanishing gradient」)。原文的说法是:ImageNet 这个大规模基准催生了一系列突破;AlexNet 用卷积网络在它上面刷出新纪录;ResNet 用残差连接解决了那个著名的「梯度消失」问题,于是网络可以更深、表达力更强;最近 ViT 把图块当序列处理,重新定义了这个领域。「卷积是拿小窗口滑过去」这个解释是补充(不在书里,来自通用知识)。

  8. 出处:「7. Learning Efficient Feature Adapters for Vision-Language Models」第 421 段(text/37-ch07-7-learning-efficient-feature-adapters-for-vision.txt:421,搜「68.73」)。这一列是表 7.3 里「零样本 CLIP」那一行的五个数:60.33 / 62.53 / 63.80 / 68.73 / 70.94。原文的结论是:主干越大、并且换成基于 Transformer 的结构,效果就一路提升。