分词器实战
真实模型怎么分词?亲手用 GPT 的分词器,看中文和英文谁更「费 token」
Tokenizer:分词的工程化实现
前面讲的子词分词是一种 思路 ,而 tokenizer(分词器) 是它的 工程化实现 :一个在海量语料上 训练一次、随后反复复用 的工具。给它一段文本,它就能按既定的词表和合并规则,快速切出 token 并查成 ID。
几个主流的 tokenizer
实际中常见的 tokenizer 大多是子词分词(BPE 或其变体)的工程实现,例如:
- tiktoken :OpenAI 为 GPT 系列模型提供的分词器。 ( GitHub )
- SentencePiece :Google 开源的分词工具,被 LLaMA、百川等许多模型采用。 ( GitHub )
把它们封装得最顺手的,是 Hugging Face 的 transformers 库,它为大量预训练模型都配好了对应的 tokenizer,几行代码就能调用。
动手:走通编码与解码
下面用 GPT-2 的 tokenizer,完整走一遍「文本 → token → ID → 文本」。 空格在分词时会被切掉,必须把它的信息记进 token, 解码时才能还原原文。不同分词器对此约定不同:《BPE 子词分词》中用词尾标记 </w> 标出一个词的结束;GPT-2 则改用一个特殊前缀 Ġ 来标记 词首的空格 。两种记号的位置和写法不同, 目的相同,都是为了在解码时准确还原词与词之间的空格。
观察真实的切分行为
真实 tokenizer 的切分常常和直觉不同: 同样含义的中文和英文,消耗的 token 数往往差别很大 。 英文常见词大多整词占一个 token;而 GPT-2 的词表主要在英文语料上训练, 处理中文时延续字节级 BPE 的做法,先把每个汉字按 UTF-8 拆成 3 个字节, 再尝试用合并规则拼接。只有少数高频汉字能拼成一个完整 token, 多数汉字要占 2–3 个字节 token;数字串、emoji、生僻字同样会被切成更多小片段。
在下面的输入框里随意输入中英文、数字或 emoji,看看它们各自要花多少个 token:
想立刻试一试,可以打开 OpenAI 的在线 Tokenizer 可视化页面 , 输入任意文本,实时看到它被切成的 token 与数量。
工程里的特殊 token 与长度对齐
真实 tokenizer 除了普通 token,还会用到几个 特殊 token 。 承接之前见过的 <unk> ,这里把常用的几个补齐:
- 句子起止 : <bos> (句首)/ <eos> (句尾), 用来标明一段文本从哪开始、到哪结束;
- 补齐占位 : <pad> ,用于把一批长短不一的句子补齐到同样长度。
为什么需要补齐?因为模型通常一次处理 一批 句子,而一批里的句子长短不一。 工程上用两个操作把它们对齐成同样的长度:
- padding(填充) :给短句末尾补上若干 <pad> ,凑到统一长度;
- truncation(截断) :把超过上限的长句截短。
编号之后呢
走到这里,文本已经被切成 token、查成了一串整数 ID。但这串整数还不能直接拿来「理解语义」, 因为 整数的大小毫无意义 :ID 为 的词和 ID 为 的词,并不比 ID 为 的词「更接近」或「更不接近」。 编号只是索引,不携带含义。
要让数字真正携带语义,还需要流水线的最后一步:把 ID 变成向量。 这正是接下来要解决的问题。