【NLP】分詞算法綜述

以前老是在看前沿文章,真正落實到工業級任務仍是須要實打實的硬核基礎,我司選用了HANLP做爲分詞組件,在使用的過程當中才感覺到本身基礎的薄弱,決定最近好好把分詞的底層算法梳理一下。算法

1. 簡介

NLP的底層任務由易到難大體能夠分爲詞法分析、句法分析和語義分析。分詞是詞法分析(還包括詞性標註和命名實體識別)中最基本的任務,能夠說既簡單又複雜。說簡單是由於分詞的算法研究已經很成熟了,大部分的準確率均可以達到95%以上,說複雜是由於剩下的5%很難有突破,主要由於三點:數組

  1. 粒度,不一樣應用對粒度的要求不同,好比「蘋果手機」能夠是一個詞也能夠是兩個詞
  2. 歧義,好比「下雨天留人天留我不留」
  3. 未登陸詞,好比「skrrr」、「打call」等新興詞語

然而,在真實的應用中每每會由於以上的難點形成分詞效果欠佳,進而影響以後的任務。對於追求算法表現的童鞋來講,不只要會調分詞包,也要對這些基礎技術有必定的瞭解,在作真正的工業級應用時有能力對分詞器進行調整。這篇文章不是着重介紹某個SOTA成果,而是對經常使用的分詞算法(不只是機器學習或神經網絡,還包括動態規劃等)以及其核心思想進行介紹。網絡

2. 分詞算法

我認爲分詞算法根據其核心思想主要分爲兩種,第一種是基於字典的分詞,先把句子按照字典切分紅詞,再尋找詞的最佳組合方式;第二種是基於字的分詞,即由字構詞,先把句子分紅一個個字,再將字組合成詞,尋找最優的切分策略,同時也能夠轉化成序列標註問題。歸根結底,上述兩種方法均可以歸結爲在圖或者機率圖上尋找最短路徑的問題。接下來將以「他說的確實在理」這句話爲例,講解各個不一樣的分詞算法核心思想。數據結構

2.1 基於詞典的分詞

2.1.1 最大匹配分詞算法

最大匹配分詞尋找最優組合的方式是將匹配到的最長詞組合在一塊兒。主要的思路是先將詞典構形成一棵Trie樹,也稱爲字典樹,以下圖:併發

Trie樹由詞的公共前綴構成節點,下降了存儲空間的同時提高查找效率。最大匹配分詞將句子與Trie樹進行匹配,在匹配到根結點時從新由下一個字開始進行查找。好比正向(從左至右)匹配「他說的確實在理」,得出的結果爲「他說/的確/實在/理」。若是進行反向最大匹配,則爲「他/說/的/確實/在理」。app

可見,詞典分詞雖然能夠在O(n)時間對句子進行分詞,可是效果不好,在實際狀況中基本不使用此種方法。機器學習

2.1.2 最短路徑分詞算法

最短路徑分詞算法首先將一句話中的全部詞匹配出來,構成詞圖(有向無環圖DAG),以後尋找從起始點到終點的最短路徑做爲最佳組合方式,引用《統計天然語言處理》中的圖:ide

圖中每條邊的權重都爲1。函數

在求解DAG圖的最短路徑問題時,老是要利用到一種性質:即兩點之間的最短路徑也包含了路徑上其餘頂點間的最短路徑。好比S->A->B->E爲S到E到最短路徑,那S->A->B必定是S到B到最短路徑,不然會存在一點C使得d(S->C->B)<d(S->A->B),那S到E的最短路徑也會變爲S->C->B->E,這就與假設矛盾了。利用上述的最優子結構性質,能夠利用貪心算法或動態規劃兩種求解算法:高併發

  1. 最短路徑分詞算法

