細說中文分詞

完整的中文自然語言處理過程一般包括以下五種中文處理核心技術:分詞、詞性標註、命名實體識別、依存句法分析、語義分析。其中,分詞是中文自然語言處理的基礎,搜素引擎、文本挖掘、機器翻譯、關鍵詞提取、自動摘要生成等等技術都會用到中文分詞,包括最近在學習的聊天機器人、文本相似性等。可以說分詞是自然語言大廈的地基,下面就讓我們從它開始談起。

 

什麼是中文分詞

中文分詞就是將中文語句中的詞彙按照使用時的含義切分出來的過程,也就是將一個漢字序列切分成一個個有單獨含義的詞語。自20世紀80年代以來,中文自動分詞就一直是一個研究熱點,由於中文語言的複雜性使之一直處於發展階段。目前,分詞主要包含細粒度分詞和粗粒度分詞兩種,在不同的應用場景需要用到不同的粒度。細粒度分詞是指將原始語句切分成最基本的詞語,而粗粒度分詞是指將原始語句中的多個基本詞組合起來切成一個詞,進而組成語義相對明確的實體。

  • 原始串:浙江大學坐落在西湖旁邊

  • 細粒度:浙江/大學/坐落/在/西湖/旁邊

  • 粗粒度:浙江大學/坐落/在/西湖/旁邊

爲什麼要中文分詞

對於中文而言,詞是承載語義的最小單元,由詞構成語句,又由語句構成篇章。但是,中文文本是由連續的字序列構成,詞與詞之間是沒有天然的分隔符。在自然語言處理領域,國外已經做出了很多卓有成效的研究,但是那些研究大多基於英文(存在天然的分隔符),也就是說是以正確切分出單詞爲前提的。於是,NLP對於中文而言要想取得較好的科研成果,就需要準確識別詞與詞之間的邊界,也就是分詞。

接下來我們就以搜索爲例,具體的闡述一下分詞的重要性與必要性。大家都知道,目前的搜素引擎是基於一種叫做倒排索引的結構,以什麼作爲索引的key值,直接影響到整個搜索引擎的準確度、召回率以及性能。

如果不使用中文分詞,可以採用單個漢字索引方式。例如“標點符”,會先索引“標”字,再索引“點”字,再索引“符”字。搜索過程中,也是先尋找“標”字關聯的所有文檔,再尋找“點”字關聯的所有文檔,再尋找“符”字關聯的所有文檔,最後對所有被檢索出的文檔做“與”運算,同時“標點符”位置連續的文檔纔算符合要求。這種方式存在一個非常挑戰性的問題,常用漢字總共3000左右,每次查詢過程中進行“與”操作的計算量會相當大。對於大數據量的搜索引擎來講,每天面臨億萬級別的查詢,這樣的索引結構無疑是災難性的。

爲了優化上面提到的速度問題,還有另外一種索引結構也是可以避開中文分詞的,那就是n元組合索引方式。用2元索引來說,“標點符”,會先索引“標點”,再索引“點符”。在搜索過程中,也是對“標點”和“點符”檢索出的文章進行“與”運算。這樣的搜索過程會大大減少在搜索過程中的計算量,但是仍會面臨另外一個問題:準確度。有很多這樣的例子,搜“北大”會檢索出“東北大學”,搜“的士”會出現“不想當將軍的士兵不是好士兵”。對於大數據量的搜索引擎系統來說,這樣的用戶體驗是極差的。

中文分詞面臨的挑戰

在知道分詞的重要性之後,那麼我們會面臨一個新的問題,如何才能把一個字序列準確的切分成詞序列,就像下面的例子會有不止一種的切分方式。

原始字符串:結婚的和尚未結婚的

  • 切分一:結婚/的/和尚/未/結婚/的

  • 切分二:結婚/的/和/尚未/結婚/的

還有更極端的例子,“中外科學名著”中,“中外”、“外科”、科學”、“學名”、“名著”都是合理的詞語。類似的例子數不勝數,“提高產品質量”,“鞭炮聲響徹夜空”。在中文分詞的世界裏,最主要的挑戰有兩個:歧義詞識別,未登錄詞識別。

歧義詞

上文提到的歧義詞例子,有學者試圖通過逆向匹配來解決。但是,碰到這句“結合成分子”時,採用逆向匹配,則會分成“結合/成分/子時”。一般當一個字可以同時作爲兩個詞的組成部分,當這兩個詞按序同時出現時,就可能會出現歧義現象。目前的歧義一般分爲三種:交叉歧義,組合歧義,真歧義。

  • 交叉歧義(字符串AJB,AJ和JB都是一個漢語詞彙,會存在多種切分交叉在一起):“你說的確實在理”,“的確”和“確實”就是交叉型歧義片段。

  • 組合歧義(字符串AB是一個詞彙,A和B同時也是詞彙,會存在不同語義下切分不同):“這個人手上有顆痣”,“目前人手緊缺”。前者是“人”/“手”兩個實體詞,後者是“人手”一個實體詞。

  • 真歧義(怎麼切分都合理):“乒乓球拍賣完了”,切分爲以下兩種情況都是合理的,“乒乓球拍/賣/完了”,“乒乓球/拍賣/完了”。

未登錄詞

所謂的未登錄詞是指在分詞詞典中沒有收錄,並且確實是大家公認的詞語的那些詞語,一般又叫做新詞。最典型的未登錄詞就是人名詞,“李勝利喜歡唱歌”中“李勝利”是個人名詞,如果把“李勝利”這個基本詞條收錄到字典中去是能解決這個問題。但是,每時每刻都有新增的姓名,完整收錄全部人名本身就是一個不現實的工程。中外人名、中國地名、機構組織名、事件名、貨幣名、縮略語、派生詞、各種專業術語以及在不斷髮展和約定俗成的一些新詞語。在當下的互聯網時代,人們還會不斷的創造出一些新詞出來,比如:“神馬”、“不明覺厲”等。未登錄詞辨別未登錄詞包括是種類繁多,形態組合各異,規模宏大的一個領域。對這些詞語的自動辨識,是一件非常困難的事。

