在现代计算语言学和大规模语言模型(LLM)的架构体系中,分词器(Tokenizer)是连接人类自然语言与机器数值计算的唯一底层接口。分词器不仅是文本预处理的一个步骤,更是决定模型理解精度、计算效率、跨语言公平性乃至商业成本的核心支柱 。
为什么需要Tokenizer
分词器的核心任务是将非结构化的原始文本转化为计算机能够处理的离散数值序列 。神经语言模型本质上是高维空间的概率密度估计器,其内部操作均基于向量运算。由于机器无法直接感悟字符的语义,必须通过一个中间层将字符流切分为具有一定统计意义的单元,即“标记”(Token),并为每个标记分配唯一的整数标识符(ID) 。
这一转换过程奠定了模型理解和生成语言的基础。分词器被设计用于识别语言内部的多样化结构,包括标识句子开头的特殊标记或实体的占位符 。如果没有分词器,模型面对的将是海量的、缺乏模式的随机数据流,这使得识别可学习模式变得几乎不可能 。通过将文本拆解为易于分析的小块,机器能够识别复杂的语言解剖结构,就像生物学家通过研究单个细胞来理解复杂的器官一样 。
本质上,Tokenizer 是为了解决计算效率、语义表达与内存占用之间的矛盾。
1. 压缩效率:节省“上下文窗口”
LLM 的输入长度是昂贵的资源:
- 字符级输入: 如果直接按字母输入,
"Understanding"这个词需要 13 个位置。 - Token 级输入: 经过 Tokenizer 处理,它可能只需要 1 个 Token。
结论: Tokenizer 就像一种“文字压缩算法”。同样的 8k 上下文窗口,使用 Tokenizer 可以读完一章小说,而按字符读取可能只能读几页。
2. 帮助模型捕捉“语义块” (Meaningful Units)
单个字母或字节通常不具备语义。
- 字母
t在table和the中没有任何共同含义。 - 但是,子词 Token(如
ing,pre,multi)携带了跨词的语义信息。
Tokenizer 通过将常见的字符组合(子词)聚合成一个单位,让模型更容易学习到这些词根、词缀的含义,而不是从零开始去猜测字母之间虚无缥缈的联系。
3. 解决显存爆炸:控制词表大小
这是最工程化的原因。如果模型直接以“单词”为单位(词典里每个词给一个编号):
- 全词表: 英语有几十万个词,中文有数万个汉字。如果加上各种组合,词表会轻松突破百万级。
- 参数量问题: 模型的输入层是一个巨大的矩阵 $V \times d$($V$ 是词表大小,$d$ 是维度)。如果词表太大,模型大部分参数都会被浪费在“记名字”上,而不是“学逻辑”上。
Tokenizer 的折中方案:
通过 BPE 等算法,将词表控制在 3万到12万 之间。既能涵盖所有常见词,又能通过“拼凑”处理不常见的词。
4. 消除“噪音”:处理空格和特殊符号
文本中充满了空格、换行符、缩进(尤其是代码)。
- 在 Python 代码中,4 个空格和 1 个 Tab 意义相同。
- Tokenizer 会将这些格式化的符号统一处理,减少模型在理解结构时的计算负担,让模型把注意力集中在核心逻辑上。
分词粒度的演进:从单词到子词的平衡艺术
在自然语言处理的发展史上,关于分词粒度的选择始终是学术界争论的焦点。这种争论在词级(Word-level)、字符级(Character-level)以及当前主流的子词级(Subword-level)分词之间展开,每种方法都在词表大小、序列长度和语义完整性之间寻找不同的平衡点 。
词级分词
词级分词通过空格和标点符号将文本划分为完整的单词。其优点是简单直观,能够很好地保持单词的语义完整性 。然而,对于形态丰富的语言(如土耳其语或德语)或词汇量巨大的语言(如英语),词级分词面临严重的“维度灾难” 。英语中仅基本词汇就超过 50 万个,如果为每个单词分配唯一 ID,词表将变得不可管理,导致内存占用激增 。此外,词级分词无法处理未登录词(OOV),任何训练期间未见过的词(如新词或拼写错误)都会被标记为特殊的 [UNK] 符号,造成信息的彻底丢失 。更严重的是,它无法捕捉词形变化之间的关联,模型会将 “dog” 和 “dogs” 视为完全独立的实体 。
字符级分词
字符级分词将每个字符视为一个标记。这种方法彻底解决了 OOV 问题,因为任何单词都可以通过字符重组而成,且词表极小 。然而,字符级分词导致输入序列变得极长,大幅增加了计算负担 。在 Transformer 架构中,自注意力机制的计算复杂度与序列长度呈平方关系,过长的序列会迅速耗尽显存并降低推理速度 。此外,单个字符(如 ‘a’ 或 ‘t’)本身往往缺乏足够的语义信息,迫使模型在极长的序列中捕捉长程依赖关系,从而增加了训练难度 。
子词分词
子词分词技术(Subword Tokenization)是目前 LLM 的行业标准,它在单词和字符之间找到了最佳折中方案 。其基本逻辑是将高频出现的词汇保持完整,而将低频或复杂的词汇拆分为更常见的子单位。例如,”unhappiness” 可能被拆分为 “un”、”happi” 和 “ness” 。这种方法极大地缩小了词表规模(通常为 3k 到 100k),同时几乎消除了 [UNK] 标记,因为模型总是可以回退到字符级别来表示罕见词 。
常用分词算法
BPE (Byte Pair Encoding)算法
BPE 最初是一种数据压缩算法,后来被引入 NLP 领域用于构建动态词表。其工作原理是贪婪地迭代合并语料库中最频繁出现的相邻字符对或子词对 。BPE 的训练过程是自底向上的:它从字符表开始,不断进行合并操作,直到达到预设的词表大小。在推理阶段,BPE 采用最大匹配策略对文本进行切分 。BPE 的主要优势在于其确定性和高效性,且在处理英语等具有清晰形态结构的语言时表现出色,它是 GPT 系列和 Llama 模型的核心技术 。
运作流程:
- 初始化: 将文本拆分为单个字符(例如:
h, u, g, g, i, n, g)。 - 统计频率: 扫描整个语料库,统计所有相邻单位对的出现次数。
- 合并: 找出频率最高的组合(假设是
g和g),将它们合并为一个新词元gg。 - 循环: 重复此过程,直到达到预设的词表大小(例如 50,000 个词元)。
优点: 能够处理词形变化(如
look,looking,looked共享共同的词根look),有效缩小词表规模。缺点: 无法处理“从未见过”的字符(OOV问题)。如果在训练语料里没见过希腊字母 $\alpha$,模型就彻底“瞎”了。
BBPE(Byte-level BPE)
为了解决 BPE 无法处理生僻字符的问题,GPT-2 开始引入了 BBPE。这是目前 Llama、GPT-4 等主流模型都在使用的技术。
核心改进:
BBPE 不再从“字符”开始合并,而是从 “字节(Bytes)” 开始合并。例如,“中国”的“中”先转换成UTF-8字节’\xe4\xb8\xad’,然后进行合并。
- 万物皆字节: 任何文字(中文、英文、Emoji)在计算机底层都是由 UTF-8 编码的字节组成的。
- 基础词表极小: 无论哪种语言,基础单位只有 256 个字节(0-255)。BBPE 的初始词表就是这 256 个字节。
- 强制合并: 即使是一个非常生僻的中文汉字,在 BBPE 眼中也只是 3 个字节。模型可以通过合并这些字节来表示它,而不会出现“无法识别”的情况。
无分词器(Token-free)架构的兴起
鉴于分词器带来的种种局限,学术界正在积极探索摆脱离散标记、直接处理字节流或字符流的端到端架构。
字节潜空间变换器 (Byte Latent Transformer, BLT)
BLT 是一种革命性的架构,它完全取消了固定词表的分词过程。BLT 通过一个轻量级的局部编码器将原始字节流动态地组合成“补丁”(Patches),然后在补丁级别进行全局计算 。
- 动态计算分配: BLT 根据文本输入的复杂度(基于熵的预测)动态调整计算资源的分配。对于复杂的字符组合分配更多计算力,而对于可预测的简单片段则减少计算量 。
- 跨语言公平性: 由于直接操作字节,BLT 消除了针对不同语言的词表偏见,实现了真正意义上的语言中立 。
- 噪声鲁棒性: 在处理拼写错误和字符变体时,BLT 表现出远超传统分词模型的稳定性 。
状态空间模型与 MambaByte
传统的 Transformer 难以直接处理字节序列,因为字节序列极长,会触及计算复杂度的天花板。然而,具有线性计算复杂度的状态空间模型(SSM,如 Mamba)为字节级语言建模提供了可能 。MambaByte 等模型证明了在不需要分词器的情况下,模型依然可以达到甚至超过子词级 Transformer 的困惑度水平,同时保持极高的推理效率 。