基於Dijkstra算法求解最短路徑。該算法適用於全部帶權有向圖,求解源節點到其餘全部節點的最短路徑,並能夠求得全局最優解。Dijkstra本質爲貪心算法,在每一步走到當前路徑最短的節點,遞推地更新原節點到其餘節點的距離。但應用於分詞問題時,因爲詞圖是帶權有向無環圖,沒法求得全局最優解。不過針對當前問題,Dijkstra算法的計算結果爲:「他/說/的/確實/在理「。可見最短路徑分詞算法能夠知足部分分詞要求。

2. N-最短路徑分詞算法

N-最短路徑分詞是對Dijkstra算法的擴展,在每一步保存最短的N條路徑,並記錄這些路徑上當前節點的前驅,在最後求得最優解時回溯獲得最短路徑。該方法的準確率優於Dijkstra算法,但在時間和空間複雜度上都更大。

2.1.3 基於n-gram model的分詞算法

在前文的詞圖中,邊的權重都爲1。而現實中卻不同,經常使用詞的出現頻率/機率確定比罕見詞要大。所以能夠將求解詞圖最短路徑的問題轉化爲求解最大機率路徑的問題,即分詞結果爲「最有可能的詞的組合「。計算詞出現的機率,僅有詞典是不夠的,還須要有充足的語料。所以分詞任務已經從單純的「算法」上升到了「建模」,即利用統計學方法結合大數據挖掘,對「語言」進行建模。

語言模型的目的是構建一句話出現的機率p(s),根據條件機率公式咱們知道:

p(他說的確實在理)=p(他)p(說|他)p(的|他說)p(確|他說的)...p(理|他說的確實在)

而要真正計算「他說的確實在理」出現的機率,就必須計算出上述全部形如 p(w_n|w_1...w_{n-1}) n=1,...,6 的機率,計算量太過龐大,所以咱們近似地認爲:

p(s) = \prod_{i=1}^{l}p(w_i|w_1...w_{i-1})\approx \prod_{i=1}^{l}p(w_i|w_{i-1}) \\

其中 s=w_1w_2...w_lw_i 爲字或單詞。咱們將上述模型成爲二元語言模型(2-gram model)。相似的,若是隻對詞頻進行統計,則爲一元語言模型。因爲計算量的限制,在實際應用中n通常取3。

咱們將基於詞的語言模型所統計出的機率分佈應用到詞圖中,能夠獲得詞的機率圖

對該詞圖用2.1.2中的算法求解最大機率的路徑,便可獲得分詞結果。

2.2 基於字的分詞

與基於詞典的分詞不一樣的是,基於字的分詞事先不對句子進行詞的匹配,而是將分詞當作序列標註問題,把一個字標記成B(Begin), I(Inside), O(Outside), E(End), S(Single)。所以也能夠當作是每一個字的分類問題,輸入爲每一個字及其先後字所構成的特徵,輸出爲分類標記。對於分類問題,能夠用統計機器學習或神經網絡的方法求解。

統計機器學習方法經過一系列算法對問題進行抽象,進而獲得模型,再用獲得的模型去解決類似的問題。也能夠將模型當作一個函數,輸入X,獲得f(X)=Y。另外,機器學習中通常將模型分爲兩類:生成式模型和判別式模型,二者的本質區別在於X和Y的生成關係。生成式模型以「輸出Y按照必定的規律生成輸入X」爲假設對P(X,Y)聯合機率進行建模;判別式模型認爲Y由X決定,直接對後驗機率P(Y|X)進行建模。二者各有利弊,生成模型對變量的關係描述更加清晰,而判別式模型容易創建和學習。下面對幾種序列標註方法作簡要介紹。

2.2.1 生成式模型分詞算法

生成式模型主要有n-gram模型、HMM隱馬爾可夫模型、樸素貝葉斯分類等。在分詞中應用比較多的是n-gram模型和HMM模型。若是將2.1.3中的節點由詞改爲字,則可基於字的n-gram模型進行分詞,不過這種方法的效果沒有基於詞的效果要好。

HMM模型是經常使用的分詞模型,基於Python的jieba分詞器和基於Java的HanLP分詞器都使用了HMM。要注意的是,該模型建立的機率圖與上文中的DAG圖並不一樣,由於節點具備觀測機率,因此不能再用上文中的算法求解,而應該使用Viterbi算法求解最大機率的路徑。

