第 14 章 表示学习与多模态

神经网络只吃数字,而世界的原始形态是离散符号、文字与像素。本章解决两个前置问题:离散符号如何变成可训练的向量(词嵌入与 Tokenization),以及文字与图像如何在同一个空间里对齐(多模态任务与 CLIP)。

学习目标

读完本章,你应当能够:

  1. 解释离散符号不能直接送入神经网络的三层原因,写出 one-hot 表示的参数量并说明维度灾难的本质;
  2. 复述分布式假设(distributional hypothesis),推导 Word2Vec 负采样目标,说明 GloVe 为何回归 log X_{ij};
  3. 手工执行一遍 BPE(byte pair encoding)的合并过程,包括每一步的频次统计与重算;
  4. 用「1 中文字符 ≈ 0.7 token」估算上下文窗口容量,并完成带重叠的 chunk 数量换算;
  5. 递推计算 CNN 感受野,说明残差连接的梯度学解释,辨析 CNN 与 ViT 的归纳偏置取舍;
  6. 辨析 VQA、图像描述、图文检索、视觉蕴含四种任务的输入输出与评价指标;
  7. 写出对比损失与温度系数的作用,解释 CLIP 零样本分类流程及嵌入必须归一化的原因。

前置知识检查

你需要知道在哪一节补齐
向量、内积、L2 范数、余弦相似度本章 14.3 节从定义讲起(完整版见第 2 章 2.2 节)
Softmax 与交叉熵、Sigmoid本章 14.2.2 节由二分类交叉熵构造负采样损失(见第 4、9 章)
梯度下降与反向传播只用到「梯度指向损失下降方向」的直觉(完整推导见第 12 章)
卷积、池化、参数量计算本章 14.5.1~14.5.2 节从头补齐感受野与参数量对比
概率归一化与条件概率本章 14.2.2、14.7.1 节的 P(c|w)、P(文本 k|图像 j) 写法

14.1 为什么离散符号不能直接喂给神经网络

14.1.1 三个层次的障碍

设词表(vocabulary)有 |V| = 50000 个词。最朴素的想法是给每个词一个编号,猫 = 17、狗 = 342。把编号当输入喂给网络,三个层次上都行不通:

障碍一:编号是任意分配的,不含语义。 猫 = 17 与 狗 = 342 差 325,猫 与 冰箱 = 18 只差 1。网络会以为「猫和冰箱很像,和狗差很远」——编号的数值大小不承载任何关于含义的信息。

障碍二:差值运算在网络里是有意义的。 线性层输出 w·x + b。若输入是编号这个标量,模型只能学到单调关系,被迫在一条一维直线上寻找全部语义。

障碍三:离散取值不可微。 「符号 → 编号」是一张查找表,导数为零矩阵。反向传播要求每层给出 ∂L/∂输入,梯度到这里就断了。所以离散符号必须先被转换成一组连续实数。

14.1.2 one-hot:解决了可微性,解决不了维度

把编号改成 one-hot(独热)向量:猫 = [0, …, 0, 1, 0, …, 0]ᵀ,第 17 位是 1。它对求导没有障碍,但代价是:

  • 每个词占 |V| = 50000 维;
  • 任意两个不同词的 one-hot 内积是 0、距离是 √2——所有词彼此等距,语义相似度全部相同,等于没有提供任何相似性信息;
  • 输入层到第一隐藏层的权重矩阵形状是 |V| × d,参数量随词表线性膨胀;d = 512 时仅第一层就有 50000 × 512 = 2.56 × 10⁷ 个参数。
表示方式维度与词表大小的关系是否携带相似度可微
整数编号1无关无否
one-hot|V|线性膨胀,且向量稀疏无(所有词两两等距)是
稠密嵌入d(128~4096)与 |V| 线性但 d ≪ |V|有(内积即相似度)是

重要

one-hot 的缺陷不是「维度高」这一件事,而是高维且正交:它把每个词放在一个与其他所有词等距的顶点上,模型无法从「猫」的表示迁移到「狗」上。嵌入(embedding)的本质是用低维稠密向量打破正交性,让语义相近的词在空间里靠得近。

14.1.3 分布式假设:语义来自上下文

如果相似度不能从符号本身得到,那从哪里得到?语言学的回答是分布式假设(distributional hypothesis):

出现在相似上下文中的词,倾向于具有相似的含义。

这条假设由 Harris(1954)与 Firth(1957,“You shall know a word by the company it keeps”)提出,是全部静态词嵌入算法的共同地基。它把「语义」这个哲学概念操作化为一个可统计的量——词的上下文分布。于是学习目标变成:构造一个映射,使上下文分布相似的词,映射后的向量也相似。14.2 节的 Word2Vec 与 GloVe 都是这条目标的不同实现。

注意

分布式假设是充分而非必要的语义来源。反例是「语义相对但上下文高度重叠」的词(如「白天/夜晚」)——这类反例正是后来自上下文嵌入(ELMo、BERT)要解决的问题:同一个词在不同句子里得到不同向量,即一词多义。


14.2 词嵌入:从 Word2Vec 到 GloVe

14.2.1 Skip-gram 与 CBOW 的直觉

Word2Vec 有两个方向相反的自监督任务:CBOW(continuous bag-of-words) 用上下文猜中心词,输入是窗口内上下文词向量的平均;Skip-gram 用中心词猜上下文,对窗口内每个位置分别预测一个上下文词。

1
2
3
4
5
6
Skip-gram(中心词 → 周围词)        CBOW(周围词 → 中心词)

  w_{t-2} ┐                            w_{t-2} ┐
  w_{t-1} ┼─▶ 预测各自位置  ←  w_t     w_t  ← 平均(w_{t-2},w_{t-1},w_{t+1},w_{t+2})
  w_{t+1} ┤                            w_{t+1} ┤
  w_{t+2} ┘                            w_{t+2} ┘

