mmseg分詞算法及實現

一、分詞方法

關於中文分詞 參考之前寫的jieba分詞源碼分析 jieba中文分詞
中文分詞算法大概分爲兩大類:

  • 一是基於字符串匹配,即掃描字符串,使用如正向/逆向最大匹配,最小切分等策略(俗稱基於詞典的)
    基於詞典的分詞算法比較常見,比如正向/逆向最大匹配,最小切分(使一句話中的詞語數量最少)等。具體使用的時候,通常是多種算法合用,或者一種爲主、多種爲輔,同時還會加入詞性、詞頻等屬性來輔助處理(運用某些簡單的數學模型)。
    這類算法優點是速度塊,時間複雜度較低,實現簡單,文中介紹的mmseg屬於這類算法。

  • 二是基於統計以及機器學習的分詞方式(非詞典方法)
    一般主要是運用概率統計、機器學習等方面的方法,目前常見的是CRF,HMM等。
    這類分詞事先對中文進行建模,根據觀測到的數據(標註好的語料)對模型參數進行估計(訓練)。 在分詞階段再通過模型計算各種分詞出現的概率,將概率最大的分詞結果作爲最終結果。
    有關HMM可以參見之前寫的 jieba HMM。CRF分詞可以參考 這裏
    這類算法對未登錄詞識別效果較好,能夠根據使用領域達到較高的分詞精度,但是實現比較複雜,通常需要大量的前期工作。

二、mmseg算法

下面介紹下mmseg算法:

mmseg算法

使用過mmseg算法的童鞋都反映其簡單、高效、實用、效果還不錯,參考論文 mmseg
其主要思想每次從一個完整的句子裏,按照從左向右的順序,識別出多種不同的3個詞的組合,然後根據下面的4條消歧規則,確定最佳的備選詞組合。
選擇備選詞組合中的第1個詞,作爲1次迭代的分詞結果;剩餘的詞(即這個句子中除了第一個已經分出的詞的剩餘部分)繼續進行下一輪的分詞運算。採用這種辦法的好處是,爲傳統的前向最大匹配算法加入了上下文信息,解決了其每次選詞只考慮詞本身,而忽視上下文相關詞的問題。 mmseg論文中說明了(The Maximum Matching Algorithm and Its Variants)Maximum Matching Algorithm的兩種方法:
1. Simple方法,即簡單的正向匹配,根據開頭的字,列出所有可能的結果。比如“國際化大都市”,可以得到:

國際
國際化

這三個匹配結果(假設這四個詞都包含在詞典裏)。
2. Complex方法,匹配出所有的“三個詞的詞組”(即原文中的chunk,“詞組”),即從某一既定的字爲起始位置,得到所有可能的“以三個詞爲一組”的所有組合。比如“研究生命起源”,可以得到
研_究_生
研_究_生命
研究生_命_起源
研究_生命_起源
這些“詞組”(根據詞典,可能遠不止這些,僅此舉例)。

四個規則

