Lucene:Ansj分词器
Posted bestlmc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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>
代码演示
1 import org.ansj.library.DicLibrary; 2 import org.ansj.splitWord.analysis.*; 3 import org.ansj.util.MyStaticValue; 4 5 6 /** 7 * AnsjAnalyzerTest 8 * 9 * @author limingcheng 10 * @Date 2019/11/26 11 */ 12 public class AnsjAnalyzerTest { 13 14 15 16 /** 17 * 基本分词(BaseAnalysis) 18 * 速度快 19 */ 20 public static void BaseAnalysisTest(){ 21 String words = "让战士们过一个欢乐祥和的新春佳节。"; 22 System.out.println(BaseAnalysis.parse(words)); 23 } 24 25 /** 26 * 精准分词(ToAnalysis) 27 * 精准分词方式兼顾精度与速度,比较均衡 28 */ 29 public static void ToAnalysisTest(){ 30 String words = "让战士们过一个欢乐祥和的新春佳节。"; 31 System.out.println(ToAnalysis.parse(words)); 32 } 33 34 /** 35 * NLP分词(NlpAnalysis) 36 * NLP分词方式可是未登录词,但速度较慢 37 */ 38 public static void NlpAnalysisTest(){ 39 String words = "洁面仪配合洁面深层清洁毛孔 清洁鼻孔面膜碎觉使劲挤才能出一点点皱纹 " + 40 "脸颊毛孔修复的看不见啦 草莓鼻历史遗留问题没辙 脸和脖子差不多颜色的皮肤才是健康的 " + 41 "长期使用安全健康的比同龄人显小五到十岁 28岁的妹子看看你们的鱼尾纹。"; 42 System.out.println(NlpAnalysis.parse(words)); 43 } 44 45 /** 46 * 面向索引分词(IndexAnalysis) 47 */ 48 public static void IndexAnalysisTest(){ 49 String words = "洁面仪配合洁面深层清洁毛孔 清洁鼻孔面膜碎觉使劲挤才能出一点点皱纹"; 50 System.out.println(IndexAnalysis.parse(words)); 51 } 52 53 /** 54 * 自定词典分词(DicLibrary) 55 * 动态添加 56 */ 57 public static void DicLibraryTest(){ 58 //添加自定义词语 【 英文,按照小写配置。(大写,不识别。拆词的结果,也转为小写了)】 59 DicLibrary.insert(DicLibrary.DEFAULT, "基于java", "n", 1); 60 61 String text = "基于Java开发的轻量级的中分分词工具包"; 62 63 System.out.println(DicAnalysis.parse(text)); 64 } 65 66 /** 67 * 自定词典分词(DicLibrary) 68 * 路径获取 69 */ 70 public static void DicLibraryPath(){ 71 // 关闭名字识别 72 MyStaticValue.isNameRecognition = false; 73 // 配置自定义词典的位置。注意是绝对路径 74 MyStaticValue.ENV.put(DicLibrary.DEFAULT, "E:\indexDir\library\default.dic"); 75 76 String text = "基于Java开发的轻量级的中分分词工具包"; 77 78 System.out.println(DicAnalysis.parse(text)); 79 } 80 81 /** 82 * 自定词典分词(DicLibrary) 83 * 配置文件 84 */ 85 public static void DicLibraryProperties(){ 86 String text = "基于Java开发的轻量级的中分分词工具包"; 87 88 System.out.println(DicAnalysis.parse(text)); 89 } 90 91 public static void main(String[] args) { 92 // 基本分词 93 // BaseAnalysisTest(); 94 // // 精准分词 95 // ToAnalysisTest(); 96 // // NLP分词 97 // NlpAnalysisTest(); 98 // // 面向索引分词 99 // IndexAnalysisTest(); 100 // 词典分词(动态添加) 101 // DicLibraryTest(); 102 // 词典分词(路径) 103 // DicLibraryPath(); 104 // 词典分词(配置文件) 105 DicLibraryProperties(); 106 } 107 }
1.1.5. 搭配Lucene
由于Ansj项目并没有提供analyzer,需要自己手动写一个来适配。因此,首先要创建以下几个类:
AnsjAnalyzer
1 import org.ansj.library.*; 2 import org.ansj.recognition.impl.StopRecognition; 3 import org.ansj.recognition.impl.SynonymsRecgnition; 4 import org.ansj.splitWord.Analysis; 5 import org.ansj.splitWord.analysis.*; 6 import org.apache.lucene.analysis.Analyzer; 7 import org.apache.lucene.analysis.Tokenizer; 8 import org.nlpcn.commons.lang.tire.domain.Forest; 9 import org.nlpcn.commons.lang.tire.domain.SmartForest; 10 import org.nlpcn.commons.lang.util.StringUtil; 11 import org.nlpcn.commons.lang.util.logging.Log; 12 import org.nlpcn.commons.lang.util.logging.LogFactory; 13 14 import java.io.BufferedReader; 15 import java.io.Reader; 16 import java.io.StringReader; 17 import java.util.ArrayList; 18 import java.util.HashMap; 19 import java.util.List; 20 import java.util.Map; 21 22 /** 23 * AnsjAnalyzer 24 * 25 * @author limingcheng 26 * @Date 2019/11/26 27 */ 28 public class AnsjAnalyzer extends Analyzer { 29 public static final Log LOG = LogFactory.getLog(); 30 31 /** 32 * dic equals user , query equals to 33 * 34 * @author ansj 35 * 36 */ 37 public static enum TYPE { 38 // 基本分词(BaseAnalysis) 39 base_ansj, 40 // 索引分词 41 index_ansj, 42 // 查询分词 43 query_ansj, 44 // 自定词典分词(DicLibrary) 45 dic_ansj, 46 // NLP分词(NlpAnalysis) 47 nlp_ansj 48 } 49 50 /** 51 * 分词类型 52 */ 53 private Map<String, String> args; 54 55 /** 56 * filter 停用词 57 */ 58 public AnsjAnalyzer(Map<String, String> args) { 59 this.args = args; 60 } 61 62 public AnsjAnalyzer(TYPE type, String dics) { 63 this.args = new HashMap<String, String>(); 64 args.put("type", type.name()); 65 args.put(DicLibrary.DEFAULT, dics); 66 } 67 68 public AnsjAnalyzer(TYPE type) { 69 this.args = new HashMap<String, String>(); 70 args.put("type", type.name()); 71 } 72 73 @Override 74 protected TokenStreamComponents createComponents(String text) { 75 BufferedReader reader = new BufferedReader(new StringReader(text)); 76 Tokenizer tokenizer = null; 77 tokenizer = getTokenizer(reader, this.args); 78 return new TokenStreamComponents(tokenizer); 79 } 80 81 /** 82 * 获得一个tokenizer 83 * 84 * @param reader 85 * @param args type 86 * @param args filter 87 * @return 88 */ 89 public static Tokenizer getTokenizer(Reader reader, Map<String, String> args) { 90 if (LOG.isDebugEnabled()) { 91 LOG.debug("to create tokenizer " + args); 92 } 93 Analysis analysis = null; 94 95 String temp = null; 96 String type = args.get("type"); 97 98 if (type == null) { 99 type = AnsjAnalyzer.TYPE.base_ansj.name(); 100 } 101 102 switch (AnsjAnalyzer.TYPE.valueOf(type)) { 103 case base_ansj: 104 analysis = new BaseAnalysis(); 105 break; 106 case index_ansj: 107 analysis = new IndexAnalysis(); 108 break; 109 case dic_ansj: 110 analysis = new DicAnalysis(); 111 break; 112 case query_ansj: 113 analysis = new ToAnalysis(); 114 break; 115 case nlp_ansj: 116 analysis = new NlpAnalysis(); 117 if (StringUtil.isNotBlank(temp = args.get(CrfLibrary.DEFAULT))) { 118 ((NlpAnalysis) analysis).setCrfModel(CrfLibrary.get(temp)); 119 } 120 break; 121 default: 122 analysis = new BaseAnalysis(); 123 } 124 125 if (reader != null) { 126 analysis.resetContent(reader); 127 } 128 129 //用户自定义词典 130 if (StringUtil.isNotBlank(temp = args.get(DicLibrary.DEFAULT))) { 131 String[] split = temp.split(","); 132 Forest[] forests = new Forest[split.length]; 133 for (int i = 0; i < forests.length; i++) { 134 if (StringUtil.isBlank(split[i])) { 135 continue; 136 } 137 forests[i] = DicLibrary.get(split[i]); 138 } 139 analysis.setForests(forests); 140 } 141 142 List<StopRecognition> filters = null; 143 //用户自定义词典 144 if (StringUtil.isNotBlank(temp = args.get(StopLibrary.DEFAULT))) { 145 String[] split = temp.split(","); 146 filters = new ArrayList<StopRecognition>(); 147 for (String key : split) { 148 StopRecognition stop = StopLibrary.get(key.trim()); 149 if (stop != null) { 150 filters.add(stop); 151 } 152 } 153 } 154 155 List<SynonymsRecgnition> synonyms = null; 156 //同义词词典 157 if (StringUtil.isNotBlank(temp = args.get(SynonymsLibrary.DEFAULT))) { 158 String[] split = temp.split(","); 159 synonyms = new ArrayList<SynonymsRecgnition>(); 160 for (String key : split) { 161 SmartForest<List<String>> sf = SynonymsLibrary.get(key.trim()); 162 if (sf != null) { 163 synonyms.add(new SynonymsRecgnition(sf)); 164 } 165 } 166 } 167 168 //歧义词典 169 if (StringUtil.isNotBlank(temp = args.get(AmbiguityLibrary.DEFAULT))) { 170 analysis.setAmbiguityForest(AmbiguityLibrary.get(temp.trim())); 171 } 172 173 // 是否开启人名识别 174 if (StringUtil.isNotBlank(temp = args.get("isNameRecognition"))) { 175 analysis.setIsNameRecognition(Boolean.valueOf(temp)); 176 } 177 178 // 是否开启数字识别 179 if (StringUtil.isNotBlank(temp = args.get("isNumRecognition"))) { 180 analysis.setIsNumRecognition(Boolean.valueOf(temp)); 181 } 182 183 //量词识别 184 if (StringUtil.isNotBlank(temp = args.get("isQuantifierRecognition"))) { 185 analysis.setIsQuantifierRecognition(Boolean.valueOf(temp)); 186 } 187 188 //是否保留原字符 189 if (StringUtil.isNotBlank(temp = args.get("isRealName"))) { 190 analysis.setIsRealName(Boolean.parseBoolean(temp)); 191 } 192 193 return new AnsjTokenizer(analysis, filters, synonyms); 194 195 } 196 197 }
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 |
是 |
是 |
是 |
是 |
是 |
代码演示:
1 package main.java.cn.lmc.collection.retrieval.web.analyzer; 2 3 import org.ansj.domain.Result; 4 import org.ansj.library.DicLibrary; 5 import org.ansj.splitWord.analysis.*; 6 import org.ansj.util.MyStaticValue; 7 8 9 /** 10 * AnsjAnalyzerTest 11 * 12 * @author limingcheng 13 * @Date 2019/11/26 14 */ 15 public class AnsjAnalyzerTest { 16 17 18 19 /** 20 * 基本分词(BaseAnalysis) 21 * 速度快 22 */ 23 public static void BaseAnalysisTest(){ 24 String words = "五月天创建的人生有限公司举报了一场演唱会,陈信宏唱了一首do you ever shine"; 25 System.out.println(BaseAnalysis.parse(words)); 26 } 27 28 /** 29 * 精准分词(ToAnalysis) 30 * 精准分词方式兼顾精度与速度,比较均衡 31 */ 32 public static void ToAnalysisTest(){ 33 String words = "五月天创建的人生有限公司举报了一场演唱会,陈信宏唱了一首do you ever shine。"; 34 System.out.println(ToAnalysis.parse(words)); 35 } 36 37 /** 38 * NLP分词(NlpAnalysis) 39 * NLP分词方式可是未登录词,但速度较慢 40 */ 41 public static void NlpAnalysisTest(){ 42 String words = "对对对对对对多多小学生 101304471127J"; 43 System.out.println(NlpAnalysis.parse(words)); 44 Result result = NlpAnalysis.parse(words); 45 System.out.println(result.toString()); 46 System.out.println(result.getTerms().toString()); 47 } 48 49 /** 50 * 面向索引分词(IndexAnalysis) 51 */ 52 public static void IndexAnalysisTest(){ 53 String words = "五月天创建的人生有限公司举报了一场演唱会,陈信宏唱了一首do you ever shine,周杰伦,杰伦"; 54 System.out.println(IndexAnalysis.parse(words)); 55 System.out.println(IndexAnalysis.parse("杰伦")); 56 } 57 58 /** 59 * 自定词典分词(DicLibrary) 60 * 动态添加 61 */ 62 public static void DicLibraryTest(){ 63 //添加自定义词语 【 英文,按照小写配置。(大写,不识别。拆词的结果,也转为小写了)】 64 DicLibrary.insert(DicLibrary.DEFAULT, "基于java", "n", 1); 65 66 String text = "基于Java开发的轻量级的中分分词工具包"; 67 68 System.out.println(DicAnalysis.parse(text)); 69 } 70 71 /** 72 * 自定词典分词(DicLibrary) 73 * 路径获取 74 */ 75 public static void DicLibraryPath(){ 76 // 关闭名字识别 77 MyStaticValue.isNameRecognition = false; 78 // 配置自定义词典的位置。注意是绝对路径 79 MyStaticValue.ENV.put(DicLibrary.DEFAULT, "E:\indexDir\library\default.dic"); 80 81 String text = "基于Java开发的轻量级的中分分词工具包"; 82 83 System.out.println(DicAnalysis.parse(text)); 84 } 85 86 /** 87 * 自定词典分词(DicLibrary) 88 * 配置文件 89 */ 90 public static void DicLibraryProperties(){ 91 String text = "基于Java开发的轻量级的中分分词工具包"; 92 93 System.out.println(DicAnalysis.parse(text)); 94 } 95 96 public static void main(String[] args) { 97 // 基本分词 98 // BaseAnalysisTest(); 99 // // 精准分词 100 // ToAnalysisTest(); 101 // // NLP分词 102 // NlpAnalysisTest(); 103 // // 面向索引分词 104 IndexAnalysisTest(); 105 // 词典分词(动态添加) 106 // DicLibraryTest(); 107 // 词典分词(路径) 108 // DicLibraryPath(); 109 // 词典分词(配置文件) 110 // DicLibraryProperties(); 111 } 112 }
以上是关于Lucene:Ansj分词器的主要内容,如果未能解决你的问题,请参考以下文章