第 02 · 分词 · 8 min
从文本到标记
文本如何变成数字。BPE、子词,以及为什么大语言模型难以计算字母数量。
为什么需要 Token?
语言模型无法直接处理文本。它处理的是数字。每次你与大语言模型对话,第一步都是将你的文本转换成一个整数序列——即 Token ID。
词元化(Tokenization)就是实现这种转换的分割过程。
为什么不一个词一个 Token?
乍一看,你可能会设想:一个词 = 一个 Token。简单明了。
但这行不通:
- 语言中有数百万个可能的词(变形、新词、专有名词、错别字……)。每词一 Token 需要一个巨大的词表。
- 模型对它从未见过的词无能为力。
- 有些语言(中文、日文)词与词之间根本没有空格。
几乎所有现代大语言模型采用的解决方案是:子词(Subword)。
子词词元化
使用子词词元器:
- 高频词成为单个 Token("的"、"是"、"在")
- 罕见词被拆分成更小的片段("tokenization" → "token" + "ization")
- 未知字符总是可以分解到单个字母
结果:一个大小合理的词表(通常从 30,000 到 250,000 个 Token 以上——这个上限还在逐年抬高,因为近几年的词元器要覆盖多得多的语言),可以表示任意文本。
使用最广泛的算法叫做 BPE(字节对编码)。它从单个字符开始,在训练语料库中迭代地合并最频繁出现的字符对。
三十秒理解 BPE
设想一个只有三个词的迷你语料库:low、lower、lowest。我们先在字符级别进行词元化:
l o w
l o w e r
l o w e s t
在每次迭代中,我们寻找最频繁出现的相邻 Token 对。这里,l o 出现了三次——我们将它合并为 lo:
lo w
lo w e r
lo w e s t
现在 lo w 是最频繁的对。我们再合并:
low
low e r
low e s t
如此继续,直到达到目标词表大小。频繁出现的片段(low)变成单个 Token。罕见的(est)则保持分解状态。这正是 BPE 所做的——只不过在数十亿词上进行,而不是三个,并且在现代模型中是基于字节而非字符(byte-level BPE),这保证了任何输入都不会"超出词表"。
几个值得了解的近亲:WordPiece(BERT)和 Unigram LM(T5、mT5)和 BPE 一样,都是构建词表的算法。这里有个常见的混淆要注意:SentencePiece 经常被和它们并列,但它并不是算法,而是一个工具库——它实现了 BPE 和 Unigram,T5、mT5 以及早期的 Llama(1 和 2)用的都是它。从 Llama 3(2024)开始,Meta 改用了 128,000 个 Token 的 byte-level BPE,路数与 tiktoken 相近。它们共享同一个思想——子词词表——只是启发式不同。
特殊 Token
除了文本子词之外,词元器还保留了一些特殊 Token,它们在自然文本中永远不会出现:
<|im_start|>、<|im_end|>(OpenAI),[INST]…[/INST](Llama 2 和 Mistral),<|start_header_id|>…<|eot_id|>(Llama 3 及之后),<|user|>/<|assistant|>——用于分隔对话轮次。每个模型家族都有自己的格式,而且往往一代一换。<|endoftext|>——文档结尾。<|fim_prefix|>、<|fim_middle|>——用于中间填充(fill-in-the-middle),常用于代码补全。
正是这些标记把*"用户说了 X,助手回复 Y"*这样的对话转换成模型可处理的单一线性 Token 序列。当你向 ChatGPT 发送消息时,这些标记会在词元化之前被自动加上。
亲自试试
右侧显示出子词:每个词元都是一个可复用的片段,不一定是一个完整的单词。常见词只占一个词元,而生僻词会被拆成多块。
有几点值得注意:
- 短而高频的词很少被拆分。
- 长词或罕见词往往会被分成多个片段。
- 词前面的空格是 Token 的一部分(这就是为什么
· hello和hello是不同的 Token)。 - 对于中文,同样内容所需的 Token 数量通常比英文更多——在 GPT-3/GPT-4 那一代词元器上尤其明显,因为模型在训练中见到的英文远多于其他语言。近年的词元器词表大得多、也多语言得多,差距被拉近了不少。但并没有被抹平:中文依然比英文更"贵"。
实际影响
Token 这件事有很多出乎意料的影响:
- 大语言模型数字母很差。"strawberry 里有几个 R?"是 2024 年最出名的翻车题:不少模型会答 2 而不是 3,因为这个词是以几个 Token 而非单独字母的形式进入模型的。今天的大模型基本都答得对了——但结构性的原因一点没变。它们学会的是绕过去:先把单词一个字母一个字母地拼出来,再去数。你去问一个新模型,看看它的推理过程,这一步拼写往往就在那里。这恰恰说明,直接数数这件事,它到现在也还是做不到。
- API 价格按 Token 计算,而不是按词数计算。词元化效率较低的语言处理起来会贵一些。
- 上下文窗口(
128k tokens、200k tokens……)也以 Token 为单位衡量。一部 10 万字的书大约相当于 15 万个 Token。
对模型来说,"tokenization"和"token·iza·tion"是同一回事。它只看到这些片段。
接下来
你的文本现在已经变成了一个整数序列,这些整数将进入模型。模型内部的第一步:它们被转换成向量,在一个数百维的空间中,意义成为几何上的位置。
这就是下一章的主题。
更新于