2.2.2 判別式模型分詞算法

判別式模型主要有感知機、SVM支持向量機、CRF條件隨機場、最大熵模型等。在分詞中經常使用的有感知機模型和CRF模型:

  1. 平均感知機分詞算法

感知機是一種簡單的二分類線性模型,經過構造超平面,將特徵空間(輸入空間)中的樣本分爲正負兩類。經過組合,感知機也能夠處理多分類問題。但因爲每次迭代都會更新模型的全部權重,被誤分類的樣本會形成很大影響,所以採用平均的方法,在處理完一部分樣本後對更新的權重進行平均。

2. CRF分詞算法

CRF能夠說是目前最經常使用的分詞、詞性標註和實體識別算法,它對未登錄詞有很好的識別能力,但開銷較大。

2.2.3 神經網絡分詞算法

目前對於序列標註任務,公認效果最好的模型是BiLSTM+CRF。結構如圖:


利用雙向循環神經網絡BiLSTM,相比於上述其它模型,能夠更好的編碼當前字等上下文信息,並在最終增長CRF層,核心是用Viterbi算法進行解碼,以獲得全局最優解,避免B,S,E這種標記結果的出現。

3. 分詞算法中的數據結構

前文主要講了分詞任務中所用到的算法和模型,但在實際的工業級應用中,僅僅有算法是不夠的,還須要高效的數據結構進行輔助。

3.1 詞典

中文有7000多個經常使用字,56000多個經常使用詞,要將這些數據加載到內存雖然容易,但進行高併發毫秒級運算是困難的,這就須要設計巧妙的數據結構和存儲方式。前文提到的Trie樹只能夠在O(n)時間完成單模式匹配,識別出「的確」後到達Trie樹對也節點,句子指針接着指向「實」,再識別「實在」,而沒法識別「確實」這個詞。若是要在O(n)時間完成多模式匹配,構建詞圖,就須要用到Aho-Corasick算法將模式串預處理爲有限狀態自動機,如模式串是he/she/his/hers,文本爲「ushers」。構建的自動機如圖:

這樣,在第一次到葉節點5時,下一步的匹配能夠直接從節點2開始,一次遍歷就能夠識別出全部的模式串。

對於數據結構的存儲,通常能夠用鏈表或者數組,二者在查找、插入和刪除操做的複雜度上各有千秋。在基於Java的高性能分詞器HanLP中,做者使用雙數組完成了Trie樹和自動機的存儲。

3.2 詞圖

圖做爲一種常見的數據結構,其存儲方式通常有兩種:

  1. 鄰接矩陣

鄰接矩陣用數組下標表明節點,值表明邊的權重,即d[i][j]=v表明節點i和節點j間的邊權重爲v。以下圖:


用矩陣存儲圖的空間複雜度較高,在存儲稀疏圖時不建議使用。

2. 鄰接表

鄰接表對圖中的每一個節點創建一個單鏈表,對於稀疏圖能夠極大地節省存儲空間。第i個單鏈表中的節點表示依附於頂點i的邊,以下圖:


在實際應用中,尤爲是用Viterbi算法求解最優路徑時,因爲是按照廣度優先的策略對圖進行遍歷,最好是使用鄰接表對圖進行存儲,便於訪問某個節點下的全部節點。

4. 總結

分詞做爲NLP底層任務之一,既簡單又重要,不少時候上層算法的錯誤都是由分詞結果致使的。所以,對於底層實現的算法工程師,不只須要深刻理解分詞算法,更須要懂得如何高效地實現。而對於上層應用的算法工程師,在實際分詞時,須要根據業務場景有選擇地應用上述算法,好比在搜索引擎對大規模網頁進行內容解析時,對分詞對速度要求大於精度,而在智能問答中因爲句子較短,對分詞的精度要求大於速度。

相關文章
相關標籤/搜索