Lucene:Ansj分词器
Ansj分词器
导入jar包
ansj_seg-5.1.6.jar
nlp-lang-1.7.8.jar
maven配置
<dependency>
<groupId>org.ansj</groupId>
<artifactId>ansj_seg</artifactId>
<version>5.1.1</version>
</dependency>
代码演示
import org.ansj.library.DicLibrary;
import org.ansj.splitWord.analysis.*;
import org.ansj.util.MyStaticValue;
/**
* AnsjAnalyzerTest
*
* @author limingcheng
* @Date 2019/11/26
*/
public class AnsjAnalyzerTest {
/**
* 基本分词(BaseAnalysis)
* 速度快
*/
public static void BaseAnalysisTest(){
String words = "让战士们过一个欢乐祥和的新春佳节。";
System.out.println(BaseAnalysis.parse(words));
}
/**
* 精准分词(ToAnalysis)
* 精准分词方式兼顾精度与速度,比较均衡
*/
public static void ToAnalysisTest(){
String words = "让战士们过一个欢乐祥和的新春佳节。";
System.out.println(ToAnalysis.parse(words));
}
/**
* NLP分词(NlpAnalysis)
* NLP分词方式可是未登录词,但速度较慢
*/
public static void NlpAnalysisTest(){
String words = "洁面仪配合洁面深层清洁毛孔 清洁鼻孔面膜碎觉使劲挤才能出一点点皱纹 " +
"脸颊毛孔修复的看不见啦 草莓鼻历史遗留问题没辙 脸和脖子差不多颜色的皮肤才是健康的 " +
"长期使用安全健康的比同龄人显小五到十岁 28岁的妹子看看你们的鱼尾纹。";
System.out.println(NlpAnalysis.parse(words));
}
/**
* 面向索引分词(IndexAnalysis)
*/
public static void IndexAnalysisTest(){
String words = "洁面仪配合洁面深层清洁毛孔 清洁鼻孔面膜碎觉使劲挤才能出一点点皱纹";
System.out.println(IndexAnalysis.parse(words));
}
/**
* 自定词典分词(DicLibrary)
* 动态添加
*/
public static void DicLibraryTest(){
//添加自定义词语 【 英文,按照小写配置。(大写,不识别。拆词的结果,也转为小写了)】
DicLibrary.insert(DicLibrary.DEFAULT, "基于java", "n", 1);
String text = "基于Java开发的轻量级的中分分词工具包";
System.out.println(DicAnalysis.parse(text));
}
/**
* 自定词典分词(DicLibrary)
* 路径获取
*/
public static void DicLibraryPath(){
// 关闭名字识别
MyStaticValue.isNameRecognition = false;
// 配置自定义词典的位置。注意是绝对路径
MyStaticValue.ENV.put(DicLibrary.DEFAULT, "E:\\indexDir\\library\\default.dic");
String text = "基于Java开发的轻量级的中分分词工具包";
System.out.println(DicAnalysis.parse(text));
}
/**
* 自定词典分词(DicLibrary)
* 配置文件
*/
public static void DicLibraryProperties(){
String text = "基于Java开发的轻量级的中分分词工具包";
System.out.println(DicAnalysis.parse(text));
}
public static void main(String[] args) {
// 基本分词
// BaseAnalysisTest();
// // 精准分词
// ToAnalysisTest();
// // NLP分词
// NlpAnalysisTest();
// // 面向索引分词
// IndexAnalysisTest();
// 词典分词(动态添加)
// DicLibraryTest();
// 词典分词(路径)
// DicLibraryPath();
// 词典分词(配置文件)
DicLibraryProperties();
}
}1.1.5. 搭配Lucene
由于Ansj项目并没有提供analyzer,需要自己手动写一个来适配。因此,首先要创建以下几个类:
AnsjAnalyzer
import org.ansj.library.*;
import org.ansj.recognition.impl.StopRecognition;
import org.ansj.recognition.impl.SynonymsRecgnition;
import org.ansj.splitWord.Analysis;
import org.ansj.splitWord.analysis.*;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Tokenizer;
import org.nlpcn.commons.lang.tire.domain.Forest;
import org.nlpcn.commons.lang.tire.domain.SmartForest;
import org.nlpcn.commons.lang.util.StringUtil;
import org.nlpcn.commons.lang.util.logging.Log;
import org.nlpcn.commons.lang.util.logging.LogFactory;
import java.io.BufferedReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* AnsjAnalyzer
*
* @author limingcheng
* @Date 2019/11/26
*/
public class AnsjAnalyzer extends Analyzer {
public static final Log LOG = LogFactory.getLog();
/**
* dic equals user , query equals to
*
* @author ansj
*
*/
public static enum TYPE {
// 基本分词(BaseAnalysis)
base_ansj,
// 索引分词
index_ansj,
// 查询分词
query_ansj,
// 自定词典分词(DicLibrary)
dic_ansj,
// NLP分词(NlpAnalysis)
nlp_ansj
}
/**
* 分词类型
*/
private Map<String, String> args;
/**
* filter 停用词
*/
public AnsjAnalyzer(Map<String, String> args) {
this.args = args;
}
public AnsjAnalyzer(TYPE type, String dics) {
this.args = new HashMap<String, String>();
args.put("type", type.name());
args.put(DicLibrary.DEFAULT, dics);
}
public AnsjAnalyzer(TYPE type) {
this.args = new HashMap<String, String>();
args.put("type", type.name());
}
@Override
protected TokenStreamComponents createComponents(String text) {
BufferedReader reader = new BufferedReader(new StringReader(text));
Tokenizer tokenizer = null;
tokenizer = getTokenizer(reader, this.args);
return new TokenStreamComponents(tokenizer);
}
/**
* 获得一个tokenizer
*
* @param reader
* @param args type
* @param args filter
* @return
*/
public static Tokenizer getTokenizer(Reader reader, Map<String, String> args) {
if (LOG.isDebugEnabled()) {
LOG.debug("to create tokenizer " + args);
}
Analysis analysis = null;
String temp = null;
String type = args.get("type");
if (type == null) {
type = AnsjAnalyzer.TYPE.base_ansj.name();
}
switch (AnsjAnalyzer.TYPE.valueOf(type)) {
case base_ansj:
analysis = new BaseAnalysis();
break;
case index_ansj:
analysis = new IndexAnalysis();
break;
case dic_ansj:
analysis = new DicAnalysis();
break;
case query_ansj:
analysis = new ToAnalysis();
break;
case nlp_ansj:
analysis = new NlpAnalysis();
if (StringUtil.isNotBlank(temp = args.get(CrfLibrary.DEFAULT))) {
((NlpAnalysis) analysis).setCrfModel(CrfLibrary.get(temp));
}
break;
default:
analysis = new BaseAnalysis();
}
if (reader != null) {
analysis.resetContent(reader);
}
//用户自定义词典
if (StringUtil.isNotBlank(temp = args.get(DicLibrary.DEFAULT))) {
String[] split = temp.split(",");
Forest[] forests = new Forest[split.length];
for (int i = 0; i < forests.length; i++) {
if (StringUtil.isBlank(split[i])) {
continue;
}
forests[i] = DicLibrary.get(split[i]);
}
analysis.setForests(forests);
}
List<StopRecognition> filters = null;
//用户自定义词典
if (StringUtil.isNotBlank(temp = args.get(StopLibrary.DEFAULT))) {
String[] split = temp.split(",");
filters = new ArrayList<StopRecognition>();
for (String key : split) {
StopRecognition stop = StopLibrary.get(key.trim());
if (stop != null) {
filters.add(stop);
}
}
}
List<SynonymsRecgnition> synonyms = null;
//同义词词典
if (StringUtil.isNotBlank(temp = args.get(SynonymsLibrary.DEFAULT))) {
String[] split = temp.split(",");
synonyms = new ArrayList<SynonymsRecgnition>();
for (String key : split) {
SmartForest<List<String>> sf = SynonymsLibrary.get(key.trim());
if (sf != null) {
synonyms.add(new SynonymsRecgnition(sf));
}
}
}
//歧义词典
if (StringUtil.isNotBlank(temp = args.get(AmbiguityLibrary.DEFAULT))) {
analysis.setAmbiguityForest(AmbiguityLibrary.get(temp.trim()));
}
// 是否开启人名识别
if (StringUtil.isNotBlank(temp = args.get("isNameRecognition"))) {
analysis.setIsNameRecognition(Boolean.valueOf(temp));
}
// 是否开启数字识别
if (StringUtil.isNotBlank(temp = args.get("isNumRecognition"))) {
analysis.setIsNumRecognition(Boolean.valueOf(temp));
}
//量词识别
if (StringUtil.isNotBlank(temp = args.get("isQuantifierRecognition"))) {
analysis.setIsQuantifierRecognition(Boolean.valueOf(temp));
}
//是否保留原字符
if (StringUtil.isNotBlank(temp = args.get("isRealName"))) {
analysis.setIsRealName(Boolean.parseBoolean(temp));
}
return new AnsjTokenizer(analysis, filters, synonyms);
}
}AnsjTokenizer
Ansj分词方式
Ansj分词器提供了以下几种分词模式,各种分词模式都有各自的优劣势,适应不同的需求场景。参考:https://blog.csdn.net/lb521200200/article/details/53696387
ToAnalysis 精准分词
精准分词在易用性,稳定性.准确性.以及分词效率上.都取得了一个不错的平衡。万金油的存在,适合测试。
DicAnalysis 用户自定义词典优先策略的分词
用户自定义词典优先策略的分词。当分词效果不能满足要求,或者待分词的词语实在太过罕见的情况下,使用用户自定义词典可以有效解决该问题。
NlpAnalysis 带有新词发现功能的分词
NLP分词是一种分词效果比较好的分词方式,能识别出未登录词。同时效果极好的后果是消耗性能较大,导致速度比较慢、稳定性差(分词速度约为40w字每秒)。
NLP的适用场景:语法实体名抽取;未登录词整理;只要是对文本进行发现分析等工作。
1.2.4. IndexAnalysis 面向索引的分词
面向索引的分词。顾名思义就是适合在lucene等文本检索中用到的分词。主要考虑以下两点
召回率
召回率是对分词结果尽可能的涵盖。比如对“上海虹桥机场南路” 召回结果是[上海/ns, 上海虹桥机场/nt, 虹桥/ns, 虹桥机场/nz, 机场/n, 南路/nr]
准确率
其实这和召回本身是具有一定矛盾性的Ansj的强大之处是很巧妙的避开了这两个的冲突 。比如我们常见的歧义句“旅游和服务”->对于一般保证召回 。大家会给出的结果是“旅游 和服 服务” 对于ansj不存在跨term的分词。意思就是。召回的词只是针对精准分词之后的结果的一个细分。比较好的解决了这个问题
1.2.5. BaseAnalysis 最小颗粒度的分词
基本就是保证了最基本的分词,词语颗粒度最非常小的。所涉及到的词大约是10万左右。基本分词速度非常快.在macAir上,能到每秒300w字每秒。同时准确率也很高,但是对于新词他的功能十分有限。
总结:
功能统计:
名称 | 用户自定义词典 | 数字识别 | 人名识别 | 机构名识别 | 新词发现 |
BaseAnalysis | 否 | 否 | 否 | 否 | 否 |
ToAnalysis | 是 | 是 | 是 | 否 | 否 |
DicAnalysis | 是 | 是 | 是 | 否 | 否 |
IndexAnalysis | 是 | 是 | 是 | 否 | 否 |
NlpAnalysis | 是 | 是 | 是 | 是 | 是 |
代码演示:
package main.java.cn.lmc.collection.retrieval.web.analyzer;
import org.ansj.domain.Result;
import org.ansj.library.DicLibrary;
import org.ansj.splitWord.analysis.*;
import org.ansj.util.MyStaticValue;
/**
* AnsjAnalyzerTest
*
* @author limingcheng
* @Date 2019/11/26
*/
public class AnsjAnalyzerTest {
/**
* 基本分词(BaseAnalysis)
* 速度快
*/
public static void BaseAnalysisTest(){
String words = "五月天创建的人生有限公司举报了一场演唱会,陈信宏唱了一首do you ever shine";
System.out.println(BaseAnalysis.parse(words));
}
/**
* 精准分词(ToAnalysis)
* 精准分词方式兼顾精度与速度,比较均衡
*/
public static void ToAnalysisTest(){
String words = "五月天创建的人生有限公司举报了一场演唱会,陈信宏唱了一首do you ever shine。";
System.out.println(ToAnalysis.parse(words));
}
/**
* NLP分词(NlpAnalysis)
* NLP分词方式可是未登录词,但速度较慢
*/
public static void NlpAnalysisTest(){
String words = "对对对对对对多多小学生 101304471127J";
System.out.println(NlpAnalysis.parse(words));
Result result = NlpAnalysis.parse(words);
System.out.println(result.toString());
System.out.println(result.getTerms().toString());
}
/**
* 面向索引分词(IndexAnalysis)
*/
public static void IndexAnalysisTest(){
String words = "五月天创建的人生有限公司举报了一场演唱会,陈信宏唱了一首do you ever shine,周杰伦,杰伦";
System.out.println(IndexAnalysis.parse(words));
System.out.println(IndexAnalysis.parse("杰伦"));
}
/**
* 自定词典分词(DicLibrary)
* 动态添加
*/
public static void DicLibraryTest(){
//添加自定义词语 【 英文,按照小写配置。(大写,不识别。拆词的结果,也转为小写了)】
DicLibrary.insert(DicLibrary.DEFAULT, "基于java", "n", 1);
String text = "基于Java开发的轻量级的中分分词工具包";
System.out.println(DicAnalysis.parse(text));
}
/**
* 自定词典分词(DicLibrary)
* 路径获取
*/
public static void DicLibraryPath(){
// 关闭名字识别
MyStaticValue.isNameRecognition = false;
// 配置自定义词典的位置。注意是绝对路径
MyStaticValue.ENV.put(DicLibrary.DEFAULT, "E:\\indexDir\\library\\default.dic");
String text = "基于Java开发的轻量级的中分分词工具包";
System.out.println(DicAnalysis.parse(text));
}
/**
* 自定词典分词(DicLibrary)
* 配置文件
*/
public static void DicLibraryProperties(){
String text = "基于Java开发的轻量级的中分分词工具包";
System.out.println(DicAnalysis.parse(text));
}
public static void main(String[] args) {
// 基本分词
// BaseAnalysisTest();
// // 精准分词
// ToAnalysisTest();
// // NLP分词
// NlpAnalysisTest();
// // 面向索引分词
IndexAnalysisTest();
// 词典分词(动态添加)
// DicLibraryTest();
// 词典分词(路径)
// DicLibraryPath();
// 词典分词(配置文件)
// DicLibraryProperties();
}
}