新詞是中文分詞算法在召回層面上最主要的難題,也是評價一個分詞系統好壞的重要標誌。如果一個新詞無法被分詞系統識別,會導致很多噪音數據被召回,進而會影響後面的句法分析和語義分析等相關處理。黃昌寧等在中文信息學報上的《中文分詞十年回顧》一文指出:新詞帶來的分詞問題是歧義的10倍~20倍,所以說新詞發現是分詞面臨的最大挑戰。

中文分詞的技術分類

從上世紀80年代開始對中文自動分詞進行研究,在過去的近40年中,中文分詞的發展過程基本上可分爲以下三個階段,如下圖所示:

我們討論的分詞算法可分爲三大類:

  • 基於詞典:基於字典、詞庫匹配的分詞方法;(字符串匹配、機械分詞法)

  • 基於統計:基於詞頻度統計的分詞方法

  • 基於規則:基於知識理解的分詞方法

基於詞典的分詞

中文自動分詞第一階段,從80年代到90年代中,以基於詞典和人工規則的方法爲主,典型的方法有:正向最大匹配,逆向最大匹配,最少詞切分法,雙向匹配法。這種方法又叫做機械分詞方法,它是按照一定的策略將待分析的漢字串與一個“充分大的”機器詞典中的詞條進行配,若在詞典中找到某個字符串,則匹配成功(識別出一個詞)。按照掃描方向的不同,串匹配分詞方法可以分爲正向匹配和逆向匹配;按照不同長度優先匹配的情況,可以分爲最大(最長)匹配和最小(最短)匹配;常用的幾種機械分詞方法如下:

最大正向匹配法(MM, MaximumMatching Method,MM)

通常簡稱爲MM法。其基本思想爲:假定分詞詞典中的最長詞有i個漢字字符,則用被處理文檔的當前字串中的前i個字作爲匹配字段,查找字典。若字典中存在這樣的一個i字詞,則匹配成功,匹配字段被作爲一個詞切分出來。如果詞典中找不到這樣的一個i字詞,則匹配失敗,將匹配字段中的最後一個字去掉,對剩下的字串重新進行匹配處理…… 如此進行下去,直到匹配成功,即切分出一個詞或剩餘字串的長度爲零爲止。這樣就完成了一輪匹配,然後取下一個i字字串進行匹配處理,直到文檔被掃描完爲止。其算法描述如下:

  • 從左向右取待切分漢語句的m個字符作爲匹配字段,m爲大機器詞典中最長詞條個數。

  • 查找大機器詞典並進行匹配。若匹配成功,則將這個匹配字段作爲一個詞切分出來。若匹配不成功,則將這個匹配字段的最後一個字去掉,剩下的字符串作爲新的匹配字段,進行再次匹配,重複以上過程,直到切分出所有詞爲止。

舉個例子:我們對“南京市長江大橋”這個句子進行分詞,根據正向最大匹配的原則。先從句子中拿出前5個字符“南京市長江”,把這5個字符到詞典中匹配,發現沒有這個詞,那就縮短取字個數,取前四個“南京市長”,發現詞庫有這個詞,就把該詞切下來;對剩餘三個字“江大橋”再次進行正向最大匹配,會切成“江/大橋”;整個句子切分完成爲:“京市長/江/大橋。”

逆向最大匹配法(ReverseMaximum Matching Method,RMM)

通常簡稱爲RMM法。RMM法的基本原理與MM法相同 ,不同的是分詞切分的方向與MM法相反,而且使用的分詞辭典也不同。逆向最大匹配法從被處理文檔的末端開始匹配掃描,每次取最末端的2i個字符(i字字串)作爲匹配字段,若匹配失敗,則去掉匹配字段最前面的一個字,繼續匹配。相應地,它使用的分詞詞典是逆序詞典,其中的每個詞條都將按逆序方式存放。在實際處理時,先將文檔進行倒排處理,生成逆序文檔。然後,根據逆序詞典,對逆序文檔用正向最大匹配法處理即可。

還是那個例子:取出“南京市長江大橋”的後四個字“長江大橋”,發現詞典中有匹配,切割下來;對剩餘的“南京市”進行分詞,整體結果爲:“南京市/江大橋”

由於漢語中偏正結構較多,若從後向前匹配,可以適當提高精確度。所以,逆向最大匹配法比正向最大匹配法的誤差要小。統計結果表明,單純使用正向最大匹配的錯誤率爲1/169,單純使用逆向最大匹配的錯誤率爲1/245。

例如切分字段“碩士研究生產”,正向最大匹配法的結果會是“碩士研究生/產”,而逆向最大匹配法利用逆向掃描,可得到正確的分詞結果“碩士/研究/生產”。但這種精度還遠遠不能滿足實際的需要。實際使用的分詞系統,都是把機械分詞作爲一種初分手段,還需通過利用各種其它的語言信息來進一步提高切分的準確率。

雙向最大匹配法(Bi-directction Matching method,BM)

雙向最大匹配法是將正向最大匹配法得到的分詞結果和逆向最大匹配法的到的結果進行比較,從而決定正確的分詞方法。據SunM.S. 和 Benjamin K.T.(1995)的研究表明,中文中90.0%左右的句子,正向最大匹配法和逆向最大匹配法完全重合且正確,只有大概9.0%的句子兩種切分方法得到的結果不一樣,但其中必有一個是正確的(歧義檢測成功),只有不到1.0%的句子,或者正向最大匹配法和逆向最大匹配法的切分雖重合卻是錯的,或者正向最大匹配法和逆向最大匹配法切分不同但兩個都不對(歧義檢測失敗)。這正是雙向最大匹配法在實用中文信息處理系統中得以廣泛使用的原因所在。

