1. 程式人生 > >Jieba中文分詞說明

Jieba中文分詞說明

結巴分詞介紹

現在開源的中文分詞工具,有IK、MMseg4j、THULAC、Ansj、Jieba、HanLP等,其中最近還在更新並維護的,也是目前分詞效果比較優秀的要屬於Ansj、Jieba、HanLP了。
之前我寫過Ansj分詞器的介紹說明部落格,現在細談一下Jieba分詞的詳細思路及其實現過程。

結巴分詞主頁

結巴分詞的github主頁地址是:https://github.com/fxsjy/jieba(python版),原作者fxsjy是用python寫的,後來有網友寫了其他程式語言的版本。

目前官方頁面推薦的java版結巴分詞地址是:https://github.com/huaban/jieba-analysis

(java版),這個java版的結巴分詞只有針對搜尋引擎分詞的功能,我在這個之上做了優化改進,並添加了詞性標誌功能後,已經pull request 給作者piaolingxue了。

結巴分詞簡介

特點

  • 支援三種分詞模式:
    1. 精確模式,試圖將句子最精確地切開,適合文字分析;
    2. 全模式,把句子中所有的可以成詞的詞語都掃描出來,速度非常快,但是不能解決歧義;
    3. 搜尋引擎模式,在精確模式的基礎上,對長詞再次切分,提高召回率,適合用於搜尋引擎分詞。
  • 支援繁體分詞
  • 支援自定義詞典

演算法

  • 基於字首詞典實現高效的詞圖掃描,生成句子中漢字所有可能成詞情況所構成的有向無環圖 (DAG)
  • 採用了動態規劃查詢最大概率路徑,找出基於詞頻的最大切分組合
  • 對於未登入詞,採用了基於漢字成詞能力的 HMM 模型,使用了 Viterbi 演算法

分詞過程

基於Trie樹結構實現詞圖掃描,生成成詞情況的有向無環圖

  1. Tire樹

    這個需要先了解trie樹的python實現,結巴分詞自帶了一個叫做dict.txt的詞典,裡面有2萬多條詞,包含了詞條出現的次數和詞性(這個詞典是清華大學、中科院等基於2014年人民日報語料等資源訓練,詞性是國標漢語詞性)。這個第一條的trie樹結構的詞圖掃描,說的就是把這2萬多條詞語,放到一個trie樹中,而trie樹是有名的字首樹,也就是說一個詞語的前面幾個字一樣,就表示他們具有相同的字首,就可以使用trie樹來儲存,具有查詢速度快的優勢。

    你可能會想到把dict.txt中所有的詞彙全部刪掉,然後再試試結巴能不能分詞,結果會發現:結巴依然能夠分詞,不過分出來的詞,大部分的長度為2。這個就是第三條,基於HMM來預測分詞了。

  2. DAG有向無環圖
    這個是後一句的“生成句子中漢字所有可能成詞情況所構成的有向無環圖”,意思是給定一個句子,要你分詞,對這個句子進行生成有向無環圖。切分詞圖是為了消除分詞中的歧異,提高切分準確度,需要找出一句話所有可能的詞,生成全切分詞圖。如果對有向無環圖想有更深入理解請google。

  3. 作者是怎麼切分的呢?

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

    舉個栗子:

    一個簡單的DAG: {0:[1,2,3]}
    表示0位置開始,在1,2,3位置都是詞
    就是說0~1,0~2,0~3這三個起始位置之間的字元,在dict.txt中是詞語

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

  1. 動態規劃

    這個在一些大學課程中講了的,忘記的同學請google,我的部落格之前也介紹過動態規劃問題

  2. 查詢最大概率路徑

    原始碼中講字典在生成trie樹的同時,也把每個詞的出現次數轉換為了頻率。動態規劃中:

    • 先查詢待分詞句子中已經切分好的詞語,對該詞語查詢該詞語出現的頻率(次數/總數),如果沒有該詞(既然是基於詞典查詢,應該是有的),就把詞典中出現頻率最小的那個詞語的頻率作為該詞的頻率,也就是說P(某詞語)=FREQ.get(‘某詞語’,min_freq)
    • 然後根據動態規劃查詢最大概率路徑的方法,對句子從右往左反向計算最大概率(一些教科書上可能是從左往右,這裡反向是因為漢語句子的重心經常落在後面,就是落在右邊,因為通常情況下形容詞太多,後面的才是主幹,因此,從右往左計算,正確率要高於從左往右計算,這個類似於逆向最大匹配),P(NodeN)=1.0,P(NodeN-1)=P(NodeN)*Max(P(倒數第一個詞))…依次類推,
    • 最後得到最大概率路徑,得到最大概率的切分組合。

