筆記轉載於GitHub項目:https://github.com/NLP-LOVE/Introduction-NLPpython
上一章中咱們實現了塊兒不許的詞典分詞,詞典分詞沒法消歧。給定兩種分詞結果「商品 和服 務」以及「商品 和 服務」,詞典分詞不知道哪一種更加合理。git
咱們人類確知道第二種更加合理,只由於咱們從小到大接觸的都是第二種分詞,出現的次數多,因此咱們斷定第二種是正確地選擇。這就是利用了統計天然語言處理。統計天然語言處理的核心話題之一,就是如何利用統計手法對語言建模,這一章講的就是二元語法的統計語言模型。github
什麼是語言模型算法
模型指的是對事物的數學抽象,那麼語言模型指的就是對語言現象的數學抽象。準確的講,給定一個句子 w,語言模型就是計算句子的出現機率 p(w) 的模型,而統計的對象就是人工標註而成的語料庫。函數
假設構建以下的小型語料庫:學習
商品 和 服務 商品 和服 物美價廉 服務 和 貨幣
每一個句子出現的機率都是 1/3,這就是語言模型。然而 p(w) 的計算很是難:句子數量無窮無盡,沒法枚舉。即使是大型語料庫,也只能「枚舉」有限的數百萬個句子。實際遇到的句子大部分都在語料庫以外,意味着它們的機率都被看成0,這種現象被稱爲數據稀疏。spa
句子幾乎不重複,單詞卻一直在重複使用,因而咱們把句子表示爲單詞列表 \(w=w_1w_2...w_k\) ,每一個 \(w_t,t\in[1,k]\) 都是一個單詞,而後定義語言模型:
\[ \begin{aligned} p(\boldsymbol{w}) &=p\left(w_{1} w_{2} \cdots w_{k}\right) \\ &=p\left(w_{1} | w_{0}\right) \times p\left(w_{2} | w_{0} w_{1}\right) \times \cdots \times p\left(w_{k+1} | w_{0} w_{1} w_{2} \dots w_{k}\right) \\ &=\prod_{t=1}^{k+1} p\left(w_{t} | w_{0} w_{1} \cdots w_{t-1}\right) \end{aligned} \]
其中,\(w_0=BOS\) (Begin Of Sentence,有時用<s>),\(w_{k+1}=EOS (End Of Sentence,有時也用</s>)\),是用來標記句子收尾的兩個特殊「單詞」,在NLP領域的文獻和代碼中常常出現。code
然而隨着句子長度的增大,語言模型會遇到以下兩個問題。對象
馬爾可夫鏈與二元語法索引
爲了解決以上兩個問題,須要使用馬爾可夫假設來簡化語言模型,給定時間線上有一串事件順序發生,假設每一個事件的發生機率只取決於前一個事件,那麼這串事件構成的因果鏈被稱做馬爾可夫鏈。
在語言模型中,第 t 個事件指的是 \(w_t\) 做爲第 t 個單詞出現。也就是說,每一個單詞出現的機率只取決於前一個單詞:
\[p(w_t|w_0w_1...w_{t-1})=p(w_t|w_{t-1})\]
基於此假設,式子一會兒變短了很多,此時的語言模型稱爲二元語法模型:
\[ \begin{aligned} p(\boldsymbol{w}) &=p\left(w_{1} w_{2} \cdots w_{k}\right) \\ &=p\left(w_{1} | w_{0}\right) \times p\left(w_{2} | w_{1}\right) \times \cdots \times p\left(w_{k+1} | w_{k}\right) \\ &=\prod_{t=1}^{k+1} p\left(w_{t} | w_{t-1}\right) \end{aligned} \]
因爲語料庫中二元連續的重複程度要高於整個句子的重要程度,因此緩解了數據稀疏的問題,另外二元連續的總數量遠遠小於句子的數量,存儲和查詢也獲得瞭解決。
n元語法
利用相似的思路,能夠獲得n元語法的定義:每一個單詞的機率僅取決於該單詞以前的 n 個單詞:
\[ p(w)=\prod_{t=1}^{k+n-1} p\left(w_{t} | w_{t-n+1} \dots w_{t-1}\right) \]
特別地,當 n=1 時的 n 元語法稱爲一元語法 ( unigram);當 n=3 時的 n 元語法稱爲三元語法(tigam); n≥4時數據稀疏和計算代價又變得顯著起來,實際工程中幾乎不使用。
數據稀疏與平滑策略
對於 n 元語法模型,n 越大,數據稀疏問題越嚴峻。好比上述語料庫中「商品 貨幣」的頻次就爲0。一個天然而然的解決方案就是利用低階 n 元語法平滑高階 n 元語法,所謂平滑,就是字面上的意思:使 n 元語法頻次的折線平滑爲曲線。最簡單的一種是線性插值法:
\[ p\left(w_{t} | w_{t-1}\right)=\lambda p_{\mathrm{ML}}\left(w_{t} | w_{t-1}\right)+(1-\lambda) p\left(w_{t}\right) \]
其中,\(\lambda\in(0,1)\) 爲常數平滑因子。通俗理解,線性插值就是劫富濟貧的稅賦制度,其中的 λ 就是我的所得稅的稅率。\(p_{ML}(w_t|w_{t-1})\) 是稅前所得,\(p(w_t)\) 是社會福利。 經過繳稅,高收人(高几率)二元語法的一部分收人 (機率)被移動到社會福利中。而零收入(語料庫統計不到頻次)的一元語法可以從社會福利中取得點低保金, 不至於餓死。低保金的額度與二元語法掙錢潛力成正比:二元語法中第二個詞詞頻越高,它將來被統計到的機率也應該越高,所以它應該多拿一點。
相似地,一元語法也能夠經過線性插值來平滑:
\[ p\left(w_{t}\right)=\lambda p_{\mathrm{ML}}\left(w_{t}\right)+(1-\lambda) \frac{1}{N} \]
其中,N 是語料庫總詞頻。
語言模型只是一個函數的骨架,函數的參數須要在語料庫上統計才能獲得。爲了知足實際工程須要,一個質量高、份量足的語料庫必不可少。如下是經常使用的語料庫:
語料庫 | 字符數 | 詞語種數 | 總詞頻 | 平均詞長 |
---|---|---|---|---|
PKU | 183萬 | 6萬 | 111萬 | 1.6 |
MSR | 405萬 | 9萬 | 237萬 | 1.7 |
AS | 837萬 | 14萬 | 545萬 | 1.5 |
CITYU | 240萬 | 7萬 | 146萬 | 1.7 |
通常採用MSR做爲分詞語料的首選,有如下緣由:
訓練指的是統計二元語法頻次以及一元語法頻次,有了頻次,經過極大似然估計以及平滑策略,咱們就能夠估計任意句子的機率分佈,即獲得了語言模型。這裏以二元語法爲例:
這裏咱們選用上面本身構造的小型語料庫:data/dictionnary/my_cws_corpus.txt
代碼請見:code/ch03/ngram_segment.py
步驟以下:
加載語料庫文件並進行詞頻統計。
對詞頻文件生成詞網
詞網指的是句子中全部一元語法構成的網狀結構,是HanLP工程上的概念。好比「商品和服務」這個句子,咱們將句子中全部單詞找出來,起始位置(offset)相同的單詞寫做一行:
0:[ ] 1:[商品] 2:[] 3:[和,和服] 4:[服務] 5:[務] 6:[ ]
其中收尾(行0和行6)分別對應起始和末尾。詞網必須保證從起點出發的全部路徑都會連通到鐘點房。
詞網有一個極佳的性質:那就是第 i 行的詞語 w 與第 i+len(w) 行的全部詞語相連都能構成二元語法。
詞圖上的維特比算法
上述詞圖每條邊以二元語法的機率做爲距離,那麼中文分詞任務轉換爲有向無環圖上的最長路徑問題。再經過將浮點數乘法轉化爲負對數之間的加法,相應的最長路徑轉化爲負對數的最短路徑。使用維特比算法求解。
這裏僅做一下簡述,詳細過程參考書本第三章。
該模型代碼輸入是句子「貨幣和服務」,獲得結果以下:
[' ', '貨幣', '和', '服務', ' ']
結果正確,可見咱們的二元語法模型具有必定的泛化能力。
詞典每每廉價易得,資源豐富,利用統計模型的消歧能力,輔以用戶詞典處理新詞,是提升分詞器準確率的有效方式。HanLP支持 2 檔用戶詞典優先級:
HanLP分詞器簡潔版:
from pyhanlp import * ViterbiSegment = SafeJClass('com.hankcs.hanlp.seg.Viterbi.ViterbiSegment') segment = ViterbiSegment() sentence = "社會搖擺簡稱社會搖" segment.enableCustomDictionary(False) print("不掛載詞典:", segment.seg(sentence)) CustomDictionary.insert("社會搖", "nz 100") segment.enableCustomDictionary(True) print("低優先級詞典:", segment.seg(sentence)) segment.enableCustomDictionaryForcing(True) print("高優先級詞典:", segment.seg(sentence))
輸出:
不掛載詞典: [社會/n, 搖擺/v, 簡稱/v, 社會/n, 搖/v] 低優先級詞典: [社會/n, 搖擺/v, 簡稱/v, 社會搖/nz] 高優先級詞典: [社會搖/nz, 擺/v, 簡稱/v, 社會搖/nz]
可見,用戶詞典的高優先級未必是件好事,HanLP中的用戶詞典默認低優先級,作項目時請讀者在理解上述說明的狀況下根據實際需求自行開啓高優先級。
按照NLP任務的通常流程,咱們已經完成了語料標註和模型訓練,如今來比較一下二元語法和詞典分詞的評測:
算法 | P | R | F1 | R(oov) | R(IV) |
---|---|---|---|---|---|
最長匹配 | 89.41 | 94.64 | 91.95 | 2.58 | 97.14 |
二元語法 | 92.38 | 96.70 | 94.49 | 2.58 | 99.26 |
相較於詞典分詞,二元語法在精確度、召回率及IV召回率上全面勝出,最終F1值提升了 2.5%,成績的提升主要受惠於消歧能力的提升。然而 OOV 召回依然是 n 元語法模型的硬傷,咱們須要更強大的語言模型。
HanLP何晗--《天然語言處理入門》筆記:
https://github.com/NLP-LOVE/Introduction-NLP
項目持續更新中......
目錄
章節 |
---|
第 1 章:新手上路 |
第 2 章:詞典分詞 |
第 3 章:二元語法與中文分詞 |
第 4 章:隱馬爾可夫模型與序列標註 |
第 5 章:感知機分類與序列標註 |
第 6 章:條件隨機場與序列標註 |
第 7 章:詞性標註 |
第 8 章:命名實體識別 |
第 9 章:信息抽取 |
第 10 章:文本聚類 |
第 11 章:文本分類 |
第 12 章:依存句法分析 |
第 13 章:深度學習與天然語言處理 |