NLP-分词算法(一):Byte-Pair Encoding (BPE) / Byte-level BPE【BPE是char级别】

Byte-Pair Encoding (BPE) / Byte-level BPE

1、BPE

BPE,即字节对编码。其核心思想在于将最常出现的子词对合并,直到词汇表达到预定的大小时停止

  • 首先,它依赖于一种预分词器pretokenizer来完成初步的切分。pretokenizer可以是简单基于空格的,也可以是基于规则的;

  • 分词之后,统计每个词出现的频次,供后续计算使用。例如,我们统计到了5个词的词频

("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)

  • 建立基础词汇表,包括所有的字符,即:

["b", "g", "h", "n", "p", "s", "u"]

  • 根据规则,我们分别考察2-gram,3-gram的基本字符组合,把高频的ngram组合依次加入到词汇表中,直到词汇表达到预定大小停止。比如,我们计算出ug/un/hug三种组合出现频次分别为20,16和15,加入到词汇表中。
  • 最终词汇表的大小= 基础字符词汇表大小 + 合并串的数量,比如像GPT,它的词汇表大小 40478 = 478(基础字符) + 40000(merges)。添加完后,我们词汇表变成:

["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"]

实际使用中,如果遇到未知字符用<unk>代表。

2、Byte-level BPE

BPE的一个问题是,如果遇到了unicode,基本字符集可能会很大。一种处理方法是我们以一个字节为一种“字符”,不管实际字符集用了几个字节来表示一个字符。这样的话,基础字符集的大小就锁定在了256

例如,像GPT-2的词汇表大小为50257 = 256 + <EOS> + 50000 mergers,<EOS>是句子结尾的特殊标记。

BPE、WordPiece和SentencePiece - 简书


简单的说BBPE比BPE有更多的粒度选择,BPE是char级别。


1. BPE 算法的原理

BPE 算法的主要思想是将输入的文本进行多轮迭代的分段和统计,每次迭代都会找到出现频率最高的相邻字符或子词序列,并将其合并成一个新的符号(或单词)。在整个过程中,所有出现过的字符和新合并出的子词都被保存在一个词汇表中。

下面,我们将从以下几个方面对 BPE 算法的原理进行详细阐述:

  • 如何定义频率?
  • 如何生成初始的词汇表?
  • 如何进行迭代合并?
  • 如何使用 BPE 对文本进行编码和解码?

1.1. 如何定义频率?

在 BPE 算法中,频率的定义非常重要。具体来说,频率需要考虑字符(单字母)和子词(多个字母组成的词)两个方面。

对于字符而言,我们可以使用它在输入文本中出现的次数作为其频率。例如,如果字符“a”在输入文本中出现了 10 次,那么我们就认为该字符的频率为 10。

对于子词而言,频率的定义则需要考虑其实际出现的次数和合并次数两个因素。具体来说,如果一个子词出现了一次,则它的频率为 1;如果一个子词被合并了 k 次,则它的频率需要乘以 2^k。这是因为 BPE 算法中每次合并时都会将原来出现的两个子词用新的合并后的子词替换,这样会导致原来的子词从输入中消失,而新的子词的频率则要加上原来的两个子词的频率之和。

1.2. 如何生成初始的词汇表?

BPE 算法的初始词汇表通常由输入文本中的所有字符组成。如果某个字符在输入文本中没有出现过,那么它不应该加入初始词汇表。在实际应用中,我们通常会额外添加一些特殊的字符,例如空格、句点、问号等,以便在后续操作中更方便地进行处理。

1.3. 如何进行迭代合并?

在 BPE 算法的每次迭代中,我们会选择出现频率最高的相邻字符或子词,将它们合并成一个新的符号(或单词),并将这个新的符号加入到词汇表中。这个过程一直持续到达到指定的词汇表大小为止。

具体来说,BPE 算法的迭代过程通常包括以下几步:

  1. 计算每对相邻字符或子词的频率;
  2. 找到出现频率最高的相邻字符或子词,并将它们合并成一个新的符号;
  3. 在词汇表中添加这个新的符号;
  4. 更新输入文本中的所有相邻字符或子词,用新的符号替换它们;
  5. 重新计算各对相邻字符或子词的频率,回到步骤 2。

在 BPE 算法中,相邻字符或子词的选择是基于前缀和后缀的组合。例如,“app”和“le”可以组成“apple”,“p”和“i”可以组成“pi”,等等。在每一轮迭代中,我们按照从左到右、从上到下的顺序遍历输入文本,找到出现频率最高的相邻字符或子词,然后进行合并。由于更新后的文本中出现的新的相邻字符或子词可能也会成为下一轮迭代中的候选,因此我们需要反复迭代,直到达到指定的词汇表大小为止。

1.4. 如何使用 BPE 对文本进行编码和解码?

BPE 算法的最终目的是生成一个包含所有输入文本中出现的字符和子词的词汇表。在使用 BPE 对文本进行编码和解码时,我们通常会根据生成的词汇表将输入文本分割成最小的可处理单位,称为 subword(子词)。

在对文本进行编码时,我们可以将每个子词编码成它在词汇表中的索引。如果某个子词不在词汇表中,我们可以将它拆分成更小的子词,并将它们分别编码。编码后的结果通常是一个由整数构成的序列。

在对文本进行解码时,我们可以根据词汇表中的索引将每个子词解码成对应的字符串,并将它们拼接起来得到原始文本。如果某个子词无法解码,我们可以尝试将它拆分成更小的子词,并将它们分别解码。

ChatGpt都这么火了,它使用的 BPE 分词算法要不要了解一下?_bpe分词原理_大家都说我身材好的博客-CSDN博客


我最近在打机器翻译的一个比赛,主要使用基于BERT的模型。在这其中,一个小的知识点引起了我的好奇,就是在将英语训练语料输入到BERT模型之前,需要对其进行「BPE」(Byte Pair Encoding)的操作。作为致力于成为一名合格算法工程师的程序员,当然是要搞清楚其中的原理啦~本篇文章就带大家一起快速搞懂BPE分词算法。

本文主要分成两个部分,内容1500字,阅读耗时大约8分钟:

  • BPE分词算法的由来
  • BPE分词算法的流程
    • 词表构建
    • 语料编码
    • 语料解码

BPE分词算法的由来

BPE算法[1],其目的是「使用一些子词来编码数据」。该方法已经成为了BERT等模型标准的数据预处理处理方式。

在机器翻译领域,模型训练之前一个很重要的步骤就是「构建词表」。对于英文语料,一个很自然的想法就是用训练语料中出现过的「所有英语单词」来构建词表,但是这样的方法存在两个问题:

  • 训练语料中出现过的单词数目很多,这样的构造方式会使得词表变得很大,从而降低训练速度;
  • 在模型测试中,很难处理罕见词或者训练过程中没有见过的词(OOV问题)。

另外一种方式是使用单个「字符」来构建词表。英文字符的个数是有限的,基于字符的方式可以有效缓解词表数目过大以及OOV的问题,但由于其粒度太细,丢失了很多单词本身所具有的语意信息。

为了解决上述问题,基于Subword(子词)的算法被提出,其中的代表就是BPE算法,「BPE算法的分词粒度处于单词级别和字符级别之间」。比如说单词"looked"和"looking"会被划分为"look","ed”,"ing",这样在降低词表大小的同时也能学到词的语意信息。

BPE分词算法的流程

BPE算法的核心主要分成三个部分:

  • 词表构建
  • 语料编码
  • 语料解码

词表构建

词表构建是BPE算法的核心,其是「根据训练语料」来构建BPE算法的词表。算法的整体步骤如下所示:

  1. 准备模型的训练语料
  2. 确定「期望的词表大小」
  3. 将训练语料中的所有单词拆分为字符序列,利用这些字符序列构建初始的词表
  4. 统计训练语料中每一个连续字节对出现的频率,「选择出现频率最高的字节对合并成新的subword,并更新词表」
  5. 重复第4步,直到词表大小达到我们设定的期望或者剩下的字节对出现频率最高为1

下面我们通过一个例子来搞懂BPE词表构建的过程。假设我们目前的训练语料中出现过的单词如下,我们构建初始词表:

值得注意的是,我们在每一个单词的后面都加入了一个新的字符<\w>来表示这个单词的结束。初始的词表大小为7,其为训练语料中所有出现过的字符。

我们之后发现lo这个字节对在训练语料中出现频率最高,为3次。我们更新词表,将lo作为新的子词加入词表,并删除在当前训练语料中不单独出现的字符lo

之后我们发现low这个字节对在训练语料中出现频率最高,为3次。我们继续组合,将low加入词表中,并删去lo。需要注意的是,由于字符w在单词newer中仍然存在,因此不予删除。

之后我们继续这个循环过程,在词表中加入er,并删去字符r

我们一直循环这个过程,直到词表大小达到我们设定的期望或者剩下的字节对出现频率最高为1。

最终我们就得到了基于训练样本构建好的词表。


编码和解码

 BPE算法详解 - mathor


1、语料编码

词表构建好后,我们需要给训练语料中的单词进行编码。编码方式如下:

  1. 我们首先「将词表中所有的子词按照长度从大到小进行排序」
  2. 对于每一个给定的单词,我们遍历排序好的词表,寻找词表中的子词是否是该单词的子字符串。如果正好「匹配」,则输出当前子词,并对单词剩下的字符串继续匹配
  3. 如果遍历完词表,单词中仍然有子字符串没有被匹配,那我们将其替换为一个特殊的子词,比如<unk>

具个例子,假设我们现在构建好的词表为

(“errrr</w>”, 
“tain</w>”, 
“moun”, 
“est</w>”, 
“high”, 
“the</w>”, 
“a</w>”)

对于给定的单词mountain</w>,其分词结果为:[mountain</w>]

语料解码

语料解码就是将所有的输出子词拼在一起,直到碰到结尾为<\w>。举个例子,假设模型输出为:

["moun", "tain</w>", "high", "the</w>"]

那么其解码的结果为

["mountain</w>", "highthe</w>"]

总结

在本文中,我们一起学习了BPE的分词算法,该算法是「利用子词来编码数据」,已经成为目前机器翻译领域标准的预处理方式。

参考文献

[1]Sennrich, Rico, Barry Haddow, and Alexandra Birch. "Neural machine translation of rare words with subword units." ACL 2016.

[2]NLP三大Subword模型详解:BPE、WordPiece、ULM - 知乎

[3]深入理解NLP Subword算法:BPE、WordPiece、ULM - 知乎

[4]https://www.cnblogs.com/huangyc/p/1




一文搞懂BPE分词算法 - 知乎

简单的搞懂BPE分词算法 - 知乎

BPE、WordPiece和SentencePiece - 简书

Byte Pair Encoding - Lei Mao's Log Book

ChatGpt都这么火了,它使用的 BPE 分词算法要不要了解一下?_bpe分词原理_大家都说我身材好的博客-CSDN博客

BPE算法详解 - mathor

Byte Pair Encoding - Lei Mao's Log Book

Byte Pair Encoding and Data Structures | Rust NLP tales

<think>嗯,用户想了解BPE算法的工作原理和实现。首先,我需要回忆BPE的基本概念。BPE是字节对编码,主要用于自然语言处理中的分词,尤其是在像GPT这样的模型里。用户提到了参考内容里的BPE分词器用于训练GPT-2、GPT-3等模型,所以可能需要详细说明BPE分词中的应用步骤。 首先,我应该解释BPE的核心思想:通过合并频率最高的字符对来逐步构建词汇表。比如,像“low”出现多次,可能会合并成“lo”和“w”,然后再继续合并。这个过程需要迭代进行,直到达到预设的词汇表大小。可能需要举个具体的例子,比如用户提到的“low”五次出现的例子,这样更直观。 然后,需要分步骤说明算法的工作流程。比如初始化词汇表为字符,统计所有相邻字符对的频率,合并最高频的对,更新词汇表,重复直到达到所需词表大小。这部分要详细但清晰,确保用户能步步理解。 接下来是实现部分。用户可能需要代码示例,比如用Python实现BPE的基本框架。这里要注意代码的正确性和可读性,比如如何统计频率,如何合并字符对。可能需要用字典来统计频率,用优先队列或者排序来找到最高频的对。然后展示训练和编码的过程,比如将单词拆分成子词。 最后,用户可能想知道BPE的优势和应用场景,比如处理未登录词,平衡词表大小和分词粒度。同时,生成相关问题,比如与WordPiece的区别,处理OOV词的方法,参数调整等。这些相关问题需要紧扣主题,帮助用户深入理解。 需要确保回答结构清晰,用中文,正确使用LaTeX格式,比如变量用$符号包裹。同时引用给定的参考内容,比如在提到BPE的应用时引用[^1]。还要检查是否有遗漏的步骤,比如预处理步骤是否需要提到,比如小写转换或处理特殊符号。另外,要注意代码示例的注释和可运行性,确保用户能理解每个部分的作用。 可能用户对BPE的具体合并过程不太清楚,比如如何选择合并对,如何处理多字节的情况。所以在例子中需要详细说明每步的合并结果,比如初始分割、第次合并后的结果,直到最终词汇表。同时,要指出BPE如何有效减少词表大小,同时保持分词的有效性。 在总结部分,需要强调BPE的优势,比如高效处理OOV,灵活适应不同语言,这对训练大模型很重要。同时,指出参数调整的重要性,如词表大小和合并次数的影响,帮助用户在实际应用中选择合适的参数。 最后,生成的相关问题要覆盖算法对比、应用中的问题和优化方法,这样用户可以进步探索。比如与WordPiece的区别,处理多语言的能力,参数调整策略等。</think>### Byte Pair Encoding (BPE) 算法解析 #### 1. BPE 核心原理 BPE种基于**频率统计**的分词算法,通过逐步合并高频字符对(byte pair)构建子词词汇表。其核心思想是:将常见字符组合编码为单符号,从而平衡词汇表大小与分词粒度。 例如,假设语料中单词 $"low"$ 出现5次,$"lower"$ 出现2次,初始词汇表为字符集合 $\{l, o, w, e, r\}$。BPE 会优先合并最高频的字符对 $"lo"$,生成新符号 $"lo"$,将其加入词汇表。 #### 2. 算法实现步骤 1. **初始化**:将文本拆分为字符级单位 ```python # 输入文本预处理为字符序列 corpus = {"low":5, "lower":2} vocab = {'l', 'o', 'w', 'e', 'r'} ``` 2. **频率统计**:计算所有相邻字符对频率 ```python # 统计字符对频率 pairs = defaultdict(int) for word, freq in corpus.items(): symbols = list(word) for i in range(len(symbols)-1): pairs[(symbols[i], symbols[i+1])] += freq ``` 3. **合并操作**:选择最高频字符对合并 ```python # 找到最高频字符对 best_pair = max(pairs, key=pairs.get) # 示例中为 ('l','o') # 更新词汇表和语料 new_symbol = ''.join(best_pair) vocab.add(new_symbol) ``` 4. **迭代更新**:重复步骤2-3直到达到预设词汇表大小或合并次数 #### 3. 分词过程示例 初始词汇表:$\{l, o, w, e, r\}$ 第次合并 $"lo"$ → 新词汇表:$\{lo, w, e, r\}$ 第二次合并 $"low"$ → 最终词汇表:$\{low, e, r\}$ 此时分词结果: - $"low"$ → `["low"]` - $"lower"$ → `["low", "er"]` #### 4. BPE 的优势(引用自参考内容) > "BPE分词器用于训练GPT-2、GPT-3等大型语言模型",其优势体现在: > - 有效处理未登录词(OOV) > 通过子词组合生成新词,如 $"ChatGPT"$ → `["Chat", "G", "PT"]` > - 平衡词汇表规模与分词粒度 > - 支持多语言混合处理 #### 5. 完整实现框架 ```python class BPE: def __init__(self, vocab_size): self.vocab_size = vocab_size def train(self, corpus): # 初始化词汇表为字符集合 vocab = set("".join(corpus.keys())) while len(vocab) < self.vocab_size: # 统计字符对频率 pairs = get_pairs(corpus) if not pairs: break # 合并最高频对 best_pair = max(pairs, key=pairs.get) corpus = merge_vocab(corpus, best_pair) vocab.add("".join(best_pair)) return vocab def encode(self, text): # 将文本拆分为子词 tokens = [] for word in text.split(): while word not in self.vocab: # 寻找可合并的子词 pair = find_mergeable_pair(word) word = word.replace("".join(pair), "_".join(pair)) tokens.extend(word.split("_")) return tokens ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值