1 Star 2 Fork 1

coderush / hanlp-plugin

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
代码介绍.md 7.33 KB
一键复制 编辑 原始数据 按行查看 历史
coderush 提交于 2022-08-26 13:31 . [吴峻申] #N/A 新特性: 异步验证

1.ES分词器简单介绍

ElasticSearch默认就有标准的英文分词器。

但是对于母语是非英语的人来说,光有英文分词器是远远不够的。

因此各国家的程序员都会开发对应自己母语的分词插件来增强ElasticSearch的分词功能

不管何种自然语言的分词器,无外乎由下列三部分组成

  • 分词器(Analyzer)
  • 分解器(Tokenizer)
  • 词元过滤器(TokenFilter)

而底层依赖的都是分词算法。

本项目使用的分词算法是 HanLP ,作者何晗。

具体官网地址可见 HanLP,号称是最好的中文分词算法。

除此之外,分词器应该还具有一些附加功能,比如下列两个功能

  • 支持用户自定义字典
  • 支持字典的热更新功能

2.HanLP 简单介绍

HanLP 是一系列模型与算法组成的 NLP (自然语言处理) 工具包,具备功能完善、性能高效、架构清晰、语料时新、可自定义特点,详情可参考 HanLP github 地址

选择它作为本项目底层分词算法理由如下

  • Java 分词包中最流行的分词算法
  • 提供多种分词器,既可基于字典也可基于分词模型
  • 坚持使用明文字典,可借助社区力量对字典进行不断完善
  • 开发文档和代码样例丰富

3.项目代码结构

见下图

项目代码结构

  • assemblies: 插件打包(plugin.xml)配置文件
  • com.wujunshen.core: 分词插件核心类
  • com.wujunshen.dictionary: 同义词字典类
  • com.wujunshen.enumation: 涉及的枚举
  • com.wujunshen.exception: 自定义异常
  • com.wujunshen.nature: 自然分词属性
  • com.wujunshen.plugin: 分词插件定义
  • com.wujunshen.update: 热词更新处理类
  • com.wujunshen.utils: 涉及的工具类
  • resources: 插件属性文件所在目录。包括插件配置、HanLP的热词更新配置、Java 安全策略、logback 日志配置等文件
  • test下的com.wujunshen.entityMyAnalyzerTest: 使用JUnit5编写的单元测试方法

4.单元测试类介绍

具体见 MyAnalyzerTest.java

其中具体说明一下私有方法 analyze

    private List<Token> analyze(SegmentationType segmentationType, String text) throws IOException {
        Tokens result = new Tokens();
        List<Token> resultList = new ArrayList<>();
        Analyzer analyzer = new MyAnalyzer(segmentationType);
        TokenStream tokenStream = analyzer.tokenStream("text", text);

        tokenStream.reset();

        while (tokenStream.incrementToken()) {
            CharTermAttribute charTermAttribute = tokenStream.getAttribute(CharTermAttribute.class);
            TypeAttribute typeAttribute = tokenStream.getAttribute(TypeAttribute.class);

            OffsetAttribute offsetAttribute = tokenStream.getAttribute(OffsetAttribute.class);

            PositionIncrementAttribute positionIncrementAttribute =
                    tokenStream.getAttribute(PositionIncrementAttribute.class);

            Token token = new Token();
            token.setToken(charTermAttribute.toString());
            token.setStartOffset(offsetAttribute.startOffset());
            token.setEndOffset(offsetAttribute.endOffset());
            token.setType(typeAttribute.type());
            token.setPosition(positionIncrementAttribute.getPositionIncrement());

            resultList.add(token);
        }

        tokenStream.close();

        result.setTokens(resultList);

        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        log.info("{}\n", objectMapper.writeValueAsString(result));

        return resultList;
    }

Analyzer 类是一个抽象类,是所有分词器基类,通过 TokenStream 类将文本转换为词汇单元流。

4.1 TokenStream 使用流程

  1. 实例化 TokenStream, 向 AttributeSource 添加属性(词汇单元文本text、位置增量position 、偏移量offset、词汇类型type等)
  2. 调用 reset 方法, 将流(stream)重置到原始(clean)状态
  3. 循环调用 incrementToken 方法,处理 Attribute 属性信息
  4. 调用 close 方法释放资源

注意 由上可知 我们需要重点关注 TokenStream 的实例化、resetincrementTokenclose这几个方法实现

5.还需重点关注安全策略文件

plugin-security.policy 文件可见前述代码结构的图里,需要放置在 resources 目录下。

这样打包后才会在插件根目录下。

但是实际执行时,ElasticSearch的日志会报 AccessControlException 错误,这个可能是远程加载自定义分词字典(见README.md文件中所述的nginx静态内容网站搭建内容) 时,需要网路连接权限。

因此我在 MyTokenizer.java 中,加入了下列代码,如果显示正常,则说明远程加载分词字典成功

    static {
        SecurityManager sm = System.getSecurityManager();

        if (sm != null) {
            sm.checkPermission(new SpecialPermission());
        }

        AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
            Nature.create("auxiliary");

            return null;
        });
        AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
            nlpSegment = HanLP.newSegment()
                    // 词性标注
                    .enablePartOfSpeechTagging(true)
                    // 计算偏移量
                    .enableOffset(true)
                    // 中文人名识别
                    .enableNameRecognize(true)
                    // 日本人名识别
                    .enableJapaneseNameRecognize(true)
                    // 数量词识别
                    .enableNumberQuantifierRecognize(true)
                    // 机构名识别
                    .enableOrganizationRecognize(true)
                    // 音译人名识别
                    .enableTranslatedNameRecognize(true);

            indexSegment = HanLP.newSegment()
                    .enableIndexMode(true)
                    // 词性标注
                    .enablePartOfSpeechTagging(true)
                    // 计算偏移量
                    .enableOffset(true);

            // 在此处显示调用一下分词,使得加载词典、缓存词典的操作可以正确执行
            log.info(String.valueOf(nlpSegment.seg("HanLP中文分词工具包!")));
            log.info(String.valueOf(indexSegment.seg("HanLP中文分词工具包!")));

            return null;
        });
    }

6.总结

本项目功能可总结为下列这些

  • 内置3种分词模式,适合不同场景(索引分词、nlp分词、同义词索引分词)
  • 支持外置字典(需要搭建nginx静态内容网站)
  • 支持分词器级别的自定义字典
  • 支持远程字典热更新

7.项目源码

8.特别感谢

本项目单元测试类 MyAnalyzerTest.java 所使用的文本解析内容,来源于倪匡老先生的小说: 卫斯理系列中的《透明光》第一章

老先生的具体生平可见 百度百科

向刚去世不久的倪匡老先生致以崇高的敬意~

R.I.P

Java
1
https://gitee.com/darkranger/hanlp-plugin.git
git@gitee.com:darkranger/hanlp-plugin.git
darkranger
hanlp-plugin
hanlp-plugin
master

搜索帮助