全文檢索幾乎是全部內容管理系統軟件(CMS)必備的功能,在對公司的CMS產品的開發維護過程當中,全文檢索始終是客戶重點關注的模塊,爲知足客戶各式各樣愈來愈高的要求,對全文檢索曾作過一段時間相對深刻的研究,尤爲是對分詞機制. 算法
1、 什麼是中文分詞 緩存
學過英文的都知道,英文是以單詞爲單位的,單詞與單詞之間以空格或者逗號句號隔開。而中文則以字爲單位,字又組成詞,字和詞再組成句子。因此對於英文,咱們能夠簡單以空格判斷某個字符串是否爲一個單詞,好比I love China,love 和 China很容易被程序區分開來;但中文「我愛中國」就不 同樣了,電腦不知道「中國」是一個詞語仍是「愛中」是一個詞語。把中文的句子切分紅有意義的詞,就是中文分詞,也稱切詞。我愛中國,分詞的結果是:我 愛 中國。 數據結構
目前中文分詞仍是一個難題———對於須要上下文區別的詞以及新詞(人名、地名等)很難完美的區分。國際上將一樣存在分詞問題的韓國、日本和中國並稱爲CJK(Chinese Japanese Korean),對於CJK這個代稱可能包含其餘問題,分詞只是其中之一。 函數
2、 中文分詞的實現 測試
Lucene自帶了幾個分詞器WhitespaceAnalyzer, SimpleAnalyzer, StopAnalyzer, StandardAnalyzer, ChineseAnalyzer, CJKAnalyzer等。前面三個只適用於英文分詞,StandardAnalyzer對可最簡單地實現中文分詞,即二分法,每一個字都做爲一個詞,這樣分出來雖然全面,但有不少缺點,好比,索引文件過大,檢索時速度慢等。ChineseAnalyzer是按字分的,與StandardAnalyzer對中文的分詞沒有大的區別。 CJKAnalyzer是按兩字切分的, 比較武斷,而且會產生垃圾Token,影響索引大小。以上分詞器過於簡單,沒法知足現實的需求,因此咱們須要實現本身的分詞算法。 搜索引擎
現有的中文分詞算法可分爲三大類:基於字符串匹配的分詞方法、基於理解的分詞方法和基於統計的分詞方法。後面二者只是據說過,沒深刻接觸過,這裏着重講下基於字符串匹配的分詞方法。 spa
基於字符串匹配的分詞方法又叫作機械分詞方法,它是按照必定的策略將待分析的漢字串與一個「充分大的」機 器詞典中的詞條進行配,若在詞典中找到某個字符串,則匹配成功(識別出一個詞)。按照掃描方向的不一樣,串匹配分詞方法能夠分爲正向匹配和逆向匹配;按照不 同長度優先匹配的狀況,能夠分爲最大(最長)匹配和最小(最短)匹配;按照是否與詞性標註過程相結合,又能夠分爲單純分詞方法和分詞與標註相結合的一體化 方法。經常使用的幾種機械分詞方法以下: 設計
1)正向最大匹配法(由左到右的方向); 繼承
2)逆向最大匹配法(由右到左的方向); 索引
3)最少切分(使每一句中切出的詞數最小)。
這種分詞方法,首先要有一個詞庫。一個好的分詞器須要一個龐大優良的詞庫以及設計優秀的數據結構來緩存該詞庫。下面使用一個名爲MMAnalyzer的開源分詞器作簡單的分詞演示,而後大體講下怎麼樣基於lucene實現本身的分詞器。MMAnalyzer 簡介:
一、支持英文、數字、中文(簡體)混合分詞 二、經常使用的數量和人名的匹配 三、超過22萬詞的詞庫整理 四、實現正向最大匹配算法 五、詞典的動態擴展 六、分詞效率: 第一次分詞須要1-2秒(讀取詞典),以後速度基本與Lucene自帶分詞器持平。內存消耗: 30M+ |
MMAnalyzer的分詞算法以下:
一、讀取一個字,而後聯想,直到聯想到不能爲止。若是當前能夠構成詞,便返回一個Token。 二、若是當前不能構成詞語,便回溯到最近的能夠構成詞語的節點,返回。 三、最差的狀況就是返回第一個單字。 四、而後從返回結果的下一個字從新開始聯想。 |
public static void main(String[] args) throws IOException { String text = "2008年前三季度,美國次貸危機升級,全球金融持續動盪,世界經濟增加全面放緩,全球經濟增加動力減弱,世界主要經濟體與新興市場正面臨巨大的外部衝擊。"; Analyzer analyzer = new MMAnalyzer(); TokenStream stream = analyzer.tokenStream("xxx", new StringReader(text)); while (true) { Token token = stream.next(); if (token == null) break; System.out.print("[" + token.termText() + "] "); } } |
返回結果以下: [2008] [年前] [三季度] [美國] [次] [貸] [危機] [升級] [全球] [金融] [持續] [動盪] [世界經濟] [增加] [全面] [放] [緩] [全球] [經濟] [增加] [動力] [減弱] [世界] [主要] [經濟] [體] [新興] [市場] [正] [面臨] [巨大] [外部] [衝擊] |
MMAnalyzer分詞器有兩個構造函數MMAnalyzer()和MMAnalyzer(int n)。
MMAnalyzer():採用正向最大匹配的中文分詞算法,至關於分詞粒度等於0。
MMAnalyzer(int n):參數爲分詞粒度:當字數 >= n,且能成詞,該詞就被切分出來。
另外MMAnalyzer還有如下經常使用方法:
addDictionary(FileReader reader):增長一個新詞典,採用每行一個詞的讀取方式。
addWord(String newWord):往詞庫裏新增長一個新詞。
其中addWord方法測試了好像只會把新詞加入到緩存了的詞庫中,並不會並永久性寫入詞典文件中。若是須要寫入詞典文件,可再按如下方法處理。
URL dictionaryPath = URLUtil.getResourceFileUrl("resources/dictionary.txt"); if(dictionaryPath != null){ // new FileWriter(String, boolean) 第二個參數true表示追加文件到尾部 BufferedWriter bw = new BufferedWriter(new FileWriter(dictionaryPath.getPath(), true)); bw.write(searchStr);//追加文件內容 bw.newLine(); bw.close(); } |
固然也可本身實現分詞器,實現過程很簡單,首先實現一個Tokenizer(須要繼承lucene包裏的Tokenizer抽象類),覆寫裏面的next()方法,這也是lucene分詞器實現的最關鍵的地方。而後再實現一個Analyzer(須要繼承lucene包裏的Analyzer抽象類),將上面實現的Tokenizer指定給該Analyzer。
三、 中文分詞一些常見問題及解決辦法
3.1 分詞的缺失
好比同義詞。用戶搜 "北京 飯店" 能不能把" 首都 飯店"也列出來呢? 這個分詞器無能爲力。因此這個問題,解決辦法就只能是在分詞以前,咱們再加一層:同義詞返回模塊。這個思路很不錯,也比較簡單,很容易實現。關鍵是詞庫的創建。
3.2 優先級
例如:我還清晰地記得咱們坐在江邊聊天的情境。
分出來是: 我 還清 晰 地 記得 咱們 坐在 江邊 聊天 的 情境。
結果: 清晰 被拆開了。
這個是基於詞庫的分詞算法固有的問題。沒有很好的解決方法。有統計結果代表,單純使用正向最大匹配的錯誤率爲1/169,單純使用逆向最大匹配的錯誤率爲1/245。有一種解決方案是正向匹配結果後再逆向匹配一次,而後比較結果,消除歧義。最好加入詞彙機率統計功能.有歧義的用機率決定。
3.3 最大匹配的問題
好比搜索「三季度」這個詞,詞庫裏同時有 「三季度」 和 「季度」這兩個詞,分詞時按最大正向匹配 則 「三季度」 被分紅一個完整的詞,按「季度」 去檢索反而搜不出來了。
解決辦法:縮短分詞粒度,當字數等於或超過該粒度參數,且能成詞,該詞就被切分出來。
3.4 新詞識別
新詞,也就是那些在字典中都沒有收錄過,但又確實能稱爲詞的那些詞。最典型的是人名,人能夠很容易理解句子「王軍虎去廣州了」中,「王軍虎」是個詞,由於是一我的的名字,但要是讓計算機去識別就困難了。若是把「王軍虎」作爲一個詞收錄到字典中去,全世界有那麼多名字,並且每時每刻都有新增的人名,收錄這些人名自己就是一項巨大的工程。即便這項工做能夠完成,仍是會存在問題,例如:在句子「王軍虎頭虎腦的」中,「王軍虎」還能不能算詞?
新詞中除了人名之外,還有機構名、地名、產品名、商標名、簡稱、省略語等都是很難處理的問題,並且這些又正好是人們常用的詞,所以對於搜索引擎來講,分詞系統中的新詞識別十分重要。目前新詞識別準確率已經成爲評價一個分詞系統好壞的重要標誌之一。
其餘的還有如熱度、高亮顯示等問題。總言之,中文分詞機制的好壞,直接影響到用戶對搜索結果的滿意度,因此如何分詞是搜索引擎的重中之重。