现在实现基本中文分词功能的代码和软件模块很多,性能也还可以,但是怎样将其应用到NUTCH中的方法介绍相对较少,下面我就实现NUTCH中文分词的NUTCH代码修改方法与步骤,抛砖引玉的介绍一下。
代码修改的切入点是通过对NUTCH处理中文的切分器Tokenizer的修改或者替换(这里介绍的是替换),使中文分词由单字切分变为词语切分。对于相关的NUTCH基本知识,我就不赘述了。因为NUTCH的检索功能基于Lucene,所以你用来替换的切分器必须满足Lucene的Tokenizer特征,最主要的是保证输入与输出相符,分词后输出的必须是Token流。幸运的是,现存的分词程序或软件模块基本上都满足此需要,即使有所偏差,一般通过对其输入输出的修改就可以满足需要。我前面提供给大家的那个分词程序就可以经过简单修改应用于NUTCH中。 数据挖掘研究院
设我们实现中文分词功能模块的主类名为MYTokenizer,package为org.apache.nutch.analysis.myt. MYTokenizer,将模块假如项目后,NUTCH代码修改如下: 数据挖掘研究院
(需要修改或新增的行后有注释,否则其它行只是为了定位,不修改)
一、修改org\apache\nutch\analysis下的NutchAnalysis.jj文件(此文件由JACC生成) 数据挖掘研究院
文件第33行附近:
import org.apache.nutch.searcher.Query.Clause; 数据挖掘研究院
import org.apache.lucene.analysis.StopFilter;
+import org.apache. nutch.analysis. myt. MYTokenizer; //新增此行,加入你的切分器
import java.io.*; 数据挖掘研究院
import java.util.*; 数据挖掘研究院
文件第8 1行附近:
PARSER_END(NutchAnalysis)
TOKEN_MGR_DECLS : {
/** use MYTokenizer to process cjk character */ //新增此行注释
private MYTokenizer myTokenizer = null; //新增此行
/** a global cjk token */ //新增此行注释 数据挖掘研究院
private org.apache.lucene.analysis.Token cjkToken = null; //新增此行,输出流 数据挖掘研究院
/** start offset of cjk sequence */ //新增此行注释
private int cjkStartOffset = 0; //新增此行 数据挖掘研究院
/** Constructs a token manager for the provided Reader. */ 数据挖掘研究院
public NutchAnalysisTokenManager(Reader reader) { 数据挖掘实验室
文件第106行附近: 数据挖掘研究院
}
// chinese, japanese and korean characters 数据挖掘研究院
| <SIGRAM: <CJK> > //删除此行 数据挖掘研究院
| <SIGRAM: (<CJK>)+ > //新增此行(#行) 数据挖掘研究院
//以下所有行均为新增,紧接上行代码(#行)书写
{ 数据挖掘研究院
/**
* use an instance of myTokenizer, myTokenizer, hold the maximum
* matched cjk chars, and cjkToken for the current token;
* reset matchedToken.image use cjkToken.termText(); 数据挖掘研究院
* reset matchedToken.beginColumn use cjkToken.startOffset(); 数据挖掘研究院
* reset matchedToken.endColumn use cjkToken.endOffset(); 数据挖掘研究院
* backup the last char when the next cjkToken is valid.
*/ 数据挖掘实验室
if(myTokenizer == null) { 数据挖掘研究院
myTokenizer = new MYTokenizer (new StringReader(image.toString()));
cjkStartOffset = matchedToken.beginColumn; 数据挖掘实验室
try { 数据挖掘研究院
cjkToken = myTokenizer.next();
} catch(IOException ioe) { 数据挖掘实验室
cjkToken = null; 数据挖掘实验室
} 数据挖掘研究院
}
if(cjkToken != null && !cjkToken.termText().equals("")) {
//sometime the myTokenizer returns an empty string, is it a bug? 数据挖掘研究院
matchedToken.image = cjkToken.termText(); 数据挖掘研究院
matchedToken.beginColumn = cjkStartOffset + cjkToken.startOffset(); 数据挖掘研究院
matchedToken.endColumn = cjkStartOffset + cjkToken.endOffset(); 数据挖掘研究院
try {
cjkToken = myTokenizer.next(); 数据挖掘研究院
} catch(IOException ioe) {
cjkToken = null; 数据挖掘研究院
} 数据挖掘实验室
if(cjkToken != null && !cjkToken.termText().equals("")) { 数据挖掘研究院
input_stream.backup(1);
}
} 数据挖掘研究院
if(cjkToken == null || cjkToken.termText().equals("")) {
myTokenizer = null;
cjkStartOffset = 0;
} 数据挖掘研究院
} 数据挖掘研究院
二、修改org\apache\nutch\searcher下的Summarizer.java文件(此处修改为提高搜索性能)
文件第189行附近: 数据挖掘研究院
将原代码段 数据挖掘研究院
if (highlight.contains(t.termText())) {
excerpt.addToken(t.termText());
excerpt.add(new Fragment(text.substring(offset, t.startOffset())));
excerpt.add(new Highlight(text.substring(t.startOffset(),t.endOffset())));
offset = t.endOffset();
endToken = Math.min(j+SUM_CONTEXT, tokens.length); 数据挖掘研究院
} 数据挖掘研究院
修改为:
if (highlight.contains(t.termText())) { 数据挖掘研究院
if(offset * 2 == (t.startOffset() + t.endOffset() )) { // cjk bi-gram
excerpt.addToken(t.termText().substring(offset - t.startOffset())); 数据挖掘研究院
excerpt.add(new Fragment(text.substring(t.startOffset() + 1,offset)));
excerpt.add(new Highlight(text.substring(t.startOffset() + 1 ,t.endOffset())));
}
else { 数据挖掘研究院
excerpt.addToken(t.termText()); 数据挖掘研究院
excerpt.add(new Fragment(text.substring(offset, t.startOffset())));
excerpt.add(new Highlight(text.substring(t.startOffset() ,t.endOffset())));
}
offset = t.endOffset(); 数据挖掘实验室
endToken = Math.min(j+SUM_CONTEXT, tokens.length); 数据挖掘研究院
}
为了提高运行效率,你的分词模块在运行中最好可以将词表读入内存。至于代码修改的原理,说来话长,容后再叙。 数据挖掘研究院

