1. 程式人生 > >jieba分詞的原理

jieba分詞的原理

hmm nod 序列 表示 1.0 str ida 詞頻 font

jieba介紹:

一、支持三種分詞模式:

  • 精確模式,試圖將句子最精確地切開,適合文本分析;
  • 全模式,把句子中所有的可以成詞的詞語都掃描出來, 速度非常快,但是不能解決歧義;
  • 搜索引擎模式,在精確模式的基礎上,對長詞再次切分,提高召回率,適合用於搜索引擎分詞。

二、jieba自帶了一個叫做dict.txt的詞典, 裏面有2萬多條詞, 包含了詞條出現的次數(這個次數是於作者自己基於人民日報語料等資源訓練得出來的)和詞性. 這個第一條的trie樹結構的詞圖掃描, 說的就是把這2萬多條詞語, 放到一個trie樹中, 而trie樹是有名的前綴樹, 也就是說一個詞語的前面幾個字一樣, 就表示他們具有相同的前綴, 就可以使用trie樹來存儲, 具有查找速度快的優勢。

三、jieba分詞應該屬於概率語言模型分詞

概率語言模型分詞的任務是:在全切分所得的所有結果中求某個切分方案S,使得P(S)最大。

jieba用到的算法:

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

1. 根據dict.txt生成trie樹。字典在生成trie樹的同時, 也把每個詞的出現次數轉換為了頻率;

2. 對待分詞句子, 根據dict.txt生成的trie樹, 生成DAG, 實際上通俗的說, 就是對待分詞句子, 根據給定的詞典進行查詞典操作, 生成幾種可能的句子切分。jieba的作者在DAG中記錄的是句子中某個詞的開始位 置, 從0到n-1(n為句子的長度), 每個開始位置作為字典的鍵, value是個list, 其中保存了可能的詞語的結束位置(通過查字典得到詞, 開始位置+詞語的長度得到結束位置)註:

所以可以聯想到,jieba支持全模 式分詞,能把句子中所有的可以成詞的詞語都掃描出來

例如:{0:[1,2,3]} 這樣一個簡單的DAG, 就是表示0位置開始, 在1,2,3位置都是詞, 就是說0~1, 0~2,0~3這三個起始位置之間的字符, 在dict.txt中是詞語.可看示例切分詞圖。

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

1.查找待分詞句子中已經切分好的詞語(我覺得這裏應該是全模式下的分詞list), 對該詞語查找該詞語出現的頻率(次數/總數), 如果沒有該詞(既然是基於詞典查找進行的分詞, 應該是有的), 就把詞典中出現頻率最小的那個詞語的頻率作為該詞的頻率, 也就是說P(某詞語)=FREQ.get(‘某詞語’,min_freq)

2.根據動態規劃查找最大概率路徑的方法, 對句子從右往左反向計算最大概率(一些教科書上可能是從左往右, 這裏反向是因為漢語句子的重心經常落在後面, 就是落在右邊, 因為通常情況下形容詞太多, 後面的才是主幹, 因此, 從右往左計算, 正確率要高於從左往右計算, 這個類似於逆向最大匹配), P(NodeN)=1.0, P(NodeN-1)=P(NodeN)*Max(P(倒數第一個詞))…依次類推, 最後得到最大概率路徑, 得到最大概率的切分組合.

 1 def calc(sentence,DAG,idx,route):  #動態規劃,計算最大概率的切分組合  
 2     #輸入sentence是句子,DAG句子的有向無環圖  
 3     N = len(sentence)  #句子長度  
 4     route[N] = (0.0,‘‘)  
 5     for idx in xrange(N-1,-1,-1):  #和range用法一樣,不過還是建議使用xrange  
 6         #可以看出是從後往前遍歷每個分詞方式的  
 7   
 8         #下面的FREQ保存的是每個詞在dict中的頻度得分,打分的公式是 log(float(v)/total),其中v就是被打分詞語的頻數  
 9          #FREQ.get(sentence[idx:x+1],min_freq)表示,如果字典get沒有找到這個key,那麽我們就使用最後的frequency來做  
10          #由於DAG中是以字典+list的結構存儲的,所以確定了idx為key之外,  
11          #仍然需要for x in DAG[idx]來遍歷所有的單詞結合方式(因為存在不同的結合方法,例如“國”,“國家”等)  
12          #以(頻度得分值,詞語最後一個字的位置)這樣的tuple保存在route中  
13         candidates = [ ( FREQ.get(sentence[idx:x+1],min_freq) + route[x+1][0] , x ) for x in DAG[idx] ]  
14         route[idx] = max(candidates) 

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

未登錄詞:詞典 dict.txt 中沒有記錄的詞(註: 就算把dict.txt中所有的詞匯全部刪掉, jieba依然能夠分詞, 不過分出來的詞, 大部分的長度為2.這個就是基於HMM來預測分詞了)

1.中文詞匯按照BEMS四個狀態來標記, B是開始begin位置, E是end, 是結束位置, M是middle, 是中間位置, S是singgle, 單獨成詞的位置, 沒有前, 也沒有後. jieba作者就是采用了狀態為(B,E,M,S)這四種狀態來標記中文詞語, 比如北京可以標註為 BE, 即 北/B 京/E, 表示北是開始位置, 京是結束位置, 中華民族可以標註為BMME, 就是開始, 中間, 中間, 結束.

2.作者對大量語料進行訓練, 得到了三個概率表。訓通過練得到的概率表和viterbi算法, 就可以得到一個概率最大的BEMS序列, 按照B打頭, E結尾的方式, 對待分詞的句子重新組合, 就得到了分詞結果. 比如 對待分詞的句子 ‘全世界都在學中國話’ 得到一個BEMS序列 [S,B,E,S,S,S,B,E,S] 這個序列只是舉例, 不一定正確, 通過把連續的BE湊合到一起得到一個詞, 單獨的S放單, 就得到一個分詞結果了: 上面的BE位置和句子中單個漢字的位置一一對應, 得到全/S 世界/BE 都/S 在/S 學/S 中國/BE 話/S 從而將句子切分為詞語.

jeiba分詞過程:

生成全切分詞圖:根據trie樹對句子進行全切分,並且生成一個鄰接鏈表表示的詞圖(DAG),查詞典形成切分詞圖的主體過程如下所示:

 1 for(int i=0;i<len;){  
 2     boolean match = dict.getMatch(sentence, i,
 3 wordMatch);//到詞典中查詢   
 4     if (match) {// 已經匹配上  
 5         for (String word:wordMatch.values)
 6 {//把查詢到的詞作為邊加入切分詞圖中  
 7             j = i+word.length();  
 8             g.addEdge(new CnToken(i, j, 10, word));  
 9         }  
10         i=wordMatch.end;  
11     }else{//把單字作為邊加入切分詞圖中  
12         j = i+1;  
13         g.addEdge(new CnToken(i,j,1,sentence.substring(i,j)));  
14         i=j;  
15     }  
16 }  

計算最佳切分路徑:在這個詞圖的基礎上,運用動態規劃算法生成切分最佳路徑。

使用了HMM模型對未登錄詞進行識別:如進行中國人名、外國人名、地名、機構名等未登錄名詞的識別。

重新計算最佳切分路徑。

以上整理於對Python中文分詞模塊結巴分詞算法過程的理解和分析

疑問:

1.識別出的未登錄詞貌似是沒有頻率的以及用戶自定義詞典中詞頻和詞性都是可省略的,那jieba重新計算最佳切分組合是怎麽算的?,是把詞典中頻率最小的那個詞語的頻率作為該詞的頻率?

jieba分詞的原理