目录
中文分词器简述
1、Apache Lucene 作为 Java 实现的一个高效的文本搜索引擎工具包,它适用于几乎所有要求全文检索的应用项目,尤其是跨平台应用。对英文的处理已经非常完善,但在中文方面仍然非常欠缺,目前 lucene 的 contribution(贡献) 中,中日韩语言只有一个分析器(CJKAnalyzer),还是按中日韩文字来逐个切分的,无论从索引效率还是搜索准确度上都很难符合要求。
2、中文与西方语言最大的区别就在于语句的词汇之间没有明显的分词界限,但是计算机自然语言处理是按词汇来进行分析的,因此中文分词的效果直接影响中文检索和自然语言处理的准确性。例如: “我购买了道具和服装”,如果采用比较简单的正向最大匹配算法,分词结果是“我 购买 了 道具 和服 装”,明显偏离了原句的意思;而采用基于HMM的智能分词模型,能够综合分析句子的结构、各个词的频率以及各个词汇之间的跳转频率,从而能产生最优化的分词结果:“我 购买 了 道具 和 服装”
Lucene 中文分词器
1、在 lucene-analyzers-common-7.4.0.jar 包中已经自带了一些分词器,如 StandardAnalyzer、CJKAnalyzer 等
2、org.apache.lucene.analysis.standard.StandardAnalyzer:单字分词,因为英文字与字之间是用空格分隔的,所以对英文准确率高;然后中文单字分词的结果比如:“我爱中国”,分词后:“我”、“爱”、“中”、“国”,导致准确率和效率都低
3、org.apache.lucene.analysis.cjk.CJKAnalyzer:二分法分词,按两个字进行切分。如:“我是中国人”,效果:“我是”、“是中”、“中国”、“国人”,同样效果不高
4、所以对于中文项目通常采用第三方分词器
常用第三方中文分词
paoding-analysis 分词器官方地址:https://gitee.com/zhzhenqin/paoding-analysis,2012年之后没有再更新
Paoding's Knives 中文分词具有极 高效率 和 高扩展性 。引入隐喻,采用完全的面向对象设计,构思先进。
高效率:在PIII 1G内存个人机器上,1秒 可准确分词 100万 汉字。
采用基于 不限制个数 的词典文件对文章进行有效切分,使能够将对词汇分类定义。
能够对未知的词汇进行合理解析
2、mmseg4j-solr
mmseg4j:https://github.com/chenlb/mmseg4j-solr,2016年后没再更新
3、ansj_seg
ansj_seg:https://github.com/NLPchina/ansj_seg,目前作者仍在更新中
摘 要
这是一个基于n-Gram+CRF+HMM的中文分词的java实现.
分词速度达到每秒钟大约200万字左右(mac air下测试),准确率能达到96%以上
目前实现了.中文分词. 中文姓名识别 . 用户自定义词典,关键字提取,自动摘要,关键字标记等功能
可以应用到自然语言处理等方面,适用于对分词效果要求高的各种项目.
下载 jar
访问 http://maven.nlpcn.org/org/ansj/ 最好下载最新版 ansj_seg/
同时下载nlp-lang.jar 需要和ansj_seg 配套..配套关系可以看jar包中的maven依赖,一般最新的ansj配最新的nlp-lang不会有错。
5、imdict-chinese-analyzer
imdict-chinese-analyzer:https://code.google.com/archive/p/imdict-chinese-analyzer/
imdict-chinese-analyzer 是 imdict智能词典 的智能中文分词模块,算法基于隐马尔科夫模型(Hidden Markov Model, HMM),是中国科学院计算技术研究所的ictclas中文分词程序的重新实现(基于Java),可以直接为lucene搜索引擎提供*简体中文*分词支持
2009 年后没有再更新
6、Jcseg
Jcseg:https://gitee.com/lionsoul/jcseg,目前作者仍在更新中。
Jcseg 是基于 mmseg 算法的一个轻量级中文分词器,同时集成了关键字提取,关键短语提取,关键句子提取和文章自动摘要等功能,并且提供了一个基于 Jetty 的 web 服务器,方便各大语言直接 http 调用,同时提供了最新版本的 lucene,solr 和elasticsearch 的分词接口!
Jcseg 自带了一个 jcseg.properties文件用于快速配置而得到适合不同场合的分词应用,例如:最大匹配词长,是否开启中文人名识别,是否追加拼音,是否追加同义词等!
Jcseg 核心功能
中文分词:mmseg算法 + Jcseg 独创的优化算法,四种切分模式。
关键字提取:基于textRank算法。
关键短语提取:基于textRank算法。
关键句子提取:基于textRank算法。
文章自动摘要:基于BM25+textRank算法。
自动词性标注:基于词库+(统计歧义去除计划),目前效果不是很理想,对词性标注结果要求较高的应用不建议使用。
命名实体标注:基于词库+(统计歧义去除计划),电子邮件,网址,大陆手机号码,地名,人名,货币,datetime时间,长度,面积,距离单位等。
Restful api:嵌入jetty提供了一个绝对高性能的server模块,包含全部功能的http接口,标准化json输出格式,方便各种语言客户端直接调用。
这个分词器官网结束内容还是比较详细的,可以直接参考官网:https://gitee.com/lionsoul/jcseg
7、IK Analyzer
IK Analyzer 是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始, IKAnalyzer已经推出了4个大版本。最初,它是以开源项目Luence为应用主体的,结合词典分词和文法分析算法的中文分词组件。从3.0版本开始,IK发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了简单的分词歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。
8、HanLP 汉语言包
1、HanLP是一系列模型与算法组成的NLP工具包,由大快搜索主导并完全开源,目标是普及自然语言处理在生产环境中的应用。HanLP具备功能完善、性能高效、架构清晰、语料时新、可自定义的特点。
2、GitHub 开源主页:https://github.com/hankcs/HanLP
Ik-Analyzer 概述与特性
1、GoogleCode 开源项目 :http://code.google.com/p/ik-analyzer/,开发包下载地址:https://code.google.com/archive/p/ik-analyzer/downloads------需要有良好的网络
2、阿里云 > 教程中心 > java教程 > IK Analyzer 中文分词器:https://www.aliyun.com/jiaocheng/337819.html
GoogleCode 官网介绍:
IK Analyzer 是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始, IKAnalyzer已经推出了4个大版本。最初,它是以开源项目Luence为应用主体的,结合词典分词和文法分析算法的中文分词组件。从3.0版本开始,IK发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了简单的分词歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。
IK Analyzer 2012 特性:
1.采用了特有的“正向迭代最细粒度切分算法“,支持细粒度和智能分词两种切分模式;
2.在系统环境:Core2 i7 3.4G双核,4G内存,window 7 64位, Sun JDK 1.6_29 64位 普通pc环境测试,IK2012具有160万字/秒(3000KB/S)的高速处理能力。
3.2012版本的智能分词模式支持简单的分词排歧义处理和数量词合并输出。
4.采用了多子处理器分析模式,支持:英文字母、数字、中文词汇等分词处理,兼容韩文、日文字符
5.优化的词典存储,更小的内存占用。支持用户词典扩展定义。特别的,在2012版本,词典支持中文,英文,数字混合词语。
版本兼容:
Ik-Analyzer 下载与使用
Ik-Analyzer 下载:
1、GoogleCode 开源项目 :http://code.google.com/p/ik-analyzer/
2、开发包下载地址:https://code.google.com/archive/p/ik-analyzer/downloads------需要有良好的网络
3、IK Analyzer安装包包含:
1)doc:帮助文档
2)《IKAnalyzer中文分词器V2012使用手册》
3)IKAnalyzer2012.jar:开发主jar包
4)IKAnalyzer.cfg.xml:分词器扩展配置文件
5)stopword.dic:停止词典
6)ext.dic:扩展字典,这是根据“分词器扩展配置文件”中的说明自己新建的文件。
7)LICENSE.TXT :apache版权申明
8)NOTICE.TXT:apache版权申明
9)无论是“停用词字典”还是“扩展字典”文件都要用 UTF-8 编码,文件中的一行表示一个词,结尾不要有空格。
10)停用词字典:表示无意义的词,中文分词的时候会忽略它们,如 “的”、“地”、“得” 等
11)扩展字典:表示一些 IK-Analyzer 自己无法解析的专有词汇,需要程序员手动告诉它,这些词是完整有语义的词,不要再做切分,如 “量子物理”、“拉普拉斯方程”、“高斯定理”、“傅里叶级数” 等等。
导入开发包:
commons-io-2.4:是为了操作文件方便而导入的,与 Lucene、IK-Analyzer 没有直接关系,可以不导入
IKAnalyzer2012FF_u1:IK-Analyzer 分词核心包
lucene-*:Lucene 开发包
本文承接《 Lucene 实战之入门案例》
《 Lucene 实战之入门案例》原来使用的是 Lucene 7.4.0,与 IKAnalyzer2012ff_u1 不兼容,所以换成 Lucene 4.10.3 版本
创建索引
1、 Lucene 4.10.3 版本代码与《 Lucene 实战之入门案例》中 Lucene 7.4.0 基本一致,但几个细节稍微不同,代码注释有说明
import org.apache.commons.io.FileUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Administrator on 2018/8/29 0029.
* Lucene 索引管理工具类
*/
public class IndexManagerUtils {
/**
* 为指定目录下的文件创建索引,包括其下的所有子孙目录下的文件
*
* @param targetFileDir :需要创建索引的文件目录
* @param indexSaveDir :创建好的索引保存目录
* @throws IOException
*/
public static void indexCreate(File targetFileDir, File indexSaveDir) throws IOException {
/** 如果传入的路径不是目录或者目录不存在,则放弃*/
if (!targetFileDir.isDirectory() || !targetFileDir.exists()) {
return;
}
/** 创建 Lucene 文档列表,用于保存多个 Docuemnt*/
List<Document> docList = new ArrayList<Document>();
/**循环目标文件夹,取出文件
* 然后获取文件的需求内容,添加到 Lucene 文档(Document)中
* 此例会获取 文件名称、文件内容、文件大小
* */
for (File file : targetFileDir.listFiles()) {
if (file.isDirectory()) {
/**如果当前是目录,则进行方法回调*/
indexCreate(file, indexSaveDir);
} else {
/**如果当前是文件,则进行创建索引*/
/** 文件名称:如 abc.txt*/
String fileName = file.getName();
/**文件内容:org.apache.commons.io.FileUtils 操作文件更加方便
* readFileToString:直接读取整个文本文件内容*/
String fileContext = FileUtils.readFileToString(file);
/**文件大小:sizeOf,单位为字节*/
Long fileSize = FileUtils.sizeOf(file);
/**Lucene 文档对象(Document),文件系统中的一个文件就是一个 Docuemnt对象
* 一个 Lucene Docuemnt 对象可以存放多个 Field(域)
* Lucene Docuemnt 相当于 Mysql 数据库表的一行记录
* Docuemnt 中 Field 相当于 Mysql 数据库表的字段*/
Document luceneDocument = new Document();
/**
* TextField 继承于 org.apache.lucene.document.Field
* TextField(String name, String value, Store store)--文本域
* name:域名,相当于 Mysql 数据库表的字段名
* value:域值,相当于 Mysql 数据库表的字段值
* store:是否存储,yes 表存储,no 为不存储
*
* 默认所有的 Lucene 文档的这三个域的内容都会进行分词,创建索引目录,后期可以根据这个三个域来进行检索
* 如查询 文件名(fileName) 包含 "web" 字符串的文档
* 查询 文件内容(fileContext) 包含 "spring" 字符串的文档
* 查询 文件大小(fileSize) 等于 2055 字节的文档 等等
*
* TextField:表示文本域、默认会分词、会创建索引、第三个参数 Store.YES 表示会存储
* 同理还有 StoredField、StringField、FeatureField、BinaryDocValuesField 等等
* 都来自于超级接口:org.apache.lucene.index.IndexableField
*/
TextField nameFiled = new TextField("fileName", fileName, Store.YES);
TextField contextFiled = new TextField("fileContext", fileContext, Store.YES);
/**如果是 Srore.NO,则不会存储,就意味着后期获取 fileSize 值的时候,值会为null
* 虽然 Srore.NO 不会存在域的值,但是 TextField本身会分词、会创建索引
* 所以后期仍然可以根据 fileSize 域进行检索:queryParser.parse("fileContext:" + queryWord);
* 只是获取 fileSize 存储的值为 null:document.get("fileSize"));
* 索引是索引,存储的 fileSize 内容是另一回事
* */
TextField sizeFiled = new TextField("fileSize", fileSize.toString(), Store.YES);
/**将所有的域都存入 Lucene 文档中*/
luceneDocument.add(nameFiled);
luceneDocument.add(contextFiled);
luceneDocument.add(sizeFiled);
/**将文档存入文档集合中,之后再同统一进行存储*/
docList.add(luceneDocument);
}
}
/** 创建分词器
* StandardAnalyzer:标准分词器,对英文分词效果很好,对中文是单字分词,即一个汉字作为一个词,所以对中文支持不足
* 市面上有很多好用的中文分词器,如 IKAnalyzer 就是其中一个
* 现在换成 IKAnalyzer 中文分词器
*/
/*Analyzer analyzer = new StandardAnalyzer();*/
Analyzer analyzer = new IKAnalyzer();
/**如果目录不存在,则会自动创建
* FSDirectory:表示文件系统目录,即会存储在计算机本地磁盘,继承于
* org.apache.lucene.store.BaseDirectory
* 同理还有:org.apache.lucene.store.RAMDirectory:存储在内存中
* Lucene 7.4.0 版本 open 方法传入的 Path 对象
* Lucene 4.10.3 版本 open 方法传入的是 File 对象
*/
Directory directory = FSDirectory.open(indexSaveDir);
/** 创建 索引写配置对象,传入分词器
* Lucene 7.4.0 版本 IndexWriterConfig 构造器不需要指定 Version.LUCENE_4_10_3
* Lucene 4.10.3 版本 IndexWriterConfig 构造器需要指定 Version.LUCENE_4_10_3
* */
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
/**创建 索引写对象,用于正式写入索引和文档数据*/
IndexWriter indexWriter = new IndexWriter(directory, config);
/**将 Lucene 文档加入到 写索引 对象中*/
for (int i = 0; i < docList.size(); i++) {
indexWriter.addDocument(docList.get(i));
}
/**最后再 刷新流,然后提交、关闭流
* Lucene 4.10.3 在 close 的时候会自动 flush,程序员无法调用
* Lucene 7.4.0 可以自己手动调用 flush 方法*/
indexWriter.commit();
indexWriter.close();
indexWriter.close();
}
public static void main(String[] args) throws IOException {
File file1 = new File("E:\\wmx\\searchsource");
File file2 = new File("E:\\wmx\\luceneIndex");
indexCreate(file1, file2);
}
}
查询索引
1、 Lucene 4.10.3 版本代码与《 Lucene 实战之入门案例》中 Lucene 7.4.0 基本一致,但几个细节稍微不同,代码注释有说明
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;
import java.io.File;
/**
* Created by Administrator on 2018/8/29 0029.
* Lucene 索引搜索工具类
*/
public class IndexSearchUtils {
/**
* 索引查询
*
* @param indexDir :Lucene 索引文件所在目录
* @param queryWord :检索的内容,默认从文章内容进行查询
* @throws Exception
*/
public static void indexSearch(File indexDir, String queryWord) throws Exception {
if (indexDir == null || queryWord == null || "".equals(queryWord)) {
return;
}
/** 创建分词器
* 1)创建索引 与 查询索引 所用的分词器必须一致
* 2)现在使用 中文分词器 IKAnalyzer
*/
/*Analyzer analyzer = new StandardAnalyzer();*/
Analyzer analyzer = new IKAnalyzer();
/**创建查询对象(QueryParser):QueryParser(String f, Analyzer a)
* 第一个参数:默认搜索域,与创建索引时的域名称必须相同
* 第二个参数:分词器
* 默认搜索域作用:
* 如果搜索语法parse(String query)中指定了域名,则从指定域中搜索
* 如果搜索语法parse(String query)中只指定了查询关键字,则从默认搜索域中进行搜索
*/
QueryParser queryParser = new QueryParser("fileName", analyzer);
/** parse 表示解析查询语法,查询语法为:"域名:搜索的关键字"
* parse("fileName:web"):则从fileName域中进行检索 web 字符串
* 如果为 parse("web"):则从默认搜索域 fileContext 中进行检索
* 1)查询不区分大小写
* 2)因为使用的是 StandardAnalyzer(标准分词器),所以对英文效果很好,如果此时检索中文,基本是行不通的
*/
Query query = queryParser.parse("fileContext:" + queryWord);
/** 与创建 索引 和 Lucene 文档 时一样,指定 索引和文档 的目录
* 即指定查询的索引库
* Lucene 7.4.0 中 FSDirectory.open 方法参数为 Path
* Lucene 4.10。3 中 FSDirectory.open 方法参数为 File
*/
/*Path path = Paths.get(indexDir.toURI());*/
Directory dir = FSDirectory.open(indexDir);
/*** 创建 索引库读 对象
* DirectoryReader 继承于org.apache.lucene.index.IndexReader
* */
DirectoryReader directoryReader = DirectoryReader.open(dir);
/** 根据 索引对象创建 索引搜索对象
**/
IndexSearcher indexSearcher = new IndexSearcher(directoryReader);
/**search(Query query, int n) 搜索
* 第一个参数:查询语句对象
* 第二个参数:指定查询最多返回多少条数据,此处则表示返回个数最多5条
*/
TopDocs topdocs = indexSearcher.search(query, 5);
System.out.println("查询结果总数:::=====" + topdocs.totalHits);
/**从搜索结果对象中获取结果集
* 如果没有查询到值,则 ScoreDoc[] 数组大小为 0
* */
ScoreDoc[] scoreDocs = topdocs.scoreDocs;
ScoreDoc loopScoreDoc = null;
for (int i = 0; i < scoreDocs.length; i++) {
System.out.println("=======================" + (i + 1) + "=====================================");
loopScoreDoc = scoreDocs[i];
/**获取 文档 id 值
* 这是 Lucene 存储时自动为每个文档分配的值,相当于 Mysql 的主键 id
* */
int docID = loopScoreDoc.doc;
/**通过文档ID从硬盘中读取出对应的文档*/
Document document = directoryReader.document(docID);
/**get方法 获取对应域名的值
* 如域名 key 值不存在,返回 null*/
System.out.println("doc id:" + docID);
System.out.println("fileName:" + document.get("fileName"));
System.out.println("fileSize:" + document.get("fileSize"));
/**防止内容太多影响阅读,只取前20个字*/
System.out.println("fileContext:" + document.get("fileContext").substring(0, 20) + "......");
}
}
public static void main(String[] args) throws Exception {
File indexDir = new File("E:\\wmx\\luceneIndex");
indexSearch(indexDir, "spring");
}
}
当查询 "spring" 时输出如下:
查询结果总数:::=====4
=======================1=====================================
doc id:0
fileName:spring.txt
fileSize:83
fileContext:The Spring Framework......
=======================2=====================================
doc id:4
fileName:1.create web page.txt
fileSize:47
fileContext:Learn how to create ......
=======================3=====================================
doc id:2
fileName:spring_README.txt
fileSize:3255
fileContext:## Spring Framework
......
=======================4=====================================
doc id:1
fileName:springmvc.txt
fileSize:2126
fileContext:Spring
2. Web......
当查询 "全文检索" 时输出如下:
查询结果总数:::=====2
=======================1=====================================
doc id:9
fileName:什么是全文检索 java.txt
fileSize:1687
fileContext:1 什么是全文检索java
1.1 全......
=======================2=====================================
doc id:10
fileName:全文检索.txt
fileSize:1687
fileContext:1 什么是全文检索java
1.1 全......Process finished with exit code 0
Ik-Analyzer 官方示例源码
1、在导入的 InAnalyzer2012FF_u1.jar 包中官方提供了示例,可以直接复制出来进行运行