【空格的呼吸】基于BPE的Tokenizer 分词原理介绍
举个例子,本人在大模型分词时有遇到下面的现象,感到疑惑。
对同一个符号,有时候,空格的存在与否,会导致分词结果不一致。
1 |
|
结果输出
1 |
|
根据Qwen2
/tokenization_note_zh.md,这里也提到了类似的现象:
一段文本在不同的上下文下可能会有不同的tokenize结果
- 疑惑1:这些奇怪的字符是什么?
- 疑惑2:为什么空格会影响分词结果?
接下来,就让我们解析tokenizer做了些什么。
基础tokenizer分词流程
原理描述
初步分割
- 使用正则表达式对输入文本进行分割
- 分割依据:标点符号、字母数字、缩写词、换行符、空白符等
- 特点:所有分割符号(包括标点、空白符)都会被保留
UTF-8编码转换
- 将分割后的片段转换为UTF-8编码
- ASCII字符(如英文字母)保持单字节编码
- 中文字符使用2-4字节编码
示例:
1 |
|
tokenizer内部的代码逻辑
- Unicode字符转换
- 将UTF-8字节序列映射为Unicode字符
- 每个字节对应特定的Unicode字符(如\xe7对应ç)
1 |
|
上述代码在tokenizer中,是基于字节序列转Unicode字符,然后进行BPE算法处理。
4. BPE算法处理
训练阶段:
- 统计字符对出现频次
- 按频次高低逐步合并字符
- 将合并规则保存至merges.txt
推理阶段:
- 生成输入文本的unicode字符串的bigram组合
- 按merges.txt中的优先级顺序进行合并
- 循环处理直至无可合并字符对
空格影响分析:以”◎”符号为例
带空格情况(” ◎”)
- Unicode序列:”ĠâĹİ”
- 分词过程:
- 初始bigram:
Ġâ
,âĹ
,Ĺİ
- 按优先级合并:
Ġâ
→ĠâĹ
+İ
- 初始bigram:
- 最终结果:
['ĠâĹ', 'İ']
无空格情况(”◎”)
- Unicode序列:”âĹİ”
- 分词过程:
- 直接合并:
âĹ
+İ
→âĹİ
- 直接合并:
- 最终结果:
[âĹİ]
分词好之后,会根据vocab.json字典,将这些token子词转换成id
结论
空格的存在会显著影响分词结果,即使是同一个符号:
- 有空格:分词为两个token
- 无空格:保持为单个token
这种差异提醒我们在文本预处理时需要特别注意空格的处理,以确保分词的一致性和准确性。
<特别提示>
如果想对词表进行扩展,在qwen系列种不适用,因为它是基于BPE算法进行训练的。所以,就算我们往词表里加入新词(例如:”piggrain”,我家狗的名字)。但是由于merges.txt中如果没有任意子词的组合能组合成piggrain,所以,新词也仍然会被切割,并不会保留这个完整的词。
【空格的呼吸】基于BPE的Tokenizer 分词原理介绍
https://abigail61.github.io/2025/01/09/空格的呼吸/