两者都不需要人工标注——语料本身就是标签,这就是后来所有预训练语言模型的原型。实践上 Skip-gram 对低频词更友好但更慢,CBOW 更快却倾向平滑掉低频词的差异。

14.2.2 从全词表 Softmax 到负采样

设中心词 w、上下文词 c,对应向量 v_w、u_c。Skip-gram 的对数似然是:

1
2
ℓ = Σ_{w∈语料} Σ_{c∈上下文(w)} log P(c | w)
P(c | w) = exp(u_cᵀ v_w) / Σ_{c'∈V} exp(u_{c'}ᵀ v_w)

分母要遍历整个词表(5 万到 100 万词),每个样本一次全词表归一化,成本不可接受。负采样把「在 5 万个词上做多分类」偷换成「对每个(中心词,上下文词)对做一次二分类——这对是真的吗?」:

1
ℓ_neg = Σ_{(w,c)∈正样本} [ log σ(u_cᵀ v_w) + Σ_{k=1}^{K} E_{c_k ∼ P_n} log σ(−u_{c_k}ᵀ v_w) ]

其中 σ(z) = 1/(1+e^(−z)),K 是每个正样本抽的负样本数(常用 5~15),P_n 是负采样分布。

为什么这个偷换是合理的? 二分类的最优解使 σ(u_cᵀ v_w) → 1(正对)与 σ(−u_{c_k}ᵀ v_w) → 1(负对),即正对内积趋大、负对内积趋小;而「区分真实上下文对与随机对」这个判别任务的最优解,理论上(NCE,noise contrastive estimation)会收敛到与全词表 softmax 相同的参数。它把一个归一化问题变成 K+1 个二分类问题,每步计算量从 O(|V|d) 降到 O(Kd)。

负采样分布取 P_n(w) ∝ freq(w)^(3/4):纯按词频采样会让 the、的 占据几乎所有负样本,3/4 次幂是压低高频词、抬高低频词的经验折中。

14.2.3 GloVe:共现矩阵的加权分解视角

Word2Vec 滑动窗口本质上在隐式利用共现统计。GloVe(global vectors)把它显式化:先统计全局共现矩阵 X(X_{ij} 是词 j 出现在词 i 上下文中的次数),再直接分解它:

1
2
J = Σ_{i,j=1}^{|V|} f(X_{ij}) · ( w_iᵀ w̃_j + b_i + b̃_j − log X_{ij} )²
f(x) = min(1, (x/x_max)^(3/4))            (x_max 常取 100)

为什么回归目标是 log X_{ij} 而不是 X_{ij}? 共现次数跨越好几个数量级(从 0 到 10⁶),回归原始计数会让高频对绝对主导,取对数把它压到可比较的尺度。这也与 Skip-gram 的软约束一致:忽略偏置时最优点附近近似有 u_cᵀ v_w ≈ log P(c|w) ≈ log X_{ic} − log X_i,即内积近似对数共现比。

为什么需要权重函数 f? X_{ij} = 0 时 log 0 = −∞ 必须跳过,而 f(0) = 0 恰好做到「零共现不贡献损失」;同时 f 在 x 很大时封顶到 1,防止极端对把梯度吃掉。3/4 次幂同样用于抑制高频词。

维度Word2Vec(Skip-gram + 负采样)GloVe
数据使用在线滑动窗口,逐样本更新离线全量共现矩阵
训练目标二分类(真对 vs 噪声对)加权最小二乘回归到 log X_{ij}
复杂度O(语料 × K × d)O(非零共现数 × d)
关键超参窗口、负样本数 K、P_n 的幂次窗口、x_max、f 的幂次
共同点都基于分布式假设,都产出静态词向量同左

14.2.4 语义算术现象及其局限

经典演示是 v(king) − v(man) + v(woman) ≈ v(queen)、v(Paris) − v(France) + v(Italy) ≈ v(Rome)。出现的原因是「性别」「首都」这类关系在嵌入空间里近似表现为一个固定的偏移向量:若「国王 − 男人」与「女王 − 女人」的差向量方向一致,把差向量加到「女人」上就会落在「女王」附近。这说明嵌入空间不仅编码了相似度,还线性地编码了部分语义关系,也成为「线性类比」评测(如 3CosAdd)的依据。

局限同样重要:只在「词汇化且规律」的关系上成立(反义词 hot/cold 在空间中往往相互靠近而非相反,因为上下文高度重叠,这正是分布式假设的直接后果);结果依赖评测口径(3CosAdd 与 3CosMul 给出的排名不同,准确率可相差十几个百分点);低频词基本失效(向量估计方差大);静态嵌入无法处理一词多义(bank 在「河岸」与「银行」两种含义下只有一个向量,它是两种上下文的平均,因而不偏向任何一方)。

警告

「词向量能算语义算术」是被传播最广、也最容易被过度解释的结论。它是趋势性统计现象,不是可靠推理算子。不要把它当成一个可依赖的「首都查询 API」。


14.3 嵌入向量与相似度:为什么用余弦,要不要归一化

设两个嵌入向量 x、y,常见度量有三类:

1
2
3
内积(dot)        s = xᵀy
余弦(cosine)     s = xᵀy / (‖x‖·‖y‖)
欧氏距离           d = ‖x − y‖₂

在单位向量(‖x‖ = ‖y‖ = 1)前提下,三者通过单调映射互换:

1
2
3
‖x − y‖² = ‖x‖² + ‖y‖² − 2xᵀy
         = 2 − 2cosθ                       (单位向量)
cosθ     = xᵀy = 1 − ‖x − y‖²/2 = 1 − d²/2

末式说明 d² 与 cosθ 是线性关系:单位向量上「最近邻」无论用内积、余弦还是欧氏距离,排序结果完全一致。

