# 第 3 章 Token：模型眼中的世界

> 本章问题：文字是怎样变成模型能算的数字的？
> 来源：https://llm.weiborao.link/#ch3

**上一章留下的问题：**神经网络只会对数字做加法和乘法。可是我们给它的是文字，“草莓”“strawberry”“你好”。文字是怎样变成它能算的数字的？

这一章要走完文字进入模型的四站：**文本 → Token → 编号 → 向量**。走完之后，序章里的草莓之谜会自己揭开，你也会第一次看到“意思”在机器里长什么样。

> 【图示】文字进入模型的四站：原始文本“我爱吃草莓”；切成 Token：我、爱、吃、草莓；每个 Token 换成词表里的编号；每个编号再查表换成一个由几千个数字组成的向量。

**图 3-1** 文字进入模型的四站。前三站只是“换个写法”，信息没有增加；到了第四站，每个 Token 变成一个由几千个数字组成的向量，模型从这里开始“思考”。

### 3.1 切多碎才合适

第一步是把一段文字切成小块。小块叫 Token（中文常译“词元”）。问题是：切多碎？

问：最简单的办法，按单个字母或单个汉字切，行不行？

答：行，词表很小，什么字都能表示。但一句话会变得很长，而且单个字母几乎不带意思，模型要花大量力气才能把它们拼回有意义的单位。

问：那按整词切呢？“strawberry”就是一块。

答：每块都有意思了，可是英语有几十万个词，还有人名、新词、拼写错误、代码里的变量名。词表会大到装不下，遇到没见过的词就束手无策。

问：有没有中间路线？

答：有：常见的词整块保留，罕见的词拆成常见的小片段。这叫**子词**（subword）切分，今天几乎所有大模型都这么做。

表 3-1 三种切法的取舍

| 切法 | 词表大小 | 一句话的长度 | 遇到新词 |
| --- | --- | --- | --- |
| 按字符 | 很小（几百到几千） | 很长 | 不怕 |
| 按整词 | 极大（几十万以上） | 短 | 无法表示 |
| 按子词（今天的主流） | 5 万到 20 万 | 适中 | 拆成小片段，照样能表示 |

类比：

子词像**乐高**。常用的形状（“the”“模型”“ing”）直接做成整块，拿来就用；罕见的形状没有现成的整块，就用几块小积木拼出来。积木盒（词表）不必无限大，却什么都拼得出。

### 3.2 BPE：从字母开始，反复合并最常见的一对

词表里该放哪些片段？没有人一个个去挑，而是用一个简单的算法从语料里“统计”出来。最常用的叫 **BPE**（Byte Pair Encoding，字节对编码）。它的规则只有一句话：**数一数哪两个相邻的片段一起出现得最多，就把它们合并成一个新片段；重复，直到词表够大**。

> 【图示】BPE 分词算法的合并过程。语料里 low 出现 5 次，lower 2 次，newest 6 次，widest 3 次。从单个字母出发，每次把出现最多的相邻一对合并成一个新 Token：e 加 s，es 加 t，l 加 o，lo 加 w，n 加 e，ne 加 w。最后 low、est、new 都成了整块。

1. **起点：**每个词都拆成单个字母。
2. **e + s：**在 newest 和 widest 里共出现 9 次，最多，合并成 es。
3. **es + t：**同样 9 次，合并成 est。
4. **l + o：**在 low 和 lower 里共 7 次。
5. **lo + w：**low 成了一整块。
6. **n + e：**6 次。
7. **ne + w：**new 成了一整块。再合并一次，newest 也会成为整块。

**图 3-2** 在一个四个词的小语料上跑 BPE。品红色是本轮新合并出的片段。几轮之后，常见的 low、est、new 都成了整块，罕见的 widest 仍然由 w、i、d、est 拼成。网页版可以逐步播放。

真实的分词器在几 TB 的文本上跑这个过程，直到词表达到预定的大小。GPT-2 的词表有 50,257 个 Token；Llama 3 用了 12.8 万个；OpenAI 新一代分词器 o200k 有约 20 万个。词表越大，常见词越可能是一整块，一段话需要的 Token 越少。

BPE 不是唯一的造词表方法，但思路相通，都是“从小片段出发，按统计把常用组合并成整块”。2018 年的 BERT 用的是 **WordPiece**，词表 3 万；同年发表的 **SentencePiece** 工具可以直接在原始句子上训练，不需要先按空格把文本切成词，这对中文、日文这类词与词之间没有空格的语言格外方便。

OpenAI 给过一个经验值：**1 个 Token 约等于 3/4 个英文单词**，100 个 Token 约 75 个词。中文的比例取决于分词器：以英文为主的早期分词器常把一个不常见的汉字拆成好几个 Token（常用汉字在 UTF-8 编码里占 3 个字节，而 GPT 系列等采用的“字节级”BPE，最底层的单位就是字节）；新一代分词器收录了大量中文词，效率高得多。

