大型语言模型(LLM)所使用的 tokenizer 主要差异来自于子词切分算法(BPE、WordPiece、SentencePiece 等)、多语言支持程度、词表规模及 OOV(未登录词)处理方式。选型时需要综合考虑模型用途、多语言需求及算力限制。
以下是一篇从原理到实践的完整解析文章,涵盖了常见 tokenizer 的实现细节与使用示例。
一、引言
在自然语言处理任务中,tokenizer(分词器)是将文本切分为模型可识别的小单元(token)的重要步骤。它直接影响到模型对文本信息的理解和表达能力。不同的大模型如 GPT-3、BERT、T5 等,往往采用各自定制的 tokenizer,这背后不仅是算法实现层面的差异,也包含了对语料、多语言支持、词表大小等多种因素的考量。
二、常见的子词切分算法
1. Byte Pair Encoding (BPE)
原理概述
- 核心思路:BPE 最初来自数据压缩领域,通过反复统计高频的字符或子词对并将其合并,从而减少分词后的总数。
- 优点:在高频词或字符组合出现时,能最大化地将其合并为一个 token,既减少 OOV 问题,也可适度压缩词表规模。
- 缺点:对极低频的词汇可能会切分得比较细碎,需要平衡词表大小与表示能力。
示例
- 示例过程:假设我们有一段英文短语 “low lower newest widest”,在 BPE 训练初始阶段,所有单词会被视为字符序列:
- “l o w”
- “l o w e r”
- “n e w e s t”
- “w i d e s t”
- BPE 会统计所有子序列的出现频率,并先合并最频繁的子序列。例如若 “l o” 最常见,就优先把 “l o” 合并为一个符号 “lo”。随着迭代继续,可能会把 “lo w” 也合并成一个 token。最终会根据预设的词表大小,停在合并次数满足阈值的位置。
代表模型
- GPT 系列(GPT-2、GPT-3、GPT-4)
- RoBERTa 等
2. WordPiece
原理概述
- 与 BPE 类似之处:WordPiece 也是一种子词切分算法,核心思路与 BPE 十分相似,都通过统计子词频率来不断合并高频子词单元。
- 不同之处:在选择合并策略时,WordPiece 更偏重保证高频词的整体性,对较高频词的切分往往会保持不变,让模型更好地学习特定常见词汇的含义。
示例
- 假设英文短语 “playing basketball quickly”,WordPiece 初始同样会按字符或最小子词拆分,再逐步合并:
- “p l a y i n g”
- “b a s k e t b a l l”
- “q u i c k l y”
- 假如合并统计发现 “basket” 出现频率特别高,就会优先保留 “basket” 作为完整的一个子词单元,而不是再拆分为 “b a s k e t”。
代表模型
- BERT 系列 (BERT-base, BERT-large, mBERT 等)
3. SentencePiece
原理概述
- 统一的文本处理:SentencePiece 不依赖空格或其它先验分词,能直接对原始文本进行子词建模。
- 支持多种算法:如 BPE 或 Unigram;后者会在训练时先为每一个子词分配一个概率,然后基于概率分配来不断迭代、优化子词集合。
- 优势:对于多语言或无空格语言(如中文)来说,SentencePiece 在预处理阶段十分灵活,可以更好地处理不同语言的混合数据。
示例
- 当处理中文短语 “我喜欢吃苹果” 时:
- SentencePiece 可能先将其当做一个完整字符串,并在训练中自动学习如何切分常见的字符组合,可能最终形成的 token 序列是 [“▁我”, “喜欢”, “吃”, “苹果”] 或更细碎一些的切分。
- 在多语言场景下,也可以将其他语言的语料合并进来,统一构建子词表,让同一 tokenizer 同时适用于中英、法德等多种语言。
代表模型
- T5
- XLM 系列
- XLNet(部分版本)
三、对中文与多语言场景的特殊考量
1. 中文处理的难点
中文没有空格作为词与词之间的天然分隔,单个汉字本身也可能是一个完整语义单位。若切分过细,模型负担增加;切分过粗则可能丢失字与字的组合含义。
2. 多语言统一词表
若需要同时处理多种语言,最好采用一个统一的 tokenizer(如 SentencePiece),保证不同语言间的通用子词(如数字、特定符号等)能被模型共享,从而提升跨语言迁移能力。
四、词表规模与 OOV 问题
1. 词表规模(Vocabulary Size)
- GPT-2 约 50,000 个 subwords;
- BERT 英文版常用 30,000 词表;
- mBERT(多语言 BERT)词表规模更大,以适应多种语言。
2. OOV(未登录词)处理
- 子词算法的好处在于,即便遇到新词,也能拆分成若干已存在的子词,最大程度减少 [UNK] 的出现。
- 例如,在中文里遇到极冷僻的专有名词,仍可将其切分为常见的汉字或字母子词进行组合。
五、实践与使用示例
在实际项目中,为了让读者更直观地感受不同 tokenizer 的区别,我们可以用一个简单的 Python 示例(以 SentencePiece 为例,BPE 或 WordPiece 也有类似的第三方库):
import sentencepiece as spm
# 假设我们有一个很小规模的中文语料示例 corpus.txt
# 文件内容:
# 我喜欢吃苹果
# 他喜欢读书
# 今天天气不错
# 1. 训练 SentencePiece 模型
spm.SentencePieceTrainer.Train('--input=corpus.txt --model_prefix=example_sp --vocab_size=50')
# 2. 加载模型
sp = spm.SentencePieceProcessor()
sp.Load("example_sp.model")
# 3. 测试切分
input_text = "我非常喜欢吃红苹果"
tokens = sp.EncodeAsPieces(input_text)
print(tokens)
- 当我们执行完上述代码后,SentencePiece 会根据训练好的 50 大小词表切分“我非常喜欢吃红苹果”
- 最终可能输出如下结果(视具体模型而定):
['▁我', '非常', '喜欢', '吃', '红', '苹果']
这里的 “▁” 代表空格或段落起始的占位符,实际在多语言、不同文本环境中会根据算法自动学习切分。
六、选型建议
-
针对单语言(尤其是英语)场景
- BPE、WordPiece 均可胜任。若开发团队对 BERT 生态更熟悉,可直接使用 Hugging Face 提供的 WordPiece tokenizer;若与 GPT-系列兼容,可选择 BPE。
-
针对多语言场景
- 优先推荐 SentencePiece。它可统一不同语言的子词表,也支持 BPE 与 Unigram 等多种模式,在跨语言任务中效果更佳。
-
考虑算力与需求
- 词表越大,模型在推理时的计算量越高;词表越小,切分会变得过细,可能导致句子长度暴涨,从而拖累训练或推理速度。需要在二者之间做平衡。
七、结论
对于大型语言模型(LLM)而言,tokenizer 的选择和设计绝非一时兴起,而是对下游任务需求、语言类型、数据规模以及硬件算力等多方因素的综合权衡。子词切分算法(BPE、WordPiece、SentencePiece 等)之所以成为主流,是因为它们能巧妙地兼顾模型的泛化能力与对稀有词的处理,极大地降低 OOV 问题。此外,在多语言场景下,统一的 tokenizer 能够让模型同时学习多语种的子词表达,进一步挖掘跨语言知识迁移的潜力。
希望通过这篇文章,您对不同大模型使用的 tokenizer 设计、算法思路以及实践要点有了更加深入、具体的了解。无论是 BPE、WordPiece 还是 SentencePiece,都各有适用场景与独到之处,关键在于根据项目需求和资源约束做出最优选择。