【深度学习】Subword Tokenization算法
? ? ? ? 在自然語言處理中,面臨的首要問題是如何讓模型認識我們的文本信息,詞,是自然語言處理中基本單位,神經網絡模型的訓練和預測都需要借助詞表來對句子進行表示。
1.構建詞表的傳統方法
? ? ? ? 在字詞模型問世之前,做自然語言處理任務時,一般通過傳統方法構建詞表,具體的構建流程如下:
? ? ? ? 1.首先對數據集中的所有句子進行分詞,通過jieba、ltp等分詞工具將句子粒度縮小到詞粒度。例如:我/愛/北京/天安門。
? ? ? ? 2.對切分出來的詞進行統計,統計并選出頻數最高的前N個詞組成詞表,對低頻詞舍棄。
? ? ? ? 通過篩選詞頻的方式構建的詞表會存在以下問題:
? ? ? ? 1.出于計算效率的考慮,通常N的選取無法包含訓練集中的所有詞,對于未在詞表中出現的詞(Out Of Vocabulary, OOV),模型將無法處理及生成;
? ? ? ? 2.詞表中的低頻詞/稀疏詞在模型訓練過程中無法得到充分訓練,進而模型不能充分理解這些詞的語義;
? ? ? ? 3.一個單詞因為不同的形態會產生不同的詞,如由"look"衍生出的"looks", "looking", "looked",顯然這些詞具有相近的意思,但是在詞表中這些詞會被當作不同的詞處理,一方面增加了訓練冗余,另一方面也造成了大詞匯量問題。
2.拋磚引玉
? ? ? ? 假設要從訓練數據集中構建一個詞表。為了構建該詞表,我們將數據集中的文本拆分成單詞,然后把唯一的單詞加入到詞表。通常,詞表包含很多單詞(Token),為了舉例的簡單,假設構造的詞表只包含下面的單詞:
vocabulary = [game, the, I, played, walked, enjoy]
? ? ? ? 有詞表之后,下面基于該詞表來對輸入進行分詞。考慮輸入句子I played the game。在英文中,只要通過空格就得得到句子中所有的單詞。所以我們有[I, play, the, game]。現在,我們檢查是否所有的單詞都在詞表中。恰好這些單詞都在詞表中,從給定句子中得到的最終標記就為:
tokens = [I, played, the, game]
????????接著我們考慮另一個句子:I enjoyed the game。首先還是根據空格分詞,得到 [I, enjoyed, the, game]。接著,檢查是否所有的單詞出現在詞表中。我們可以看到,除了單詞enjoyed,其他的單詞都在詞表中,因此我把enjoyed替換為未知標記<UNK>,這樣我們最終的標記為:
tokens = [ I, <UNK>, the, game]
????????我們也可以看到,盡管詞表中有單詞enjoy,但是由于沒有完全一樣的單詞enjoyed,它就變成未知詞了。可能是因為詞表太小了,然而哪怕有一個超大的詞表,先不說這樣的詞表會帶來內存和性能方面的壓力,仍然有可能無法處理沒有出現過的詞(詞表中也沒有出現過)。
????????針對上述問題,Subword(子詞)模型方法橫空出世。它的劃分粒度介于詞與字符之間,比如可以將”enjoyed”劃分為”enjoy”和”ed”兩個子詞,而劃分出來的"enjoy",”ed”又能夠用來構造其它詞,如"engoy"和"ing"子詞可組成單詞"engoying",因而Subword方法能夠大大降低詞典的大小,同時對相近詞能更好地處理。
????????在子詞Tokenization中,我們將單詞拆分為更小的子詞。假設我們拆分單詞played為子詞[play, ed];拆分單詞walked為子詞[walk, ed]。在拆分子詞后,我們將這些子詞加到詞表中。由于詞表中不包含重復單詞,所以我們的詞表變成了:
vocabulary = [game, the, I, play, walk, ed, enjoy]
? ? ? ? 在來看之前的句子: I enjoyed the game。同樣我們先根據空格將句子拆分單詞,我們有[ I, enjoyed, the, game]。接著我們檢測是否所有的單詞都在詞表中。我們可以看到除了單詞enjoyed,其他所有單詞都在詞表中。此時,我們不會將該單詞替換為未知詞,而是將它繼續拆分為子詞:[enjoy, ed]。同樣我們先根據空格將句子拆分單詞,我們有[ I, enjoyed, the, game]。接著我們檢測是否所有的單詞都在詞表中。我們可以看到除了單詞enjoyed,其他所有單詞都在詞表中。此時,我們不會將該單詞替換為未知詞,而是將它繼續拆分為子詞:[enjoy, ed]。
tokens = [ I, enjoy, ##ed, the, game]
????????我們可以看到ed前面有兩個#。這代表##ed是一個子詞,而且它前面有另一個單詞。我們不會為單詞拆分后的第一個子詞增加##符號,這就是為什么子詞enjoy前面沒有#。這樣子詞Tokenization處理了未知詞,也就是沒有出現在詞表中的單詞。
????????現在問題是,我們知道我們將單詞played和walked拆分成子詞,然后增加它們的子詞到詞表中。但是為什么我們只拆分這些單詞呢?為什么不是詞表中的其他單詞?我們如何決定哪些單詞要拆分,哪些不要?這就是子詞Tokenization算法起作用的地方。
3.Subword Tokenization算法
3-1?字節對編碼(Byte pair encoding,BPE)
3-1-1 BPE簡介
????????BPE最早是一種數據壓縮算法,由Sennrich等人于2015年引入到NLP領域并很快得到推廣。該算法簡單有效,因而目前它是最流行的方法。GPT-2和RoBERTa使用的Subword算法都是BPE。BPE算法的步驟如下:
????????1.給定的數據集中抽取單詞以及相應的頻次,并確定詞表大小;
????????2.將單詞拆分為字符序列,比如英文中26個字母加上各種符號,這些作為初始詞表;
????????3.將所有字符序列中的字符不重復地添加到詞表中,選擇并合并頻次最高的相鄰字符對;
? ? ? ? 4.重復步驟3直到滿足詞表大小。
3-1-2 BPE實例
????????下面以一個例子來說明。
? ? ? ? 1.假設有語料集經過統計后表示為:(cost, 2), (best, 2), (menu, 1), (men, 1),(camel, 1)。
? ? ? ? 2.將這些單詞拆分成字符變成一個字符序列,并定義要構建Subword詞表大小為14。下面的表格顯示這些字符序列以及它們對應的單詞次數:
????????3. 將所有字符序列中的字符不重復地添加到詞表中:
????????現在的詞表(右邊的Vocabulary)大小為 11。下面看看如何增加新標記到詞表中。
????????首先,我們找出出現次數最多的相鄰的字符對。然后我們合并這個字符對并加入到詞表中。我們不斷重復該步驟知道達到詞表大小。讓我們來看下具體細節。
????????看下面的字符序列,我們能觀察到出現最多的字符對是s和t,因為s和t相鄰地出現了4次:
?????????所以,我們合并這兩個字符s和t成st,并將st加入到詞表,現在的詞表(右邊的Vocabulary)大小為 12,仍然需要添加新的subword到詞表中。
? ? ? ? ?接著,我們重復這一步驟。我們繼續尋找出現最多的相鄰字符對。我們又發現了現在最頻繁的字符對是m和e,它們出現了3次。
????????所以我們合并字符m和e為me并加入詞表,?現在的詞表(右邊的Vocabulary)大小為 13,仍然需要添加新的subword到詞表中:?
???????再一次,我們繼續檢查出現最多的字符對。我們此時發現最常見的字符對是me和n,它們一起出現了2次:?
?????????所以,我們繼續合并me和n為men,然后加入詞表,現在的詞表(右邊的Vocabulary)大小為 14,詞表大小達到了預定義的值,BPE算法停止。
????????在本例中,使用BPE算法創建了大小為 14的詞表:
vocabulary = {a,b,c,e,l,m,n,o,s,t,u,st,me,men}
3-1-3?BPE詞表分詞實例?
? ? ? ? 假設輸入文本只包含一個單詞,我們目前有三個文本,分別是mean、star、men,我們使用BPE算法創建的詞表進行分詞。
? ? ? ? 1.mean
? ? ? ? 首先檢查單詞mean是否出現在詞表中,檢查后發現此單詞并未出現在詞表中。所以我們將該單詞進行拆分,首先拆分為兩個子詞[me, an];然后檢查這些子詞是否出現在詞表中;發現me出現在詞表中,an則沒有;因此,繼續拆分子詞an,所以現在子詞包含[me, a, n]。現在檢查字符a和n是否出現在詞表中,現在拆分的所有子詞都出現在了詞表中,因此我們最終得到的標記如下:
tokens = [me,a,n]
????????2.star
????????首先檢查單詞star是否出現在詞表中,檢查后發現此單詞并未出現在詞表中。所以我們拆分為子詞[st, ar];然后檢查子詞是否在詞表中;發現子詞st在詞表中,而ar不在;下面繼續拆分子詞ar,所以現在子詞包括[st, a, r]。現在我們檢查字符a和r是否在詞表中。顯然a在而r不在。因為我r只是一個字符,所以我們無法繼續拆分,因此只能將它替換為<UNK>標記,這樣我們最終的標記為:
tokens = [st,a,<UNK>]
????????我們知道BPE可以很好的處理稀有詞,但是現在我們有一個<UNK>標記。這是因為我們的例子很小,字符r沒有出現在詞表中。如果我們用一個較大的語料庫創建詞表,我們的詞表肯定會包含所有的字符。
????????3.men
?????????首先檢查單詞men是否出現在詞表中,檢查后發現此單詞出現在詞表中。最終得到的標記為:
tokens = [men]
3-2?字節級字節對編碼(Byte-level byte pair encoding,BBPE)
3-2-1 BBPE簡介
????????BBP的分詞算法是針對于字符級別的,而BBPE是針對于字節級的,所以就有了字節級的字節對編碼存在的意義。BBP不是將單詞轉換為字符序列,而是轉換為字節序列。
3-2-2 實例
????????假設輸入文本僅包含一個單詞best。我們知道在BPE中,我們可以將該單詞拆分為字符序列,可以得到:
Character sequence: b e s t
????????然而在BBPE中,不是將單詞轉換為字符序列,而是轉換為字節序列。我們該單詞轉換為如下的字節序列。這樣,每個Unicode字符都轉換成一個字節。單個字符可以包含1到4個字節。
Byte sequence: 62 65 73 74
????????假設輸入是一個中文詞匯:你好。現在,我們將它轉換為字節級序列,得到:
Byte sequence: e4 bd a0 e5 a5 bd
????????通過這種方式,我們先將文本轉換為字節級序列,然后應用BPE算法根據字節級符號對構建詞表就可以了。
? ? ? ?BBPE對于多語言情況下非常有用,它能非常有效地處理未登錄詞,同時處理多語言詞表也非常出色。
3-3 ?WordPiece
3-3-1 WordPiece 簡介
????????Google的Bert模型在分詞的時候使用的是WordPiece算法。?與BPE算法類似,WordPiece算法也是每次從詞表中選出兩個子詞合并成新的子詞。與BPE的最大區別在于,如何選擇兩個子詞進行合并:BPE選擇頻數最高的相鄰子詞合并,而WordPiece選擇能夠提升語言模型概率最大(似然概率)的相鄰子詞加入詞表。所以我們合并在給定訓練集上訓練的語言模型中具有最高概率的字符對。
????????假設句子S=(t1,t2,...,tn)由n個子詞組成,ti表示子詞,且假設各個子詞之間是獨立存在的,則句子S的語言模型似然值等價于所有子詞概率的乘積:????????
????????假設把相鄰位置的x和y兩個子詞進行合并,合并后產生的子詞記為z,此時句子S似然值的變化可表示為:?
?????????似然值的變化就是兩個子詞之間的互信息。簡而言之,WordPiece每次選擇合并的兩個子詞,他們具有最大的互信息值,也就是兩子詞在語言模型上具有較強的關聯性,它們經常在語料中以相鄰方式同時出現。
3-3-2 實例
????????在BPE中,合并的是最高頻次的字符對。在BPE中,我們會合并s和t,因為它們一起出現的頻次最高。??
????????但是現在,在WordPiece中,會根據似然來合并單詞。首先,會檢查語言模型(通過給定數據集訓練的語言模型)輸出的每個字符對出現的概率。然后選取概率最高的那一對。字符對s和t出現的概率可以計算為:?
????????如果它們的概率是最高的,那么我們就合并它們并加入到詞表中。這樣,我們就計算了每個字符對出現的概率并合并概率最高的那一對到詞表中。
?3-3-3?WordPiece詞表分詞實例?
????????假設根據WordPiece構建的詞表如下:
vocabulary = {a,b,c,e,l,m,n,o,s,t,u,st,me}
???????? 假設輸入文本只包含一個單詞:stem,我們使用WordPiece算法創建的詞表進行分詞。??首先檢查單詞stem是否出現在詞表中,檢查后發現此單詞并未出現在詞表中;下面所以我們拆分為子詞[st, ##em],接下來我們檢查是否這些子詞出現在詞表中;發現子詞st在詞表中,而em不在。我們繼續拆分子詞em,現在我們得到的子詞為:[be, ##e, ##m]。現在我們繼續檢查是否字符e和m出現在詞表中。因為它們都在詞表中,所以我們最終得到的標記為:
tokens = [st, ##e, ##m]
3-4?Unigram Language Model (ULM)
3-4-1 ULM簡介
????????與WordPiece一樣,Unigram Language Model(ULM)同樣使用語言模型來挑選子詞。不同之處在于,BPE和WordPiece算法的詞表大小都是從小到大變化,屬于增量法。而Unigram Language Model則是減量法,即先初始化一個大詞表,根據評估準則不斷丟棄詞表,直到滿足限定條件。ULM算法考慮了句子的不同分詞可能,因而能夠輸出帶概率的多個子詞分段。
????????對于句子S=(x1,x2,...,xm)為句子的一個分詞結果,由m個子詞組成。所以,當前分詞下句子S的似然值可以表示為:
?????????對于句子S,挑選似然值最大的作為分詞結果,則可以表示為:
????????這里U(x)包含了句子的所有分詞結果。在實際應用中,詞表大小有上萬個,直接羅列所有可能的分詞組合不具有操作性。針對這個問題,可通過維特比算法得到來解決。?
?????????ULM通過EM算法(期望最大算法)來估計每個子詞的概率P(xi)。假設當前詞表V, 則M步最大化的對象是如下似然函數:
????????其中,|D|是語料庫中語料數量。上述公式的一個直觀理解是,將語料庫中所有句子的所有分詞組合形成的概率相加。?
????????初始時,詞表V并不存在。因而,ULM算法采用不斷迭代的方法來構造詞表以及求解分詞概率:?
????????1.初始時,建立一個足夠大的詞表。一般,可用語料中的所有字符加上常見的子字符串初始化詞表,也可以通過BPE算法初始化。
????????2.針對當前詞表,用EM算法求解每個子詞在語料上的概率。
? ? ? ? 3.對于每個子詞,計算當該子詞被從詞表中移除時,總的loss降低了多少,記為該子詞的loss。
????????4.將子詞按照loss大小進行排序,丟棄一定比例loss最小的子詞(比如20%),保留下來的子詞生成新的詞表。這里需要注意的是,單字符不能被丟棄,這是為了避免OOV情況。
????????5.重復步驟2到4,直到詞表大小減少到設定范圍。
????????ULM會保留那些以較高頻率出現在很多句子的分詞結果中的子詞,因為這些子詞如果被丟棄,其損失會很大。
Reference:
1.https://helloai.blog.csdn.net/article/details/120211601
2.NLP三大Subword模型詳解:BPE、WordPiece、ULM - 知乎?
總結
以上是生活随笔為你收集整理的【深度学习】Subword Tokenization算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 询盘是什么意思 怎么理解询盘的意思
- 下一篇: JPA EntityListeners中