zoukankan      html  css  js  c++  java
  • [NLP]subword理解:BPE,WordPiece,ULM

    构建词表是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 t </w>':6 'w i d es t </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编码序列,从而子词算法不用依赖于语言的表示。

     

    参考资料:

    [1]https://mp.weixin.qq.com/s/la3nZNFDviRcSFNVso29oQ

    [2]https://zhuanlan.zhihu.com/p/86965595

  • 相关阅读:
    Siri
    ArcSDE和Geodatabase10.1抢先版谍照介绍(3)——ArcToolbox工具和其他功能
    ‘马太’效应
    Linux琐屑下Resin JSP MySQL的布置和设置配备陈设2
    平安防御:分级防御对Linux服务器的攻击
    Linux无法解析域名的处理活动
    Ubuntu Linux Server的用户僻静后果分析
    大概要领确保确保Linux体系中SSH的安适性
    无错版Vsftpd Mysql Pam设置虚拟用户要领
    Redhat Linux AS4下的LAMP与Discuz装置1
  • 原文地址:https://www.cnblogs.com/mj-selina/p/13687291.html
Copyright © 2011-2022 走看看