消除歧義的規則”有四個,使用中依次用這四個規則進行過濾,直到只有一種結果或者第四個規則使用完畢,4條消歧規則包括:
1. 備選詞組合的長度之和最大(Maximum matching (Chen & Liu 1992));
2. 備選詞組合的平均詞長最大(Largest average word length (Chen & Liu, 1992));
3. 備選詞組合的詞長變化最小(Smallest variance of word lengths (Chen & Liu, 1992));
4. 備選詞組合中,單字詞的出現頻率統計值最高(取單字詞詞頻的自然對數,然後將得到的值相加,取總和最大的詞)(Largest sum of degree of morphemic freedom of one-character words)。
下面分別舉例說明一下4個規則:

  • 備選詞組合的長度之和最大(Maximum matching (Chen & Liu 1992))
    有兩種情況,分別對應於使用“simple”和“complex”的匹配方法。對“simple”匹配方法,選擇長度最大的詞,用在上文的例子中即選擇“國際化”;
    對“complex”匹配方法,選擇“詞組長度最大的”那個詞組,然後選擇這個詞組的第一個詞,作爲切分出的第一個詞,如上文 的例子中即“研究生_命_起源”中的“研究生”,或者“研究_生命_起源”中的“研究”。
    對於有超過一個詞組滿足這個條件的則應用下一個規則。

  • 備選詞組合的平均詞長最大(Largest average word length (Chen & Liu, 1992))
    經過規則1過濾後,如果剩餘的詞組超過1個,那就選擇平均詞語長度最大的那個(平均詞長=詞組總字數/詞語數量)。比如“生活水平”,可能得到如下詞組:
    生_活水_平 (4/3=1.33)
    生活_水_平 (4/3=1.33)
    生活_水平 (4/2=2)
    根據此規則,就可以確定選擇“生活_水平”這個詞組。
    對於上面的 “研究生命起源” 結果:”研究生_命_起源“ “研究_生命_起源” 利用rule2有
    研究生_命_起源 (6/3=2)
    研究_生命_起源 (6/3=2)
    可見這條規則沒有起作用,因爲兩者平均長度相同。論文中也有說明:


    This rule is useful only for condition in which one or more word position in the chunks are empty. When the chunks are real three-word chunks, this rule is not useful. Because three-word chunks with the same total length will certainly have the same average length. Therefore we need another solution.

  • 備選詞組合的詞長變化最小(Smallest variance of word lengths (Chen & Liu, 1992))
    由於詞語長度的變化率可以由標準差反映,所以此處直接套用標準差公式即可。比如 對於上面的case “研究生命起源”有:
    研究_生命_起源 (標準差=sqrt(((2-2)^2+(2-2)^2+(2-2^2))/3)=0)
    研究生_命_起源 (標準差=sqrt(((2-3)^2+(2-1)^2+(2-2)^2)/3)=0.8165)
    於是通過這一規則選擇“研究_生命_起源”這個詞組。

  • 備選詞組合中,單字詞的出現頻率統計值最高(Largest sum of degree of morphemic freedom of one-character words)
    這裏的degree of morphemic freedom可以用一個數學公式表達:log(frequency),即詞頻的自然對數(這裏log表示數學中的ln)。這個規則的意思是“計算詞組中的所有單字詞詞頻的自然對數,然後將得到的值相加,取總和最大的詞組”。比如:
    “設施和服務”,這個會有如下幾種組合
    設施_和服_務_
    設施_和_服務_
    設_施_和服_

    經過規則1過濾得到:
    設施_和服_務_
    設施_和_服務_

    規則2和規則3都無法得到唯一結果,只能利用最後一個規則 第一條中的“務”和第二條中的“和”,從直觀看,顯然是“和”的詞頻在日常場景下要高,這依賴一個詞頻字典和的詞頻決定了的分詞。
    假設“務”作爲單字詞時候的頻率是30,“和”作爲單字詞時候的頻率是100,對30和100取自然對數,然後取最大值者,所以取“和”字所在的詞組,即“設施_和_服務”。

    也許會問爲什麼要對“詞頻”取自然對數呢?可以這樣理解,詞組中單字詞詞頻總和可能一樣,但是實際的效果並不同,比如
    A_BBB_C (單字詞詞頻,A:3, C:7)
    DD_E_F (單字詞詞頻,E:5,F:5)
    表示兩個詞組,A、C、E、F表示不同的單字詞,如果不取自然對數,單純就詞頻來計算,那麼這兩個詞組是一樣的(3+7=5+5),但實際上不同的詞頻範圍所表示的效果也不同,所以這裏取自然對數,以表區分(ln(3)+ln(7) < ln(5)+ln(5), 3.0445<3.2189)。


