利用統計進行中文分詞與詞性分析 - Iveely Liu - 博客園
利用統計進行中文分詞與詞性分析
今天,翻出了我以前在本科階段寫的一些論文,雖然有幾篇沒有發表。突然發現很多還是比較實用,雖然學術價值并不是很大,于是我重新整理了下,用最簡單的方式,摘要了部分出來拼成此文,當然拼的原料都是自己的,本文適合初學者,如若轉載,請著名版權。
中文分詞已經是老調重彈的話題了,傳統的基于詞庫的分詞技術應該是目前最基本的分詞技術,在這里我不去深入挖掘,什么好什么不好的問題,今天我只想根據我自己的經驗,來設計和實現一套中文分詞與詞性分析的一套系統,系統其實已經實現與Iveely Search Engine中。
我們采用隱馬爾可夫模型(HMM)來實現中文分詞和詞性分析。在使用HMM之前,我們先了解下HMM的基本內容,然后在編碼上我們才知道為什么要這樣去寫(這對讀編碼非常重要)。當然會和code對比起來。
利用學院派的講解是這樣:隱馬爾可夫模型是馬爾可夫鏈的一種,它的狀態不能直接觀察到,但能通過觀測向量序列觀察到,每個觀測向量都是通過某些概率密度分布表現為各種狀態,每一個觀測向量是由一個具有相應概率密度分布的狀態序列產生。也許看完這個,你一頭霧水,因為在N年前,我最開始接觸HMM的時候,也是領悟了一段時間,當然如果你很熟悉HMM,直接跳過。從工程的角度:
HMM 的五元素(學術名 ) 統計中含義( 工程含義) 隱含狀態S 詞有的4種隱含狀態:單字成詞、詞頭、詞中、詞尾 觀察狀態O 所有語料庫中的漢字 初始狀態概率矩陣Pi 詞中各種隱含狀態的初始概率 隱含狀態轉移概率矩陣A 4種隱含狀態的轉移概率,例如:詞頭到詞尾的轉移概率,是一個4*4的矩陣 觀察狀態轉移概率矩陣B 語料庫中每一個漢字到4種隱含狀態的概率。例如:漢字“我”到詞頭的概率,到詞尾的概率。第一步:統計HMM五種元素值
通過上圖的表,你似乎可以開始工作了,但是你知道,沒有Corpus,這件事是做不好的,所以,Corpus的選擇,是非常好的,我們選用這個Corpus,下一步工作,我們就是利用工程學統計出上述五個元素的。
語料庫中的形式如下:
“...廢/B除/E ?存/B在/E ?的/S ?所/B有/M制/E ?關/B系/E ?并/S ?不/S ?是/S ?共/B產/M主/M義/E ?所/S ?獨/B具/E ?的/S ?特/B征/E...”
B表示:begin(詞頭),M表示:Middle(詞中),S表示Single(單字成詞),E表示End(詞尾),我想給你這樣一個語料庫,讓你統計上面五個元素的值,應該不是什么問題了。當然我也會附加上主要的代碼:
private void Train( string corpus) { string [] states = new string [] { " 單字成詞 " , " 詞頭 " , " 詞中 " , " 詞尾 " }; // 添加隱含狀態 this .AddStates(states); string [] sentences = corpus.Split( this .Delimiter.ToArray(), StringSplitOptions.RemoveEmptyEntries); string lastwordType = states[ 0 ]; string currentWordType = string .Empty; foreach ( var sentence in sentences) { char [] list = sentence.ToArray(); for ( int i = 0 ; i < list.Length; i = i + 3 ) { string word = list[i].ToString(CultureInfo.InvariantCulture); string wordType = list[i + 2 ].ToString(CultureInfo.InvariantCulture).ToLower(); // 添加觀察狀態元素 this .AddObserver(word); if (wordType.Equals( " s " )) { currentWordType = states[ 0 ]; } else if (wordType.Equals( " e " )) { currentWordType = states[ 3 ]; } else if (wordType.Equals( " m " )) { currentWordType = states[ 2 ]; } else if (wordType.Equals( " b " )) { currentWordType = states[ 1 ]; } // 初始狀態轉移矩陣 AddInitialStateProbability(currentWordType, 1 ); // 觀察狀態轉移矩陣 AddComplexProbability(currentWordType, word); // 隱含狀態轉移矩陣 AddTransferProbability(lastwordType, currentWordType, 1 ); lastwordType = currentWordType; } } }第二步:維特比最佳路徑
?維特比是什么?維特比算法是一種動態規劃算法用于尋找最有可能產生觀測事件序列的-維特比路徑-隱含狀態序列,特別是在馬爾可夫信息源上下文和隱馬爾可夫模型中。術語“維特比路徑”和“維特比算法”也被用于尋找觀察結果最有可能解釋相關的動態規劃算法。例如在統計句法分析中動態規劃算法可以被用于發現最可能的上下文無關的派生(解析)的字符串,有時被稱為“維比特分析”。
有了上面維特比的基礎知識以及統計完HMM的基本五種元素之后,你會很納悶,這五種元素計算出來后,意義是什么?如何利用它解決分詞問題?下面的問題,我們以一個實例來解說:用戶輸入了關鍵字“Iveely 是一款開源的搜索引擎?!?我們該如何分詞(解碼過程)?
首先,將輸入詞組分為一個Array:
Iveely
是
一
款
開
源
搜
索
引
擎
S
S
S
S
S
S
S
S
S
S
B
B
B
B
B
B
B
B
B
B
M
M
M
M
M
M
M
M
M
M
E
E
E
E
E
E
E
E
E
E
那么分詞的作用就是,在每一個詞有不同的狀態的時候,怎么使得他們的概率最大?這就變成了一個路徑選擇問題。我們可以不利用維特比算法來解決,因為暴力方法一定是一個解決思路,但是算法效率太低,每個字有4種狀態,假設10個字,就有4的10次方計算量,然后取出概率最大的那一組就是我們所求的最佳路徑。但是我們需要高效率解決這些問題,所以維特比是一種常用的方式。維比特怎么解決這個問題的呢?參考 這里 ?(我開始寫了很多,但是都不如這位兄弟專業,偷個小懶啦,哈哈?^_^)
當然,我不會吝嗇的保存自己的代碼,Share 給大家:
public int [] Decode( string [] input, out double probability) { int inputLength = input.Length; int stateCount = this .state.Length; int minState; double minWeight; int [,] s = new int [stateCount,inputLength]; double [,] a = new double [stateCount,inputLength]; for ( int i = 0 ; i < stateCount; i++ ) { object obj = complex.Table[ this .state[i]][input[ 0 ]]; if (obj != null ) { a[i, 0 ] = ( 1.0 *Math.Log(initialState[ this .state[i]])) - Math.Log( double .Parse(obj.ToString())) ; } } for ( int t = 1 ; t < inputLength; t++ ) { for ( int j = 0 ; j < stateCount; j++ ) { minState = 0 ; minWeight = a[ 0 , t - 1 ] - Math.Log( double .Parse( this .transition.Table[ this .state[ 0 ]][ this .state[j]].ToString())); for ( int i = 0 ; i < stateCount; i++ ) { double weight = a[i, t - 1 ] - Math.Log( double .Parse( this .transition.Table[ this .state[i]][ this .state[j]].ToString())); if (weight < minWeight) { minState = i; minWeight = weight; } } object obj = complex.Table[ this .state[j]][input[t]]; if (obj != null ) { a[j, t] = minWeight - Math.Log( double .Parse(obj.ToString())); s[j, t] = minState; } } } minState = 0 ; minWeight = a[ 0 , inputLength - 1 ]; for ( int i = 1 ; i < stateCount; i++ ) { if (a[i, inputLength - 1 ] < minWeight) { minState = i; minWeight = a[i, inputLength - 1 ]; } } int [] path = new int [input.Length]; path[inputLength - 1 ] = minState; for ( int m = inputLength - 2 ; m >= 0 ; m-- ) { path[m] = s[path[m + 1 ], m + 1 ]; } probability = Math.Exp(- minWeight); return path; }? ? 利用維特比算法,它返回了一個路徑數組,數組是一個長度跟我們輸入的字符串的長度一樣,數組值是0-3的隱含狀態,表示該次更屬于哪一個狀態,通過維特比算法處理后,將詞與狀態路徑合并,我們可以得到這樣的一張表:
Iveely 是 一 款 開 源 搜 索 引 擎S
S
S S S S S S S S B BB
BB
BB
B B B M M M M M M MM
M
M E E EE
EE
E E EE
我想看到上面的表,你已經知道具體的含義了,不用我多說吧?呵呵,讓我們看看程序跑完的截圖吧:
![]()
? ? 上面,講完了基本的分詞后,你一定想知道,我們怎么做中文詞性分析,在自然語言處理(NLP)中,詞性分析難度是很大的,目前據我所知, 哈工大NLP實驗室 的,感覺還挺不錯,而且是開源。好了,繼續我們的詞性分析。
第一步:分詞
幸運的是,我們上面已經做好了分詞,那么用戶給定的輸入,我們已經切分好詞了:”iveely/是/一款/開源/搜索引擎/“。
第二步:統計學習
對,我們再次進行統計學習,依然利用HMM,不過語料庫變了,我們利用1998年01月份人民日報的語料庫,大致形式如下:
"...邁向/v ?充滿/v ?希望/n ?的/u ?新/a ?世紀/n ?——/w ?一九九八年/t ?新年/t ?講話/n ?(/w ?附/v ?圖片/n ?1/m ?張/q ?)/w ..."
每一個詞的后面都有一個詞性,詞性表?查看 這里 ?
是不是感覺跟分詞的時候統計類似?看看下表你就知道了:
HMM 的五元素 ( 學院名 ) 統計中含義 ( 工程含義 ) 隱含狀態S 詞有的N種隱含狀態:名詞、動詞、介詞… 觀察狀態O 所有語料庫中的詞語 初始狀態概率矩陣Pi 詞中各種隱含狀態的初始概率 隱含狀態轉移概率矩陣A N種隱含狀態的轉移概率,例如:動詞到到名詞的轉移概率。 觀察狀態轉移概率矩陣B 語料庫中每一個詞語到N種隱含狀態的概率。例如:漢字“我”是名詞的概率。? 我想看到這里,大家都應該很清楚了。后面利用維特比算法解碼,算出給定的路徑,然后將詞性附加給我們的詞組。
作者說: HMM并不是一種很完美的方法,只是從某種角度,它可以做分詞,可以做詞性分析,所有的code,點擊 這里 查看, 當然效果需要不斷調整和修復 。還有很多網友發郵件問我,為何IveelySE 0.4.0還沒有發布,原因是很多方面的,最重要的是準確率上不去,不忍心將效果很差的智能搜索給大家,但是在繼續整改之后,我也期待早日和大家分享。 謝謝一直以來對IveelySE支持的朋友 。
------------------------------------------------------------
我的郵箱: liufanping@iveely.com
我的微博: weibo.com/liufanping
世界上最快樂的事情,莫過于為理想而奮斗
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
