构建词表是NLP任务中的一个基本要求,传统的方法是对各个句子进行分词,然后选取频率最高的N个词组成词表。但是这样的做法不可避免的会带来一些问题,如OOV问题,低频次/稀疏词的语义很难获取(因为没有训练)等。

为解决上述问题,提出了subword模型。该模型的划分粒度介于词与字符之间,如将"looking"分割为“look”和“ing”两个子词,因而它能够大大降低词典的大小,同时对相近词能更好的处理

subword主要有三种方法:BPE,WordPiece,UniLM

一、BPE

GPT-2和RoBERTa使用的subword算法都是BPE。

算法步骤:

①准备足够大的训练语料,并确定期望的subword词表大小

②将单词拆分为最小单元。比如英文中26个字母加上各种符号,这些作为初始词表。

③在语料上统计单词内相邻单元对的频数,选取频数最高的单元对合并成新的subword单元。

④重复第③步直到达到第①步设定的subword词表大小或下一个最高频的字节对出现频率为1

举例说明:假设语料集经过统计后表示为:{'low':5,'lower':2,'newest':6,'widest':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

'l','o','w', 'e','r','n','s','t','i','d','</w>'

②在语料上统计相邻单元的频数。这里,“e”和“s”出现了6+3=9次,将其合并成“es”,有下表:

语料

词表

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

'l','o','w', 'e','r','n','s','t','i','d','</w>','es'

由于语料中不存在“s”子词,因此将其从词表中删除,同时加入新的子词“es”。一增一减,词表大小保持不变。

③继续统计相邻子词的频数。此时最高频连续子词对“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

'l','o','w', 'e','r','n','s','t','i','d','</w>','es','est'

④接着,最高频连续子词对为"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

'l','o','w', 'e','r','n','s','t','i','d','</w>','est','est</w>'

⑤继续上述迭代知道达到预设的subword词表大小或下一个最高频的字节对出现频率为1.

实际上,随着合并的次数增加,词表大小通常先增加后减小。

得到subword词表后,针对每一个单词,我们可以采用如下的方式进行编码:

①将词典中的所有子词按照长度由大到小进行排序;

②对于单词w,依次遍历排好序的词典。查看当前子词是否是该单词的子字符串,如果是,则输出当前子词,并对剩余单词字符串继续匹配。

③如果遍历完字典后,仍然有子字符串没有匹配,则将剩余字符串替换为特殊符号输出,如“<unk>”

④单词的表示即为上述所有输出子词。

优点:可以有效的平衡词汇表大小和步数(编码句子所需的token数量)

缺点:基于贪婪和确定的符号替换,不能提供带概率的多个分片结果

二、WordPiece

bert在分词的时候使用的是WordPiece算法。与BPE算法类似,WordPiece算法也是从词表中选出两个子词合并成新的子词。但他们的区别是:BPE选择频数最高的相邻子词合并,而WordPiece选择能够提升语言模型概率最大的相邻子词加入词表。

具体做法如下:假设句子S=(t1,t2,...,tn)由n个子词组成,t_i表示子词,且假设各个子词之间是独立存在的,则句子S的语言模型似然值等价于所有子词概率的乘积:,

假设把相邻位置的x和y两个子词进行合并,合并后产生的子词记为z,此时句子S似然值的变化可表示为:

logP(tz) -(log(P(tx)) + log(P(ty))) = log(P(tx) / P(tx)P(ty))

从上面的公式可以看出,似然值的变化就是两个子词之间的互信息,我们选择具有最大的互信息值的两个词进行合并,表明这两个词在语言模型上具有较强的关联性。

3、Unigram Language Model(ULM)

ULM同WordPiece一样,也是选择语言模型来挑选子词。但是ULM的计算方法是,先初始化一个大词表,根据评估准则不断丢弃词表,直到满足限定条件。ULM算法考虑了句子的不同分词可能,因而能够输出带概率的多个分词分段。

具体过程如下:

对于句子S,x=(x1,x2,...,xm)为句子的一个分词结果,由m个子词组成。所以,当前分词下句子S的似然值可表示为:P(x) = P(x1)P(x2)...P(xm)

对于句子S,挑选似然值最大的作为分词结果,则可以表示为:x* = arg max_{x in U(x)} P(x)

这里U(x)包含了句子的所有分词结果。因为词表很大,直接罗列所有的分词组合不具操作性。针对这个问题,可通过维特比算法来解决。

至于每个子词的概率P(xi),ULM通过EM算法来估计。假设当前词表V,则M步最大化的对象是如下似然函数:

L = sum{s=1,|D|}(logP(Xs) = sum(log(sum{x in U(Xs)}(P(x))))

其中,|D|是语料库中语料数量。上述公式的一个直观理解是,将语料库中所有句子的所有分词组合形成的概率相加。

但是,初始时,词表V并不存在。因而,ULM算法采用不断迭代的方法来构造词表以及求解分词概率:

①初始时,建立一个足够大的词表。一般,可用语料中的所有字符加上常见的子字符串初始化词表,也可以通过BPE算法初始化。

②针对当前词表,用EM算法求解每个子词在语料上的概率

③对于每个子词,计算当该子词被从词表中移除时,总的loss降低了多少,记为该子词的loss

④将子词按照loss大小进行排序,丢弃一定比例loss最小的子词(比如20%),保留下来的子词生成新的词表。这里需要注意的是,单字符不能被丢弃,这是为了避免OOV情况。

⑤重复步骤②-④,直到词表大小减少到设定范围。

可以看出,ULM算法会保留那些以较高频率出现在很多句子的分词结果中的子词,因为这些子词如果被丢弃,其损失会很大。

 

SentencePiece

它是谷歌推出的子词开源工具包,其中集成了BPE、ULM子词算法。除此之外,SentencePiece还能支持字符和词级别的分词。更进一步,为了能够处理多语言问题,sentencePiece将句子视为Unicode编码序列,从而子词算法不用依赖于语言的表示。