還是那個例子:雙向的最大匹配,即把所有可能的最大詞都分出來,上面的句子可以分爲:南京市、南京市長、長江大橋、江、大橋

最少切分法

使每一句中切出的詞數最小。

由於漢語單字成詞的特點,正向最小匹配和逆向最小匹配一般很少使用。一般說來,逆向匹配的切分精度略高於正向匹配,遇到的歧義現象也較少。

基於詞典分詞這類算法優點是速度塊,都是O(n)時間複雜度,實現簡單,效果尚可。但也有缺點,這種基於規則的機械匹配法缺乏歧義切分處理,上面提到的幾種切分方法是從不同的角度來處理歧義問題,但是任何一種方法只能解決有限類別的歧義問題。隨着詞典的增大,詞與詞之間的交叉會變得更加嚴重,歧義帶來的負面影響會更加嚴重。同時,基於規則的切分方法對於新詞的切分是完全無能爲力的。

基於統計的分詞法

中文自動分詞第二階段,從90年代中到03年,分詞算法開始引入基於語料庫的統計學習方法,最典型的方法就是基於詞典全切分加上最大概率路徑。首先,介紹一下全切分方法,它是基於詞的頻度統計的分詞方法的基礎。全切分顧名思義就是獲取原字序列的所有可能成詞的切分結果,這樣就不會遺漏可能正確的切分方式。所以建立在部分切分基礎上的分詞方法不管採取何種歧義糾正策略,都可能會遺漏正確的切分,造成分詞錯誤或失敗。而建立在全切分基礎上的分詞方法,由於全切分取得了所有可能的切分形式,因而從根本上避免了可能切分形式的遺漏,克服了部分切分方法的缺陷。

將全切分的結構構件一個有向無環圖,比如“杭州亞運會”的全切分有向無環圖如下所示。

構成有向無環圖之後,在此圖中找到一條概率最大的路徑,使用的是N元文法模型(N-gram)模型,該模型基於這樣一種假設,第n個詞的出現只與前面N-1個詞相關,而與其它任何詞都不相關,整句的概率就是各個詞出現概率的乘積。我們給定一個詞,然後猜測下一個詞是什麼。當我說“豔照門”這個詞時,你想到下一個詞是什麼呢?我想大家很有可能會想到“陳冠希”,N-gram模型的主要思想就是這樣的。

其中,w值是指用全切分方法切分出來的詞語。基於全切分最大概率路徑的切分算法也是需要依賴詞典,全切分在實際使用過程,一般會通過詞典將所有成詞的切分方式找出來構成有向無環圖。第一階段的中文分詞相比,它也是無法完成識別新詞,但是歧義詞識別的問題基本被解決。在實際使用的工業分詞系統中,詞典中的詞一般會帶有詞頻屬性。同時,還會有一份詞與詞之間的跳轉頻率表,最大概率的計算往往是基於詞頻和詞之間的跳轉頻率進行的。

全切分算法能取得所有可能的切分形式,它的句子覆蓋率和分詞覆蓋率均爲100%,但全切分分詞並沒有在文本處理中廣泛地採用,原因有以下幾點:

  • 全切分算法只是能獲得正確分詞的前提,因爲全切分不具有歧義檢測功能,最終分詞結果的正確性和完全性依賴於獨立的歧義處理方法,如果評測有誤,也會造成錯誤的結果。

  • 全切分的切分結果個數隨句子長度的增長呈指數增長,一方面將導致龐大的無用數據充斥於存儲數據庫;另一方面當句長達到一定長度後,由於切分形式過多,造成分詞效率嚴重下降。

基於規則的分詞法

從03年至今,中文分詞由基於詞的方法開始向基於字的方法轉變。當前的方法都是首先根據語料訓練分詞模型,然後對每一個字進行標註,最後根據標註結果來進行分詞。其實就是根據語料訓練分類模型,對每一個字進行類別標註,最後根據類別進行分詞。這類分詞基於人工標註的詞性和統計特徵,對中文進行建模,即根據觀測到的數據(標註好的語料)對模型參數進行估計,即訓練。在分詞階段再通過模型計算各種分詞出現的概率,將概率最大的分詞結果作爲最終結果。這類分詞算法能很好處理歧義和未登錄詞問題,效果比前一類效果好,但是需要大量的人工標註數據,以及較慢的分詞速度。最典型的方法就是HMM和CRF,其中,CRF比HMM有更弱的上下文無關性假設,當然效果要好一些。

隱馬爾科夫模型(Hidden Markov Model, HMM)

全切分這種方法存在兩個致命的缺陷:一個缺陷是參數空間過大,不可能實用化;另外一個缺陷是數據稀疏嚴重。爲了解決這個問題,我們引入了馬爾科夫假設:一個詞的出現僅僅依賴於它前面出現的有限的一個或者幾個詞。如果一個詞的出現僅依賴於它前面出現的一個詞,那麼我們就稱之爲bigram。即

P(T) =P(W1W2W3…Wn)=P(W1)P(W2|W1)P(W3|W1W2)…P(Wn|W1W2…Wn-1)≈P(W1)P(W2|W1)P(W3|W2)…P(Wn|Wn-1)

如果一個詞的出現僅依賴於它前面出現的兩個詞,那麼我們就稱之爲trigram。在實踐中用的最多的就是bigram和trigram了,而且效果很不錯。高於四元的用的很少,因爲訓練它需要更龐大的語料,而且數據稀疏嚴重,時間複雜度高,精度卻提高的不多。一般的小公司,用到二元的模型就夠了,像Google這種巨頭,也只是用到了大約四元的程度,它對計算能力和空間的需求都太大了。

