使用BPE算法处理未登录词

本文深入讲解了BPE算法的工作原理,展示了如何通过迭代合并高频符号对来优化词典,解决最大匹配分词算法中的未登录词问题。通过具体示例说明了BPE算法在分词准确性上的提升。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

BPE算法的输入是原始字典,其中每个单词被表示成一串字符,BPE算法迭代的执行k次,每次选择一个出现频率最高的符号对进行合并,生成一个新的符号,注意这种合并是不超过单词边界的。
例如原始词典中有张晨光张晨磊两个单词,频数均为3,但是没有出现张晨

张晨光:3
张晨磊:3

所以我们在使用最大匹配算法进行分词时,如果使用该字典,就会造成张晨被错误分割为两个词的情况,因为最大匹配算法无法正确处理未登录词:

max match cut result:
我/是/张/晨/,/我/爱/自然语言/处理

假设我们使用BPE算法时,k=1,并且第一次选中的符号对是(“张”,“晨”),那么我们将其合并后,会生成新的符号“张晨”,并且其出现的频数为6. 经过BPE算法处理后的词典中,将会包含原始词典中的单词和新生成的符号,那些没有合并的单字符符号也会包括其中,新的词典中会包括下面三个词:

张晨光:3
张晨磊:3
张晨:6

此时我们再使用最大匹配算法进行分词,并将经过BPE算法处理后新词典作为输入,我们就能得到正确的分词结果了(未登录词“张晨”被正确识别了):

max match cut(with BPE) result:
我/是/张晨/,/我/爱/自然语言/处理

BPE算法的核心代码如下:

def bpe(dic_of_words_with_space, num_merges):
    dic_to_merge = dic_of_words_with_space
    dic_merged = {}
    for i in range(num_merges):
        pairs = get_pair_stats(dic_of_words_with_space, char_separator=constants.space)
        # best_pair = max(pairs, key=pairs.get)
        best_pair = ("张", "晨")
        dic_merged = merge_vocab(best_pair, dic_to_merge)
        dic_to_merge = dic_merged
    return dic_merged

注意我们这里故意把best_pair设置成("张", "晨")以方便演示算法效果,其完整代码可以在我的github上下载.

### BPE算法的原理与实现 #### 原理概述 BPEByte Pair Encoding)是一种子词级别的分词方法,最初设计用于数据压缩[^1]。其核心思想是从字符级别出发逐步构建更高级别的单元,通过统计分析训练语料库中的频繁出现的字符对并将其合并为新的符号,从而形成一个动态扩展的词汇表。这种方法能够有效缓解传统固定大小词汇表带来的 OOV(Out-of-Vocabulary)问题。 具体来说,在自然语言处理中,BPE 将单词拆分为若干个子词单位(subwords),这些子词可以是完整的单词、前缀、后缀或者中间部分。相比固定的词汇表,BPE 能够灵活适应不同规模的数据集,并减少未登录词的影响[^2]。 --- #### 算法流程 以下是 BPE 的典型实现步骤: 1. **初始化词汇表** 初始词汇表由单个字符组成,例如对于英语字母表而言,初始词汇表可能只包含 `a, b, c,...` 等字符以及特殊标记如空格 `' '` 或其他标点符号[^3]。 2. **计算最频发的字符对** 遍历整个语料库,统计相邻字符对的频率,找到出现次数最多的字符对。例如,如果 `"th"` 是最常见的字符对,则下一步会将该对替换为一个新的符号。 3. **更新词汇表** 创建一个新的符号来表示这个高频字符对,并将其加入到词汇表中。例如,可以用 `<th>` 表示 `"t h"` 这一对。 4. **重复迭代** 不断重复上述两步操作,直到达到预定条件为止,比如设定的最大迭代次数或目标词汇表大小。 5. **编码阶段** 使用最终得到的词汇表对输入文本进行编码,即把原始字符串分解成一系列子词序列。 6. **解码阶段** 如果需要还原回原句,则按照相反顺序重新拼接各个子词即可完成解码过程。 --- #### Python代码实现 下面提供了一个简单的 BPE 实现例子: ```python from collections import defaultdict def get_stats(vocab): pairs = defaultdict(int) for word, freq in vocab.items(): symbols = word.split() for i in range(len(symbols)-1): pairs[symbols[i], symbols[i+1]] += freq return pairs def merge_vocab(pair, v_in): v_out = {} bigram = ' '.join(pair) replacement = ''.join(pair) for word in v_in: w_out = word.replace(bigram, replacement) v_out[w_out] = v_in[word] return v_out vocab = {'l o w': 5, 'l o w e r': 2, 'n e w e s t': 6} num_merges = 50 for i in range(num_merges): pairs = get_stats(vocab) if not pairs: break best_pair = max(pairs, key=pairs.get) vocab = merge_vocab(best_pair, vocab) print("Final Vocabulary:", list(vocab.keys())) ``` 此脚本展示了如何从简单词汇表开始并通过多次迭代生成更加复杂的子词结构。 --- #### 应用场景 BPE 广泛应用于现代神经网络架构之中,尤其是在涉及大规模文本建模的任务里表现优异。它常被用来替代传统的基于完整词语的方法,因为后者容易遇到稀疏性和未知词汇等问题。一些典型的案例包括但不限于以下方面: - 构造 Transformer 类型的语言模型(如 GPT 和 RoBERTa) - 提升机器翻译系统的性能 - 改善语音识别转录质量 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值