一、背景
1. 問題
- 一切模型始於問題,我們首先拋出一個問題:如何計算一段文本出現的概率?
這個其實是語言模型要解決的問題,如果它解決了,那么對話系統就可以從生成句子的候選集中選擇出現概率最大的進行回答;翻譯模型也可以選擇最合理的一句話作為翻譯結果,一切變得簡單直接。
2. 形式化描述
文本序列用S表示,由詞條w1, w2 ,..., wT構成。
![]()
基於統計的聯合概率表示如下:
3. 直觀方案
解決這個問題最直觀的想法是:把全部文本遍歷完,統計該文本出現的頻數,計算出現概率。不過這樣的回答你自己都會覺得心虛吧~ 首先文本有着數據稀疏的特點,語料中每個文本的頻數都很低,很有可能也導致該文本出現的概率為0。如果該文本和語料中某句話只一字之差,忽略了語句的相似度,這樣計算出現概率就非常不合理。
看來直觀方案並不理想,接下來我們來看n-gram模型是如何解決這個問題的。
二、n-gram模型
1. 介紹
n-gram模型是一種簡單有效的統計語言模型,它作為語言模型的基石,很多其他的模型都是從它開始改進演變的。它基於馬爾科夫假設,假設第T個詞的出現只與前面 n-1 個詞相關,與其它任何詞都不相關。那么,該文本出現的概率就是各個詞出現的后驗概率的乘積。這些概率可以通過似然函數,也就是頻數直接從語料中統計獲得。通常n由於計算量太大,參數受限,常采用1-3之間的值,它們分別稱為unigram、bigram和trigram,其他值可以直接稱為n-gram,例如:4-gram、5-gram..
另外,n-gram除了以上所說的語言模型,還指的是長度為n的詞段,比如這句話 John read a book 可以分解:
- bigram序列:{John, read}, {read, a}, {a, book}
- trigram序列:{John, read, a}, {read, a, book}
2. 解決方案
利用n-gram解決該問題的思路如下:

其中,
![]()
例如 bigram利用貝葉斯理論可得如下公式,等式右邊可根據詞頻數統計得到出現概率,到此為止以上問題解決。

舉個🌰:
給定訓練語料合計三個文檔如下:
D1: John read Moby Dick
D2: Mary read a different book
D3: She read a book by Cher
利用bigram求出句子“John read a book”的概率?
p(John read a book)
= p(John | <begain>) * p(read | John) * p(a | read) * p(book | a) * p(<end> | book)
= 1/3 * 1 * 2/3 * 1/2 * 1/2 =1/18
注意:開始符號<begain>和結束符號<end>。
3. 遇到問題
(1)拉普拉斯平滑
add-one
add-one是最簡單、最直觀的一種平滑算法。我們希望沒有出現過的n-gram的概率不再是0,那就規定任何一個n-gram在訓練語料至少出現一次(注意這里是指任意組成n-gram詞段,不分語序)。
默認任意一個n-gram頻數都加 1,則wt-1的頻數加|V|,V是指wt-1和所有的詞可能組成不同n-gram的數量(也就是語料的總詞條數量)。由於語料中n-gram頻數為0的太多,平滑后,它們占據了整個概率分布中的一個很大的比例。而且,對於未出現在語料中的n-gram,都增加同樣的頻度值1也不太合適。
add-k
由Add-one衍生出來的另外一種算法就是 Add-k。上面我們認為加1有點多,那么可以選擇0-1之間的k值。

(2)內插
把n階拆解為不同低階別的n-gram模型線形加權組合后再來使用,權重可以自由指定,也可以通過em算法學習得到。這樣n階頻數為0時,我們可以利用低階的頻數來計算。
![]()
(3)回溯
與內插有點像,它會盡可能地用最高階組合計算概率,當高階組合不存在時,退而求其次找次低階,直到找到非零組合為止。參考下式,這是一個遞歸運算。