設w1,w2,w3,…,wn是長度爲n的字符串,規定任意詞wi只與它的前兩個相關,得到三元概率模型。以此類推,N元模型就是假設當前詞的出現概率只同它前面的N-1個詞有關。這是一種全切分方法。它不依靠詞典,而是將文章中任意兩個字同時出現的頻率進行統計,次數越高的就可能是一個詞。它首先切分出與詞表匹配的所有可能的詞,運用統計語言模型和決策算法決定最優的切分結果。它的優點在於可以發現所有的切分歧義並且容易將新詞提取出來。

HMM模型示意圖

HMM模型介紹

HMM的典型介紹就是這個模型是一個五元組:

  • StatusSet: 狀態值集合

  • ObservedSet: 觀察值集合

  • TransProbMatrix: 轉移概率矩陣

  • EmitProbMatrix: 發射概率矩陣

  • InitStatus: 初始狀態分佈

HMM模型可以用來解決三種問題:

  • 參數(StatusSet, TransProbMatrix, EmitRobMatrix, InitStatus)已知的情況下,求解觀察值序列。(Forward-backward算法)

  • 參數(ObservedSet, TransProbMatrix, EmitRobMatrix, InitStatus)已知的情況下,求解狀態值序列。(viterbi算法)

  • 參數(ObservedSet)已知的情況下,求解(TransProbMatrix, EmitRobMatrix, InitStatus)。(Baum-Welch算法)

其中,第三種問題最玄乎也最不常用,第二種問題最常用,中文分詞、語音識別、新詞發現、詞性標註都有它的一席之地。這裏主要介紹第二種問題,即viterbi算法求解狀態值序列的方法。

五元組參數在中文分詞中的具體含義:

StatusSet & ObservedSet

狀態值集合爲(B, M, E, S): {B:begin, M:middle, E:end, S:single}。分別代表每個狀態代表的是該字在詞語中的位置,B代表該字是詞語中的起始字,M代表是詞語中的中間字,E代表是詞語中的結束字,S則代表是單字成詞。觀察值集合爲就是所有漢字,甚至包括標點符號所組成的集合。

狀態值也就是我們要求的值,在HMM模型中文分詞中,我們的輸入是一個句子(也就是觀察值序列),輸出是這個句子中每個字的狀態值。

比如:小明碩士畢業於中國科學院計算所

  • 輸出的狀態序列爲:BEBEBMEBEBMEBES

  • 根據這個狀態序列我們可以進行切詞:BE/BE/BME/BE/BME/BE/S

  • 所以切詞結果如下:小明/碩士/畢業於/中國/科學院/計算/所

同時我們可以注意到:B後面只可能接(M or E),不可能接(B or S)。而M後面也只可能接(M or E),不可能接(B, S)。

上文只介紹了五元組中的兩元StatusSet、ObservedSet,下文介紹剩下的三元InitStatus、TransProbMatrix、EmitProbMatrix。這五元的關係是通過一個叫Viterbi的算法串接起來,ObservedSet序列值是Viterbi的輸入, 而StatusSet序列值是Viterbi的輸出,輸入和輸出之間Viterbi算法還需要藉助三個模型參數,分別是InitStatus, TransProbMatrix, EmitProbMatrix。

InitStatus

初始狀態概率分佈是最好理解的,可以示例如下:

 

1

2

3

4

5

6

7

8

#B

-0.26268660809250016

#E

-3.14e+100

#M

-3.14e+100

#S

-1.4652633398537678

示例數值是對概率值取對數之後的結果(可以讓概率相乘的計算變成對數相加),其中-3.14e+100作爲負無窮,也就是對應的概率值是0。也就是句子的第一個字屬於{B,E,M,S}這四種狀態的概率,如上可以看出,E和M的概率都是0,這和實際相符合,開頭的第一個字只可能是詞語的首字(B),或者是單字成詞(S)。

TransProbMatrix

轉移概率是馬爾科夫鏈很重要的一個知識點,大學裏面學過概率論的人都知道,馬爾科夫鏈最大的特點就是當前T=i時刻的狀態Status(i),只和T=i時刻之前的n個狀態有關。也就是:{Status(i-1), Status(i-2), Status(i-3), … Status(i – n)}。更進一步的說,HMM模型有三個基本假設作爲模型的前提,其中有個有限歷史性假設,也就是馬爾科夫鏈的n=1。即Status(i)只和Status(i-1)相關,這個假設能大大簡化問題。

回過頭看TransProbMatrix,其實就是一個4×4(4就是狀態值集合的大小)的二維矩陣,示例如下:

矩陣的橫座標和縱座標順序是BEMS x BEMS

 

1

2

3

4

-3.14e+100 -0.510825623765990 -0.916290731874155 -3.14e+100

-0.5897149736854513 -3.14e+100 -3.14e+100 -0.8085250474669937

-3.14e+100 -0.33344856811948514 -1.2603623820268226 -3.14e+100

-0.7211965654669841 -3.14e+100 -3.14e+100 -0.6658631448798212

比如TransProbMatrix[0][0]代表的含義就是從狀態B轉移到狀態B的概率,由 TransProbMatrix[0][0] = -3.14e+100 可知,這個轉移概率是0,這符合常理。由狀態各自的含義可知,狀態B的下一個狀態只可能是ME,不可能是BS,所以不可能的轉移對應的概率都是0,也就是對數值負無窮,在此記爲-3.14e+100。

由上TransProbMatrix矩陣可知,對於各個狀態可能轉移的下一狀態,且轉移概率對應如下:

 

1

2

3

4

5

6

7

8

#B

#E:-0.510825623765990,M:-0.916290731874155

#E

#B:-0.5897149736854513,S:-0.8085250474669937

#M

#E:-0.33344856811948514,M:-1.2603623820268226

#S

#B:-0.7211965654669841,S:-0.6658631448798212

EmitProbMatrix

這裏的發射概率(EmitProb)其實也是一個條件概率而已,根據HMM模型三個基本假設裏的觀察值獨立性假設,觀察值只取決於當前狀態值,也就是:P(Observed[i], Status[j]) = P(Status[j]) * P(Observed[i]|Status[j])。其中P(Observed[i]|Status[j])這個值就是從EmitProbMatrix中獲取。