對於未登入詞,採用了HMM模型,使用了Viterbi演算法

  1. 未登入詞

    什麼意思?其實就是詞典 dict.txt 中沒有記錄的詞。上面說了,把 dict.txt 中的所有詞語都刪除了,結巴分詞一樣可以分詞,就是說的這個。

  2. 怎麼做到的?

    這個就基於jieba採用的HMM模型了,中文詞彙按照BEMS四個狀態來標記。

    BEMS:

    B是開始begin位置
    E是end是結束位置
    M是middle是中間位置
    S是singgle,單獨成詞的位置

    採用了狀態為(B,E,M,S)這四種狀態來標記中文詞語,比如北京可以標註為 BE,即 北/B 京/E,表示“北”是開始位置,“京”是結束位置,“中華民族”可以標註為BMME,就是開始-中間-中間-結束。

  3. 概率表

    經過對大量語料的訓練,得到了finalseg目錄下的三個概率表文件(來自結巴專案的issues):

    • prob_trans.py 位置轉換概率,即B(開頭),M(中間),E(結尾),S(獨立成詞)四種狀態的轉移概率

      {‘B’: {‘E’: 0.8518218565181658, ‘M’: 0.14817814348183422},
      ‘E’: {‘B’: 0.5544853051164425, ‘S’: 0.44551469488355755},
      ‘M’: {‘E’: 0.7164487459986911, ‘M’: 0.2835512540013088},
      ‘S’: {‘B’: 0.48617017333894563, ‘S’: 0.5138298266610544}}

      P(E|B) = 0.851, P(M|B) = 0.149,說明當我們處於一個詞的開頭時,下一個字是結尾的概率要遠高於下一個字是中間字的概率,符合我們的直覺,因為二個字的詞比多個字的詞更常見。

    • prob_emit.py 位置到單字的發射概率,比如P(“和”|M)表示一個詞的中間出現“和”這個字的概率

    • prob_start.py 詞語以某種狀態開頭的概率,其實只有兩種:要麼是B,要麼是S。這個就是起始向量,就是HMM系統的最初模型狀態

實際上,BEMS之間的轉換有點類似於二元模型,就是兩個詞之間的轉移二元模型考慮一個單詞後出現另外一個單詞的概率,是N元模型中的一種。

舉個栗子:

“中國”之後出現”北京”的概率大於“中國”之後出現“北海”的概率
也就是:中國北京 比 中國北海 出現的概率大些,更有可能是一個詞語。

不過,作者這裡應該不是用的二元分詞模型的,這裡的BEMS只提供了單個漢字之間的轉換,發射概率,並沒有提供粒度更大的,基於詞語的發射和轉移概率。給定一個待分詞的句子,就是觀察序列,對HMM(BEMS)四種狀態的模型來說,就是為了找到一個最佳的BEMS序列,這個就需要使用viterbi演算法來得到這個最佳的隱藏狀態序列,具體的python版的viterbi演算法請看維基百科的維特比演算法

通過概率表和viterbi演算法,就可以得到一個概率最大的BEMS序列,按照B打頭,E結尾的方式,對待分詞的句子重新組合,就得到了分詞結果。

舉個栗子:

帶分詞的句子: “全世界都在學中國話”
BEMS序列: [S,B,E,S,S,S,B,E,S]
把連續的BE湊合到一起得到一個詞,單獨的S放單,就得到一個分詞結果了:
得到全/S 世界/BE 都/S 在/S 學/S 中國/BE 話/S 從而將句子切分為詞語。

以上,完成中文分詞,對於上面的解釋有任何術語不理解,請使用google。

參考文獻