jieba中文分詞的.NET版本:jieba.NET
簡介
平時經常用Python寫些小程序。在做文本分析相關的事情時免不了進行中文分詞,于是就遇到了用Python實現的 結巴中文分詞 。jieba使用起來非常簡單,同時分詞的結果也令人印象深刻,有興趣的可以到它的 在線演示站點 體驗下(注意第三行文字)。
.NET平臺上常見的分詞組件是 盤古分詞 ,但是已經好久沒有更新了。最明顯的是內置詞典,jieba的詞典有50萬個詞條,而盤古的詞典是17萬,這樣會造成明顯不同的分詞效果。另外,對于未登錄詞,jieba“采用了基于漢字成詞能力的HMM模型,使用了Viterbi算法”,效果看起來也不錯。
基于以上兩點,加上對于中文分詞的興趣,就嘗試將jieba移植到.NET平臺上,已經把代碼放在了github上: jieba.NET 。在試用jieba.NET之前,先簡單介紹下jieba的實現思路。
jieba實現淺析
jieba本身提供的文檔較少,但我們可以在《 對Python中文分詞模塊結巴分詞算法過程的理解和分析 》、《 jieba 分詞源代碼研讀(1) 》這一系列文章中一窺jieba實現的整體思路。簡言之,它的核心模塊和分詞過程大致是:
- 前綴詞典(Trie) :用于存儲主詞典,也可以動態增刪詞條,這個詞典可以理解為jieba所“知道”的詞,或者說已登錄詞;
- 有向無環圖(DAG) :通過前綴詞典,可以找出句子所有可能的成詞結果;
- 最大概率路徑 :通過DAG,可以了解 所有的 成詞結果,每個結果對應于一條路徑及其概率。由于不同詞條的出現概率不同,不同的結果就對應了不同的概率,我們找出概率最大的那條路徑。到這里,我們對于已登錄詞做出了最合理的劃分;
- HMM模型 和 Viterbi算法 :最大概率路徑之后,我們可能會遇到一些未登錄詞(不包含在前綴詞典中的詞),這時通過HMM和Viterbi嘗試進一步的劃分,得到最終結果
這個過程與人的分詞過程很類似。比如看到這句話:“語言學家參加學術會議 ”,我們會把它劃分為:“語言學家 參加 學術會議”。盡管這個過程是瞬間完成的,但是它確實包含了上述過程的前三步:分詞之前,大腦中已有一個“前綴詞典”,它包括語言、語言學、語言學家等等各個詞條;大腦知道這句話確實存在多種分詞的可能;但它最后還是選出了那個最可能的結果,舍棄了諸如“語言學 家 參加 學術 會議”這樣的結果。
前面這句話僅包含了已登錄詞,再來看另一句話:“ 他來到了網易杭研大廈 ”。一般人可以迅速做出劃分:“ 他 來到 了 網易 (杭研)? 大廈 ”,除了“ 杭研”這兩個字,其它的都屬于已登錄詞,容易劃分出來。對于“ 杭研 ”,我們要想一想它們是兩個單字呢,還是一個新詞。最后,可能會伴隨著這樣一個過程:我知道網易是有研發中心或研究院之類的在杭州的,那么“杭研”可能是與此相關的一個縮寫,嗯,又知道了一個新詞。盡管這個過程與HMM不同,但我們至少了解到,jieba確實是通過某種方式去嘗試尋找未登錄詞。
不過可以想象的是,基于狀態轉移概率的HMM模型(建議參考文章《 中文分詞之HMM模型詳解 》)能夠發現的詞應該也是比較自然或正常的詞,對于新的人名、機構名或網絡詞(喜大普奔之類的),效果不會很好。
jieba.NET用法
jieba.NET當前版本是0.37.1,與jieba保持一致,可以通過NuGet安裝:
PM> Install-Package jieba.NET
安裝之后,把Resources目錄copy到程序集所在目錄即可。下面分別是分詞、詞性標注和關鍵詞提取的示例。
分詞
var segmenter = new JiebaSegmenter(); var segments = segmenter.Cut("我來到北京清華大學", cutAll: true); Console.WriteLine("【全模式】:{0}", string.Join("/ ", segments)); segments = segmenter.Cut("我來到北京清華大學"); // 默認為精確模式 Console.WriteLine("【精確模式】:{0}", string.Join("/ ", segments)); segments = segmenter.Cut("他來到了網易杭研大廈"); // 默認為精確模式,同時也使用HMM模型 Console.WriteLine("【新詞識別】:{0}", string.Join("/ ", segments)); segments = segmenter.CutForSearch("小明碩士畢業于中國科學院計算所,后在日本京都大學深造"); // 搜索引擎模式 Console.WriteLine("【搜索引擎模式】:{0}", string.Join("/ ", segments)); segments = segmenter.Cut("結過婚的和尚未結過婚的"); Console.WriteLine("【歧義消除】:{0}", string.Join("/ ", segments));
運行結果為:
【全模式】:我/ 來到/ 北京/ 清華/ 清華大學/ 華大/ 大學 【精確模式】:我/ 來到/ 北京/ 清華大學 【新詞識別】:他/ 來到/ 了/ 網易/ 杭研/ 大廈 【搜索引擎模式】:小明/ 碩士/ 畢業/ 于/ 中國/ 科學/ 學院/ 科學院/ 中國科學院/ 計算/ 計算所/ ,/ 后/ 在/ 日本/ 京都/ 大學/ 日本京都大學/ 深造 【歧義消除】:結過婚/ 的/ 和/ 尚未/ 結過婚/ 的
JiebaSegmenter.Cut方法可通過cutAll來支持兩種模式,精確模式和全模式。 精確模式 是最基礎和自然的模式,試圖將句子最精確地切開,適合 文本分析 ;而全模式,把句子中所有的可以成詞的詞語都掃描出來, 速度更快,但是不能解決歧義 ,因為它不會掃描最大概率路徑,也不會通過HMM去發現未登錄詞。
CutForSearch采用的是搜索引擎模式, 在精確模式的基礎上對長詞再次切分 ,提高召回率, 適合用于搜索引擎分詞 。
詞性標注
詞性標注采用和ictclas兼容的標記法,關于ictclas和jieba中使用的標記法列表,請參考: 詞性標記 。
var posSeg = new PosSegmenter(); var s = "一團碩大無朋的高能離子云,在遙遠而神秘的太空中迅疾地飄移"; var tokens = posSeg.Cut(s); Console.WriteLine(string.Join(" ", tokens.Select(token => string.Format("{0}/{1}", token.Word, token.Flag))));
運行結果
一團/m 碩大無朋/i 的/uj 高能/n 離子/n 云/ns ,/x 在/p 遙遠/a 而/c 神秘/a 的/uj 太空/n 中/f 迅疾/z 地/uv 飄移/v
關鍵詞提取
看下面來自維基百科的關于算法的文字:
在數學和計算機科學/算學之中,算法/算則法(Algorithm)為一個計算的具體步驟,常用于計算、數據處理和自動推理。精確而言,算法是一個表示為有限長列表的有效方法。算法應包含清晰定義的指令用于計算函數。
算法中的指令描述的是一個計算,當其運行時能從一個初始狀態和初始輸入(可能為空)開始,經過一系列有限而清晰定義的狀態最終產生輸出并停止于一個終態。一個狀態到另一個狀態的轉移不一定是確定的。隨機化算法在內的一些算法,包含了一些隨機輸入。
形式化算法的概念部分源自嘗試解決希爾伯特提出的判定問題,并在其后嘗試定義有效計算性或者有效方法中成形。這些嘗試包括庫爾特·哥德爾、雅克·埃爾布朗和斯蒂芬·科爾·克萊尼分別于1930年、1934年和1935年提出的遞歸函數,阿隆佐·邱奇于1936年提出的λ演算,1936年Emil Leon Post的Formulation 1和艾倫·圖靈1937年提出的圖靈機。即使在當前,依然常有直覺想法難以定義為形式化算法的情況。
jieba.NET提供了TF-IDF和TextRank兩種算法來提取關鍵詞,TF-IDF對應的類是JiebaNet.Analyser. TfidfExtractor ,TextRank的是JiebaNet.Analyser. TextRankExtractor 。
var extractor = new TfidfExtractor(); // 提取前十個僅包含名詞和動詞的關鍵詞 var keywords = extractor.ExtractTags(text, 10, Constants.NounAndVerbPos); foreach (var keyword in keywords) { Console.WriteLine(keyword); }
運行結果是
算法
定義
計算
嘗試
形式化
提出
狀態
指令
輸入
包含
相應的ExtractTagsWithWeight方法的返回結果中除了包含關鍵詞,還包含了相應的權重值。TextRankExtractor的接口與TfidfExtractor完全一致,不再贅述。
小結
分詞、詞性標注和關鍵詞提取是jieba的 三個主要功能模塊,jieba.NET目前盡量在功能和接口上與jieba保持一致,但以后可能會在jieba基礎上提供其它擴展功能。 jieba.NET的開發剛剛開始,還有很多細節需要完善。非常歡迎大家的試用和反饋,也希望能和大家一起討論,共同實現更好的中文分詞庫。
參考: