2.9 Tokenization与词嵌入

Transformer 处理的不是原始文字,而是向量。在一段文字变成模型输入之前,需要先经过两道关键变换:Tokenization(分词)Embedding(词嵌入)。这两步看似简单,却决定了模型能”看到”什么、能”理解”什么——是整个 Transformer 流水线的起点,也是语言建模的基础认知。

📑 目录


1. 为什么需要把文字变成向量

计算机只认识数字,不认识文字。”猫”、”cat”、”Katze”——这些对计算机来说都是无意义的符号串,它不知道”猫”和”狗”都是动物,也不知道”happy”和”joyful”意思相近。

但神经网络的输入必须是数字矩阵。所以在文字进入 Transformer 之前,需要解决两个问题:

  1. 切分问题:怎么把一段连续文字拆成一个个可以处理的”单元”(Tokenization)?
  2. 编码问题:怎么把每个”单元”表示成一个能捕捉语义关系的数字向量(Embedding)?

这两个问题的解决方案,深刻影响着模型的理解能力和推理效率。


2. Tokenization:把文字切分成基本单元

2.1 Token 是什么

Token 是 Transformer 处理语言的最小单元——不是字,也不是词,而是介于两者之间的”词元”。

根据不同的切分策略,同一段文字会产生不同的 token 序列:

1
2
3
4
5
输入: "Tokenization"

字符级切分: T / o / k / e / n / i / z / a / t / i / o / n (12 tokens)
词级切分: Tokenization (1 token)
子词切分: Token / ization (2 tokens) ← 主流大模型采用

2.2 子词切分:大模型的实际选择

现代大模型普遍采用子词(Subword)切分策略,最常用的是 BPE(Byte Pair Encoding)

📌 关键点:BPE 从字符出发,把高频的字符组合逐步合并成更大的 token,最终得到一个覆盖常用词、生词用子词拼接的词表。

直觉上理解:BPE 把高频组合合并为单一 token,把低频组合保留为子词拼接。常见词(”play”、”run”)通常作为完整 token 存在;生僻词或新词(如 “tokenization”)可能被拆成 “token” + “ization”,用已有子词拼接表示,不会出现未知词(OOV)问题。需要注意的是,BPE 的切分是纯数据驱动的统计结果,不一定按语言学意义上的词根/后缀切分,具体结果取决于训练词表。

实际大模型的词表规模:

模型 词表大小 分词策略
GPT-2 / GPT-3 50,257 BPE
LLaMA-2 32,000 SentencePiece BPE
LLaMA-3 128,256 Tiktoken BPE
GPT-4 ~100,000 Tiktoken cl100k

⚠️ 注意:词表越大,覆盖越好,但 Embedding 矩阵(下文讲)也越大。LLaMA-3 把词表从 32K 扩到 128K,直接让 Embedding 层参数量增加了 4 倍。

2.3 Tokenization 的中文处理

中文没有天然的空格分隔,处理方式与英文不同:

  • 大多数大模型对中文做字符级 Tokenization(每个汉字一个 token),或者将汉字的 UTF-8 字节序列作为切分依据
  • 专门优化中文的模型(如 Qwen、ChatGLM 系列)通常有更大的词表,包含更多中文词组作为单独 token,生成效率更高

3. 独热编码:第一次尝试的失败

有了 Token ID,最简单的向量化方法是独热编码(One-Hot Encoding):假设词表有 $V$ 个 token,每个 token 用一个长度为 $V$ 的向量表示,对应位置为 1,其余全为 0。

举例:假设词表 = [“猫”, “狗”, “鱼”, “河”, “银行”],词表大小 $V = 5$:

$$
\text{猫} = [1, 0, 0, 0, 0] \qquad \text{狗} = [0, 1, 0, 0, 0] \qquad \text{鱼} = [0, 0, 1, 0, 0]
$$

看起来可以用,但有两个致命问题:

问题一:维度爆炸

真实词表有数万个 token,每个 token 的向量就有数万维,存储和计算开销极大。更严重的是,把这个稀疏向量作为神经网络输入,后续层需要极大的权重矩阵才能处理它——效率极差。

问题二:语义丢失

独热向量之间完全正交——“猫”和”狗”的向量内积为 0,”猫”和”鱼”的内积也是 0。从数学上看,所有 token 的距离完全相等,彼此之间毫无关联。这意味着模型无法从编码中学到任何语义关系:”猫”和”狗”都是宠物、都是动物,这个信息在独热编码里完全不存在。

$$
\text{猫} \cdot \text{狗} = 0, \quad \text{猫} \cdot \text{鱼} = 0 \quad \Rightarrow \text{模型看来”猫狗”关系 = “猫鱼”关系}
$$


4. Word Embedding:几何空间里的语义地图

4.1 核心思想:语义相似的词距离相近

Word Embedding(词嵌入)的核心思想是:把每个 token 映射到一个低维连续向量空间,使得语义相似的词在这个空间里距离相近

从独热编码的 $V$ 维稀疏向量,压缩到 $d_{model}$ 维稠密向量(通常 $d_{model} \ll V$):

模型 词表大小 $V$ 向量维度 $d_{model}$ 压缩比
BERT-base 30,522 768 ~40:1
LLaMA-2-7B 32,000 4,096 ~8:1
GPT-3 50,257 12,288 ~4:1

用一个二维示意图来理解(真实 Embedding 空间维度远高于此):

1
2
3
4
5
6
7
8
9
10
     动物类
狗 ● ● 猫
● 兔
● 鸟
金融类
● 银行 ● 钱
● 贷款

● 河流 地理类
● 湖泊

