1. 程式人生 > >自然語言處理中的N-Gram模型詳解

自然語言處理中的N-Gram模型詳解

N-Gram(有時也稱為N元模型)是自然語言處理中一個非常重要的概念,通常在NLP中,人們基於一定的語料庫,可以利用N-Gram來預計或者評估一個句子是否合理。另外一方面,N-Gram的另外一個作用是用來評估兩個字串之間的差異程度。這是模糊匹配中常用的一種手段。本文將從此開始,進而向讀者展示N-Gram在自然語言處理中的各種powerful的應用。

  • 基於N-Gram模型定義的字串距離
  • 利用N-Gram模型評估語句是否合理
  • 使用N-Gram模型時的資料平滑演算法

歡迎關注白馬負金羈的部落格 http://blog.csdn.net/baimafujinji,為保證公式、圖表得以正確顯示,強烈建議你從該地址上檢視原版博文。本部落格主要關注方向

包括:數字影象處理、演算法設計與分析、資料結構、機器學習、資料探勘、統計分析方法、自然語言處理。

基於N-Gram模型定義的字串距離

在自然語言處理時,最常用也最基礎的一個操作是就是“模式匹配”,或者稱為“字串查詢”。而模式匹配(字串查詢)又分為精確匹配模糊匹配兩種。

所謂精確匹配,大家應該並不陌生,比如我們要統計一篇文章中關鍵詞 “information” 出現的次數,這時所使用的方法就是精確的模式匹配。這方面的演算法也比較多,而且應該是計算機相關專業必修的基礎課中都會涉及到的內容,例如KMP演算法、BM演算法和BMH演算法等等。

另外一種匹配就是所謂的模糊匹配,它的應用也隨處可見。例如,一般的文書處理軟體(例如,Microsoft Word等)都會提供拼寫檢查功能。當你輸入一個錯誤的單詞,例如 “ informtaion

” 時,系統會提示你是否要輸入的詞其實是 “information” 。將一個可能錯拼單詞對映到一個推薦的正確拼寫上所採用的技術就是模糊匹配。

模糊匹配的關鍵在於如何衡量兩個長得很像的單詞(或字串)之間的“差異”。這種差異通常又稱為“距離”。這方面的具體演算法有很多,例如基於編輯距離的概念,人們設計出了 Smith-Waterman 演算法和Needleman-Wunsch 演算法,其中後者還是歷史上最早的應用動態規劃思想設計的演算法之一。現在Smith-Waterman 演算法和Needleman-Wunsch 演算法在生物資訊學領域也有重要應用,研究人員常常用它們來計算兩個DNA序列片段之間的“差異”(或稱“距離”)。甚至於在LeetCode上也有一道

“No.72 Edit Distance”,其本質就是在考察上述兩種演算法的實現。可見相關問題離我們並不遙遠。

N-Gram在模糊匹配中的應用

事實上,筆者在新出版的《演算法之美——隱匿在資料結構背後的原理》一書中已經詳細介紹了包括Needleman-Wunsch演算法、Smith-Waterman演算法、N-Gram演算法、Soundex演算法、Phonix演算法等在內的多種距離定義演算法(或模糊匹配演算法)。而今天為了引出N-Gram模型在NLP中的其他應用,我們首先來介紹一下如何利用N-Gram來定義字串之間的距離。

我們除了可以定義兩個字串之間的編輯距離(通常利用Needleman-Wunsch演算法或Smith-Waterman演算法)之外,還可以定義它們之間的N-Gram距離。N-Gram(有時也稱為N元模型)是自然語言處理中一個非常重要的概念。假設有一個字串 ss,那麼該字串的N-Gram就表示按長度 N 切分原詞得到的詞段,也就是 ss 中所有長度為 N 的子字串。設想如果有兩個字串,然後分別求它們的N-Gram,那麼就可以從它們的共有子串的數量這個角度去定義兩個字串間的N-Gram距離。但是僅僅是簡單地對共有子串進行計數顯然也存在不足,這種方案顯然忽略了兩個字串長度差異可能導致的問題。比如字串 girl 和 girlfriend,二者所擁有的公共子串數量顯然與 girl 和其自身所擁有的公共子串數量相等,但是我們並不能據此認為 girl 和girlfriend 是兩個等同的匹配。

