赞
踩
先构建词表。先按单个字符拆分,根据统计结果进行逐个合并,知道得到最终的词表
再把当前字符根据进行单个字符拆分,然后按照词表进行合并
概述:
bpe(byte pair encoding),是一种根据字节对进行编码的算法。主要目的是为了数据压缩,算法描述为字符串里频率最常见的一对字符被一个没有在这个字符中出现的字符代替的层层迭代过程。该算法在论文:https://arxiv.org/abs/1508.07909 Neural Machine Translation of Rare Words with Subword Units详细介绍
训练过程:
对于使用子词作为基本单位进行训练的神经机器翻译模型,训练的第一步就是根据语料生成bpe的code资源,以英文为例,该资源会将训练语料以字符为单位进行拆分,按照字符对进行组合,并对所有组合的结果根据出现的频率进行排序,出现频次越高的排名越靠前,排在第一位的是出现频率最高的子词。如图所示:e 为出现频率最高的子词,其中表示这个e是作为单词结尾的字符。训练过程结束,会生成codec文件。如下图所示:
解码过程:
以单词“where”为例,首先按照字符拆分开,然后查找codec文件,逐对合并,优先合并频率靠前的字符对。85 319 9 15 表示在该字符对在codec文件中的评率排名。
最终where可以在codec文件中被找到,因此where的bpe分词结果为where,对于其他并不能像where一样能在codec文件中找到整个词的词来说,bpe分词结果以最终查询结束时的分词结果为准。
昨天总结实验数据分析的时候发现一个机器翻译的其中的一个脚本,其中用到的算法就是BPE算法,刚开始感觉很高大上的,因为总是听到带上算法帽子的东西就觉得666。等自己好好研究研究,网上各种找资料才知道,其实还挺好理解的,所以真的应了那句老话,眼见为实呀。 总说 BPE,(byte pair encoder)字节对编码,也可以叫做digram coding双字母组合编码,主要目的是为了数据压缩,算法描述为字符串里频率最常见的一对字符被一个没有在这个字符中出现的字符代替的层层迭代过程。具体在下面描述。该算法首先被提出是在Philip Gage的C Users Journal的 1994年2月的文章“A New Algorithm for Data Compression”。 算法过程 这个算法个人感觉很简单,下面就来讲解下: 比如我们想编码: aaabdaaabac 我们会发现这里的aa出现的词数最高(我们这里只看两个字符的频率),那么用这里没有的字符Z来替代aa: ZabdZabac Z=aa 此时,又发现ab出现的频率最高,那么同样的,Y来代替ab: ZYdZYac Y=ab Z=aa 同样的,ZY出现的频率大,我们用X来替代ZY: XdXac X=ZY Y=ab Z=aa 最后,连续两个字符的频率都为1了,也就结束了。就是这么简单。 解码的时候,就按照相反的顺序更新替换即可。
在NLP任务中,神经网络模型的训练和预测都需要借助词表来对句子进行表示。传统构造词表的方法,是先对各个句子进行分词,然后再统计并选出频数最高的前N个词组成词表。通常训练集中包含了大量的词汇,以英语为例,总的单词数量在17万到100万左右。出于计算效率的考虑,通常N的选取无法包含训练集中的所有词。因而,这种方法构造的词表存在着如下的问题:
一种解决思路是使用字符粒度来表示词表,虽然能够解决OOV问题,但单词被拆分成字符后,一方面丢失了词的语义信息,另一方面,模型输入会变得很长,这使得模型的训练更加复杂难以收敛。
针对上述问题,Subword(子词)模型方法横空出世。它的划分粒度介于词与字符之间,比如可以将”looking”划分为”look”和”ing”两个子词,而划分出来的"look",”ing”又能够用来构造其它词,如"look"和"ed"子词可组成单词"looked",因而Subword方法能够大大降低词典的大小,同时对相近词能更好地处理。
目前有三种主流的Subword算法,它们分别是:Byte Pair Encoding (BPE), WordPiece和Unigram Language Model。
BPE最早是一种数据压缩算法,由Sennrich等人于2015年引入到NLP领域并很快得到推广。该算法简单有效,因而目前它是最流行的方法。GPT-2和RoBERTa使用的Subword算法都是BPE。
BPE获得Subword的步骤如下:
下面以一个例子来说明。假设有语料集经过统计后表示为{'low':5,'lower':2,'newest':6,'widest':3},其中数字代表的是对应单词在语料中的频数。
1) 拆分单词成最小单元,并初始化词表。这里,最小单元为字符,因而,可得到
需要注意的是,在将单词拆分成最小单元时,要在单词序列后加上”</w>”(具体实现上可以使用其它符号)来表示中止符。在子词解码时,中止符可以区分单词边界。
2) 在语料上统计相邻单元的频数。这里,最高频连续子词对"e"和"s"出现了6+3=9次,将其合并成"es",有
由于语料中不存在's'子词了,因此将其从词表中删除。同时加入新的子词'es'。一增一减,词表大小保持不变。
3) 继续统计相邻子词的频数。此时,最高频连续子词对"es"和"t"出现了6+3=9次, 将其合并成"est",有
4) 接着,最高频连续子词对为"est"和"</w>",有
5) 继续上述迭代直到达到预设的Subword词表大小或下一个最高频的字节对出现频率为1。
从上面的示例可以知道,每次合并后词表大小可能出现3种变化:
实际上,随着合并的次数增加,词表大小通常先增加后减小。
在得到Subword词表后,针对每一个单词,我们可以采用如下的方式来进行编码:
解码过程比较简单,如果相邻子词间没有中止符,则将两子词直接拼接,否则两子词之间添加分隔符。
Google的Bert模型在分词的时候使用的是WordPiece算法。与BPE算法类似,WordPiece算法也是每次从词表中选出两个子词合并成新的子词。与BPE的最大区别在于,如何选择两个子词进行合并:BPE选择频数最高的相邻子词合并,而WordPiece选择能够提升语言模型概率最大的相邻子词加入词表。
看到这里,你可能不清楚WordPiece是如何选取子词的。这里,通过形式化方法,能够清楚地理解WordPiece在合并这一步是如何作出选择的。假设句子由n个子词组成,表示子词,且假设各个子词之间是独立存在的,则句子的语言模型似然值等价于所有子词概率的乘积:
假设把相邻位置的x和y两个子词进行合并,合并后产生的子词记为z,此时句子似然值的变化可表示为:
从上面的公式,很容易发现,似然值的变化就是两个子词之间的互信息。简而言之,WordPiece每次选择合并的两个子词,他们具有最大的互信息值,也就是两子词在语言模型上具有较强的关联性,它们经常在语料中以相邻方式同时出现。
与WordPiece一样,Unigram Language Model(ULM)同样使用语言模型来挑选子词。不同之处在于,BPE和WordPiece算法的词表大小都是从小到大变化,属于增量法。而Unigram Language Model则是减量法,即先初始化一个大词表,根据评估准则不断丢弃词表,直到满足限定条件。ULM算法考虑了句子的不同分词可能,因而能够输出带概率的多个子词分段。
我们接下来看看ULM是如何操作的。
对于句子S,为句子的一个分词结果,由m个子词组成。所以,当前分词下句子S的似然值可以表示为:
对于句子S,挑选似然值最大的作为分词结果,则可以表示为
这里包含了句子的所有分词结果。在实际应用中,词表大小有上万个,直接罗列所有可能的分词组合不具有操作性。针对这个问题,可通过维特比算法得到来解决。
那怎么求解每个子词的概率呢?ULM通过EM算法来估计。假设当前词表V, 则M步最大化的对象是如下似然函数:
其中,|D|是语料库中语料数量。上述公式的一个直观理解是,将语料库中所有句子的所有分词组合形成的概率相加。
但是,初始时,词表V并不存在。因而,ULM算法采用不断迭代的方法来构造词表以及求解分词概率:
可以看出,ULM会保留那些以较高频率出现在很多句子的分词结果中的子词,因为这些子词如果被丢弃,其损失会很大。
如何使用上述子词算法?一种简便的方法是使用SentencePiece,它是谷歌推出的子词开源工具包,其中集成了BPE、ULM子词算法。除此之外,SentencePiece还能支持字符和词级别的分词。更进一步,为了能够处理多语言问题,sentencePiece将句子视为Unicode编码序列,从而子词算法不用依赖于语言的表示。
感谢关注和点赞,欢迎分享给更多人~
文章转载自:知乎@ IT民工boby,已获取授权。
温馨提示:新智元主页新增“实用贴合集”,含AI技术教程、开源工具、书籍及视频下载等,欢迎大家参与。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。