這個四個過濾規則中,如果使用simple的匹配方法,只能使用第一個規則過濾,如果使用complex的匹配方法,則四個規則都可以使用。實際使用中,一般都是使用complex的匹配方法+四個規則過濾。(實際中complex用的最多)。
通過上面的敘述可以發現MMSEG是一個“直觀”的分詞方法。它把一個句子“儘可能長” && “儘可能均勻”(“儘可能長”是指所切分的詞儘可能的長)的區切分,這與中文的語法習慣比較相符,其實MMSEG的分詞效果與詞典關係較大,尤其是詞典中單字詞的頻率。可以根據使用領域,專門定製詞典(比如計算機類詞庫,生活信息類詞庫,旅遊類詞庫等,歡迎體驗我狗的 細胞詞庫 ),儘可能的細分詞典,這樣得到的分詞效果會好很多。
總的來說 MMSEG是一個簡單、可行、快速的分詞方法。

三、mmseg分詞算法實現

有關mmseg的分詞策略通過上面的介紹,大家有一定的認識了,其**主要**思想就是每次從一個完整的句子裏,按照從左向右的順序,識別出多種不同的3個詞的組合(chunk),然後根據mmseg的4條消歧規則,確定最佳的備選詞組合,選擇備選詞組合中的最佳詞(即第1個詞),作爲1次迭代的分詞結果;剩餘的詞(即這個句子中除了第一個已經分出的詞的剩餘部分)繼續進行下一輪的分詞運算。可以仔細閱讀mmseg的論文,寫的挺詳細的。指導算法思路,下面來看下實現過程: mmseg分詞所需要的工作分爲下面幾點:
  • 轉碼,string轉換成utf16( unicode),以及逆轉換。
    關鍵部分,是分詞的前提。因爲每次進行分詞前,都需要將string decode成utf16(unicode),分詞完要輸出的時候又需要將unicode encode成string。 因爲在分詞算法中,對於句子是按一個字一個字來計算和分詞的。所以轉換成unicode是完成分詞算法的必要前提。轉碼部分利用了StringUtil.hpp的轉碼模塊。
  • trie字典樹
    將詞庫字典轉換成trie樹,從而可以進行高效查找。實現起來利用c++的數據結構unordered_map即可實現,詳細參見代碼。
    mmseg實現起來採用了{.h, .cpp} and {.hpp} 兩種組織方式,爲什麼採用hpp?

mmseg的主要目錄結構如下:

mmseg
├── build : 生成*.so 動態鏈接庫
├── data :詞典數據
└── src : 實現源碼
└── util : 字符串處理通用庫

代碼採用c++11實現(g++ version >= 4.8 is recommended),已有詳細註釋,不在敷述,請見源碼。
https://github.com/ustcdane/mmseg

  • 使用方法
    在主程序中 如main.cpp
    • 如果使用hpp格式的文件 即#include “src/Mmseg.hpp”,編譯方式爲:
      g++ -Ofast -march=native -funroll-loops -o mmseg -std=c++11 main.cpp
    • 如果使用h格式的文件 即#include “src/Mmseg.h”,編譯方式爲:
      g++ -Ofast -march=native -funroll-loops -o mmseg -std=c++11 main.cpp src/Mmseg.cpp
      另外爲了能夠方便的在其他地方使用mmseg分詞,也可以把這部分代碼編譯成*.so的動態鏈接庫,進入目錄build , 裏面有已經寫好的Makefile文件 直接make 即可生成 libmmseg.so的 動態鏈接庫文件,從而方便在其它程序中調用。

注意 爲了更清楚的看mmseg的過程,我分別在 Mmseg.hpp 和 Mmseg.cpp中定義了宏
#define DEBUG_LEVEL , 如果不想看計算過程可以直接註釋掉這一行代碼。

四、測試結果

進入mmesg ,運行命令,編譯main.cpp,生成mmseg可執行文件。

g++ -Ofast -march=native -funroll-loops -o mmseg -std=c++11 main.cpp src/Mmseg.cpp

運行mmseg即可測試,在我的機器上測試結果如下。

mmseg測試 By Daniel

源碼見: https://github.com/ustcdane/mmseg ,實現過程參考了 jdeng 關於mmseg的代碼。艾瑪,搞了一天終於寫完。

參考

  1. http://technology.chtsai.org/mmseg/
  2. http://bjzhkuang.iteye.com/blog/1988127
  3. https://github.com/jdeng/mmseg
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章