4. 其他應用
(1)字符串比較相似度
以上介紹的n-gram可以將字符串分解成由 n 個詞組成的詞段集合,可利用詞段集合的交集求得字符串之間的相似度。對於兩個句子 S 和 T ,相似度如下:
Similarity = |GN(S)| + |GN(T)| − 2∗|GN(S)∩GN(T)|
其中,GN(S) 和 GN(T) 分別表示字符串 S 和 T 中 n-gram 的集合,n一般取 2或 3。字符串距離越近,它們就越相似,當兩個字符串完全相等時,距離為0。
獲取長度為n的詞段參考代碼(環境python2.7),以下列出兩種方式:
方式1:
1 # coding:utf-8 2 import json 3 4 tokens = ['小', '狗', '愛', '啃', '骨', '頭'] 5 6 def get_ngrams(tokens, n): 7 length = len(tokens) 8 for i in range(length - n + 1): 9 yield tokens[i:i+n] 10 11 ngrams_generator = get_ngrams(tokens, 3) 12 13 for tmp_ngram in ngrams_generator: 14 print(json.dumps(tmp_ngram, ensure_ascii=False))
方式2:
1 def get_ngrams(tokens, n): 2 length = len(tokens) 3 # 生成器 4 slices = (tokens[i:length-n+i+1] for i in range(n)) 5 #print(slices, type(slices)) 6 return zip(*slices) 7 8 ngrams_generator = get_ngrams(tokens, 3) 9 10 for tmp_ngram in ngrams_generator: 11 print(tmp_ngram)
輸出:
["小", "狗", "愛"]
["狗", "愛", "啃"]
["愛", "啃", "骨"]
["啃", "骨", "頭"]
(2)分詞
n-gram用在分詞中,可以計算每種切分方式得到的候選句子出現的概率最大,獲取分詞結果。
一般也用來結合正向最大匹配和反向最大匹配分詞方法使用,使用兩種匹配方式得到的分詞結果,如果相同,則返回。如果不同,則利用n-gram只計算不同的部分,看哪種分詞更合理。
(3)搜索提示詞

三、其他語言模型
n-gram是基於馬爾科夫假設,當n=1 時,就會回退為一種最簡單的假設:假設每個詞和其他詞出現相互條件獨立。大家是不是想到了朴素貝葉斯算法,它也是利用了這個假設。以下就是n-gram里的unigram,即:
![]()
雖然利用似然函數通過頻數可計算得出以上等式右邊每個詞出現的概率,但這種完全忽略了詞條之間的語義相似度,嚴重依賴訓練語料,導致計算結果不理想。
我們發現,n-gram這種基於統計的語言模型,會有以下缺點:
(1)數據離散化,忽略詞條的語義相似性
(2)因為數據稀疏導致出現概率為0的問題
(3)n不能選擇太大,否則計算量太大
基於此,引入了后續的神經網絡語言模型。
1. NNLM
NNLM和統計語言模型不同的是,不再是基於頻數來計算詞條在已知上文的前提下的條件概率,而是通過構造非線性函數 f(wt, wt-1,..., wt-n+1;θ) 利用最大似然求得未知參變量,最后得到該詞出現的后驗概率。
最大化目標函數:

網絡架構只有三層,如圖所示:

前n-1個詞通過詞向量矩陣找到對應向量,相拼接輸入到隱層,最后通過softmax獲得各詞出現的概率。
缺點:
2. RNNLM
面臨NNLM存在的問題,Mikolov在2010年提出了RNNLM,它實際上是一個時序模型,充分利用語料庫中中心詞條前面的所有單詞為條件進行語言模型建模。同時,RNN的引入后又演進出很多變體,像LSTM、BLSTM、GRU等等,從而在時間序列建模上進行更多更豐富的優化。網絡結構如下:

優點:
(1)由於共享模型參數,參數大大減少,提高訓練速度
(2)可以接受任意長度輸入
(3)n不受局限,利用完整的上文信息
缺點:
(1)每一個時間步需要依賴上一個時間步,計算速度慢
(2)容易發生梯度彌散和梯度爆炸
3. word2vec
和NNLM、RNNLM不同的是,word2vec通過借助中心詞的上下文窗口信息,來預測后驗概率。提供兩個框架CBOW和Skip-gram,CBOW是利用上下文信息來預測中心詞,輸入的上下文信息並不是拼接而是簡單加和作為輸入,而Skip-gram利用中心詞預測上下文信息。針對NNLM計算量大的缺點提出了新的訓練技巧Hierarchical Softmax(將Softmax多分類轉換為多個二分類)和Negative Sampling(負采樣)。該模型在訓練過程中獲得很有價值的副產品-詞向量,將一個高維稀疏離散向量映射到低維稠密連續向量。
參考:
https://blog.csdn.net/songbinxu/article/details/80209197
https://www.cnblogs.com/iloveai/p/word2vec.html
https://blog.csdn.net/baimafujinji/article/details/51281816
https://blog.csdn.net/baimafujinji/article/details/51297802
