Byte-Pair-Encoding for Tokenization

BPE概述

Byte-Pair-Encoding是用于解决未登录词的一种方法。首先简单提一句什么是未登录词,未登录词可以理解为训练语料库中没有出现的,但是在测试语料库中出现的词。我们在处理NLP任务时,通常会根据语料生成一个词典,把语料中词频大于某个阈值的词放入词典中,而低于该阈值的词统统编码成"#UNK"。这种处理方法的好处是简单,但是问题也很大,在测试语料库中如果出现了未登录词,那么我们的模型很难处理这种词。由于本文并不是为了解释NLP中如何处理未登录词的,因此其他的废话不多说,我们只说BPE算法是怎么解决未登录词的。

通常我们的词典都是word-level的,也就是说词典是以单词或者短语为基本单位的,但这就无可避免的会遇到未登录词的问题,因为不可能设计一个非常大的能涵盖所有单词的词典。此外另一种词典是character-lever,即以单个字母或汉字(中文)为基本单词设计词典,这种方法理论上可以解决未登录词的问题,因为所有的词都是由字母组成的,但是这样做的缺点是模型粒度太细,缺少语义信息,实际也有人做过实验证明这种方法不好。

后来2016年rich等人提出了基于subword来生成词典的方法,这种方法综合了word-level和charater-level的优势,从语料中学习所有单词里频次高的字符子串,然后把这些频次高的字符子串合并为一个词典。在这个词典里,既存在word-level级别的子串,也存在charater-level的子串。其中,用于寻找高频子串的方法,就是本文即将解释的BPE算法。

BEP算法很简单,它主要是用来寻找字符串中的高频子串的方法。具体来说,我们把语料库中的每个单词结尾添加一个stop token “”。然后我们将每个单词拆分成字母的形式。例如,起初我们有如下的words:

{'low': 5, 'lower': 2, 'newest': 6, 'widest': 3}

添加stop token并拆分后,变成了下面的形式:

{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w e s t </w>': 6, 'w i d e s t </w>': 3}

接下来我们计算相邻两字母出现的频率,选择频率最高的一对进行合并,在上面的例子中,'e’与’s’共出现了9次,频率最高,我们将其合并:

{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w es t </w>': 6, 'w i d es t </w>': 3}

继续重复刚刚的方法,发现’es’与’t’共出现了9次,频率最高,将其合并:

{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est </w>': 6, 'w i d est </w>': 3}


以此类推,迭代n次,直到达到预设的subwords词表大小或下一个最高频的字节对出现频率为1。

BPE实现

import re, collections

def get_stats(vocab):
    pairs = collections.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 = re.escape(' '.join(pair))
    p = re.compile(r'(?<!\S)' + bigram + r'(?!\S)')
    for word in v_in:
        w_out = p.sub(''.join(pair), word)
        v_out[w_out] = v_in[word]
    return v_out


vocab = {'l o w </w>' : 5, 'l o w e r </w>' : 2, 'n e w e s t </w>': 6, 'w i d e s t </w>': 3}
num_merges = 10


if __name__ == '__main__':
    for i in range(num_merges):
        pairs = get_stats(vocab)
        best = max(pairs, key=pairs.get)
        vocab = merge_vocab(best, vocab)
        print(best)
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值