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();
    }
}