為了解決該問題,有學者便提出以非重複的N-Gram分詞為基礎來定義 N-Gram距離這一概念,可以用下面的公式來表述: 

|GN(s)|+|GN(t)|−2×|GN(s)∩GN(t)||GN(s)|+|GN(t)|−2×|GN(s)∩GN(t)|


此處,|GN(s)||GN(s)| 是字串 ss 的 N-Gram集合,N 值一般取2或者3。以 N = 2 為例對字串Gorbachev和Gorbechyov進行分段,可得如下結果(我們用下畫線標出了其中的公共子串)。 


 


結合上面的公式,即可算得兩個字串之間的距離是8 + 9 − 2 × 4 = 9。顯然,字串之間的距離越小,它們就越接近。當兩個字串完全相等的時候,它們之間的距離就是0。

利用N-Gram計算字串間距離的Java例項

《演算法之美——隱匿在資料結構背後的原理》一書中,我們給出了在C++下實現的計算兩個字串間N-Gram距離的函式,鑑於全書程式碼已經在本部落格中釋出,這裡不再重複列出。事實上,很多語言的函式庫或者工具箱中都已經提供了封裝好的計算 N-Gram 距離的函式,下面這個例子演示了在Java中使用N-Gram 距離的方法。

針對這個例子,這裡需要說明的是:

  • 呼叫函式需要引用lucene的JAR包,我所使用的是lucene-suggest-5.0.0.jar
  • 前面我們所給出的演算法計算所得為一個絕對性的距離分值。而Java中所給出的函式在此基礎上進行了歸一化,也就是說所得之結果是一個介於0~1之間的浮點數,即0的時候表示兩個字串完全不同,而1則表示兩個字串完全相同。
import org.apache.lucene.search.spell.*;

public class NGram_distance {

