Tokenizer与分词技术
概述
分词(Tokenization)是将输入文本切分成一个个token(子词/词),然后映射为词表中索引的过程。它是大模型处理文本的第一步,直接影响模型对语言的理解能力,尤其是中文分词非常关键。
主流分词算法都是子词分词(Subword Tokenization):平衡词表大小和OOV问题,既不会词表太大,也能处理任何生僻词。
常见分词算法
1. BPE (Byte-Pair Encoding)
算法流程构建词表:
- 准备足够训练语料,设定期望词表大小;
- 初始化:将每个单词拆分成字符序列,每个字符作为初始token,统计单词频率;
- 合并:统计所有相邻字节对出现频率,将最高频的那一对合并成一个新的子词,加入词表;
- 重复步骤3,直到词表达到设定大小,或者下一个最高频字节对出现频率为1。
编码过程:
从左到右,尽可能合并成词表中最长的子词,即贪心最大匹配。
特点:
- 基于统计频率合并,简单有效
- 可以处理任何词,即使未见过也能拆成已知子词
- GPT-2、LLaMA、BART都使用BPE
2. WordPiece
WordPiece本质上还是BPE的思想,区别只在于如何选择合并对:
- BPE:选择频次最高的相邻子词合并
- WordPiece:选择能让语言模型似然概率增益最大的相邻子词合并
特点:
- 考虑了语言模型概率,理论上更优
- 实践中和BPE差别不大
- BERT使用WordPiece
3. BBPE (Byte-level BPE)
GPT-2提出的改进,直接在字节层面做BPE,而不是字符层面:
- 好处:不需要预先设置词典,任何UTF-8字符都能处理,包括生僻字、标点、 emoji 都能处理,不会有OOV
- 所有字节都在初始词表中(256个基本token),然后逐步合并
- 缺点:对中文来说,一个UTF-8汉字占3字节,容易切得太碎
GPT-2、GPT-3、LLaMA都使用BBPE。
4. SentencePiece
SentencePiece是Google提出的一个分词库,核心特点:
- 把空格也当作一种特殊字符来处理,直接对原始纯文本训练,不需要预先分词
- 支持BPE和unigram两种算法
- 对各种语言尤其是没有明确分词边界的语言(如中文、日语)更友好
- 不需要依赖预分词,端到端训练
应用:ChatGLM、BLOOM、PaLM都使用SentencePiece。
不同分词算法对比
| 算法 | 合并依据 | 训练粒度 | OOV处理 | 代表模型 |
|---|
| BPE | 频率最高 | 字符级 | 可拆分为子词,无严格OOV | GPT-2, LLaMA |
| WordPiece | 似然增益最大 | 字符级 | 同上 | BERT |
| BBPE | 频率最高 | 字节级 | 天然无OOV | GPT-2, LLaMA |
| SentencePiece | 支持BPE/unigram | 直接对原始文本 | 天然支持多语言 | ChatGLM, BLOOM, PaLM |
中文分词的特殊问题
中文和英文不同:中文没有天然的分词空格,一个汉字就是一个字符,所以分词结果对中文理解影响很大。
不同模型中文分词效果对比
| 模型 | 词表大小 | 中文平均token数/汉字 | 特点 |
|---|
| LLaMA | 32000 (原) | ~1.45 | 中文分词碎,一个汉字常分成2+个token,编码效率低 |
| Chinese-LLaMA 扩词表 | 40000+-50000+ | ~1.1-1.2 | 扩展中文词表后,效率显著提升 |
| ChatGLM-6B | 130528 | ~1.12 | 平衡中英文最好,中文分词效率较高 |
| BLOOM | 250880 | ~1.13 | 多语种词表,中文分词效率和ChatGLM差不多 |
主要结论:
- 原始LLaMA对中文不友好:中文分词非常碎,平均一个汉字要1.45个token,浪费了大量上下文空间,同一个上下文能放的汉字变少
- 扩展中文词表能显著改善:增加中文高频词到词表后,平均token数显著下降,编码效率提升
- ChatGLM做了更好的中文优化:词表设计对中文更友好,平衡中英文效果最好
Tokenizer训练流程
- 数据收集:收集大规模覆盖目标语言/领域的语料
- 预处理:根据算法需要做预处理(SentencePiece不需要预分词,直接用原始文本)
- 训练:按算法流程学习合并规则/词表
- BPE/WordPiece:逐步合并得到最终词表
- Unigram:从大词表开始裁剪掉概率低的token,得到最终词表
- 保存:保存词表和合并规则,供编码解码使用
面试常见问题
1. 为什么要用子词分词,不用词级或字符级?
回答要点:
- 词级:词表太大,参数太多,而且OOV问题严重,遇到没见过的词处理不了
- 字符级:词表很小(只有几百个字符),没有OOV,但每个词序列太长,计算效率低,模型难学习到语义
- 子词:平衡了两者,词表不大(通常几万),没有OOV(任何词都可以拆成子词),序列长度也不会太长,是最优折中
2. BPE和WordPiece的区别?
回答要点:
- 都是迭代合并的子词算法,核心思想相同
- BPE按频率最高合并相邻对,WordPiece按语言模型似然增益最大选择合并对
- BPE更简单,WordPiece理论上更优,实际差别不大
- GPT用BPE,BERT用WordPiece
3. 什么是BBPE,和原始BPE有什么区别?
回答要点:
- BBPE是Byte-level BPE,直接在字节层面做BPE,初始词表就是256个字节
- 原始BPE一般在字符层面做
- BBPE好处是天然支持任何UTF-8字符,包括生僻字、emoji,不会有OOV,不需要预先处理字符
- GPT-2开始都是BBPE
4. SentencePiece的优势是什么?
回答要点:
- 将空格当作普通字符处理,不需要预先分词,可以直接对原始文本训练
- 特别适合没有明确分词边界的语言,如中文、日语
- 支持BPE和Unigram多种算法,统一处理多语言
- 现在很多大模型(ChatGLM、BLOOM)都用它
5. 中文分词需要注意什么问题?
回答要点:
- 中文没有空格分词边界,需要特殊处理
- 原始BBPE对中文不友好,容易把一个汉字切成多个字节,浪费上下文空间
- 通过扩展中文词表或者用SentencePiece直接训练,可以显著提升中文分词效率
- 词表设计要平衡中英文,不能只照顾英文
6. 什么是OOV问题,子词分词怎么解决OOV?
回答要点:
- OOV就是Out-of-Vocabulary,指测试时遇到训练词表中没有的词
- 子词分词任何词都可以拆分成词表中已有的子词拼接表示,所以不会有严格意义上的OOV
- 即使非常生僻的词,也能拆,所以能处理
7. 分词对大模型效果影响大吗?
回答要点:
- 影响很大,尤其对非英语语言
- 分词不好会导致:同一个上下文能放的有效字符少,编码浪费,模型理解差
- 中文分词不好会严重影响效果,很多开源大模型中文差就是因为分词问题
- 好的分词能提升编码效率,让相同上下文放下更多信息,效果更好