好的,我们来详细讲解 BPE Tokenizer。它是现代自然语言处理(尤其是像GPT系列这样的模型)的基石之一。
一、核心思想:解决词典的“大小-覆盖度”两难问题 #
在传统分词方法中,我们面临一个两难选择:
- 词级词典:词典巨大,且无法处理未见词(Out-of-Vocabulary, OOV)。
- 例如,如果词典里有“猫”、“老鼠”,但没有“猫抓老鼠”,模型就无法理解这个常见的短语。
- 字符级词典:词典极小(比如英文只有26个字母),能表示任何词,但单个token信息量少,序列过长,训练和推理效率低。
- 例如,“apple”需要5个token
a, p, p, l, e来表示,丢失了“app”等子词的含义。
- 例如,“apple”需要5个token
BPE的解决方案:取二者之长。它基于子词进行分词,让最常见的词或词组作为一个独立的token,而将不常见的词拆分成更有意义的子词甚至字符。
二、BPE是什么? #
BPE 是一种数据压缩算法,在2015年被引入到NLP领域用于分词。它的名字就揭示了其工作原理:
- Byte Pair: 最初指的是在压缩数据中寻找最常相邻出现的字节对。
- Encoding: 用一个新的、未使用的字节来替换这个频繁出现的字节对。
在NLP中,我们把“字节”换成“字符”或“子词单元”。
三、BPE分词器的工作流程 #
BPE分词器的工作分为两个独立的阶段:1. 训练 和 2. 编码。
阶段一:训练 - 从文本中学习合并规则 #
目标是基于一个训练语料库,构建一个合并规则列表。
假设我们有以下训练数据(已经过初步归一化,如小写化):
"low lower newest widest"
初始化:
- 将每个词分割成字符,并在词尾添加一个特殊的结束符
(表示单词边界)。 - 初始词汇表是所有基础字符的集合。
初始词典:l o w </w> l o w e r </w> n e w e s t </w> w i d e s t </w>{‘l’, ‘o’, ‘w’, ‘e’, ‘r’, ‘n’, ‘s’, ‘t’, ‘w’, ‘i’, ‘d’, ‘</w>’}
- 将每个词分割成字符,并在词尾添加一个特殊的结束符
迭代合并:
- 统计所有相邻的符号对(byte pairs)的频率。
- 选择出现频率最高的符号对。
- 合并这个符号对,创建一个新的符号,并将其加入到词汇表中。
- 重复此过程,直到达到预定的合并次数(或词汇表大小)。
迭代过程演示:
迭代1: 最频繁的字节对是
(e, s),出现了2次(在newest和widest中)。合并它们,创建新符号es。l o w </w> l o w e r </w> n e w es t </w> # newest -> n e w e s t -> n e w es t w i d es t </w> # widest -> w i d e s t -> w i d es t词汇表更新:
{..., ‘es’}迭代2: 最频繁的字节对现在是
(es, t),出现了2次。合并它们,创建新符号est。l o w </w> l o w e r </w> n e w est </w> # newest -> n e w es t -> n e w est w i d est </w> # widest -> w i d es t -> w i d est词汇表更新:
{..., ‘est’}迭代3: 最频繁的字节对是
(l, o),出现了2次。合并它们,创建新符号lo。(但注意,它在两个词里都是“low”的一部分)lo w </w> lo w e r </w> n e w est </w> w i d est </w>词汇表更新:
{..., ‘lo’}迭代4: 最频繁的字节对是
(lo, w),出现了2次。合并它们,创建新符号low。low </w> low e r </w> n e w est </w> w i d est </w>词汇表更新:
{..., ‘low’}
... 如此继续,我们还可以合并 (low, </w>),(n, e) 等等。
最终,我们得到的合并规则列表(按合并顺序排列)是:
e s -> eses t -> estl o -> lolo w -> lowlow </w> -> low</w>n e -> ne- ...
这个合并规则列表和最终的词汇表就是训练好的BPE模型。
阶段二:编码 - 对新文本进行分词 #
现在,我们有一串新的文本,比如 "lowering the widest landscape",如何用训练好的模型对它分词?
- 预处理:将词按空格分开,并添加
。"lowering </w> the </w> widest </w> landscape </w>" - 按字符分割:
“l o w e r i n g </w>”, “t h e </w>”, “w i d e s t </w>”, “l a n d s c a p e </w>” 应用合并规则:
- 我们遍历在训练阶段学到的合并规则列表(按顺序!)。
- 对于第一个词
l o w e r i n g </w>:- 查看规则1 (
e s -> es),不适用。 - 查看规则2 (
es t -> est),不适用。 - 查看规则3 (
l o -> lo),适用!合并:lo w e r i n g </w> - 查看规则4 (
lo w -> low),适用!合并:low e r i n g </w> - 查看规则5 (
low </w> -> low</w>),不适用,因为后面是e不是</w>。 - ... 继续应用后续规则,直到没有规则可以应用。
- 查看规则1 (
- 最终,
“lowering”可能被分词为[‘low’, ‘e’, ‘r’, ‘i’, ‘n’, ‘g’]或类似的子词。
输出:将每个词应用规则后得到的子词序列作为最终的tokens。
四、BPE的优势与特点 #
- 平衡了效率与覆盖度:在常见的词(如“the", "ing”)和罕见的词(如“Transformer”)之间取得了很好的平衡。
- 有效处理未知词:即使遇到没见过的词,也能将其分解成已知的子词来理解。例如,模型没见过“ChatGPT”,但它认识“Chat”和“GPT”,就能很好地理解。
- 压缩率高:能有效地表示文本,序列长度介于词级和字符级之间。
- 语言无关性:不依赖于特定的语言规则(如空格),对黏着语(如土耳其语)、屈折语等都有效。
五、实际应用中的细节 #
- GPT系列:使用BPE的变体Byte-level BPE,以字节为基础,实现了真正意义上的“一个分词器处理所有文本”。
- SentencePiece:一个流行的开源库,实现了BPE以及类似算法(如Unigram LM),它的一个特点是不依赖预分词,直接将原始文本作为输入,对带空格的语言(如中文、日文)更友好。
- WordPiece:BERT使用的算法,与BPE类似,但在选择合并哪个符号对时,它不只看频率,而是看频率和概率的综合(似然值),选择能最大程度提高语言模型似然值的对。
总结 #
BPE Tokenizer 是一个通过迭代合并频繁共现的相邻符号,从而从字符开始自动构建一个子词词汇表的过程。它的核心在于训练阶段学到的合并规则,这使得它非常灵活和强大,成为当今大语言模型不可或缺的组成部分。