那为什么工程上普遍推荐余弦? 因为嵌入向量的模长常常承载与语义无关的信息:低频词模长偏小(训练更新次数少);句向量模长随 token 数增长;某些模型会把「常见度」编码进范数。不归一化时,内积会把「模长大」当成「更相似」,检索出大量高频但无关的结果。余弦只保留方向,等价于对每个向量除以自身范数后做内积,因此对这类尺度变化不敏感。

度量对模长敏感取值范围归一化后与余弦的关系典型场景
内积是(−∞, +∞)相等已归一化的双塔召回、最大内积搜索
余弦否[−1, 1]自身语义检索、RAG 召回、词类比
欧氏距离是[0, +∞)d² = 2 − 2cosθ聚类、KNN、需要真实距离时

归一化放在哪? 一是离线归一化:写入向量库前做 x ← x/‖x‖,检索时用内积——因为归一化后内积等于余弦,而内积可由矩阵乘法或专用 SIMD 指令高效批量计算,比每次算两个范数快得多。二是在线归一化:保留原始向量、逐对计算余弦,灵活但慢,且很多近似最近邻(ANN)索引只支持内积或欧氏距离。

提示

一句话记忆:余弦 = 归一化后的内积。当 ANN 索引只提供内积时,先归一化再建索引,与用余弦检索等价,同时省掉在线除法。


14.4 Tokenization:从字符到子词

词嵌入解决「已知词怎么表示」,Tokenization(分词/子词切分)解决**「哪些单位算一个词、没见过的东西怎么办」**。

14.4.1 三个粒度

粒度词表规模未登录词(OOV)序列长度主要问题
词级10⁵~10⁶严重(形态变化、新词、拼写错误全 OOV)短词表爆炸,<UNK> 吞掉信息
字符级10²~10³几乎没有很长(英文约 4~5 倍)序列过长,单字符语义太弱
子词级10⁴~10⁵可消解为子词片段适中切分可能不唯一,跨语言不一致

子词级(subword)是当前主流大模型的默认选择,核心思想是高频词保留为整体、低频词拆成有意义的片段。unhappiness 可由 un + happi + ness 表示,即使训练时从未见过整词,模型也能通过片段拼出含义。

14.4.2 BPE 算法步骤(含合并频次计算)

BPE 原是数据压缩算法(Sennrich 等 2016 引入 NMT)。流程是:① 每个词拆成字符序列,词尾附加结束符 </w>(表示词边界,防止跨词合并);② 统计全部相邻符号对的频次;③ 合并频次最高的一对,记入规则表,词表新增一个符号;④ 重复 ②③ 直到词表达到目标大小或最高频次为 1。

完整算例。 语料为 low:5、lower:2、newest:6、widest:3(数字是出现次数)。初始拆成字符,</w> 简记为 E:l o w E ×5、l o w e r E ×2、n e w e s t E ×6、w i d e s t E ×3。

第 1 步:统计全部相邻对频次(只列 ≥ 2 的):

1
2
3
(e,s)=6+3=9 ←并列最高   (s,t)=6+3=9 ←并列最高   (t,E)=6+3=9 ←并列最高
(w,e)=2+6=8   (l,o)=5+2=7   (o,w)=5+2=7   (e,w)=6   (n,e)=6
(w,E)=5   (w,i)=3   (i,d)=3   (d,e)=3   (e,r)=2   (r,E)=2

三个对并列最高(都是 9)。确定性实现按 (频次, 符号对) 排序取字典序最小者,于是取 (e, s)。

第 2~5 步:依次合并,每步都重算受影响对的频次。

1
2
3
4
5
① (e,s) → es     频次 9   →  n e w es t E ×6 ; w i d es t E ×3 ; l o w es r E ×2
② (es,t) → est    频次 9   →  n e w est E ×6 ; w i d est E ×3 ; l o w es r E ×2
③ (est,E) → estE  频次 9   →  n e w estE ×6 ; w i d estE ×3 ; l o w es r E ×2
④ (l,o) → lo      频次 7   →  l o w E ×5 ; l o w es r E ×2 (其余词不变)
⑤ 继续:(lo,w) → low 频次 7 ; (low,E) → lowE 频次 5 ;……

第 1 步之后 (s,t) 已不存在(s 被吸收进 es),新的候选对是 (es,t) = 9——这就是「每步必须重算频次」的具体含义。把全部合并按顺序记成规则表,编码新词时严格重放:widest → w i d est E → w i d estE。BPE 有两个必须分开记的阶段:训练(离线一次,产出规则表与词表)与编码(在线每次,按规则表顺序合并)。est 能被整体保留,是因为规则表里它先于更细的 es 生效;顺序决定切分结果,这正是 BPE 是确定性算法的原因。

参考实现(可直接运行,语料与算例一致):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from collections import Counter


def get_pairs(corpus):
    """统计所有相邻符号对的频次;corpus 是 {符号元组: 次数}。"""
    pairs = Counter()
    for symbols, freq in corpus.items():
        for i in range(len(symbols) - 1):
            pairs[(symbols[i], symbols[i + 1])] += freq
    return pairs


def merge_pair(pair, corpus):
    """把 corpus 中出现的所有 pair 合并成一个新符号。"""
    merged, new_corpus = pair[0] + pair[1], {}
    for symbols, freq in corpus.items():
        out, i = [], 0
        while i < len(symbols):
            # 只有当前位置真的匹配 pair 才合并,否则原样搬一个符号
            if i < len(symbols) - 1 and (symbols[i], symbols[i + 1]) == pair:
                out, i = out + [merged], i + 2
            else:
                out, i = out + [symbols[i]], i + 1
        new_corpus[tuple(out)] = freq
    return new_corpus


