06

为什么需要 tokenizer——字、词、子词各有什么问题

阅读约 7 分钟第 6/60 课

上一章节说到,直接用字符编码作为模型输入不是最好的方案。这一章节来看为什么,以及 tokenizer(分词器)是怎么解决问题的。

方案一:按字符切

最直觉的方案是把文本拆成最小的单位——字符。英文就一个字母一个字母地切,中文就一个汉字一个汉字地切:

"machine learning" → ['m', 'a', 'c', 'h', 'i', 'n', 'e', ' ', 'l', 'e', 'a', 'r', 'n', 'i', 'n', 'g']
"机器学习"         → ['机', '器', '学', '习']

英文被切成了 16 个 token(包括空格),中文只有 4 个。这暴露了第一个问题:

英文效率极低。 同一个概念——machine learning 和机器学习——英文要 16 步,中文只要 4 步。模型一次只预测一个 token,16 步比 4 步慢四倍。这还只是一个词,整篇文章累积下来差距巨大。

第二个问题:词表虽小,但模型需要更深才能理解。 英文只有 26 个字母加一些标点,词表极小。但模型要从单个字母出发「拼读」出词义——它需要先学会 m-a-c-h-i-n-e 组合起来是 “machine”,然后才能理解 “machine” 的意思。这个拼读过程消耗模型的层数和参数。

中文按字切的问题小一些——每个汉字本身有语义——但也不是没有问题。「华盛顿」三个字分开看是「华」「盛」「顿」,各自的独立含义和它们组合起来的含义(一座城市的名字)完全无关。按字切迫使模型在每次见到「华」时都要从上下文去判断这是「华盛顿」的「华」还是「中华」的「华」。

方案二:按词切

另一个方向是按完整的词来切:

"machine learning" → ['machine', 'learning']
"机器学习"         → ['机器', '学习']  或  ['机器学习']

看起来很好——每个 token 都是一个有完整语义的词,效率也高。但这个方案有更严重的问题。

词表爆炸。 英文有多少个词?韦氏词典收录了约 47 万个词条。加上专有名词、技术术语、网络用语、拼写错误,实际数量远不止此。中文更棘手——中文的「词」边界不明确(「机器学习」是一个词还是两个词?「中华人民共和国」呢?),如果把所有可能出现的组合都列入词表,数量是天文数字。

模型的词表大小直接决定了嵌入层(embedding layer)的参数量。如果词表有 50 万个词,嵌入层就有 50 万 × 1,536 = 7.68 亿个参数——光一层嵌入就占掉了 Sophia 十亿参数的大部分。

未登录词。 无论词表多大,总会遇到词表里没有的词。一个新造的网络用语、一个罕见的人名、一段代码中的变量名——模型看到这些词时完全无法处理,只能输出一个代表「未知」的特殊标记。

想象一个词表里没有 “PyTorch” 这个词的模型。每次遇到 “PyTorch” 它只能说「我不认识这个词」——这在一门讲深度学习的课程里是灾难性的。

方案三:子词——两者的折中

子词(subword)方案找到了中间地带:保留常见的词作为一个整体,把罕见的词拆成更小的片段。

"unforgettable" → ['un', 'forget', 'table']
"machine"       → ['machine'](足够常见,保留为一个整体)
"PyTorch"       → ['Py', 'Tor', 'ch'](不够常见,被拆开)

对于最常见的几千个词(the, is, machine, 学习, 模型……),它们在训练数据中出现得足够多,被保留为一个完整的 token。模型一步就能处理它们。

对于罕见的词(supercalifragilistic、某个生僻的人名),它们被拆成常见的子片段。每个片段在词表中都有位置,所以模型不会遇到「未知词」——任何文本都能被拆成已知片段的组合。

词表大小被控制在几万的量级——足够涵盖常见词和常见片段,又不会大到占用太多参数。

这就是现代语言模型几乎无一例外使用的方案。GPT 系列、Claude、LLaMA、Sophia——全都用子词分词。

Tokenizer 的职责

现在可以正式定义 tokenizer 了。Tokenizer 是一个固定的映射表加一套切分规则,负责两个方向的转换:

编码(encode):把一段文本切成 token 序列,每个 token 用一个整数 ID 表示。

"机器学习很有趣" → [3847, 2190, 1523, 892, 4401]

解码(decode):把 token ID 序列还原成文本。

[3847, 2190, 1523, 892, 4401] → "机器学习很有趣"

模型看到的始终是整数序列。它不知道什么是汉字、什么是英文——它只看到数字。Tokenizer 在模型的入口和出口各做一次转换:入口把文本变成数字,出口把数字变回文本。

需要注意:tokenizer 的切分是确定性的——同一段文本每次切出来的结果完全一样。它不涉及任何学习或随机性,是一个纯粹的规则系统。tokenizer 在训练开始之前就已经固定好了,训练过程中不会改变。

一个直观的类比

如果把语言模型比作一个只会看数字的阅读机器,tokenizer 就是它的翻译眼镜。

没有眼镜,它看到的是一串无意义的数字。戴上眼镜(tokenizer),每个数字都对应一个有意义的文本片段。眼镜的质量——片段切得是否合理、词表是否覆盖了常见的词——直接影响阅读的效率和准确性。

好的 tokenizer 让常用的词用一个 token 就能表示(高效),同时保证任何罕见的文本也能被拆成已有 token 的组合(不遗漏)。坏的 tokenizer 可能把常用词拆得太碎(浪费步骤),或者词表过大导致很多 token 训练不充分。

Sophia 的 tokenizer

Sophia 使用的是 Qwen2.5 系列的 tokenizer,词表大小是 151,936 个 token。但 Sophia 实际只使用了其中的 65,536 个——为什么要截断、为什么选 65,536 这个数字,是第 08 章节的内容。

在此之前,下一章节先来看 tokenizer 的词表是怎么生成的——一种叫 BPE 的算法,是目前几乎所有语言模型 tokenizer 背后的核心方法。