利用Lucene制作中文搜尋應用
chinese, lucene, search at March 16th, 2005 by 小影
之前討論過想要有自行組合新聞的BLOG軟件,這種軟件最重要的部份就是搜尋和相似度統計的功能。要自行制作一個功能完整又效能高的搜尋器絕不容易,幸好早有開放源碼的軟件替我們做此工作。
Apache Lucene
Apache Lucene是一個開放源碼的搜尋器引擎,利用它可以輕易地為Java軟件加入全文搜尋功能。Lucene的最主要工作是替文件的每一個作索引,索引讓搜尋的效率比傳統的逐字比較大大提高,試想想google怎樣可以瞬即自全世界的文件中找到有關鍵字的網頁,但傳統的Windows檔案搜索卻常常要花半小時? Lucen提供一組解讀、過濾、分析文件、編排和使用索引的API,它的強大之處除了高效和簡單外,最重要的是使用者可以隨時應自己需要自訂其功能。
分析器
要為應用提高搜尋效率,不同語言,甚至不同類型的文章都需要特別的分析器(Analyzer)。Lucene 的分析器由分解器(Tokenizer)和過濾器(TokenFilter)組成,前者分割文字、找出文件中的最少單位(Token),後者過濾和改變輸入去方便處理。英語的文字分割簡單直接,但對於中文來說要有意義地分割則困難得多了。 雖然Lucene Sandbox Project下己有兩個可以分析中文的分析器 ChineseAnalyzer和CJKAnalyzer,但因為他們策略太簡單,各有不盡理想的地方。
中文詞語分割
以簡單句子為例:「我是中國人」,ChineseTokenizer會將之分割為五個中文字:「我、是、中、國、人」,CJKTokenizer則會將之分割為「我是、是中、中國、國人」四個二節的詞。前者的問題是沒有考慮中文詞語的問題,如搜尋「國中」一樣搜尋到「我是中國人」。後者的問題則是制做了大量沒意義的詞如「是中」「國人」,讓索引沒必要地增大、降低搜尋效率。理想的方法是按詞語分割,如「我、是 、中國人」,但要完美地分割中文卻很困難,多年以來直到現在都有無數的研究都在想有效的新方法。
如何研究更好的方法是一回事,實作一個足夠應用的分析器又是另一回事,要是不滿足於ChineseTokenizer和CJKTokenizer的簡單,又不想花時間研究,可以參考和應用前人所作的解決方案。
實作
管理線上中文工具網站的 Erik Peterson 開發了一個基於詞語的中文分割器。它的算法很簡單:準備一本中文詞典,將輸入的文章的每一字逐字和詞典比較,順序地找文章中可以對應詞語。例如「中國人」,首先找到「中」、找著找到「中國」、再找到「中國人」,分割器就會返回「中國人」作為Token。這種算法簡單但有效,字典用Tree實作的話效能也相當好,Eric的實作包括Perl和Java版本的程式。
怎樣應用這個分割器去制作Lucene的Tokenizer呢?當然我們可以研究它的算法自行寫一個實作Tokenizer介面的Segmenter Class,這樣效率最高但必須重寫整個程式。為了重用已有的資源,我的方案是先準備好輸入字串、經過segmenter分割成有空格的中文字,再用跟英文字一樣的用空格分割方法。
實作時發現以下一些問題(和解決方法):
- Segmenter要將整本字典放進記憶體的樹結構中,視電腦速度而定需要3秒到30秒準備,為了避免重覆這漫長過種使用了Singleton去產生Segmenter的實體,同時使用serialize的方法讓第二次執行時不用重新制作樹
- 原裝的StandardTokenizer設計以英語系為目標,原以為只要輸入有空格的中文即可分割中文,不過發現原來它對中日韓字體另有處理,它跟ChineseTokenizer一樣會把每個中文字當成一個token。幸好StandardTokenizer是使用JavaCC (類似YACC,是JAVA版的語言編譯器) 制作,只要更改原本StandardTokenizer.jj中的語法,把中文當作一般英文字母去處理就能達到原先的目標了。 (更改了的CStandardTokenizer.jj)
完成的Segmenter所造的index比CJK Analyzer或者Chinese Analyzer更少,但要注意由於要載入一個佔記憶體3M以上的大字典所以記憶體方面更求更高,而在分析少量文件時預載字典的overhead隨時比分析少,只有大量使用才會見到它的優點。
相關檔案:
- 源碼 (文字檔)
- 源碼和Ant的build檔
相關連結:
- Apache Lucene
- Lucene Tutorial
- Lucene的語彙分析與其擴充方式
- Chinese Word Segment @ google
- Lucene中的基本概念
- Lucene学习笔记(1)
- 在应用中加入全文检索功能
- lucene与搜索引擎技术
24 Responses to “利用Lucene制作中文搜尋應用”
By Samuel on May 14, 2005 | Reply
RAR压缩档不能下载,403 forbidden。
请问是否只需要在索引和查询的时候使用这个token和analyzer就可以呢?
不知道您是否能提供dotLuncene的.net版本?
谢谢
By XUNTW on Jun 17, 2005 | Reply
源碼和Ant的build檔 無法連接,可修補嗎?
THKS!!
By 小影 on Jun 20, 2005 | Reply
己經修好了!
當時做的時候純是為了解Lucene而作。寫了這篇之後再想,也許這個中文分割並無實用,不過由於我沒有真正的中文應用要造,所以沒有測試數據啦…
By 过客 on Jul 29, 2005 | Reply
学习笔记打开好像是乱码!
能修改一下吗?谢谢!
我写简体中文你看得懂吧!
By 小影 on Jul 30, 2005 | Reply
那是外面的連結,不在我的控制範圍。不過內容是沒問題的,只需要手動將編碼設為GB就可以了
By 过客 on Aug 1, 2005 | Reply
你的lucene中文分词有三个包,cn,cw,cjk,按照你说的你的cw实现了基于词典的分词,生成的索引应该比较小吧,但我测试生成的索引文件比较大,我个人感觉好像不是基于词典的(具体程序没有研究);分词效果好像不是特别好:查全率还可以,准确率不高!
By 过客 on Aug 1, 2005 | Reply
接上:还没有stopword的过滤!
By 小影 on Aug 1, 2005 | Reply
分詞確是基於字典的,如下圖。
由query的輸出可以看見「這是一隻十分神秘的箱子」被分成了「一隻 十分 神秘 箱子」,基本的分詞和stop word還是有的。
我估計你遇到的問題是用了GB,是這個分詞是針對UTF-8的,要先將字碼轉換成UTF-8再index才有效!
By 过客 on Aug 2, 2005 | Reply
我还是不太明白
我的qq:30931347
能联系请教些问题吗?谢谢!
By 过客 on Aug 3, 2005 | Reply
麻烦你,再向你请教个问题
我是在eclipse下运行程序的,想用lucene基于词典检索中文,IndexWriter indexWriter= new IndexWriter(file,new SrandardAnalyzer(),true);这个语句中的分析器是不是要换成CWordAnalyzer()?
我的存储文件的格式是ANSI的时候用StandardAnalyzer也可以,但是当我保存一文件是UTF-8格式后,就不能检索到这些文件,不知道什么原因
多谢解答,我是个初学者!
By 小影 on Aug 3, 2005 | Reply
1. Yes. StandardAnalyzer is the standard implementation, CWordAnalyzer is my implementation
2. If you change the encoding, you need to redo the index before you can search them
I do not have QQ, you can contact me via siu [d o t] ying [a t] gmail [d o t] com~
By 兔子 on Jan 16, 2006 | Reply
請問站長,有沒有中文字頻分析的程式啊。譬如統計:一一二三,一(2)、二(1)、三(1)
我找了好久了,網路上有嗎?多謝阿
By 小影 on Jan 17, 2006 | Reply
Why not write one? If no lexicon is needed, it can be as simple as (in php) …
function count_chinese($content) {
$content_len = mb_strlen($content);
for ($i=0; $i< $content_len; $i++){
$words[$j++] = mb_substr($content, $i, 1);
}
return array_count_values($words);
}
Of course you will need more logic on english/stopword, but it is still trivial. You may review my previous attempt here (not finished…).
By 兔子 on Jan 17, 2006 | Reply
小影多謝啊,可是我對這些程式一竅不通,目前亟需一款可以分析文字檔中用字頻率的。
嗚,沒時間自己寫(重點是對程式還只是門外漢而已),以後若有機會也會寫一些免費軟件供大家使用的,現在只想先找現成的來用,所以問了一下,多謝啊。
By 兔子 on Jan 19, 2006 | Reply
版主我找到了,好康的東西,也讓你看一下吧。http://ccl.pku.edu.cn/doubtfire/Course/Chinese%20Information%20Processing/2002_2003_1.htm
現成的,嘿,挺適合我這懶蟲的…=.=
By qlqsh on Nov 27, 2006 | Reply
老大,救急问个问题。
我建了一个文字的索引。用的gbk编码保存的数据。但是当我的前台程序打开索引的时候,程序报错,无法打开索引。java.lang.ArrayIndexOutOfBoundsException: -1
at org.apache.lucene.search.IndexSearcher.(IndexSearcher.java:43)
at _jsp._phone._new_0search__jsp._jspService(phone/new_search.jsp:128)。但在我的系统的模拟情况下却没什么问题(我的是windows中文)。经过排查估计可能是保存的编码有问题(系统是Linux,英文编码),将索引保存的数据转换为utf8格式,不再报这个问题。我想知道这是为什么?有什么解决方法吗?
By qlqsh on Nov 27, 2006 | Reply
IndexSearcher is = new IndexSearcher(fsDir);这就是错误的位置,它连索引都无法打开,刚才忘说了。
By Chian on Mar 20, 2007 | Reply
您好,
小弟最近正在找一些關於「詞庫」的資源,其中也包含了
「stopword」、「數字」、「人名」、「路名」等詞
請問您是否知道這方面的資源哪可取得呢? 謝謝您。
By kentsin on Apr 1, 2008 | Reply
我想問一句, 不是應該對文件做索引是才需要分詞
查詢是否不需要分詞?其實簡單地將查詢處理就可以呢?
By 小影 on Apr 1, 2008 | Reply
如果索引用了分詞,查詢也同樣要分詞。
By Dennis on Jun 9, 2008 | Reply
中文分詞不應該使用 stopwords
例如 “是” “的” “和”
它們都有各自再組成的詞語
“是非” “的士” “和服” 等等
但 stopwords 都是在分詞之前進行
而基另一個事實: 語言是活的
新詞/特有詞的出現會令到只使用字典算法的程式出問題; 不過其實一般來說 bi-gram + 常用詞典 雙重應用的分詞已經不太差
By 小影 on Jun 9, 2008 | Reply
Dennis
之前確實沒有考慮 stopwords 帶來的問題。
絕對同意你的說法… 基本上如不是特殊的應用,我會選擇只用 bi-gram 了。固定的字典和簡單的算法解決不了人名、新詞的問題。使用全自動的方法又會有機會抓到一些攪笑或不能出街的詞…
By Gary on Jul 22, 2008 | Reply
“它的算法很簡單:準備一本中文詞典,將輸入的文章的每一字逐字和詞典比較,順序地找文章中可以對應詞語。”
請問,能否提供一份中文詞典? 謝謝.