def train_bpe(word_freqs, num_merges):
    """word_freqs: {'low': 5, ...};返回合并规则列表与最终词表。"""
    # 词尾加 </w> 标记边界,否则 'est' 会跨词合并到下一词的开头
    corpus = {tuple(word) + ('</w>',): n for word, n in word_freqs.items()}
    merges, vocab = [], set().union(*corpus)
    for _ in range(num_merges):
        pairs = get_pairs(corpus)
        if not pairs:
            break
        # 按 (频次, 符号对) 取最大:频次并列时选字典序最小,保证可复现
        best = max(pairs.items(), key=lambda kv: (kv[1], kv[0]))[0]
        corpus = merge_pair(best, corpus)
        merges, vocab = merges + [best], vocab | {best[0] + best[1]}
    return merges, vocab
# train_bpe({'low': 5, 'lower': 2, 'newest': 6, 'widest': 3}, 6) 的前三条规则
# 依次是 ('e','s')、('es','t')、('est','</w>'),与正文算例一致

实现里 best 用 (频次, 符号对) 做键,是为了在并列时给出确定性结果;真实实现(如 HuggingFace tokenizers)用同一策略,但底层是增量更新的堆,单步复杂度远低于这份教学实现。

14.4.3 WordPiece 与 Unigram / SentencePiece

WordPiece(BERT 使用)与 BPE 结构相同,差别在选哪一对合并:BPE 只看频次,WordPiece 看 score(a, b) = freq(ab) / (freq(a) · freq(b)),即改取互信息更大的对。频次最高的对可能只是两个本来高频的符号凑在一起(如 t 与 h),而分母惩罚了「各自都很常见」的情况,从而选出真正有搭配价值的片段。工程上它还有个差异:编码时用最长匹配优先,所以「能长则长」。

Unigram / SentencePiece(T5、LLaMA 系列使用)走另一条路:假设每个词由若干子词独立生成,为每个子词指定概率 p(x),切分 x = (x₁, …, x_k) 的概率是 Π p(x_i),目标是最大化语料总似然。流程是:① 用足够大的种子词表初始化(如所有高频子串);② EM 式迭代——用当前词表求每种切分的最优概率(维特比算法),累积每个子词的期望使用次数;③ 按使用次数裁掉低贡献子词,重复直到词表缩到目标大小。它给出的不是确定性规则表,而是一个概率模型,因此支持 subword regularization:训练时按概率随机采多个合法切分(tokenization 可切成 token/ization 或 to/ken/ization),起数据增强作用。SentencePiece 的另一个贡献是把空格当成普通字符(记为 ▁),不再依赖语言特定的预分词器——中文、日文无须先分词,也不会有「重建原文本」的偏差。

算法合并/裁剪准则编码方式输出形态代表模型
BPE最高相邻对频次按规则表顺序重放确定性切分GPT 系列、RoBERTa
WordPiecefreq(ab)/(freq(a)·freq(b)) 最大最长匹配优先确定性切分BERT、ELECTRA
Unigram LM最大化语料似然后裁词表维特比求最优切分(可采样)概率模型T5、mBART、LLaMA

为什么子词能同时兼顾词表大小与 OOV? 子词词表是封闭有界的(比如 5 万),但任意字符串都能由子词拼出——最坏情况回退到单个字节(byte-level BPE)。于是:词表规模由超参 vocab_size 直接控制,不随语料增长;只要字符集覆盖就永不出现 <UNK>;高频词是整体 token(语义完整),低频词拆成片段后仍能由组合表达含义,与「词由词素构成」的语言学直觉一致。代价是序列变长与切分不一致:同一个 2026 可能切成 202、6,也可能切成 2、0、2、6,对「数字比较」「算术」类任务不利。

14.4.4 中文字符与 token 的经验比例

英文里 1 token ≈ 4 个字符 ≈ 0.75 个单词;中文完全不同。主流 BPE 词表里中文单字常独占一个 token,但高频双字词(如「我们」「可以」)会被合成一个 token,于是有经验比例:

1
2
3
1 个中文字符 ≈ 0.7 个 token      (常见取值区间 0.6 ~ 1.0,随词表而变)
1 token ≈ 1.4 个中文字符
预计 token 数 ≈ 中文字符数 × 0.7
模型词表中文压缩倾向同一段中文的 token 数
英文为主、中文覆盖少中文常按 UTF-8 字节回退,1 字符可能 >1 token偏多
中英平衡、含大量中文词高频双字词合并,接近 0.6~0.7适中
中文语料占比高常见四字短语合并,可能 < 0.6偏少

重要

比例是词表相关的经验值,不是常数。做容量规划必须用目标模型自己的 tokenizer 实测抽样,而不是套固定系数。考试中若题目给出比例,就按题目给的算(见自测题 Q4)。

14.4.5 特殊 token

特殊 token(special token)不参与常规语言建模,承担结构信号:

特殊 token作用为什么需要
[CLS] / <s>序列首部,其最终表示常作整句表示给「整句语义」一个固定汇合点
[SEP] / </s>分隔片段或标记结束让模型知道句子边界,双句任务必需
[PAD]填充到统一长度批处理要求等长,须配合 attention mask 屏蔽
[MASK]掩码语言模型的预测位置提供训练目标,推理时不应出现
[UNK]未登录词字节级词表下基本不用,接口保留
<image>图像占位符多模态模型把图像 patch 嵌入插进文本序列

警告

[PAD] 若只在输入里补零、没有在 attention 中被掩掉,模型会对补齐位置算注意力,padding 越多结果越偏。这就是 attention_mask 存在的原因,也是第 15 章因果掩码的同一套机制。


14.5 视觉骨干:从 CNN 到 ViT

14.5.1 CNN 的归纳偏置

卷积层对输入的局部窗口做加权求和,由此引入两条归纳偏置(inductive bias):局部性(locality)——一个输出神经元只看输入的 k × k 邻域,因为相邻像素高度相关,远距离交互可先通过多层堆叠间接完成;平移等变性(translation equivariance)——同一个卷积核在整张图上滑动(权重共享),输入平移 Δ 则输出也平移 Δ。

