Lucene3.0分词原理与分词系统
分词原理
建立索引和查询的过程中,都是以基本的语素项为单位的。基本的语素项就是通过分词得到。这个过程决定了索引单元金额最终的匹配过程。
分词在文本索引的建立过程和用户提交检索过程中都存在。利用相同的分词器,把短语或者句子切分成相同的结果,才能保证检索过程顺利进行。
1、 英文分词的原理
基本的处理流程是:输入文本、词汇分割、词汇过滤(去除停留词)、词干提取(形态还原)、大写转为小写、结果输出。
2、 中文分词原理
中文分词比较复杂,并没有英文分词那么简单。这主要是因为中文的词与词之间并不像英文中那样用空格来隔开。
主要的方法有三种:基于词典匹配的分词方法、基于语义理解的分词、基于词频统计的分词。
① 基于词典匹配的分词方法
基于字典匹配的分词方法按照一定的匹配策略将输入的字符串与机器字典词条进行匹配。如果在词典中找到当前字符串,则匹配成功输出识别的词汇。按照匹配操作的扫描方向不同,字典匹配分词方法可以分为正向匹配和逆向匹配,以及结合了两者的双向匹配算法;按照不同长度优先匹配的情况,可以分为最大(最长)匹配和最小(最短)匹配;按照是否与词性标注过程相结合,又可以分为单纯分词方法和分词与词性标注相结合的方法。几种常用的词典分词方法如下所示:
● 正向最大匹配(由左到右的方向)。
● 逆向最大匹配(由右到左的方向)。
● 最少切分(是每一句中切除的词数最小)。
实际应用中上述各种方法经常组合使用,达到最好的效果,从而衍生出了结合正向最大匹配方法和逆向最大匹配算法的双向匹配分词法。由于中分词最大的问题是歧义处理,结合中文语言自身的特点,经常采用逆向匹配的切分算法,处理的精度高于正向匹配,产生的切分歧义现象也较少。
真正实用的分词系统,都是把词典分词作为基础手段,结合各种语言的其他特征信息来提高切分的效果和准确度。有的实用系统中将分词和词性标注结合起来,利用句法和词法分析对分词决策提高帮助,在词性标注过程中迭代处理,利用词性和语法信息对分词结果进行检验、调整。
既然是基于分词词典的分词方法,当然还要考虑选择一个好的查找词典算法。一般有数字搜索树, Trie 算法等等。
所谓的数字搜索树( retrieve 树)就是要求树的一个节点保留一个字符。如果一个单词比一个字符长,则包含第一个字符串的节点有指向下一个字符的节点,以此类推。这样组成一个层次结构的树,树的第一层包括所有单词的第一个字符,树的第二层包括所有单词的第二个字符,以此类推,数字搜索树的最大高度是词典中最长单词的长度。但是这样的树,每一个节点都需要很多内存。假设每个词都是由一个小写英文字母组成的,这个节点中会有 26 个指针。所以不太可能直接用这样的数字搜索树来存储中文这样的大字符集。
Trie 树,即三叉搜索树,每一个节点包括一个字符,但只有三个指针,一个指向左边的树,一个指向右边的树,还有一个向下,指向单词的下一个数据单元。三叉搜索树是二叉搜索树和数字搜索树的混合体。它有和数字搜索树差不多的速度但是和二叉搜索树一样只需要相对较少的内存空间。单词的读入顺序对于创建平衡的三叉搜索树很重要,但对于二叉搜索树就不是太重要了。由于限于篇幅,这里就不写上代码了。
② 基于语义理解的分词
基于语义理解的分词方法是模拟人脑对语言和句子的理解,达到识别词汇单元的效果。基本模式是把分词、句法、语义分析并行进行,利用句法和语义信息来处理分词的歧义。
一般结构中通常包括分词子系统、句法语义子系统、调度系统。在调度系统的协调下,分词子系统可以获得有关词、句子等的句法和语义信息,模拟人脑对句子的理过程。基于语义理解的分词方法需要使用大量的语言知识和信息。
目前国内外对汉语语言知识的理解和处理能力还没有达到语义层面,具体到语言信息很难组织成机器可直接读取、计算的形式,因此目前基于语义理解的分词系统还处在试验阶段。
③ 基于词频统计的分词
这种做法基于人们对中文词语的直接感觉。通常词是稳定的词的组合,因此在中文文章的上下文中,相邻的字搭配出现的频率越多,就越有可能形成一个固定的词。根据 n 元语法知识可以知道,字与字相邻同时出现的频率或概率能够较好地反映成词的可信度。
实际的系统中,通过对精心准备的中文语料中相邻共现的各个字的组合的频度进行统计,计算不同字词的共现信息。根据两个字的统计信息,计算两个汉字的相邻共现概率。统计处来的信息体现了中文环境下汉字之间结合紧密程度。当紧密程度高于某一个阈值时,便可认为此字组可能构成一个词。
基于词频统计的分词方法只需要对语料中的字组频度进行统计,不需要切分词典,因而又叫做无词典分词法或统计分词方法。这种方法经常抽出一些共现频度高、但并不是词的常用字组,需要专门处理,提高精确度。实际应用的统计分词系统都使用一个基本的常用词词典,把字典分词和统计分词结合使用。基于统计的方法能很好地解决词典未收录新词的处理问题,即将中文分词中的串频统计和串匹配结合起来,既发挥匹配分词切分速度快、效率高的特点,有利用了无词典分词结合上下文识别生词、自动消除歧义的优点。
3、 Lucene 分词
在 Lucene3.0 中,对分词主要依靠 Analyzer 类解析实现。 Analyzer 内部主要通过 TokenStream 类实现。 Tonkenizer 类, TokenFilter 类是 TokenStream 的两个子类。 Tokenizer 处理单个字符组成的字符流,读取 Reader 对象中的数据,处理后转换成词汇单元。 TokneFilter 完成文本过滤器的功能,但在使用过程中必须注意不同的过滤器的使用的顺序。
Lucene 3.0 的几种分词系统
1、StopAnalyzer
StopAnalyzer能过滤词汇中的特定字符串和词汇,并且完成大写转小写的功能。
2、StandardAnalyzer
StandardAnalyzer根据空格和符号来完成分词,还可以完成数字、字母、E-mail地址、IP地址以及中文字符的分析处理,还可以支持过滤词表,用来代替StopAnalyzer能够实现的过滤功能。
3、SimpleAnalyzer
SimpleAnalyzer具备基本西文字符词汇分析的分词器,处理词汇单元时,以非字母字符作为分割符号。分词器不能做词汇的过滤,之进行词汇的分析和分割。输出地词汇单元完成小写字符转换,去掉标点符号等分割符。
在全文检索系统开发中,通常用来支持西文符号的处理,不支持中文。由于不完成单词过滤功能,所以不需要过滤词库支持。词汇分割策略上简单,使用非英文字符作为分割符,不需要分词词库的支持。
4、WhitespaceAnalyzer
WhitespaceAnalyzer使用空格作为间隔符的词汇分割分词器。处理词汇单元的时候,以空格字符作为分割符号。分词器不做词汇过滤,也不进行小写字符转换。
实际中可以用来支持特定环境下的西文符号的处理。由于不完成单词过滤和小写字符转换功能,也不需要过滤词库支持。词汇分割策略上简单使用非英文字符作为分割符,不需要分词词库支持。
5、KeywordAnalyzer
KeywordAnalyzer把整个输入作为一个单独词汇单元,方便特殊类型的文本进行索引和检索。针对邮政编码,地址等文本信息使用关键词分词器进行索引项建立非常方便。
6、CJKAnalyzer
CJKAnalyzer内部调用CJKTokenizer分词器,对中文进行分词,同时使用StopFilter过滤器完成过滤功能,可以实现中文的多元切分和停用词过滤。在Lucene3.0版本中已经弃用。
7、ChineseAnalyzer
ChineseAnalyzer功能与StandardAnalyzer分析器在处理中文是基本一致,都是切分成单个的双字节中文字符。在Lucene3.0版本中已经弃用。
8、PerFieldAnalyzerWrapper
PerFieldAnalyzerWrapper功能主要用在针对不同的Field采用不同的Analyzer的场合。比如对于文件名,需要使用KeywordAnalyzer,而对于文件内容只使用StandardAnalyzer就可以了。通过addAnalyzer()可以添加分类器。
9、 IKAnalyzer
实现了以词典为基础的正反向全切分,以及正反向最大匹配切分两种方法。IKAnalyzer是第三方实现的分词器,继承自Lucene的Analyzer类,针对中文文本进行处理。
10、JE-Analysis
JE-Analysis是Lucene的中文分词组件,需要下载。
11、 ICTCLAS4J
ictclas4j中文分词系统是sinboy在中科院张华平和刘群老师的研制的FreeICTCLAS的基础上完成的一个java开源分词项目,简化了原分词程序的复杂度,旨在为广大的中文分词爱好者一个更好的学习机会。
12、 Imdict-Chinese-Analyzer
imdict-chinese-analyzer 是 imdict智能词典 的智能中文分词模块,算法基于隐马尔科夫模型(Hidden Markov Model, HMM),是中国科学院计算技术研究所的ictclas中文分词程序的重新实现(基于Java),可以直接为lucene搜索引擎提供简体中文分词支持。
13、 Paoding Analysis
Paoding Analysis中文分词具有极 高效率 和 高扩展性。引入隐喻,采用完全的面向对象设计,构思先进。其效率比较高,在PIII 1G内存个人机器上,1秒可准确分词100万汉字。采用基于不限制个数的词典文件对文章进行有效切分,使能够将对词汇分类定义。能够对未知的词汇进行合理解析。
14、 MMSeg4J
mmseg4j 用 Chih-Hao Tsai 的 MMSeg 算法(http://technology.chtsai.org/mmseg/ )实现的中文分词器,并实现 lucene 的 analyzer 和 solr 的TokenizerFactory 以方便在Lucene和Solr中使用。 MMSeg 算法有两种分词方法:Simple和Complex,都是基于正向最大匹配。Complex 加了四个规则过虑。官方说:词语的正确识别率达到了 98.41%。mmseg4j 已经实现了这两种分词算法
Lucene3.0之分词(3):一个简单实例
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.wltea.analyzer.lucene.IKAnalyzer;
import org.apache.lucene.analysis.tokenattributes.TermAttribute;
publicclass TestLucene {
publicstaticvoid main(String[] args) {
// TODO Auto-generated method stub
String str = "我们测试的字符串是:中华人民共和国在1949年建立,从此开始了新中国的伟大篇章。";
try {
ik_CAnalyzer(str);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void ik_CAnalyzer(String str) throws Exception{
//Analyzer analyzer = new MIK_CAnalyzer();
//Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
//Analyzer analyzer = new IK_CAnalyzer();
//Analyzer analyzer = new CJKAnalyzer(Version.LUCENE_30);
Analyzer analyzer = new IKAnalyzer();
//Analyzer analyzer = new ChineseAnalyzer();
Reader reader = new StringReader(str);
TokenStream tokenStream = (TokenStream)analyzer.tokenStream(" ", reader);
System.out.println( "=====IK_CAnalyzer==== ");
System.out.println( "分析方法:字典分词,正反双向搜索 "+tokenStream.toString());
displayTokenStream(tokenStream);
}
publicstaticvoid displayTokenStream(TokenStream ts) throws IOException
{
TermAttribute termAttribute = (TermAttribute) ts.getAttribute(TermAttribute.class);
// TypeAttribute typeAttribute = (TypeAttribute) ts.getAttribute(TypeAttribute.class);
while (ts.incrementToken()) {
System.out.print(termAttribute.term());
System.out.print("\n");
// System.out.println(typeAttribute.type());
}
}
}
Lucene3.0之分词(3):一个简单实例
package com.mbb.test;
import java.io.StringReader;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.KeywordAnalyzer;
import org.apache.lucene.analysis.SimpleAnalyzer;
import org.apache.lucene.analysis.StopAnalyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.tokenattributes.TermAttribute;
import org.apache.lucene.util.Version;
publicclass AnalyzerTest {
publicstaticvoid analyze(Analyzer analyzer, String text) throws Exception {
System.out.println("分词器:" + analyzer.getClass());
TokenStream tokenStream = analyzer.tokenStream("content",new StringReader(text));
tokenStream.addAttribute(TermAttribute.class);
while (tokenStream.incrementToken()) {
TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class);
System.out.println(termAttribute.term());
}
}
publicstaticvoid main(String[] args) throws Exception {
String enText = "China is a great country!";
String chText = "昨天,一阵雷阵雨后,空气异常地清新!";
SimpleAnalyzer analyzer1 = new SimpleAnalyzer();
analyze(analyzer1, enText);
analyze(analyzer1, chText);
StopAnalyzer analyzer2 = new StopAnalyzer(Version.LUCENE_30);
analyze(analyzer2, enText);
analyze(analyzer2, chText);
StandardAnalyzer analyzer3 = new StandardAnalyzer(Version.LUCENE_30);
analyze(analyzer3, enText);
analyze(analyzer3, chText);// 逐个字切分
WhitespaceAnalyzer analyzer4 = new WhitespaceAnalyzer();
analyze(analyzer4, enText);
analyze(analyzer4, chText);
KeywordAnalyzer analyzer5 = new KeywordAnalyzer();
analyze(analyzer5, enText);
analyze(analyzer5, chText);
}
}