簡(jiǎn)介
NLP的底層任務(wù)由易到難大致可以分為詞法分析、句法分析和語義分析。分詞是詞法分析(還包括詞性標(biāo)注和命名實(shí)體識(shí)別)中最基本的任務(wù),可以說既簡(jiǎn)單又復(fù)雜。說簡(jiǎn)單是因?yàn)榉衷~的算法研究已經(jīng)很成熟了,大部分的準(zhǔn)確率都可以達(dá)到95%以上,說復(fù)雜是因?yàn)槭O碌?%很難有突破,主要因?yàn)槿c(diǎn):
粒度,不同應(yīng)用對(duì)粒度的要求不一樣,比如“蘋果手機(jī)”可以是一個(gè)詞也可以是兩個(gè)詞
歧義,比如“下雨天留人天留我不留”
未登錄詞,比如“skrrr”、“打call”等新興詞語
然而,在真實(shí)的應(yīng)用中往往會(huì)因?yàn)橐陨系碾y點(diǎn)造成分詞效果欠佳,進(jìn)而影響之后的任務(wù)。對(duì)于追求算法表現(xiàn)的童鞋來說,不僅要會(huì)調(diào)分詞包,也要對(duì)這些基礎(chǔ)技術(shù)有一定的了解,在做真正的工業(yè)級(jí)應(yīng)用時(shí)有能力對(duì)分詞器進(jìn)行調(diào)整。這篇文章不是著重介紹某個(gè)SOTA成果,而是對(duì)常用的分詞算法(不僅是機(jī)器學(xué)習(xí)或神經(jīng)網(wǎng)絡(luò),還包括動(dòng)態(tài)規(guī)劃等)以及其核心思想進(jìn)行介紹。
分詞算法
我認(rèn)為分詞算法根據(jù)其核心思想主要分為兩種,第一種是基于詞典的分詞,先把句子按照字典切分成詞,再尋找詞的最佳組合方式;第二種是基于字的分詞,即由字構(gòu)詞,先把句子分成一個(gè)個(gè)字,再將字組合成詞,尋找最優(yōu)的切分策略,同時(shí)也可以轉(zhuǎn)化成序列標(biāo)注問題。歸根結(jié)底,上述兩種方法都可以歸結(jié)為在圖或者概率圖上尋找最短路徑的問題。接下來將以“他說的確實(shí)在理”這句話為例,講解各個(gè)不同的分詞算法核心思想。
1.基于詞典的分詞
最大匹配分詞算法
最大匹配分詞尋找最優(yōu)組合的方式是將匹配到的最長(zhǎng)詞組合在一起。主要的思路是先將詞典構(gòu)造成一棵Trie樹,也稱為字典樹,如下圖:
Trie樹由詞的公共前綴構(gòu)成節(jié)點(diǎn),降低了存儲(chǔ)空間的同時(shí)提升查找效率。最大匹配分詞將句子與Trie樹進(jìn)行匹配,在匹配到根結(jié)點(diǎn)時(shí)由下一個(gè)字重新開始進(jìn)行查找。比如正向(從左至右)匹配“他說的確實(shí)在理”,得出的結(jié)果為“他/說/的確/實(shí)在/理”。如果進(jìn)行反向最大匹配,則為“他/說/的/確實(shí)/在理”。
可見,詞典分詞雖然可以在O(n)時(shí)間對(duì)句子進(jìn)行分詞,但是效果很差,在實(shí)際情況中基本不使用此種方法。
最短路徑分詞算法
最短路徑分詞算法首先將一句話中的所有詞匹配出來,構(gòu)成詞圖(有向無環(huán)圖DAG),之后尋找從起始點(diǎn)到終點(diǎn)的最短路徑作為最佳組合方式,引用《統(tǒng)計(jì)自然語言處理》中的圖:
我們認(rèn)為圖中每個(gè)詞的權(quán)重都是相等的,因此每條邊的權(quán)重都為1。
在求解DAG圖的最短路徑問題時(shí),總是要利用到一種性質(zhì):即兩點(diǎn)之間的最短路徑也包含了路徑上其他頂點(diǎn)間的最短路徑。比如S->A->B->E為S到E到最短路徑,那S->A->B一定是S到B到最短路徑,否則會(huì)存在一點(diǎn)C使得d(S->C->B)<d(S->A->B),那S到E的最短路徑也會(huì)變?yōu)镾->C->B->E,這就與假設(shè)矛盾了。利用上述的最優(yōu)子結(jié)構(gòu)性質(zhì),可以利用貪心算法或動(dòng)態(tài)規(guī)劃兩種求解算法:
1. 最短路徑分詞算法
基于Dijkstra算法求解最短路徑。該算法適用于所有帶權(quán)有向圖,求解源節(jié)點(diǎn)到其他所有節(jié)點(diǎn)的最短路徑,并可以求得全局最優(yōu)解。Dijkstra本質(zhì)為貪心算法,在每一步走到當(dāng)前路徑最短的節(jié)點(diǎn),遞推地更新原節(jié)點(diǎn)到其他節(jié)點(diǎn)的距離。針對(duì)當(dāng)前問題,Dijkstra算法的計(jì)算結(jié)果為:“他/說/的/確實(shí)/在理“。可見最短路徑分詞算法可以滿足部分分詞要求。但當(dāng)存在多條距離相同的最短路徑時(shí),Dijkstra只保存一條,對(duì)其他路徑不公平,也缺乏理論依據(jù)。
2. N-最短路徑分詞算法
N-最短路徑分詞是對(duì)Dijkstra算法的擴(kuò)展,在每一步保存最短的N條路徑,并記錄這些路徑上當(dāng)前節(jié)點(diǎn)的前驅(qū),在最后求得最優(yōu)解時(shí)回溯得到最短路徑。該方法的準(zhǔn)確率優(yōu)于Dijkstra算法,但在時(shí)間和空間復(fù)雜度上都更大。
基于n-gram model的分詞算法
在前文的詞圖中,邊的權(quán)重都為1。而現(xiàn)實(shí)中卻不一樣,常用詞的出現(xiàn)頻率/概率肯定比罕見詞要大。因此可以將求解詞圖最短路徑的問題轉(zhuǎn)化為求解最大概率路徑的問題,即分詞結(jié)果為“最有可能的詞的組合“。計(jì)算詞出現(xiàn)的概率,僅有詞典是不夠的,還需要有充足的語料。因此分詞任務(wù)已經(jīng)從單純的“算法”上升到了“建模”,即利用統(tǒng)計(jì)學(xué)方法結(jié)合大數(shù)據(jù)挖掘,對(duì)“語言”進(jìn)行建模。
語言模型的目的是構(gòu)建一句話出現(xiàn)的概率p(s),根據(jù)條件概率公式我們知道:
而要真正計(jì)算“他說的確實(shí)在理”出現(xiàn)的概率,就必須計(jì)算出上述的概率,計(jì)算量太過龐大,因此我們近似地認(rèn)為:
其中
為字或單詞。我們將上述模型稱為二元語言模型(2-gram model)。類似的,如果只對(duì)詞頻進(jìn)行統(tǒng)計(jì),則為一元語言模型。由于計(jì)算量的限制,在實(shí)際應(yīng)用中n一般取3。
我們將基于詞的語言模型所統(tǒng)計(jì)出的概率分布應(yīng)用到詞圖中,可以得到詞的概率圖:
對(duì)該詞圖用最短路徑的算法求解最大概率的路徑,即可得到分詞結(jié)果。
2.基于字的分詞
與基于詞典的分詞不同的是,基于字的分詞事先不對(duì)句子進(jìn)行詞的匹配,而是將分詞看成序列標(biāo)注問題,把一個(gè)字標(biāo)記成B(Begin), I(Inside), O(Outside), E(End), S(Single)。因此也可以看成是每個(gè)字的分類問題,輸入為每個(gè)字及其前后字所構(gòu)成的特征,輸出為分類標(biāo)記。對(duì)于分類問題,可以用統(tǒng)計(jì)機(jī)器學(xué)習(xí)或神經(jīng)網(wǎng)絡(luò)的方法求解。
統(tǒng)計(jì)機(jī)器學(xué)習(xí)方法通過一系列算法對(duì)問題進(jìn)行抽象,進(jìn)而得到模型,再用得到的模型去解決相似的問題。也可以將模型看成一個(gè)函數(shù),輸入X,得到f(X)=Y。另外,機(jī)器學(xué)習(xí)中一般將模型分為兩類:生成式模型和判別式模型,兩者的本質(zhì)區(qū)別在于X和Y的生成關(guān)系。生成式模型以“輸出Y按照一定的規(guī)律生成輸入X”為假設(shè)對(duì)P(X,Y)聯(lián)合概率進(jìn)行建模;判別式模型認(rèn)為Y由X決定,直接對(duì)后驗(yàn)概率P(Y|X)進(jìn)行建模。兩者各有利弊,生成模型對(duì)變量的關(guān)系描述更加清晰,而判別式模型容易建立和學(xué)習(xí)。下面對(duì)幾種序列標(biāo)注方法做簡(jiǎn)要介紹。
生成式模型分詞算法
生成式模型主要有n-gram模型、HMM隱馬爾可夫模型、樸素貝葉斯分類等。在分詞中應(yīng)用比較多的是n-gram模型和HMM模型。如果將2.1.3中的節(jié)點(diǎn)由詞改成字,則可基于字的n-gram模型進(jìn)行分詞,不過這種方法的效果沒有基于詞的效果要好。
HMM模型認(rèn)為在解決序列標(biāo)注問題時(shí)存在兩種序列,一種是觀測(cè)序列,即人們顯性觀察到的句子,而序列標(biāo)簽是隱狀態(tài)序列,即觀測(cè)序列為X,隱狀態(tài)序列是Y,因果關(guān)系為Y->X。因此要得到標(biāo)注結(jié)果Y,必須對(duì)X的概率、Y的概率、P(X|Y)進(jìn)行計(jì)算,即建立P(X,Y)的概率分布模型。例句的隱馬爾科夫序列如下圖:
HMM模型是常用的分詞模型,基于Python的jieba分詞器和基于Java的HanLP分詞器都使用了HMM。要注意的是,該模型創(chuàng)建的概率圖與上文中的DAG圖并不同,因?yàn)楣?jié)點(diǎn)具有觀測(cè)概率,所以不能再用上文中的算法求解,而應(yīng)該使用Viterbi算法求解最大概率的路徑。
判別式模型分詞算法
判別式模型主要有感知機(jī)、SVM支持向量機(jī)、CRF條件隨機(jī)場(chǎng)、最大熵模型等。在分詞中常用的有感知機(jī)模型和CRF模型:
- 平均感知機(jī)分詞算法
感知機(jī)是一種簡(jiǎn)單的二分類線性模型,通過構(gòu)造超平面,將特征空間(輸入空間)中的樣本分為正負(fù)兩類。通過組合,感知機(jī)也可以處理多分類問題。但由于每次迭代都會(huì)更新模型的所有權(quán)重,被誤分類的樣本會(huì)造成很大影響,因此采用平均的方法,在處理完一部分樣本后對(duì)更新的權(quán)重進(jìn)行平均。
- CRF分詞算法
CRF可以看作一個(gè)無向圖模型,對(duì)于給定的標(biāo)注序列Y和觀測(cè)序列X,對(duì)條件概率P(Y|X)進(jìn)行定義,而不是對(duì)聯(lián)合概率建模。CRF可以說是目前最常用的分詞、詞性標(biāo)注和實(shí)體識(shí)別算法,它對(duì)未登陸詞有很好的識(shí)別能力,但開銷較大。
神經(jīng)網(wǎng)絡(luò)分詞算法
在NLP中,最常用的神經(jīng)網(wǎng)絡(luò)為循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN,Recurrent Neural Network),它在處理變長(zhǎng)輸入和序列輸入問題中有著巨大的優(yōu)勢(shì)。LSTM為RNN變種的一種,在一定程度上解決了RNN在訓(xùn)練過程中梯度消失和梯度爆炸的問題。雙向(Bidirectional)循環(huán)神經(jīng)網(wǎng)絡(luò)分別從句子的開頭和結(jié)尾開始對(duì)輸入進(jìn)行處理,將上下文信息進(jìn)行編碼,提升預(yù)測(cè)效果。
目前對(duì)于序列標(biāo)注任務(wù),公認(rèn)效果最好的模型是BiLSTM+CRF。結(jié)構(gòu)如圖:
利用雙向循環(huán)神經(jīng)網(wǎng)絡(luò)BiLSTM,相比于上述其它模型,可以更好的編碼當(dāng)前字等上下文信息,并在最終增加CRF層,核心是用Viterbi算法進(jìn)行解碼,以得到全局最優(yōu)解,避免B,S,E這種標(biāo)記結(jié)果的出現(xiàn)。
3.分詞算法中的數(shù)據(jù)結(jié)構(gòu)
前文主要講了分詞任務(wù)中所用到的算法和模型,但在實(shí)際的工業(yè)級(jí)應(yīng)用中,僅僅有算法是不夠的,還需要高效的數(shù)據(jù)結(jié)構(gòu)進(jìn)行輔助。
詞典
中文有7000多個(gè)常用字,56000多個(gè)常用詞,要將這些數(shù)據(jù)加載到內(nèi)存雖然容易,但進(jìn)行高并發(fā)毫秒級(jí)運(yùn)算是困難的,這就需要設(shè)計(jì)巧妙的數(shù)據(jù)結(jié)構(gòu)和存儲(chǔ)方式。前文提到的Trie樹只可以在O(n)時(shí)間完成單模式匹配,識(shí)別出“的確”后到達(dá)Trie樹對(duì)也節(jié)點(diǎn),句子指針接著指向“實(shí)”,再識(shí)別“實(shí)在”,而無法識(shí)別“確實(shí)”這個(gè)詞。如果要在O(n)時(shí)間完成多模式匹配,構(gòu)建詞圖,就需要用到Aho-Corasick算法將模式串預(yù)處理為有限狀態(tài)自動(dòng)機(jī),如模式串是he/she/his/hers,文本為“ushers”。構(gòu)建的自動(dòng)機(jī)如圖:
這樣,在第一次到葉節(jié)點(diǎn)5時(shí),下一步的匹配可以直接從節(jié)點(diǎn)2開始,一次遍歷就可以識(shí)別出所有的模式串。
對(duì)于數(shù)據(jù)結(jié)構(gòu)的存儲(chǔ),一般可以用鏈表或者數(shù)組,兩者在查找、插入和刪除操作的復(fù)雜度上各有千秋。在基于Java的高性能分詞器HanLP中,作者使用雙數(shù)組完成了Trie樹和自動(dòng)機(jī)的存儲(chǔ)。
詞圖
圖作為一種常見的數(shù)據(jù)結(jié)構(gòu),其存儲(chǔ)方式一般有兩種:
- 鄰接矩陣
鄰接矩陣用數(shù)組下標(biāo)代表節(jié)點(diǎn),值代表邊的權(quán)重,即d[i][j]=v代表節(jié)點(diǎn)i和節(jié)點(diǎn)j間的邊權(quán)重為v。如下圖:
用矩陣存儲(chǔ)圖的空間復(fù)雜度較高,在存儲(chǔ)稀疏圖時(shí)不建議使用。
- 鄰接表
鄰接表對(duì)圖中的每個(gè)節(jié)點(diǎn)建立一個(gè)單鏈表,對(duì)于稀疏圖可以極大地節(jié)省存儲(chǔ)空間。第i個(gè)單鏈表中的節(jié)點(diǎn)表示依附于頂點(diǎn)i的邊,如下圖:
在實(shí)際應(yīng)用中,尤其是用Viterbi算法求解最優(yōu)路徑時(shí),由于是按照廣度優(yōu)先的策略對(duì)圖進(jìn)行遍歷,最好是使用鄰接表對(duì)圖進(jìn)行存儲(chǔ),便于訪問某個(gè)節(jié)點(diǎn)下的所有節(jié)點(diǎn)。
總結(jié)
分詞作為NLP底層任務(wù)之一,既簡(jiǎn)單又重要,很多時(shí)候上層算法的錯(cuò)誤都是由分詞結(jié)果導(dǎo)致的。因此,對(duì)于底層實(shí)現(xiàn)的算法工程師,不僅需要深入理解分詞算法,更需要懂得如何高效地實(shí)現(xiàn)。而對(duì)于上層應(yīng)用的算法工程師,在實(shí)際分詞時(shí),需要根據(jù)業(yè)務(wù)場(chǎng)景有選擇地應(yīng)用上述算法,比如在搜索引擎對(duì)大規(guī)模網(wǎng)頁進(jìn)行內(nèi)容解析時(shí),對(duì)分詞對(duì)速度要求大于精度,而在智能問答中由于句子較短,對(duì)分詞的精度要求大于速度。
參考:https://zhuanlan.zhihu.com/p/50444885
答疑
tire樹:https://zhuanlan.zhihu.com/p/52419343
序列標(biāo)注:https://zhuanlan.zhihu.com/p/50184092