参数量对比能说明权重共享的威力(输入 H×W×C_in,输出通道 C_out,卷积核 k×k):

1
2
卷积层参数量   = k² · C_in · C_out + C_out
全连接层参数量 = (H·W·C_in) · C_out + C_out

取 H=W=224、C_in=3、C_out=64、k=3:卷积是 3²×3×64 ≈ 1728 个参数,全连接是 224×224×3×64 ≈ 9.6×10⁶——相差约 5500 倍。这不是优化技巧,而是把「图像是局部的、平移不变的」这条先验直接写进了结构。

14.5.2 感受野:一层能"看到"多大

感受野(receptive field, RF) 指输出特征图上一个点对应输入图像的区域大小。L 层堆叠后:

1
2
RF_0 = 1
RF_l = RF_{l−1} + (k_l − 1) · Π_{i=1}^{l−1} s_i

其中 k_l 是第 l 层卷积核尺寸,s_i 是第 i 层步长。乘积项 Π s_i 是前面所有层步长的累积,因为下采样会按比例放大后续层的贡献。两个算例:

1
2
3
4
① k=3, s=1 重复三次                     ② k=3,s=2 后接 k=3,s=1
   RF_1 = 1 + (3−1)·1 = 3                  RF_1 = 1 + (3−1)·1 = 3   (只看 3×3)
   RF_2 = 3 + (3−1)·1 = 5                  RF_2 = 3 + (3−1)·2 = 7   (步长 2 使每点跨 2 像素)
   RF_3 = 5 + (3−1)·1 = 7

三个 3×3 卷积的感受野是 7×7,参数量 3 × (3²C²) = 27C²;而一个 7×7 卷积是 49C²。所以「用小核堆深度」既增多了非线性(层间夹激活函数),又省参数——这就是 VGG 的核心论证,也是深度网络合理性的第一层解释。

14.5.3 残差连接:让"更深"真的更好

有更多层本该有更强表达力,但实验发现 56 层网络的训练误差反而高于 20 层。这不是过拟合(过拟合是训练误差低、测试误差高),而是优化困难。ResNet 的解释是:若恒等映射已是最优,让一堆非线性层去逼近恒等映射很难;但若显式提供一条恒等通路,网络只需学习残差:

1
2
y = F(x, W) + x                        (F 通常由两层卷积 + BN + ReLU 组成)
∂y/∂x = ∂F/∂x + I

末行是关键:反向传播的梯度里有一项恒等项 I,不会被连乘消掉。即使 ∂F/∂x 很小,梯度也能经 +I 无损回流到浅层,缓解梯度消失与退化。这与第 13 章的梯度流、第 15 章每个子层都包残差是同一件事。

14.5.4 ViT:图像即序列

Vision Transformer(Dosovitskiy 等 2020)把图像当成一句话:切成固定大小的 patch,每个 patch 线性投影成一个向量,再套用标准 Transformer 编码器。

1
2
3
4
5
6
7
8
图像 x ∈ R^(H×W×C),设 patch 大小 P,则 patch 数 N = (H·W)/P²

① 切块 + 展平: 每个 patch → 长度 P²·C 的向量
② 线性投影:    z_i = W_E · flatten(patch_i) + b        (W_E: (P²C) × d)
③ 加类别 token:z_0 = 可学习的 cls 嵌入
④ 加位置编码:  z_i ← z_i + p_i                         (没有它则完全无位置信息)
⑤ 送入 L 层 Transformer 编码器
⑥ 取 z_0(或全部 patch 的平均)接分类头

以 ViT-Base 处理 224×224×3、P = 16 为例:N = (224×224)/16² = 196 个 patch,序列长度 196(加 cls 为 197);每个 patch 展平后是 16²×3 = 768 维,恰好等于 d_model = 768,所以最简单的 ViT 里 patch embedding 就是一次 768×768 线性层。

性质CNNViT
局部性结构内置(卷积核只连局部)无(每层全局注意力,第 1 层即可跨块交互)
平移等变性内置(权重共享)依赖数据学习,仅靠位置编码近似,等变性弱
感受野增长逐层线性/指数增长,需足够深第一层即全局
数据需求小数据即可训练出好结果小数据易过拟合,需大规模预训练或强增强/蒸馏
大数据表现好,但扩展性受结构限制更好,随数据与参数持续受益

为什么 ViT 在足够大的数据上反超? 因为 CNN 的归纳偏置是双刃剑:数据少时它提供正确的先验,等价于极强正则;数据足够多时,这个先验反而限制了模型去学习「不满足局部性/平移等变」的真实规律。ViT 论文标题点出了这个结论(An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale)——规模(scale)可以替代归纳偏置。

提示

同一思想在第 15 章会复用:Transformer 处理序列时也没有任何「顺序」先验,必须靠位置编码把顺序注入进去。ViT 与语言模型共享的正是这套「无先验 + 显式位置编码」的范式。


14.6 多模态任务全景

「多模态」指模型同时处理两种以上模态(文本/图像/音频/视频)。按输入输出组合分类是最容易拿分也最容易失分的地方。

任务输入输出评价指标
VQA(视觉问答)图像 + 文本问题文本答案准确率(开放式按答案一致性折算)
图像描述(image captioning)图像文本句子BLEU / CIDEr / METEOR / SPICE
图文检索(image-text retrieval)一个模态做查询另一模态的排序列表Recall@k、mAP、Median Rank
视觉蕴含(visual entailment, VTE)图像 + 文本假设三分类:蕴含/中立/矛盾准确率
图文匹配(ITM)图像 + 文本二分类:匹配/不匹配准确率
视觉定位(grounding)图像 + 文本短语图像中的区域框IoU、Acc@IoU
文生图文本图像FID / CLIPScore

VQA 的要点(对应 【08-05 选择 Q2】):它同时消费图像与文本,产出文本。三个常见干扰项正是其他任务的组合——「输入文本、输出图像」是文生图;「输入图像、输出图像」是图像翻译/风格迁移/超分;把输入输出颠倒则不再是 VQA。

