赞
踩
在NLP中,模型如Bert、GPT)的输入通常需要先进行tokenize,其目的是将输入的文本流,切分为一个个子串,每个子串都有完整的语义,便于学习embedding表达和后续模型的使用。tokenize有三种粒度:word/subword/char。
BPE最早是一种数据压缩算法,由Sennrich等人于2015年引入到NLP领域并很快得到推广,可参考Neural Machine Translation of Rare Words with Subword Units (Sennrich et al., 2015) 。该算法简单有效,因而目前它是最流行的方法。GPT-2和RoBERTa使用的Subword算法都是BPE。
算法
停止符"</w>"的意义在于表示subword是词后缀。举例来说:"st"字词不加"</w>"可以出现在词首如"st ar",加了"</w>"表明改字词位于词尾,如"wide st</w>",二者意义截然不同。
每次合并后词表可能出现3种变化:
实际上,随着合并的次数增加,词表大小通常先增加后减小。
例子
输入:
{'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}
Iter 1, 最高频连续字节对"e"和"s"出现了6+3=9次,合并成"es"。输出:
{'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}
Iter 2, 最高频连续字节对"es"和"t"出现了6+3=9次, 合并成"est"。输出:
{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est </w>': 6, 'w i d est </w>': 3}
Iter 3, 以此类推,最高频连续字节对为"est"和"</w>" 输出:
{'l o w </w>': 5, 'l o w e r </w>': 2, 'n e w est</w>': 6, 'w i d est</w>': 3}
……
Iter n, 继续迭代直到达到预设的subword词表大小或下一个最高频的字节对出现频率为1。
WordPiece与BPE非常相似,也是每次从词表中选出两个子词合并成新的子词,可参考Japanese and Korean Voice Search (Schuster et al., 2012)。区别在于,BPE选择频数最高的相邻子词合并,而WordPiece选择能够提升语言模型概率最大的相邻子词加入词表。
也许你还不清楚WordPiece是如何选取子词的,接下来详细说明下WordPiece在合并这一步是如何做的。假设句子
假设把相邻位置的x和y两个子词进行合并,合并后产生的子词为z,此时句子S似然值的变化可表示为:
可以看见似然值的变化就是两个子词之间的互信息。简而言之,WordPiece每次选择合并的两个子词,他们具有最大的互信息,也就是两个子词在语言模型上具有较强的关联性,它们经常在语料中以相邻的方式同时出现。
ULM的介绍可参考 Subword Regularization: Improving Neural Network Translation Models with Multiple Subword Candidates (Kudo, 2018)。Unigram与BPE和WordPiece的区别在于,BPE和Worpiece算法的词表都是一点一点增加,由小到大的。而Unigram则是先初始化一个非常巨大的词表,然后根据标准不断的丢弃,知道词表大小满足限定条件。Unigram算法考虑了句子的不同分词可能,因而能够出输出带概率的子词分段。
接下来,我们看看ULM是如何操作的。
对于句子S,为句子的一个分词结果,由m个子词组成。所以,当前分词下句子S的似然值可以表示为:
对于句子S,挑选似然值最大的作为分词结果,则可以表示为
这里U(x)包含了句子的所有分词结果。在实际应用中,词表大小有上万个,直接罗列所有可能的分词组合不具有操作性。针对这个问题,可通过维特比算法得到来解决。
那怎么求解每个子词的概率P(xi)呢?ULM通过EM算法来估计。假设当前词表V, 则M步最大化的对象是如下似然函数:
其中,|D|是语料库中语料数量。上述公式的一个直观理解是,将语料库中所有句子的所有分词组合形成的概率相加。
但是,初始时,词表V并不存在。因而,ULM算法采用不断迭代的方法来构造词表以及求解分词概率:
可以看出,ULM会保留那些以较高频率出现在很多句子的分词结果中的子词,因为这些子词如果被丢弃,其损失会很大。
上述的所有算法都有一个前提:输入以空格来进行区分。然而并不是所有语言的词语都是使用空格来进行分割(比如中文、日文),一种比较常见的做法是使用预分词。为了更加一般化的解决这个问题,谷歌推出了开源工具包SentencePiece 。SentencePiece是把一个句子看做一个整体,再拆成片段,而没有保留天然的词语的概念。一般地,它把space也当做一种特殊的字符来处理,再用BPE或者Unigram算法来构造词汇表。比如,XLNetTokenizer就采用了_来代替空格,解码的时候会再用空格替换回来。目前,Tokenizers库中,所有使用了SentencePiece的都是与Unigram算法联合使用的,比如ALBERT、XLNet、Marian和T5.
- from transformers import BertTokenizer
- tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
Text: I have a new GPU!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。