NLP中Tokenizers总结(BPE、WordPiece、Unigram和SentencePiece)
1. 介紹
? ? ? ? 在NLP中,模型如Bert、GPT)的輸入通常需要先進(jìn)行tokenize,其目的是將輸入的文本流,切分為一個(gè)個(gè)子串,每個(gè)子串都有完整的語義,便于學(xué)習(xí)embedding表達(dá)和后續(xù)模型的使用。tokenize有三種粒度:word/subword/char。
- word/詞:詞是最自然的語言單元,對于英文來說其天然存在空格進(jìn)行,切分相對容易,常用的分詞器有spaCy和Moses?。中文不具備這樣的分割符,所以相對困難一些,不過目前也有Jieba、HanLP、LTP等分詞器,這些分詞器基于規(guī)則與模型,可以取得良好的分詞效果。使用詞時(shí)會(huì)有2個(gè)問題:1.詞表通常是基于語料進(jìn)行分詞獲得,但遇到新的語料時(shí)可能會(huì)出現(xiàn)OOV的情況;2.詞表過于龐大,對于模型來說大部分參數(shù)都集中在輸入輸出層,不利于模型學(xué)習(xí),且容易爆內(nèi)存(顯存)。通常情況下詞表大小不超過5w。
 - char/字符:字符是一種語言最基本的組成單元,如英文中的'a','b','c'或中文中的‘你’,‘我’,‘他’等。使用字符有如下問題:1.字符數(shù)量是有限的通常數(shù)量較少,這樣在學(xué)習(xí)每個(gè)字符的embedding向量時(shí),每個(gè)字符中包含非常多的語義,學(xué)習(xí)起來比較困難;2.以字符分割,會(huì)造成序列長度過長,對后續(xù)應(yīng)用造成較大限制。
 - subword/子詞:它介于char和word之間,可以很好的平衡詞匯量和語義獨(dú)立性。它的切分準(zhǔn)則是常用的詞不被切分,而不常見的詞切分為子詞。
 
2. 子詞算法
2.1. Byte Pair Encoding (BPE)
? ? ? ? BPE最早是一種數(shù)據(jù)壓縮算法,由Sennrich等人于2015年引入到NLP領(lǐng)域并很快得到推廣,可參考Neural Machine Translation of Rare Words with Subword Units (Sennrich et al., 2015) 。該算法簡單有效,因而目前它是最流行的方法。GPT-2和RoBERTa使用的Subword算法都是BPE。
算法
停止符"</w>"的意義在于表示subword是詞后綴。舉例來說:"st"字詞不加"</w>"可以出現(xiàn)在詞首如"st ar",加了"</w>"表明改字詞位于詞尾,如"wide st</w>",二者意義截然不同。
每次合并后詞表可能出現(xiàn)3種變化:
- +1,表明加入合并后的新字詞,同時(shí)原來的2個(gè)子詞還保留(2個(gè)字詞不是完全同時(shí)連續(xù)出現(xiàn))
 - +0,表明加入合并后的新字詞,同時(shí)原來的2個(gè)子詞中一個(gè)保留,一個(gè)被消解(一個(gè)字詞完全隨著另一個(gè)字詞的出現(xiàn)而緊跟著出現(xiàn))
 - -1,表明加入合并后的新字詞,同時(shí)原來的2個(gè)子詞都被消解(2個(gè)字詞同時(shí)連續(xù)出現(xiàn))
 