可以看到,”猫”和”狗”在这个空间里距离很近(都是常见宠物)。需要注意:在静态 Word Embedding(如 Word2Vec)中,”银行”这个 token 只有一个固定向量,位置由它在所有语料中的平均共现模式决定——金融语境和河岸语境各占一部分,向量会落在两者之间。真正消除歧义是 Self-Attention 的工作:它根据上下文中其他词(”money” 或 “river”)动态调整每个 token 的表示,为”银行”在不同语境中生成不同的上下文向量。Embedding 层提供的是起点,Self-Attention 完成动态消歧。

4.2 Embedding 空间的神奇属性

经过大量语料训练的 Word Embedding 会涌现出令人惊叹的几何属性:

词语类比关系

$$
\text{king} - \text{man} + \text{woman} \approx \text{queen}
$$

这意味着在 Embedding 空间里,”王”→”女王”的方向向量,和”男人”→”女人”的方向向量几乎平行!语义关系被编码成了几何关系。

语法变形的平行性

$$
\text{walking} - \text{walk} \approx \text{swimming} - \text{swim} \approx \text{running} - \text{run}
$$

所有动词从原形到进行时的位移向量几乎相同——“时态变化”这个语法规律变成了一个统一的位移方向。

💡 提示:这些属性不是人工设计的,而是模型从海量文本中自动学习到的。正是因为全人类几百年的文字记录里,”狗追猫”、”猫抓鱼”这类共现关系远多于”猫追猫”,这些词语的空间位置才逐渐形成了今天的格局。

4.3 Embedding 矩阵:一张可学习的查找表

在 Transformer 中,Word Embedding 实现为一个可学习的查找矩阵 $W_{emb}$:

$$
W_{emb} \in \mathbb{R}^{V \times d_{model}}
$$

输入一个 Token ID,直接取对应行,得到 $d_{model}$ 维的向量:

1
Token ID = 1234  →  W_emb[1234, :]  →  [0.12, -0.34, 0.07, ..., 0.91]  (d_model 维)

这等价于一个输入为独热向量的线性层,但实现为查表(index lookup)操作,计算效率远高于实际的矩阵乘法。

Embedding 层的参数量:$V \times d_{model}$

以 LLaMA-2-7B 为例:$32000 \times 4096 \approx 131M$ 参数。看起来不少,但在 6.7B 总参数中只占约 2%,原因是其余参数都在 32 层 Decoder Block 里。


5. 位置信息的空缺:为什么还需要 Positional Encoding

Word Embedding 解决了”语义编码”的问题,但留下了一个关键缺陷:它不包含任何位置信息

把句子 “猫追狗” 的三个 token 打乱为 “狗猫追”,它们的 Embedding 向量完全不变——模型根本分不清顺序。但语言的意思高度依赖词序:”猫追狗”和”狗追猫”意思截然相反。

这就是为什么 Transformer 在输入层还需要额外加上 Positional Encoding(位置编码)

$$
\text{Input}{i} = W{emb}[\text{token}_i] + \text{PE}(\text{pos}_i)
$$

Word Embedding 提供语义信息,Positional Encoding 提供位置信息,两者相加,才构成 Transformer 的完整输入。Positional Encoding 的详细设计(Sinusoidal 和 RoPE)在 2.5 位置编码深入理解 中详细讲解。


6. 从 Token ID 到输入矩阵:Transformer 的输入层

把上面的流程串起来,看一个完整的例子:

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
用户输入: "猫在桌子上"

Step 1: Tokenization
────────────────────────────────────────────────────
"猫在桌子上" → Tokenizer → [3421, 528, 11082, 319]
猫 在 桌子 上
(4 个 Token ID)

Step 2: Embedding Lookup
────────────────────────────────────────────────────
Token 3421 (猫) → W_emb[3421] → e_1: (d_model,)
Token 528 (在) → W_emb[528] → e_2: (d_model,)
Token 11082(桌子)→ W_emb[11082]→ e_3: (d_model,)
Token 319 (上) → W_emb[319] → e_4: (d_model,)

组合成输入矩阵 X: (4, d_model)

Step 3: 加 Positional Encoding
────────────────────────────────────────────────────
X[0] = e_1 + PE(0) ← "猫" 在位置 0
X[1] = e_2 + PE(1) ← "在" 在位置 1
X[2] = e_3 + PE(2) ← "桌子" 在位置 2
X[3] = e_4 + PE(3) ← "上" 在位置 3

最终输入矩阵 X: (4, d_model) ← 送入第 1 层 Decoder Block

最终,4 个 token 变成一个 $(4, d_{model})$ 的矩阵,每行是一个 token 的向量表示(包含语义 + 位置信息)。这个矩阵就是 Transformer Block 的输入。

AI Infra 视角:Embedding 层的 $W_{emb}$ 矩阵有时会和输出层的 LM Head 共享权重(称为 Weight Tying),这样可以节省 $V \times d_{model}$ 的显存。例如 LLaMA-2 就采用了 Weight Tying,将 Embedding 参数量从 262M 降到 131M(只算一次)。在做量化时,Embedding 层通常不量化(因为它是查表操作,量化收益有限但精度损失较大)。


🎯 自我检验清单

  • 能说清 Tokenization 的三种粒度(字符级、词级、子词级),以及现代大模型为什么选择子词切分
  • 能解释独热编码的两大缺陷:维度爆炸和语义丢失
  • 能说清 Word Embedding 的几何直觉:语义相近的词在 Embedding 空间中距离相近
  • 能说出 king - man + woman ≈ queen 这类类比关系背后的几何含义
  • 能写出 Embedding 层的参数量计算公式($V \times d_{model}$),并用 LLaMA-2-7B 实例验证
  • 能解释为什么 Word Embedding 之上还需要 Positional Encoding(位置信息缺失)
  • 能完整描述 “文字” → Token ID → Embedding → 加位置编码 → 输入矩阵的完整流程

📚 参考资料