EmitProbMatrix示例如下:

 

1

2

3

4

5

6

7

8

#B

耀:-10.460283,涉:-8.766406,談:-8.039065,伊:-7.682602,洞:-8.668696,...

#E

耀:-9.266706,涉:-9.096474,談:-8.435707,伊:-10.223786,洞:-8.366213,...

#M

耀:-8.47651,涉:-10.560093,談:-8.345223,伊:-8.021847,洞:-9.547990,....

#S

蘄:-10.005820,涉:-10.523076,唎:-15.269250,禑:-17.215160,洞:-8.369527...

雖然EmitProbMatrix也稱爲矩陣,這個矩陣太稀疏了,實際工程中一般是將上面四行發射轉移概率存儲爲4個Map,詳見代碼HMMSegment。

到此,已經介紹完HMM模型的五元參數,假設現在手頭上已經有這些參數的具體概率值,並且已經加載進來,(也就是有該模型的字典了,詳見HMMDict裏面的hmm_model.utf8),那麼我們只剩下Viterbi這個算法函數,這個模型就算可以開始使用了。所以接下來講講Viterbi算法。

Viterbi算法

輸入樣例:小明碩士畢業於中國科學院計算所

Viterbi算法計算過程如下:

1、定義變量

二維數組 weight[4][15],4是狀態數(0:B,1:E,2:M,3:S),15是輸入句子的字數。比如 weight[0][2] 代表 狀態B的條件下,出現’碩’這個字的可能性。

二維數組 path[4][15],4是狀態數(0:B,1:E,2:M,3:S),15是輸入句子的字數。比如 path[0][2] 代表 weight[0][2]取到最大時,前一個字的狀態,比如 path[0][2] = 1, 則代表 weight[0][2]取到最大時,前一個字(也就是明)的狀態是E。記錄前一個字的狀態是爲了使用viterbi算法計算完整個 weight[4][15] 之後,能對輸入句子從右向左地回溯回來,找出對應的狀態序列。

2、使用InitStatus對weight二維數組進行初始化

已知InitStatus如下:

 

1

2

3

4

5

6

7

8

#B

-0.26268660809250016

#E

-3.14e+100

#M

-3.14e+100

#S

-1.4652633398537678

且由EmitProbMatrix可以得出

 

1

2

3

4

Status(B) -> Observed(小)  :  -5.79545

Status(E) -> Observed(小)  :  -7.36797

Status(M) -> Observed(小)  :  -5.09518

Status(S) -> Observed(小)  :  -6.2475

所以可以初始化 weight[i][0] 的值如下:

 

1

2

3

4

weight[0][0] = -0.26268660809250016 + -5.79545 = -6.05814

weight[1][0] = -3.14e+100 + -7.36797 = -3.14e+100

weight[2][0] = -3.14e+100 + -5.09518 = -3.14e+100

weight[3][0] = -1.4652633398537678 + -6.2475 = -7.71276

注意上式計算的時候是相加而不是相乘,因爲之前取過對數的原因。

3、遍歷句子計算整個weight二維數組

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

//遍歷句子,下標i從1開始是因爲剛纔初始化的時候已經對0初始化結束了

for(size_t i = 1; i < 15; i++)

{

    // 遍歷可能的狀態

    for(size_t j = 0; j < 4; j++)

    {

        weight[j][i] = MIN_DOUBLE;

        path[j][i] = -1;

        //遍歷前一個字可能的狀態

        for(size_t k = 0; k < 4; k++)

        {

            double tmp = weight[k][i-1] + _transProb[k][j] + _emitProb[j][sentence[i]];

            if(tmp > weight[j][i]) // 找出最大的weight[j][i]值

            {

                weight[j][i] = tmp;

                path[j][i] = k;

            }

        }

    }

}

如此遍歷下來,weight[4][15] 和 path[4][15] 就都計算完畢。

4、確定邊界條件和路徑回溯

邊界條件如下:對於每個句子,最後一個字的狀態只可能是 E 或者 S,不可能是 M 或者 B。

所以在本文的例子中我們只需要比較 weight[1(E)][14] 和 weight[3(S)][14] 的大小即可。在本例中:

 

1

2

weight[1][14] = -102.492;

weight[3][14] = -101.632;

所以 S > E,也就是對於路徑回溯的起點是 path[3][14]。

  • 回溯的路徑是:SEBEMBEBEMBEBEB

  • 倒序一下就是:BE/BE/BME/BE/BME/BE/S

  • 切詞結果就是:小明/碩士/畢業於/中國/科學院/計算/所

到此,一個HMM模型中文分詞算法過程就闡述完畢了。也就是給定我們一個模型,我們對模型進行載入完畢之後,只要運行一遍Viterbi算法,就可以找出每個字對應的狀態,根據狀態也就可以對句子進行分詞。

模型的訓練問題

以上講的前提是基於模型來進行切詞,也就是假設我們手頭上的HMM模型已經是被訓練好了的(也就是InitStatus, TransProbMatrix, EmitProbMatrix這三個模型的關鍵參數都是已知的),沒有涉及到這三個參數是如何得到的。這三個參數其實也是基於已分詞完畢的語料進行統計計算,計算出相應的頻率和條件概率就可以算出這三個參數。

HMM模型的三個基本假設如下:

  • 有限歷史性假設:P(Status[i]|Status[i-1],Status[i-2],… Status[1]) = P(Status[i]|Status[i-1])

  • 齊次性假設(狀態和當前時刻無關):P(Status[i]|Status[i-1]) = P(Status[j]|Status[j-1])

  • 觀察值獨立性假設(觀察值只取決於當前狀態值):P(Observed[i]|Status[i],Status[i-1],…,Status[1]) = P(Observed[i]|Status[i])

條件隨機場(Conditional Random Field,CRF)