图文检索的 Recall@k 是工程中最常用的指标:

1
2
3
4
5
Recall@k = 命中率:在 k 个候选里能找到正确配对的比例

例:1000 张图,对每张图按其与 1000 条文本的相似度排序
    Recall@1 / @5 / @10 = 正确文本排在第 1 / 前 5 / 前 10 位的比例
    Median Rank = 正确项排名的中位数(越小越好)

注意 Recall@k 只关心「在不在前 k 名」,不关心具体排第几,所以必须与 Median Rank 之类指标搭配看,否则 Recall@10 相同的两个模型实际体验可能差很多。

视觉蕴含把 NLI(自然语言推理)搬到图文对上:给定图像(前提)+ 文本(假设),判断是「蕴含、中立还是矛盾」。它比 VQA 更严格地考察推理而非识别——图中有两只猫在窗台上时,「有动物在室内」是蕴含,「猫在睡觉」是中立。

14.6.1 融合的三种时机

多模态模型的核心设计问题是:两个模态在哪里相遇?

1
2
3
4
5
6
7
早期融合(early fusion)        晚期融合(late fusion)        交叉注意力(cross-attention)

 图 ─┐                           图 ─▶ 视觉编码器 ─┐         图 ─▶ 视觉编码器 ─┐
     ├─▶ 拼接 ─▶ 单一编码器 ─▶ 头                  ├─▶ 合并 ─▶ 头            │
 文 ─┘                           文 ─▶ 文本编码器 ─┘         文 ─▶ 文本编码器 ─┤
                                                              ▲   交叉注意力  │
                                                              └──────来回────┘
融合方式交互发生在优点缺点代表
早期融合输入层:token 直接拼接交互最充分,实现简单强绑定模态对齐;某模态缺失时无法运行简单 VQA 基线、LLaVA 式拼接
晚期融合决策层:各自编码后合并模态独立、可分别预训练、鲁棒交互太浅,难做细粒度对齐双塔检索(CLIP)
交叉注意力中间层:一模态 token 查询另一模态细粒度、可解释(可画热图)结构复杂,计算量大Flamingo、BLIP-2

14.7 模态对齐的基础:CLIP、对比损失与温度

14.7.1 CLIP 的对比学习目标

CLIP(Contrastive Language-Image Pretraining)用 4 亿个(图像,文本)配对训练两个独立编码器,在共享空间里做对齐:

1
2
3
4
5
6
7
图像 → 视觉编码器 f_I → 归一化 → 图像嵌入 i_k ∈ R^d
文本 → 文本编码器 f_T → 归一化 → 文本嵌入 t_k ∈ R^d

一个 batch 有 B 个配对,得到 B×B 相似度矩阵:
      S = (i · tᵀ) · exp(τ)                形状 B×B
      S_{jk} = 图像 j 与文本 k 的匹配分数
      对角线 S_{jj} 是正样本对,其余 B²−B 个都是负样本对

损失是两个方向的交叉熵(与 InfoNCE 同形):

1
2
3
L_img2txt = −(1/B) Σ_{j=1}^{B} log [ exp(S_{jj}) / Σ_{k=1}^{B} exp(S_{jk}) ]
L_txt2img = −(1/B) Σ_{k=1}^{B} log [ exp(S_{kk}) / Σ_{j=1}^{B} exp(S_{jk}) ]
L = (L_img2txt + L_txt2img) / 2

为什么「批内其他样本天然是负样本」就够了? 随机采样下,batch 里第 j 张图配第 k 条文本(j ≠ k)几乎必然无关。这使标注成本降为零——配对本身就是唯一的监督信号。整个 batch 的 B² 个格子自动构成 B 个正样本与 B²−B 个负样本,B 越大(CLIP 用到 32768)负样本越多,表示越好,这也是对比学习「需要大 batch」的根本原因。

14.7.2 零样本分类:把标签变成句子

CLIP 最漂亮的地方是不需要为新类别重新训练:把每个类别名塞进模板句子编码成文本嵌入,再看图像嵌入与哪个最接近。

1
2
3
标签集合 {猫, 狗, 鸟}
模板    "a photo of a {label}"
预测    ŷ = argmax_{k} cos(i, t_k)

为什么模板是必要的? 训练时文本侧见到的都是完整句子,单个词「猫」与句子的分布不同(即分布偏移)。用 "a photo of a {label}" 把标签放回训练分布中,准确率可提升若干个百分点;集成多个模板("a blurry photo of a {label}" 等)再平均嵌入还能进一步稳定提升。

零样本的边界要清楚:它依赖类别名本身的语义可分性——若类别是 型号 A / 型号 B 这类纯符号,文本侧无法提供区分信息,零样本失效;它对细粒度任务(区分 100 种鸟)弱于有监督微调;但它对分布偏移的鲁棒性常常优于在 ImageNet 上监督训练的 ResNet,因为没被单一数据集的标注偏差绑住。

14.7.3 温度系数在做什么

τ(温度,实现时常取其倒数作为可学习标量)控制相似度分布的尖锐程度:

1
P(文本 k | 图像 j) = softmax(S_{jk}) = exp(τ·s_{jk}) / Σ_{k'} exp(τ·s_{k'})

τ 大(温度低)时分布尖锐,softmax 集中在最大项,梯度几乎只来自「最难的负样本」;τ 小(温度高)时分布平坦,所有负样本被平均对待,梯度分散、学习信号弱。CLIP 把 τ 实现为可学习的对数参数(初值约 1/0.07),让模型自己找到合适的判别难度。

为什么必须归一化嵌入? 因为 S = i·tᵀ·τ,若 i、t 不归一化,模型可以靠放大模长降低损失——模长增长让 softmax 更尖锐,等价于绕过 τ 直接控制温度。归一化堵住这条捷径,使 τ 成为唯一的温度旋钮。这与 14.3 节「余弦 = 归一化内积」是同一结论的两个用途。

