2025年4月22日

Tokenizer 体系 — BPE / SentencePiece / Unigram / Chat Template

Tokenizer 是将原始文本转换为模型可处理整数序列的桥梁,其设计直接决定模型的词汇覆盖度、多语言能力和推理效率。

知识库大模型基础原理llmpretrainingtokenizerBPEsentencepiece

先说结论

Tokenizer 是将原始文本转换为模型可处理整数序列的桥梁,其设计直接决定模型的词汇覆盖度、多语言能力和推理效率。

为什么我会单独记这一篇

原始文本是连续的字符流,神经网络只能接受离散的数值输入。Tokenizer 解决的核心问题是:如何将文本切分为有限词表中的子词单元(subword),使得任意输入文本都能被表示,同时控制词表大小在合理范围内。一个好的 tokenizer 需要同时平衡以下需求:

  1. 覆盖度:任何语言的任何文本都能被编码,不出现 UNK(unknown token)
  2. 紧凑性:编码后的 token 数量尽可能少,减少序列长度和推理开销
  3. 语义性:切分结果与语言直觉一致,"un-""break-""able" 比 "unb""rea""kable" 好
  4. 效率:编码和解码速度快,支持大规模数据预处理
  5. 多语言公平性:不应对某一语言系统性分配过多 token

如果 tokenizer 设计失误,下游影响极其深远:非英语文本编码膨胀 5-10 倍、训练效率下降、推理成本翻倍,且无法通过后训练弥补。

先把核心脉络捋清楚

Byte Pair Encoding (BPE)

  • 由 Sennrich et al. (2016) 引入 NMT 领域,源自数据压缩算法
  • 训练过程
    1. 将语料以字符级(或字节级)初始化为初始词表
    2. 统计所有相邻 token 对的共现频率
    3. 合并频率最高的 token 对,加入词表
    4. 重复步骤 2-3 直到词表达到目标大小
  • 编码过程:对输入文本贪心地从左到右应用学到的合并规则
  • 确定性:BPE 的分词结果是确定性的(给定合并规则序列)
  • 代表模型:GPT-2/3/4、RoBERTa、Claude、Qwen(部分版本)

Byte-level BPE (BBPE)

  • GPT-2 引入的改进:以 256 个字节作为基础词表,而非 Unicode 字符
  • 好处:天然覆盖所有语言、不会产生 UNK
  • 缺点:字节级基础词表导致初始序列较长,需要更多合并步才能收敛到合理词表

Unigram Language Model

  • 由 Kudo (2018) 提出,是 SentencePiece 框架的两种核心算法之一
  • 训练过程(与 BPE 方向相反):
    1. 从一个巨大的候选词表开始(如所有子串或高频子串)
    2. 用期望最大化(EM)算法估计每个子串的概率
    3. 计算移除每个子串后对整体损失的影响
    4. 移除对损失影响最小的子串(贪心剪枝)
    5. 重复直到词表缩减到目标大小
  • 编码过程:给定输入,用 Viterbi 算法找到概率最大的分词路径
  • 概率性:同一个字符串可能有多种合法分词,Unigram 自然支持这种多义性
  • 代表模型:T5、ALBERT、mT5、Gemma

SentencePiece

  • Google 开发的语言无关 tokenizer 工具包
  • 核心设计理念:将输入视为原始字节流,不依赖任何语言特定的预分词(不需要空格切分)
  • 同时支持 BPE 和 Unigram 两种算法
  • 空格也用特殊字符(▁, U+2581)编码,实现了真正的语言无关性
  • 代表模型:LLaMA(SentencePiece BPE)、T5(SentencePiece Unigram)

WordPiece

  • Google 为 BERT 开发的算法,类似 BPE 但使用互信息(PMI)而非频率来选择合并对
  • 合并的是使得 P(AB) / (P(A) * P(B)) 最大的对
  • BERT、DistilBERT 使用此算法

Chat Template

  • HuggingFace 提出的标准化机制,用 Jinja2 模板定义对话消息到模型输入格式的映射
  • 存储在 tokenizer_config.jsonchat_template 字段中
  • 通过 tokenizer.apply_chat_template(messages) 调用
  • 不同模型的格式差异
模型 特殊 Token 格式示例
LLaMA 2 [INST], <<SYS>> [INST] <<SYS>>\n{system}\n<</SYS>>\n{user} [/INST]
LLaMA 3 `< begin_of_text
ChatML (Qwen) `< im_start
Mistral [INST], [/INST] 类似 LLaMA 2

原理拆开看

BPE 的信息论直觉