2001年Lafferty在最大熵模型(MEM)和隱馬爾科夫模型(HMM)的基礎上提出來了一種無向圖模型–條件隨機場(CRF)模型,它能在給定需要標記的觀察序列的條件下,最大程度提高標記序列的聯合概率。常用於切分和標註序列化數據的統計模型。

CRF(Conditionalrandom field,條件隨機場)是用來標註和劃分結構數據的概率化結構模型,通常使用在模式識別和機器學習中,在自然語言處理和圖像處理等領域中得到廣泛應用。和HMM類似,當對於給定的輸入觀測序列X和輸出序列Y,CRF通過定義條件概率P(Y|X),而不是聯合概率分佈P(X,Y)來描述模型。CRF算法的具體算法可以參考維基百科詞條。

不同概率模型之間的關係及演化圖

以CRF爲例,它把分詞看作是對一個字序列進行標註的過程,一般會標記爲4種狀態:詞首(B)、詞中(M)、詞尾(E)、單獨成詞(S)。例如,對於一個輸入序列“我來到網易杭州研究院”會標記爲“我/S 來/B 到/E 網/B 易/E 杭/B 州/E 研/B 究/M 院/E”,根據標註結果就得到最終的分詞結果。CRF模型的最優函數形式如下所示:

其中,Z是歸一化函數,f是特徵函數,前面是特徵對應的權重。CRF分詞的過程就是找到一個標註序列使得其最優函數達到最大值。由於CRF模型不是基於詞典的,可以有效的識別新詞。同時,其分詞的準確率也已經達到工業界使用的要求。但是,CRF分詞的效率和一致性會存在一定問題。一致性問題是指同一個待切分片段會隨着上下文的不同可能做成完全不同的切分結果,所以在搜索業務中在召回層面使用是不合適的。當前CRF的主流用法是在線進行人名識別和歧義片段解析,並使用詞典來保持分詞結果的一致性,以及離線識別新詞補充至詞典。

在實際應用中有很多工具包可以使用,比如CRF++,CRFsuite,SGD,Wapiti 等,其中CRF++的準確度較高。在分詞中使用CRF++時,主要的工作是特徵模板的配置。CRF++支持unigram,bigram兩種特徵,分別以U和B開頭。舉例來講U00:%x[-2,0]表示第一個特徵,特徵取值是當前字的前方第二個字,U01:%x[-1,0]表示第二個特徵,特徵取值當前字前一個字,U02:%x[0,0]表示第三個特徵,取當前字,以此類推。特徵模板可以支持多種特徵,CRF++會根據特徵模板提取特徵函數,用於模型的建立和使用。特徵模板的設計對分詞效果及訓練時間影響較大,需要分析嘗試找到適用的特徵模板。

常見中文分詞工具

結巴中文分詞

結巴中文分詞采用的算法

  • 基於Trie樹結構實現高效的詞圖掃描,生成句子中漢字所有可能成詞情況所構成的有向無環圖(DAG)

  • 採用了動態規劃查找最大概率路徑, 找出基於詞頻的最大切分組合

  • 對於未登錄詞,採用了基於漢字成詞能力的HMM模型,使用了Viterbi算法

目前結巴分詞支持三種分詞模式:

  • 精確模式,試圖將句子最精確地切開,適合文本分析;

  • 全模式,把句子中所有的可以成詞的詞語都掃描出來, 速度非常快,但是不能解決歧義;

  • 搜索引擎模式,在精確模式的基礎上,對長詞再次切分,提高召回率,適合用於搜索引擎分詞。

項目地址:

  • Python版本:https://github.com/fxsjy/jieba

IK分詞器

IKAnalyzer是一個開源的,基於java語言開發的輕量級的中文分詞工具包。從2006年12月推出1.0版開始,IKAnalyzer已經推出了3個大版本。最初,它是以開源項目Luence爲應用主體的,結合詞典分詞和文法分析算法的中文分詞組件。新版本的 IKAnalyzer3.0則發展爲面向Java的公用分詞組件,獨立於Lucene項目,同時提供了對Lucene的默認優化實現。

IKAnalyzer採用的算法:

  • 正向迭代最細粒度切分算法

  • 針對Lucene全文檢索優化的查詢分析器IKQueryParser,引入簡單搜索表達式,採用歧義分析算法優化查詢關鍵字的搜索排列組合,能極大的提高Lucene檢索的命中率。

IKAnalyzer支持的分詞模式:

  • 支持細粒度和最大詞長兩種切分模式

項目地址:

  • https://github.com/wks/ik-analyze

  • https://github.com/medcl/elasticsearch-analysis-ik

  • https://github.com/quentinxxz/Search(源碼保存)

中科院NLPIR分詞(ICTCLAS)

NLPIR分詞系統前身爲2000年發佈的ICTCLAS詞法分析系統,從2009年開始,爲了和以前工作進行大的區隔,並推廣NLPIR自然語言處理與信息檢索共享平臺,調整命名爲NLPIR分詞系統。NLPIR支持的功能:全文精準檢索、新詞發現、分詞標註、統計分析與術語翻譯、大數據聚類及熱點分析、大數據分類過濾、自動摘要、關鍵詞提取、文檔去重、HTML正文提取、編碼自動識別與轉換等。

NLPIR使用的算法:

  • 基於HMM的分詞庫

項目地址:

  • http://ictclas.nlpir.org/

  • https://github.com/NLPIR-team/NLPIR

Ansj中文分詞

Ansj 是一個開源的 Java 中文分詞工具,基於中科院的 ictclas 中文分詞算法。目前實現了中文分詞、中文姓名識別、用戶自定義詞典、關鍵字提取、自動摘要、關鍵字標記等功能。

Ansj使用的算法:

  • n-Gram+CRF+HMM

分詞效果速度都超過開源版的ICTCLAS

項目地址:https://github.com/NLPchina/ansj_seg

MMSEG分詞