重要

对比损失的三要素:正样本定义(这里是配对)、负样本来源(批内其他样本或采样噪声)、温度(控制判别难度)。任何多模态对比模型(ALIGN、SigLIP 及 CLIP 各变体)的差异几乎都落在这三处。


本章小结

关键结论一句话表述常见错误
离散符号不可直接输入编号无语义、不可微、无法泛化到相近词以为 one-hot「维度高」是唯一问题
one-hot 的本质缺陷所有词两两正交等距,不携带相似度混淆「高维」与「正交」
分布式假设上下文相似的词语义相似,是静态词嵌入的地基忽略它对反义词失效
负采样把 |V| 类 softmax 换成 K+1 个二分类,成本降到 O(Kd)忘记负样本分布是 freq^(3/4)
GloVe显式分解共现矩阵,目标取 log X_{ij} 并加权重 f直接回归原始计数导致高频对主导
余弦相似度单位向量上内积=余弦,且 d² = 2 − 2cosθ以为单位向量下余弦与欧氏排序不同
归一化位置写入向量库前归一化 → 检索用内积,等价且更快在 ANN 索引里做在线归一化
子词的动机平衡词表大小与 OOV:高频词整体、低频词可拼接以为子词能消除分词歧义
BPE 合并准则合并最高频相邻对,记录规则表并顺序重放忘记词尾 </w>、忘记顺序决定切分
中文 token 比例1 中文字符 ≈ 0.7 token,token 数 ≈ 字符数 × 0.7套用英文的 1 token ≈ 4 字符
CNN 归纳偏置局部性 + 平移等变性(权重共享)把 ViT 也当成具备平移等变
感受野递推RF_l = RF_{l−1} + (k_l−1)·Π_{i<l} s_i忘记累乘步长
残差的作用∂y/∂x = ∂F/∂x + I 提供恒等梯度通路把退化问题误诊为过拟合
ViT 的思想图像切 patch → 线性投影 → 当作 token 序列以为 ViT 不需要位置编码
VQA输入图像 + 文本问题,输出文本答案与文生图/图像翻译混淆
CLIP 与零样本双塔 + 批内对比;标签填模板后比余弦以为需要人工负样本标注
温度的作用控制 softmax 尖锐度即判别难度;嵌入须归一化以免绕过它不归一化导致模长失控
融合三时机早期(输入拼接)/晚期(决策合并)/交叉注意力(中间层)把双塔检索说成早期融合

自测题

Q1(计算)设词表 |V| = 40000、第一隐藏层维度 d = 256。分别计算 one-hot 输入层与稠密嵌入层引入的参数量,并说明嵌入为什么仍更优。

答案与解析

one-hot:矩阵形状 |V| × d = 40000 × 256 = 1.024 × 10⁷,参数量 10 240 000。稠密嵌入:本质是一张查找表,形状同为 |V| × d,参数量也是 10 240 000——两者相同。

所以参数量不是差别所在。真正的差别是:① one-hot 的输入本身占 |V| 维,前向要拿 1 × 40000 的稀疏向量去乘 40000 × 256,实际算的是「取第 k 行」,却要求输入是 |V| 维稠密张量(或额外的稀疏索引机制);② one-hot 下任意两词正交,第一层必须自己学出「哪些词相似」,而嵌入把词间相似度变成可学习的连续参数,优化器能在相似词的梯度之间共享信息,样本效率远高于 one-hot。

常见错误:答「嵌入参数量更少」——对比的是同一层,规模自然一样。正确表述是:嵌入用同一张矩阵同时完成了「降维表示」与「相似度建模」。

Q2(推导/判断)判断题:在单位向量前提下,用内积、余弦、欧氏距离做最近邻检索,排序结果相同。请说明理由;若未归一化,结论是否仍成立?

答案与解析

成立(前提是都已归一化)。因为 ‖x − y‖² = ‖x‖² + ‖y‖² − 2xᵀy = 2 − 2cosθ,即 ‖x − y‖² 与 cosθ 是严格线性递减关系:余弦越大,欧氏距离越小;线性单调变换不改变排序。又因单位向量上内积等于余弦,故三者排序完全一致。

不归一化时不成立。 取 x = [1, 1]、y₁ = [5, 0]、y₂ = [1, 1]:内积给出 y₁ 更近(5 > 2),余弦给出 y₂ 更近(1 > 0.707),结论相反。原因是内积同时受方向与模长影响,模长大的向量获得不公平优势。常见错误:丢掉前提后在未归一化的向量库上改用内积检索,召回一堆「长而泛」的文档。

Q3(计算)语料为 low:5, lower:2, newest:6, widest:3。写出初始字符序列,并给出前三次合并的符号对及其频次。

答案与解析

初始序列(</w> 标词尾):l o w </w> ×5、l o w e r </w> ×2、n e w e s t </w> ×6、w i d e s t </w> ×3。

初始相邻对频次:

1
2
(e,s)=9  (s,t)=9  (t,</w>)=9  (w,e)=8  (l,o)=7  (o,w)=7
(e,w)=6  (n,e)=6  (w,</w>)=5  (w,i)=3  (i,d)=3  (d,e)=3  (e,r)=2  (r,</w>)=2

前三次合并(频次并列 9,按 (频次, 符号对) 字典序依次取):① (e, s) → es 频次 9;② (es, t) → est 频次 9(合并后 es 只存在于 newest、widest);③ (est, </w>) → est</w> 频次 9。之后最高频变成 (w, e) = 8。

易错点:忘记 </w> 会让第 3 步去合并跨词对,切分毫无意义;合并后必须重算频次——(s,t) 在第 1 步后已不存在(s 被吸收进 es),沿用旧表会继续合并一个不存在的对。