    public static void main(String[] args) {

        NGramDistance ng = new NGramDistance();
        float score1 = ng.getDistance("Gorbachev", "Gorbechyov");
        System.out.println(score1);
        float score2 = ng.getDistance("girl", "girlfriend");
        System.out.println(score2);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

有興趣的讀者可以在引用相關JAR包之後在Eclipse中執行上述Java程式,你會發現,和我們預期的一樣,字串Gorbachev和Gorbechyov所得之距離評分較高(=0.7),說明二者很接近;而girl和girlfriend所得之距離評分並不高(=0.3999),說明二者並不很接近。

利用N-Gram模型評估語句是否合理

從現在開始,我們所討論的N-Gram模型跟前面講過N-Gram模型從外在來看已經大不相同,但是請注意它們內在的聯絡(或者說本質上它們仍然是統一的概念)。

為了引入N-Gram的這個應用,我們從幾個例子開始。 
首先,從統計的角度來看,自然語言中的一個句子 ss 可以由任何詞串構成,不過概率 P(s)P(s) 有大有小。例如:

  • s1s1 = 我剛吃過晚飯
  • s2s2 = 剛我過晚飯吃

顯然,對於中文而言 s1s1 是一個通順而有意義的句子,而s2s2 則不是,所以對於中文來說,P(s1)>P(s2)P(s1)>P(s2) 。但不同語言來說,這兩個概率值的大小可能會反轉。

其次,另外一個例子是,如果我們給出了某個句子的一個節選,我們其實可以能夠猜測後續的詞應該是什麼,例如

  • the large green __ . Possible answer may be “mountain” or “tree” ?
  • Kate swallowed the large green __ . Possible answer may be “pill” or “broccoli” ?

顯然,如果我們知道這個句子片段更多前面的內容的情況下,我們會得到一個更加準確的答案。這就告訴我們,前面的(歷史)資訊越多,對後面未知資訊的約束就越強。

如果我們有一個由 mm 個片語成的序列(或者說一個句子),我們希望算得概率 P(w1,w2,⋯,wm)P(w1,w2,⋯,wm) ,根據鏈式規則,可得 

P(w1,w2,⋯,wm)=P(w1)P(w2|w1)P(w3|w1,w2)⋯P(wm|w1,⋯,wm−1)P(w1,w2,⋯,wm)=P(w1)P(w2|w1)P(w3|w1,w2)⋯P(wm|w1,⋯,wm−1)


這個概率顯然並不好算,不妨利用馬爾科夫鏈的假設,即當前這個詞僅僅跟前面幾個有限的詞相關,因此也就不必追溯到最開始的那個詞,這樣便可以大幅縮減上訴算式的長度。即 

P(wi|w1,⋯,wi−1)=P(wi|wi−n+1,⋯,wi−1)P(wi|w1,⋯,wi−1)=P(wi|wi−n+1,⋯,wi−1)


特別地,對於 nn 取得較小值的情況 
當 n=1n=1, 一個一元模型(unigram model)即為 

P(w1,w2,⋯,wm)=∏i=1mP(wi)P(w1,w2,⋯,wm)=∏i=1mP(wi)


當 n=2n=2, 一個二元模型(bigram model)即為 

P(w1,w2,⋯,wm)=∏i=1mP(wi|wi−1)P(w1,w2,⋯,wm)=∏i=1mP(wi|wi−1)


當 n=3n=3, 一個三元模型(trigram model)即為 

P(w1,w2,⋯,wm)=∏i=1mP(wi|wi−2wi−1)P(w1,w2,⋯,wm)=∏i=1mP(wi|wi−2wi−1)


接下來的思路就比較明確了,可以利用最大似然法來求出一組引數,使得訓練樣本的概率取得最大值。

  • 對於unigram model而言,其中c(w1,..,wn)c(w1,..,wn) 表示 n-gram w1,..,wnw1,..,wn 在訓練語料中出現的次數,MM 是語料庫中的總字數(例如對於 yes no no no yes 而言,M=5M=5) 

    P(wi)=C(wi)MP(wi)=C(wi)M

  • 對於bigram model而言, 

    P(wi|wi−1)=C(wi−1wi)C(wi−1)P(wi|wi−1)=C(wi−1wi)C(wi−1)

  • 對於nn-gram model而言, 

    P(wi|wi−n−1,⋯,wi−1)=C(wi−n−1,⋯,wi)C(wi−n−1,⋯,wi−1)P(wi|wi−n−1,⋯,wi−1)=C(wi−n−1,⋯,wi)C(wi−n−1,⋯,wi−1)

來看一個具體的例子,假設我們現在有一個語料庫如下,其中<s1><s2><s1><s2> 是句首標記,</s2></s1></s2></s1> 是句尾標記: 

<s1><s2>yesnonononoyes</s2></s1><s1><s2>nononoyesyesyesno</s2></s1><s1><s2>yesnonononoyes</s2></s1><s1><s2>nononoyesyesyesno</s2></s1>


下面我們的任務是來評估如下這個句子的概率: 

<s1><s2>yesnonoyes</s2></s1><s1><s2>yesnonoyes</s2></s1>


我們來演示利用trigram模型來計算概率的結果 

P(yes|<s1><s2>)=12,P(no|<s2>yes)=1P(no|yesno)=12,P(yes|nono)=25P(</s2>|noyes)=12,P(</s1>|yes</s2>)=1P(yes|<s1><s2>)=12,P(no|<s2>yes)=1P(no|yesno)=12,P(yes|nono)=25P(</s2>|noyes)=12,P(</s1>|yes</s2>)=1


所以我們要求的概率就等於: 

12×1×12×25×12×1=0.0512×1×12×25×12×1=0.05

再舉一個來自文獻[1]的例子,假設現在有一個語料庫,我們統計了下面一些詞出現的數量


 


下面這個概率作為其他一些已知條件給出: 

P(i|<s>)=0.25P(english|want)=0.0011P(food|english)=0.5P(</s>|food)=0.68P(i|<s>)=0.25P(english|want)=0.0011P(food|english)=0.5P(</s>|food)=0.68


下面這個表給出的是基於Bigram模型進行計數之結果 


 


例如,其中第一行,第二列 表示給定前一個詞是 “i” 時,當前詞為“want”的情況一共出現了827次。據此,我們便可以算得相應的頻率分佈表如下。 


 


因為我們從表1中知道 “i” 一共出現了2533次,而其後出現 “want” 的情況一共有827次,所以P(want|i)=827/2533≈0.33P(want|i)=827/2533≈0.33 
現在設s1=“<s>iwantenglishfood</s>”s1=“<s>iwantenglishfood</s>” ,則可以算得 

P(s1)=P(i|<s>)P(want|i)P(english|want)P(food|english)P(</s>|food)=0.25×0.33×0.0011×0.5×0.68=0.000031P(s1)=P(i|<s>)P(want|i)P(english|want)P(food|english)P(</s>|food)=0.25×0.33×0.0011×0.5×0.68=0.000031

使用N-Gram模型時的資料平滑演算法

有研究人員用150萬詞的訓練語料來訓練 trigram 模型,然後用同樣來源的測試語料來做驗證,結果發現23%的 trigram 沒有在訓練語料中出現過。這其實就意味著上一節我們所計算的那些概率有空為 0,這就導致了資料稀疏的可能性,我們的表3中也確實有些為0的情況。對語言而言,由於資料稀疏的存在,極大似然法不是一種很好的引數估計辦法。

這時的解決辦法,我們稱之為“平滑技術”(Smoothing)或者 “減值” (Discounting)。其主要策略是把在訓練樣本中出現過的事件的概率適當減小,然後把減小得到的概率密度分配給訓練語料中沒有出現過的事件。實際中平滑演算法有很多種,例如: 
  ▸ Laplacian (add-one) smoothing 
  ▸ Add-k smoothing 
  ▸ Jelinek-Mercer interpolation 
  ▸ Katz backoff 
  ▸ Absolute discounting 
  ▸ Kneser-Ney

對於這些演算法的詳細介紹,我們將在後續的文章中結合一些例項再來進行討論。

A Final Word

如果你能從前面那些繁冗、複雜的概念和公式中挺過來,恭喜你,你對N-Gram模型已經有所認識了。儘管,我們還沒來得及探討平滑演算法(但它即將出現在我的下一篇博文裡,如果你覺得還未過癮的話),但是其實你已經掌握了一個相對powerful的工具。你可以能會問,在實踐中N-Gram模型有哪些具體應用,作為本文的結束,主頁君便在此補充幾個你曾見過的或者曾經好奇它是如何實現的例子。

Eg.1 
搜尋引擎(Google或者Baidu)、或者輸入法的猜想或者提示。你在用百度時,輸入一個或幾個詞,搜尋框通常會以下拉選單的形式給出幾個像下圖一樣的備選,這些備選其實是在猜想你想要搜尋的那個詞串。再者,當你用輸入法輸入一個漢字的時候,輸入法通常可以聯絡出一個完整的詞,例如我輸入一個“劉”字,通常輸入法會提示我是否要輸入的是“劉備”。通過上面的介紹,你應該能夠很敏銳的發覺,這其實是以N-Gram模型為基礎來實現的,如果你能有這種覺悟或者想法,那我不得不恭喜你,都學會搶答了!


 

Eg.2 
某某作家或者語料庫風格的文字自動生成。這是一個相當有趣的話題。來看下面這段話(該例子取材自文獻【1】):

“You are uniformly charming!” cried he, with a smile of associating and now and then I bowed and they perceived a chaise and four to wish for.

你應該還沒有感覺到它有什麼異樣吧。但事實上這並不是由人類寫出的句子,而是計算機根據Jane Austen的語料庫利用trigram模型自動生成的文段。(Jane Austen是英國著名女作家,代表作有《傲慢與偏見》等)

再來看兩個例子,你是否能看出它們是按照哪位文豪(或者語料庫)的風格生成的嗎?

  • This shall forbid it should be branded, if renown made it empty.
  • They also point to ninety nine point six billion dollars from two hundred four oh three percent of the rates of interest stores as Mexico and Brazil on market conditions.

答案是第一個是莎士比亞,第二個是華爾街日報。最後一個問題留給讀者思考,你覺得上面兩個文段所運用的n-gram模型中,n應該等於多少?

推薦閱讀和參考文獻:

[1] Speech and Language Processing. Daniel Jurafsky & James H. Martin, 3rd. Chapter 4 
[2] 本文中的一些例子和描述來自 北京大學 常寶寶 以及 The University of Melbourne “Web Search and Text Analysis” 課程的幻燈片素材