### 3.3 草莓之谜揭晓

现在回到序章。当你输入 strawberry，模型收到的不是 s-t-r-a-w-b-e-r-r-y 十个字母，而是类似这样的几块：

你看到的strawberry

模型看到的（示意）straw berry

每一块对模型来说只是一个编号。编号里没有字母，就像“3 号楼”这个门牌号里看不出楼里住了几个姓王的人。要回答“有几个 r”，模型必须**间接地**知道 straw 里有一个 r、berry 里有两个 r。这种关于拼写的知识，它只能从训练文本里偶然出现的拼写讲解中零星地学到，很不牢靠。

**停一下：为什么“先把单词一个字母一个字母地写出来，再数”，就能数对？**

**参考答案：**

因为写出来之后，每个字母都变成了一个独立的 Token：str……这时“数 r”就变成了“数前文里有几个 r 这个 Token”，这正是模型擅长的事：它能直接“看到”前文的每一个 Token。换句话说，模型是用生成新 Token 的方式，把题目改写成了适合自己的形式。第 5 章讲推理模型时，你会发现“边写边想”是同一个道理。

同样的道理还能解释几种常见的“低级错误”：把一个单词倒过来写、比较 9.11 和 9.9 的大小、做多位数乘法。数字常被切成一到三位一块，模型看到的 9.11 和我们心里的小数并不是同一种东西。**这些错误不说明它笨，而说明它的“眼睛”和我们不同。**

### 3.4 从编号到向量：意思住在哪里

编号解决了“怎么表示”，没有解决“什么意思”。编号 40517 和 40518 在数值上挨着，意思可能毫不相干，就像学号相邻的两个同学不一定是朋友。

于是模型里有一张巨大的表，叫**嵌入表**（embedding table）：每个编号对应表里的一行，一行有几千个数字。查表得到的这一串数字，叫这个 Token 的**向量**（也叫嵌入，embedding）。这张表本身也是参数，和其他旋钮一起在训练中被调整。

训练结束后，奇妙的事情发生了：**意思相近的词，向量也相近**。没有人告诉模型“猫和狗都是动物”，但因为它们常出现在相似的上下文里（“我家的\_\_\_很可爱”），为了把下一个词猜准，模型自己把它们放在了向量空间里相邻的位置。

> 【图示】词向量空间示意（压缩到二维）：水果聚在一起，动物聚在一起，城市聚在一起。国王到王后的方向，与男人到女人的方向几乎平行，所以 国王 − 男人 + 女人 ≈ 王后。

**图 3-3** 压缩到二维的词向量示意。相似的词聚成团；更有意思的是方向：“男人→女人”和“国王→王后”几乎是同一个方向。这类例子因 2013 年的 word2vec 而广为人知：国王 − 男人 + 女人 ≈ 王后。

类比：

向量像**地图上的坐标**。北京和天津的坐标接近，因为它们地理上近；“猫”和“狗”的向量接近，因为它们在语言里“住得近”。不同的是，这张地图有几千个维度，每个方向编码着某种我们说不清的语义特征。

##### 词向量是

- 一个 Token 在“意思空间”里的坐标，由训练自动得到
- 可以计算的：相加、相减、比较远近

##### 词向量不是

- 字典里的定义，也不是人手工标注的特征
- 每一维都对应一个人能读懂的概念（多数维度没有简单的解释）

### 3.5 怎样量“意思有多近”

两个向量有多相似，最常用的量法是看它们的**夹角**：方向越一致，意思越近。夹角的余弦值叫**余弦相似度**，在 −1 到 1 之间。

> 【图示】用夹角衡量意思的远近：猫和狗的向量夹角小，余弦相似度约 0.8；猫和汽车的夹角接近直角，余弦相似度约 0.1。数值为示意。

**图 3-4** 余弦相似度只看方向、不看长短。真实计算是在几千维里做的，算法完全一样：对应位置相乘再相加，除以两个向量的长度。

记住这个工具。到了第 7 章，它会变成一种全新的“搜索”：不按关键字搜，而是按意思搜。把整段文档也变成向量存起来，用户提问时找夹角最小的那几段——这就是**向量数据库**和 RAG 的核心。

文字 → *Token* → 编号 → *向量*：  
模型从最后一站开始思考。

##### 本章带走

模型眼中的世界是一串 Token，每个 Token 是一个编号，每个编号对应一个向量；意思就藏在向量的方向里。

- 子词切分在“词表大小”和“句子长度”之间取平衡；BPE 靠反复合并最常见的一对来造词表。
- 草莓之谜：模型看到的是 strawberry 两个编号，不是十个字母。
- 嵌入向量由训练自动得到，相近的意思方向相近，可以用余弦相似度来量。

**于是，下一个问题**：现在模型手里有了一串向量。它要怎样从这串向量里算出“下一个词”？而且，为什么只做“猜下一个词”这一件事，就能长出解奥数题的能力？