Q4(计算)某文档共 4800 个中文字符,tokenizer 比例约为 1 中文字符 ≈ 0.7 token。若 chunk 大小 1024 token、重叠 128 token,至少需要切多少个 chunk?若换成 token ≈ 1 中文字符的 tokenizer,结果怎样变化?

答案与解析

第一问(对应 【09-04 选择 Q6】):T = 4800 × 0.7 = 3360;首块覆盖 1024;每前进一块新覆盖 1024 − 128 = 896;剩余 3360 − 1024 = 2336,还需 ⌈2336/896⌉ = ⌈2.607⌉ = 3 块,总共 4 块。验算:第 4 块起点 3 × 896 = 2688,终点 2688 + 1024 = 3712 ≥ 3360,覆盖到末尾。公式化:T > C 时块数 = ⌈(T − C)/(C − O)⌉ + 1,T ≤ C 时为 1。

第二问:若 1 字符 ≈ 1 token 则 T = 4800,块数 = ⌈(4800−1024)/896⌉ + 1 = 6。同一份中文文档的块数从 4 变成 6,索引规模、存储与召回调用的成本都对应上升。

常见错误:只算 3360/1024 = 3.28 取整得 3 或 4——忽略了重叠,每块实际只新覆盖 896;忘记向上取整——2336/896 = 2.607 必须进位到 3。

Q5(推导/辨析)为什么对比学习里的嵌入向量必须做 L2 归一化?不归一化会发生什么?

答案与解析

CLIP 的相似度是 S_{jk} = τ · i_jᵀ t_k,损失对 S 做 softmax 交叉熵。若 i、t 不归一化,模型有一条捷径:

1
2
把 ‖i‖ 与 ‖t‖ 同时放大 α 倍  ⟹  S_{jk} → α² · τ · (i_jᵀ t_k)
等价于把温度从 τ 变成 α²τ

于是模型可以靠增大嵌入模长降低损失,而不是学更好的表示:模长无上界增长,FP16 下容易溢出;τ 被这个「影子温度」覆盖,可学习温度的初衷失效;模长增长形成正反馈(模长更大 → softmax 更尖锐 → 损失更小 → 继续放大),训练不稳定。

归一化把 ‖i‖ = ‖t‖ = 1 固定下来,使 τ 成为唯一的温度旋钮,并把相似度限制在 [−1, 1]。这也呼应 14.3 节:归一化不只是「换一种度量」,而是把尺度这个自由度从模型手中拿走。

Q6(辨析)判断并说明理由:ViT 与 CNN 一样具备平移等变性,因此可以像 CNN 一样在小数据集上直接训练。

答案与解析

两个断言都错。

第一句错:CNN 的平移等变性来自卷积核的权重共享——同一组权重在空间上滑动,输入平移 Δ 时输出精确平移 Δ。ViT 的 patch embedding 虽然也是共享线性层,但每个 patch 的位置由位置编码唯一确定,而位置编码本身不是平移不变的;后续自注意力是全局的置换等变操作,它对 patch 顺序没有偏好,却对「第几个 patch」有明确依赖。因此 ViT 只在「patch 整体平移恰好是 patch 尺寸整数倍」这种离散情况下近似等变,一般不具备连续平移等变性。

第二句错:正因为缺少局部性与平移等变这两条先验,ViT 在中小数据集(如 ImageNet-1k 从头训练)上不如同量级 ResNet;原始论文的结论是 ViT 需要 JFT-300M 级别的大规模预训练,或配合强数据增强/知识蒸馏才能反超 CNN。

背后的规律是:归纳偏置是「数据不足时的免费正则」——数据少则先验帮助大,数据多则先验成为表达力的天花板。常见错误:把「Transformer 是置换等变的」直接推成「ViT 平移等变」——置换等变关于顺序,与图像空间的平移不变性是两回事(详见第 15 章)。

与真题的关联

真题考查角度对应本章小节
【08-05 选择 Q2】 VQA 的输入与输出多模态任务按输入输出组合分类,区分 VQA/文生图/图像翻译14.6 节(含任务全表)
【09-04 选择 Q6】 4800 中文字符与 0.7 token/字符的比例换算中文 token 比例 → 总 token → 带重叠 chunk 数公式14.4.4、14.4.5(自测题 Q4 完整复算)
【08-05 选择 Q8】 余弦相似度余弦只看方向,不随模长变化14.3 节
【09-04 选择 Q2】 单位向量下距离与余弦的换算d² = 2 − 2cosθ14.3 节(自测题 Q2)
【08-05 选择 Q10】 RAG 语义检索与相似度分级嵌入相似度作为检索分数14.3 节

延伸阅读

  • Mikolov 等,Efficient Estimation of Word Representations in Vector Space 与 Distributed Representations of Words and Phrases and their Compositionality(2013):Word2Vec 与负采样原始出处。
  • Pennington 等,GloVe: Global Vectors for Word Representation(2014):共现矩阵分解视角与权重函数 f 的动机。
  • Sennrich 等,Neural Machine Translation of Rare Words with Subword Units(2016):BPE 引入 NLP,第 3 节有可手算的例子。
  • Kudo,Subword Regularization 与 Kudo & Richardson,SentencePiece(2018):Unigram 分词与 ▁ 空格处理。
  • Dosovitskiy 等,An Image is Worth 16x16 Words(2020):ViT 原文,“规模可以替代归纳偏置” 的实证。
  • Radford 等,Learning Transferable Visual Models From Natural Language Supervision(2021):CLIP,含温度参数化与 prompt 模板消融。
  • He 等,Deep Residual Learning for Image Recognition(2016):残差连接与退化问题论证。
  • 本套书相关章节:第 2 章 2.2 节(范数与余弦)、第 4 章(交叉熵与 KL 散度)、第 15 章(自注意力与位置编码)、第 17 章(向量检索与重排)。