實(shí)際上,隨著合并的次數(shù)增加,詞表大小通常先增加后減小。
例子
輸入:
{'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, 最高頻連續(xù)字節(jié)對"e"和"s"出現(xiàn)了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, 最高頻連續(xù)字節(jié)對"es"和"t"出現(xiàn)了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, 以此類推,最高頻連續(xù)字節(jié)對為"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, 繼續(xù)迭代直到達(dá)到預(yù)設(shè)的subword詞表大小或下一個(gè)最高頻的字節(jié)對出現(xiàn)頻率為1。
2.2 WordPiece
? ? ? ? WordPiece與BPE非常相似,也是每次從詞表中選出兩個(gè)子詞合并成新的子詞,可參考Japanese and Korean Voice Search (Schuster et al., 2012)。區(qū)別在于,BPE選擇頻數(shù)最高的相鄰子詞合并,而WordPiece選擇能夠提升語言模型概率最大的相鄰子詞加入詞表。
? ? ? ? 也許你還不清楚WordPiece是如何選取子詞的,接下來詳細(xì)說明下WordPiece在合并這一步是如何做的。假設(shè)句子是由n個(gè)子詞組成,表示子詞,且假設(shè)各個(gè)子詞之間是獨(dú)立存在的,則句子S的語言模型似然值等價(jià)與所有子詞概率的乘積:
?假設(shè)把相鄰位置的x和y兩個(gè)子詞進(jìn)行合并,合并后產(chǎn)生的子詞為z,此時(shí)句子S似然值的變化可表示為:
可以看見似然值的變化就是兩個(gè)子詞之間的互信息。簡而言之,WordPiece每次選擇合并的兩個(gè)子詞,他們具有最大的互信息,也就是兩個(gè)子詞在語言模型上具有較強(qiáng)的關(guān)聯(lián)性,它們經(jīng)常在語料中以相鄰的方式同時(shí)出現(xiàn)。
2.3 Unigram Language Model(ULM)
? ? ? ? ULM的介紹可參考?Subword Regularization: Improving Neural Network Translation Models with Multiple Subword Candidates (Kudo, 2018)。Unigram與BPE和WordPiece的區(qū)別在于,BPE和Worpiece算法的詞表都是一點(diǎn)一點(diǎn)增加,由小到大的。而Unigram則是先初始化一個(gè)非常巨大的詞表,然后根據(jù)標(biāo)準(zhǔn)不斷的丟棄,知道詞表大小滿足限定條件。Unigram算法考慮了句子的不同分詞可能,因而能夠出輸出帶概率的子詞分段。
接下來,我們看看ULM是如何操作的。
對于句子S,為句子的一個(gè)分詞結(jié)果,由m個(gè)子詞組成。所以,當(dāng)前分詞下句子S的似然值可以表示為:
對于句子S,挑選似然值最大的作為分詞結(jié)果,則可以表示為
這里U(x)包含了句子的所有分詞結(jié)果。在實(shí)際應(yīng)用中,詞表大小有上萬個(gè),直接羅列所有可能的分詞組合不具有操作性。針對這個(gè)問題,可通過維特比算法得到來解決。
那怎么求解每個(gè)子詞的概率P(xi)呢?ULM通過EM算法來估計(jì)。假設(shè)當(dāng)前詞表V, 則M步最大化的對象是如下似然函數(shù):
其中,|D|是語料庫中語料數(shù)量。上述公式的一個(gè)直觀理解是,將語料庫中所有句子的所有分詞組合形成的概率相加。
但是,初始時(shí),詞表V并不存在。因而,ULM算法采用不斷迭代的方法來構(gòu)造詞表以及求解分詞概率:
可以看出,ULM會(huì)保留那些以較高頻率出現(xiàn)在很多句子的分詞結(jié)果中的子詞,因?yàn)檫@些子詞如果被丟棄,其損失會(huì)很大。
3.? SentencePiece
? ? ? ?上述的所有算法都有一個(gè)前提:輸入以空格來進(jìn)行區(qū)分。然而并不是所有語言的詞語都是使用空格來進(jìn)行分割(比如中文、日文),一種比較常見的做法是使用預(yù)分詞。為了更加一般化的解決這個(gè)問題,谷歌推出了開源工具包SentencePiece?。SentencePiece是把一個(gè)句子看做一個(gè)整體,再拆成片段,而沒有保留天然的詞語的概念。一般地,它把space也當(dāng)做一種特殊的字符來處理,再用BPE或者Unigram算法來構(gòu)造詞匯表。比如,XLNetTokenizer就采用了_來代替空格,解碼的時(shí)候會(huì)再用空格替換回來。目前,Tokenizers庫中,所有使用了SentencePiece的都是與Unigram算法聯(lián)合使用的,比如ALBERT、XLNet、Marian和T5.
4. 舉例
4.1 BertTokenizer/WordPiece
from transformers import BertTokenizer tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")????????Text:? I have a new GPU! 🤗 ?You know!
????????Token:['i', 'have', 'a', 'new', 'gp', '##u', '!', '[UNK]', 'you', 'know', '!']
????????Text:"嫑怎么讀呢,我也不知道
????????Token:['[UNK]', '[UNK]', '[UNK]', '[UNK]', '[UNK]', ',', '我', '也', '不', '[UNK]', '道']
在進(jìn)行分詞時(shí)會(huì)將"gpu"切分為 ["gu", "##u"]。"##"表示在進(jìn)行解碼時(shí),這個(gè)子詞需要和前一個(gè)子詞直接連接起來,中間不會(huì)添加空格。由于使用的是"bert-base-uncased"分詞,出現(xiàn)較多[UNK]表示沒有識別出來,更換"bert-base-chinese"即可,所以在使用時(shí)要根據(jù)自己的場景選擇合適的分詞方式。
4.2 XLNetTokenizer/SentencePiece
from transformers import XLNetTokenizer tokenizer = XLNetTokenizer.from_pretrained("xlnet-base-cased")????????Text:? I have a new GPU! 🤗 ?You know!
????????Token:['▁I', '▁have', '▁a', '▁new', '▁G', 'PU', '!', '▁', '🤗', '▁You', '▁know', '!']
????????Text:"嫑怎么讀呢,我也不知道
????????Token:['▁', '嫑怎么讀呢', ',', '我也不知道']
XLNet使用SentencePiece的分詞方式,在進(jìn)行分詞時(shí)會(huì)將Transformers切分為 ["Transform", "ers"]。"'▁"表示以該子詞開頭。
參考
[1] Summary of the tokenizers
 [2]深入理解NLP Subword算法:BPE、WordPiece、ULM
 [3]NLP三大Subword模型詳解:BPE、WordPiece、ULM
 [4]BPE、WordPiece和SentencePiece
總結(jié)
以上是生活随笔為你收集整理的NLP中Tokenizers总结(BPE、WordPiece、Unigram和SentencePiece)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: R语言如何做配对t检验?
 - 下一篇: CSS浏览器兼容性的4个解决方案