BPE 本质上在做 贪心熵减:每次合并最高频的 token 对,相当于用一个新符号替代两个频繁共现的符号,从而减少描述语料所需的总比特数。这与经典的数据压缩(如 gzip 使用的 LZ77)在精神上一致。

Unigram 的概率直觉

Unigram 假设每个子串的出现服从 unigram 语言模型,即 P(text) = ∏ P(subword_i)。训练目标是最大化语料的边际似然。剪枝的标准是:移除哪个 token 对似然的损害最小。这保证了保留在词表中的 token 都是"信息量最大"的。

为何子词而非字符或整词?

  • 整词:词表爆炸(尤其 agglutinative 语言如土耳其语),OOV 问题严重
  • 字符:序列过长,模型难以学习长距离依赖
  • 子词:高频词保留完整,低频词拆成有意义的片段,是两者的最优折中

放到工程里怎么落

  1. 语料采样:从训练数据中采样代表性子集(通常 5M-50M 文档)
  2. 归一化:NFKC Unicode 归一化、小写化(取决于设计决策)
  3. 预分词(BPE 系列):用正则(如 GPT-2 的 's|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+)预切分
  4. 训练:运行 BPE/Unigram 算法至目标词表大小
  5. 人工审核:检查高频 token 是否合理,多语言覆盖是否均衡
  6. 集成:将 tokenizer 与模型绑定,确保训练和推理使用完全相同的分词逻辑

工具链:tiktoken(OpenAI)、tokenizers(HuggingFace)、sentencepiece(Google)、sentencepiece_pb2(protobuf 接口)

与相邻概念的区别

概念 区别
预训练目标 Tokenizer 在预训练之前,决定输入表示;预训练目标决定输出目标
Word Segmentation(中文分词) 中文分词是语言学驱动的整词切分;子词 tokenization 是统计驱动的,不追求语言学正确性
Embedding Layer Tokenizer 输出 token ID,Embedding 层将 ID 映射为向量;两者串联但职责不同

设计时真正要权衡什么

取舍 分析
词表大小 太小 → 序列过长、推理慢;太大 → Embedding 矩阵内存开销大、稀疏 token 学不好
字节级 vs 字符级基础 字节级保证覆盖所有语言但初始编码长;字符级对 ASCII 高效但 Unicode 处理复杂
BPE vs Unigram BPE 简单快速但分词固定;Unigram 概率化但训练慢
预分词规则 GPT-2 的正则预分词决定了数字、标点、空格的处理方式,影响深远且无法后期更改
多语言词表配比 英文过多 → 其他语言编码膨胀;需精心设计采样比例

容易踩的坑

  1. 多语言编码膨胀:GPT-4 的 cl100k 对中文编码效率比英文低 3-5 倍,导致推理成本剧增
  2. Chat Template 不匹配:用 LLaMA 2 模板格式化 LLaMA 3 模型的输入,导致模型输出混乱
  3. 词表残留问题:复用他人的 tokenizer 但其词表包含不需要的语言 token,浪费词表空间
  4. Tokenizer 训练数据偏差:只在英文数据上训练 tokenizer,导致日韩文等 CJK 语言全被切成单字节
  5. 特殊 token 冲突:自定义特殊 token 与已有 token ID 冲突,导致训练不稳定
  6. 忽略 eos_token / bos_token:不同模型对首尾 token 要求不同,遗漏会导致生成异常

工程落地时我会怎么做

  1. 永远用 apply_chat_template(),不要手工拼格式——模板细节极易出错
  2. 训练新 tokenizer 时,采样数据需与训练数据分布一致(包括多语言比例)
  3. 评估 tokenizer 质量:用 Fertility(每词平均 token 数)和 Parity(多语言公平性)指标
  4. 扩展词表时(如添加中文词到英文 tokenizer),用 embed resize 技术,新 token 的 embedding 用已有 token 的均值初始化
  5. 锁死 tokenizer 后不要再改——改 tokenizer 等于让所有已训练的 checkpoint 作废

如果要对外讲,可以怎么概括

Tokenizer 是 LLM 的"眼睛",它决定了模型如何"看到"世界。主流方法有 BPE(贪心合并高频对)和 Unigram(概率剪枝大词表),分别被 GPT 系列和 T5 系列采用。关键 trade-off 是词表大小 vs 编码效率。做错的后果很严重:多语言支持差会导致推理成本翻倍,且无法通过后训练修复。工程上,chat template 是连接 tokenizer 与对话格式的桥梁,必须与模型严格匹配。

最后记几条

  1. BPE 是自底向上(合并),Unigram 是自顶向下(剪枝)
  2. SentencePiece 是语言无关的,不需要预分词
  3. 词表大小通常在 32K-256K 之间,太大太小都有问题
  4. Chat Template 必须与模型匹配,用 apply_chat_template() 而非手拼
  5. Tokenizer 一旦确定就不可更改——它绑定在整个模型生命周期中

面试高频题

QUESTION BPE 和 WordPiece 的核心区别是什么?

特性 BPE WordPiece
合并标准 频率最高 互信息(PMI)最大
选择依据 P(AB)P(AB) 最大 P(AB)/(P(A)P(B))P(AB) / (P(A) \cdot P(B)) 最大
直觉 最常一起出现的对 最不应该被拆分的对
代表模型 GPT-2/3/4, LLaMA, Qwen BERT, DistilBERT

BPE 简单贪心合并高频对;WordPiece 更关注统计显著性,优先合并在语料中"关联度最高"的对。

QUESTION BPE 和 Unigram 的训练方向有什么不同? BPE 是自底向上:从字符级词表开始,逐步合并最高频对,词表从小到大。 Unigram 是自顶向下:从巨大候选词表开始,用 EM 算法估计概率,逐步剪枝对损失影响最小的子串,词表从大到小。

特性 BPE Unigram
方向 合并(自底向上) 剪枝(自顶向下)
编码确定性 确定(贪心合并规则) 概率性(Viterbi 最优路径)
多义性支持 不支持 自然支持
训练速度 慢(需 EM 迭代)
代表模型 GPT, LLaMA, Qwen T5, mT5, Gemma

QUESTION SentencePiece 解决了什么问题? 传统 tokenizer 依赖空格分词(如 BPE 在英文空格处预切分),这对中文、日文等不使用空格的语言不友好。SentencePiece 将输入视为原始字节流,空格也用特殊字符 (U+2581) 编码,实现了语言无关的分词。同时支持 BPE 和 Unigram 两种算法。

QUESTION 字节级 BPE (BBPE) 相比字符级 BPE 有什么优势? GPT-2 引入 BBPE,以 256 个字节为基础词表。优势:(1) 天然覆盖所有语言的字符,不会产生 UNK;(2) 无需 Unicode 归一化处理;(3) 词表完全固定。代价是初始编码序列较长,需要更多合并步才能达到合理词表。

QUESTION Tokenizer 对多语言支持有什么影响? 如果 tokenizer 训练数据以英文为主,非英语语言(尤其 CJK)会被切成更多、更短的 token,导致:

  1. 编码膨胀:同一段中文编码后的 token 数可能是英文的 3-5 倍
  2. 推理成本增加:更多 token = 更长序列 = 更多计算
  3. 信息密度降低:单个 token 携带的信息更少,影响模型对文本的理解

这也是为什么 Qwen、Yi 等国产模型会专门优化中文 token 覆盖。

QUESTION 为什么说 Tokenizer 一旦确定就不能改? Tokenizer 决定了模型的"词汇表"——每个 token ID 对应的子串。更改 tokenizer 意味着所有已训练的 embedding 和 output 权重都失效了。例如,如果将 "apple" 从 token #1234 改为两个 token "app" #5678 + "le" #9012,那么 token #1234 对应的 embedding 向量就不再有意义。因此 tokenizer 绑定在整个模型生命周期中,无法通过后训练修复。

主流模型 Tokenizer 配置

模型 算法 词表大小 工具 特点
GPT-4 BBPE ~100K tiktoken 字节级,多语言覆盖好
LLaMA 3 BPE 128K SentencePiece 大词表,多语言优化
Qwen 2 BBPE 151K tiktoken 中文覆盖优秀
BERT WordPiece 30K WordPiece 英文为主
T5 Unigram 32K SentencePiece 语言无关
Gemma Unigram 256K SentencePiece 超大词表

延伸阅读

参考资料

  • Sennrich et al., "Neural Machine Translation of Rare Words with Subword Units" (ACL 2016)
  • Kudo, "Subword Regularization: Improving Neural Network Translation Models with Multiple Subword Candidates" (ACL 2018)
  • Kudo & Richardson, "SentencePiece: A simple and language independent approach to neural network text generation" (ACL 2018)
  • HuggingFace Tokenizer Documentation: https://huggingface.co/docs/tokenizers/
  • OpenAI tiktoken: https://github.com/openai/tiktoken
  • Ahia et al., "Do All Languages Cost the Same? Tokenization in the Era of Commercial Language Models" (2023)