MMSeg是蔡志浩(Chih-Hao Tsai)提出的基於字符串匹配的中文分詞算法。以正向最大匹配爲主,多種消除歧義的規則爲輔。根據作者在原文中的闡述,對MMSEG的解釋分爲“匹配算法(Matching algorithm)”和“消除歧義的規則(Ambiguity resolution rules)”這兩部分。“匹配算法”是說如何根據詞典裏保存的詞語,對要切分的語句進行匹配;“消除歧義的規則”是說當一句話可以這樣分,也可以那樣分的時候,用什麼規則來判定使用哪中分法,比如“設施和服務”這個短語,可以分成“設施/和服/務”,也可以分成“設施/和/服務”,選擇哪個分詞結果,就是“消除歧義的規則”的功能。

MMSeg的字符串匹配算法分爲兩種:

  • Simple,簡單的正向最大匹配,即按能匹配上的最長詞做切分;

  • Complex,在正向最大匹配的基礎上,考慮相鄰詞的詞長,設計了四個去歧義規則(Ambiguity Resolution Rules)指導分詞。

在complex分詞算法中,MMSeg將切分的相鄰三個詞作爲詞塊(chunk),應用如下四個歧義規則:

  • 備選詞塊的長度最大(Maximum matching),即三個詞的詞長之和最大;

  • 備選詞塊的平均詞長最大(Largest average word length),即要求詞長分佈儘可能均勻;

  • 備選詞塊的詞長變化最小(Smallest variance of word lengths );

  • 備選詞塊中(若有)單字的出現詞自由度最高(Largest sum of degree of morphemic freedom of one-character words)。

這個四個過濾規則中,如果使用simple的匹配方法,只能使用第一個規則過濾,如果使用complex的匹配方法,則四個規則都可以使用。實際使用中,一般都是使用complex的匹配方法+四個規則過濾。

項目地址:

  • http://technology.chtsai.org/mmseg/

MMSeg算法說明

首先來理解一下chunk,它是MMSeg分詞算法中一個關鍵的概念。Chunk中包含依據上下文分出的一組詞和相關的屬性,包括長度(Length)、平均長度(Average Length)、標準差的平方(Variance)和自由語素度(Degree Of Morphemic Freedom)。下面列出了這4個屬性:

  • 長度(Length):chuck中各個詞的長度之和

  • 平均長度(Average Length):長度(Length)/詞數

  • 標準差的平方(Variance):同數學中的定義

  • 自由語素度(Degree Of Morphemic Freedom):各單字詞詞頻的對數之和

Chunk中的4個屬性只有在需要該屬性的值時才進行計算,而且只計算一次。

其次來理解一下規則(Rule),它是MMSeg分詞算法中的又一個關鍵的概念。實際上我們可以將規則理解爲一個過濾器(Filter),過濾掉不符合要求的chunk。MMSeg分詞算法中涉及了4個規則:

  • 規則1:取最大匹配的chunk (Rule 1: Maximum matching)

  • 規則2:取平均詞長最大的chunk (Rule 2: Largest average word length)

  • 規則3:取詞長標準差最小的chunk (Rule 3: Smallest variance of word lengths)

  • 規則4:取單字詞自由語素度之和最大的chunk (Rule 4: Largest sum of degree of morphemic freedom of one-character words)

這4個規則符合漢語成詞的基本習慣。

再來理解一下匹配方式複雜最大匹配(Complex maximum matching):複雜最大匹配先使用規則1來過濾chunks,如果過濾後的結果多於或等於2,則使用規則2繼續過濾,否則終止過濾過程。如果使用規則2得到的過濾結果多於或等於2,則使用規則3繼續過濾,否則終止過濾過程。如果使用規則3得到的過濾結果多於或等於2,則使用規則4繼續過濾,否則終止過濾過程。如果使用規則 4得到的過濾結果多於或等於2,則拋出一個表示歧義的異常,否則終止過濾過程。

最後通過一個例句–“研究生命起源”來簡述一下複雜最大匹配的分詞過程。MMSeg分詞算法會得到7個chunk,分別爲:

 

1

2

3

4

5

6

7

8

編號    chunk   長度

0   研_究_生    3

1   研_究_生命  4

2   研究_生_命  4

3   研究_生命_起    5

4   研究_生命_起源  6

5   研究生_命_起    5

6   研究生_命_起源  6

使用規則1過濾後得到2個chunk,如下:

 

1

2

3

編號    chunk   長度

4   研究_生命_起源  6

6   研究生_命_起源  6

計算平均長度後爲:

 

1

2

3

編號    chunk   長度    平均長度

4   研究_生命_起源  6   2

6   研究生_命_起源  6   2

使用規則2過濾後得到2個chunk,如下:

 

1

2

3

編號    chunk   長度    平均長度

4   研究_生命_起源  6   2

6   研究生_命_起源  6   2

計算標準差的平方後爲:

 

1

2

3

編號    chunk   長度    平均長度    標準差的平方

4   研究_生命_起源  6   2   0

6   研究生_命_起源  6   2   4/9

使用規則3過濾後得到1個chunk,如下:

 

1

2

編號    chunk   長度    平均長度    標準差的平方

4   研究_生命_起源  6   2   0

匹配過程終止。最終取“研究”成詞,以相同的方法繼續處理“生命起源”。

分詞效果:

 

1

研究_生命_起源_

 

其他分詞工具

  • 清華大學THULAC(CRF):http://thulac.thunlp.org/

  • 哈工大LTP(CRF):https://github.com/HIT-SCIR/ltp

  • 哈工大語言云(基於LTP):http://www.ltp-cloud.com/

  • 復旦大學NLP:https://github.com/FudanNLP/fnlp

  • 斯坦福大學(CRF):http://nlp.stanford.edu/software/segmenter.shtml

  • scws(詞頻):https://github.com/hightman/scws

  • 庖丁(詞庫):https://github.com/cslinmiso/paoding-analysis

  • bamboo(CRF):https://github.com/zhuomingliang/nlpbamboo

  • PullWord:http://www.pullword.com/

  • 盤古分詞:http://pangusegment.codeplex.com/

  • BosonNLP:http://bosonnlp.com/

  • 搜狗分詞:http://www.sogou.com/labs/webservice/

  • 騰訊文智:http://www.qcloud.com/wiki/API%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3
    新浪雲:http://www.sinacloud.com/doc/sae/python/segment.html

  • HanLP:https://github.com/hankcs/HanLP

