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 }