中文分詞使用案例

考拉海淘

目前考拉海淘使用的分詞是基於ansj開源分詞進行二次開發的版本,目的是優化ansj原有的分詞策略以及增加適合電商的策略。

召回問題

通過日誌以及用戶反饋,我們發現對於同一商品往往存在不同表述,這可能會導致某種描述無法召回結果。例如,對於商品“掃地機器人”來說,有人會用“掃地機器”這樣的query去查詢,也有人會用“掃地機”去查詢。但是按照最大概率路徑分詞策略,會把商品切分成”掃地”和”機器人”來進行建立索引。爲了保證召回的準確性,底層NDIR是採用全匹配的方式來進行召回,也就是query中的所有詞都必須出現在同一個文檔中。對於上面的兩種query,分詞結果分別是“掃地/機器”,“掃地/機”,自然就無法召回用戶所希望的商品。我們採用多路徑切分的方式,來保證所有在詞典的詞語都出現在路徑中,同時,對所有的路徑進行索引建立。我們會把“掃地機器人”切分成“掃地/機器人”、“掃地/機器/人”、“掃地機/器/人”三條路徑,這樣就解決了無法召回的問題。

詞性問題

當用戶輸入的query比較詳細時,可能考拉並沒有此款商品,卻有同品牌或者其他相似的商品,這個時候就需要用到關鍵詞提取。例如,query爲”耐克黑色球鞋詹姆斯同款”時,因爲無貨而無法召回商品,當我們提取關鍵詞“耐克黑色籃球鞋”時可能會召回商品,當然這個關鍵詞提取的粒度是需要控制的。在ansj原有詞典的基礎上,我們補充了電商品牌詞典和電商類詞典,分詞過程中會給出相應的標記爲”耐克/brand 黑色/adj 球鞋/ecom 詹姆斯/nrf 同款/nz”,根據詞性是可以進行關鍵詞初步識別的。

分詞粒度問題

當詞典較大時,會面臨一個新的問題,那就是分詞的粒度問題。對於query爲“耐克鞋”來說,詞典中是包含這個實體詞的,分詞的切分結果就是“耐克鞋”。但是,考拉用來建索引的商品描述中,這三個字是沒有連續出現的,自然就沒有“耐克鞋”對應的文檔,這個時候就無法召回結果。那麼,這時候你會說那就使用最小粒度的分詞就解決這個問題了。比如,某一款商品可能有“參數表”這三個字,如果有最小粒度的分詞策略,分詞的結果爲“參數/表”。很不幸,當query爲“表”時,你會發現會召回莫名其妙的結果。目前,是使用詞頻表來進行粒度控制,基本可以解決絕大多數問題。

新詞問題

分詞器的詞表一般是由基礎詞彙和各領域常用詞彙來組成,可以應用於大多數基本的分詞場景。當分詞器專門應用於某個領域時,一份領域相關的詞典是必不可少的。例如,有很多出問題的case是由於無法識別這個詞造成的,像“馬油”、“肌研”、“極潤”這樣的詞,只能切分成單字,隨之而來的就是一些不相關的商品被召回。目前的解決方案是從相關網站獲取了一批電商詞語當做基礎電商詞語,儘量減少詞語的無法識別率。但是這種解決方案是不滿足可持續發展戰略的,只能用於分詞的初級階段。

未來工作的方向

根據考拉海淘搜索的現狀,接下來會針對解決關鍵詞提取/新詞發現以及實體識別所帶來的問題。打算開發一套term weight的關鍵詞識別系統,利用詞法依存關係以及詞性等知識給切分後每一個詞語進行一個打分,其應用場景會非常廣泛。另外一個方向是解決新詞發現所涉及的問題,會根據用戶日誌以及新商品的信息,利用詞頻統計以及CRF序列標註等方案來定期離線自動化補充新詞。

知乎

知乎在重構知乎搜索的時候,權衡標註工作量和性能,以及代碼實現的複雜程度,採用基於字符串匹配的分詞方法。除了標註量,準確率和效果的考量,分詞粒度也是一個需要考慮的指標。

案例:“團購網站的本質是什麼?”

在這個例子中,如果使用單一粒度分詞,會有:“團購”、“團購網”、“網站”、“團購網站”、“本質”、“是”、“什麼”

按最大匹配分詞結果是:“團購網站/的/本質/是/什麼”。當用戶輸入“團購網的本質”,分詞結果是“團購網/的/本質”,團購網這個詞顯然是沒有匹配的。按最小匹配分詞的結果也會出現問題。

因此,知乎考慮基於字符串匹配的分詞方法最好能夠匹配出多粒度的結果,即能分出“團購網站/團購/團購網/網站/的/本質/是/什麼” 這樣多粒度的結果。

知乎最終採用了採ikanalyzer:

  • 基於文本匹配,不需要投入大量人力進行訓練和標註

  • 可以自定詞典,方便加入domain specific的詞語

  • 能分出多粒度的結果。

參考資料:

  • http://dataunion.org/21674.html

  • http://yanyiwu.com/work/2014/04/07/hmm-segment-xiangjie.html

  • https://www.zhihu.com/question/19578687

  • http://sobuhu.com/ml/2012/12/23/chinese-word-spliter.html

  • http://www.isnowfy.com/introduction-to-chinese-segmentation/

  • http://www.aboutyun.com/thread-18901-1-1.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章