深入解析 Elasticsearch IK 分词器:ik_smart 和 ik_max_word 的区别与应用场景

科技   2024-12-12 10:00   安徽  

👉 欢迎加入小哈的星球,你将获得: 专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2期已完结,演示链接:http://116.62.199.48/;

截止目前,累计输出 72w+ 字,讲解图 3088+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有2500+小伙伴加入






1、Elasticsearch ik 分词器常见问题

最近在 git 上看看 ik 的相关问题,发现大家问的比较多的是 ik 分词器的 ik_smart 和 ik_max_word 两个分词模式,以及它俩之间的分词差异。


这里来集中解释一波,期望对大家有帮助。

2、ik_smart 与 ik_max_word 的异同

首先来看下官方的FAQs

What is the difference between ik_max_word and ik_smart?

ik_max_word: Performs the finest-grained segmentation of the text. For example, it will segment "中华人民共和国国歌" into "中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌", exhaustively generating various possible combinations, suitable for Term Query.

ik_smart: Performs the coarsest-grained segmentation of the text. For example, it will segment "中华人民共和国国歌" into "中华人民共和国,国歌", suitable for Phrase queries.

Note: ik_smart is not a subset of ik_max_word.

官方这里简单的描述了一下使用用途,即:


  1. ik_smart 比较适合 match_phrase query,而 ik_max_word 更合适 term query。

    1. ik_smart 的分词结果并不是 ik_max_word 的分词结果的子集。

    那这两个分词器在具体实现上会有什么不一样呢?

    哪些场景两个分词器的分词结果肯定不同呢?

    造成分词结果不一样的原因是什么?

    3、ik 分词器源码分析

    3.1. 量词处理源码剖析

    这里先看一下这段代码。

      private void compound(Lexeme result){

      if(!this.cfg.isUseSmart()){
       return ;
      }
         //数量词合并处理
      if(!this.results.isEmpty()){

       if(Lexeme.TYPE_ARABIC == result.getLexemeType()){
        Lexeme nextLexeme = this.results.peekFirst();
        boolean appendOk = false;
        if(Lexeme.TYPE_CNUM == nextLexeme.getLexemeType()){
         //合并英文数词+中文数词
         appendOk = result.append(nextLexeme, Lexeme.TYPE_CNUM);
        }else if(Lexeme.TYPE_COUNT == nextLexeme.getLexemeType()){
         //合并英文数词+中文量词
         appendOk = result.append(nextLexeme, Lexeme.TYPE_CQUAN);
        }
        if(appendOk){
         //弹出
         this.results.pollFirst(); 
        }
       }
       
       //可能存在第二轮合并
       if(Lexeme.TYPE_CNUM == result.getLexemeType() && !this.results.isEmpty()){
        Lexeme nextLexeme = this.results.peekFirst();
        boolean appendOk = false;
         if(Lexeme.TYPE_COUNT == nextLexeme.getLexemeType()){
          //合并中文数词+中文量词
          appendOk = result.append(nextLexeme, Lexeme.TYPE_CQUAN);
         }  
        if(appendOk){
         //弹出
         this.results.pollFirst();       
        }
       }

      }
     }
     

    这里由 smart 模式触发的 合并英文数词+中文量词 的处理中,把 token 的属性修改成了 TYPE_CQUAN (中文数量词)。

    这是 smart 模式下拥有而 max 模式下没有的分词方式和 token 类型。

    举个例子:“7天” 这个词的分词结果,结果中分别展示了位置:内容:类型

    ik_max_word:
    0-1 : 7 :  ARABIC
    1-2 : 天 :  COUNT

    ik_smart
    0-2 : 7天 :  TYPE_CQUAN

    也就是说 ik_max_word 与 ik_smart 在‘英文数词+中文量词’的分词场景下,分词结果必定不一样。

    3.2. 切分模式和歧义消除剖析

    ik分词器的算法原则还是基于中文字典进行字典树的匹配。

    也就是说词元匹配的前提是丰富的中文字典库(ik 已经默认加载了几十万的字典库了)。

    我们先来看 ik_max_word 的切分模式:执行文本的最细粒度分割,将分段详尽地生成各种可能的组合。

    来看下“中华人民共和国国歌”的例子,这里为了更加直观的体现字典树的匹配模式,我们把字典库的内容也列出来。

    文本:中华人民共和国国歌

    字典库:中华人民共和国国歌,中华人民,中华,华人,人民共和国,人民,共和国,共和,国国,国歌

    ik_max_word 分词结果:
    0-9 : 中华人民共和国国歌 :  CN_WORD
    0-4 : 中华人民 :  CN_WORD
    0-2 : 中华 :  CN_WORD
    1-3 : 华人 :  CN_WORD
    2-7 : 人民共和国 :  CN_WORD
    2-4 : 人民 :  CN_WORD
    4-7 : 共和国 :  CN_WORD
    4-6 : 共和 :  CN_WORD
    6-8 : 国国 :  CN_WORD
    7-9 : 国歌 :  CN_WORD

    可以看出 ik_max_word 分词器把所有的字典结果都匹配出来了,同时也看到了好几个词元的位置是有重叠的,比如:“中华人民”“中华”“华人”这几个词元,位置在0-4这段有着不同的重叠。

    这也就是造成了代码中所需要处理的“歧义”,我们这里可以把“歧义”理解为多个词元组合去代表一段内容。

    而 ik_smart 分词器主要作用就是通过对词元组合进行歧义裁决来消除词元间的歧义,消除歧义后的直观体现就是不再会有位置重叠的词元(这也是 ik_smart 更适合 match_phrase 查询的原因)。

    ik_smart 遵循歧义裁决主要原则顺序如下:

    1. 比较有效文本长度,越长越好;
    2. 比较词元个数,越少越好;
    3. 路径跨度越大越好;
    4. 根据统计学结论,逆向切分概率高于正向切分,因此位置越靠后的优先;
    5. 词元位置权重比较,词长越平均越好。

    同样的文本内容,同样的字典库,ik_smart 的分词结果如下:

    ik_smart 分词结果:
    0-9 : 中华人民共和国国歌 :  CN_WORD

    由于字典库中“中华人民共和国国歌”可以覆盖整个文本,并满足上诉大多数条件,ik_smart 就只保留了第一个词元。

    为了更直观的感受,我们把“中华人民共和国国歌”从词库中去除。

    字典库:中华人民,中华,华人,人民共和国,人民,共和国,共和,国国,国歌

    ik_smart 分词结果:
    0-4 : 中华人民 :  CN_WORD
    4-7 : 共和国 :  CN_WORD
    7-9 : 国歌 :  CN_WORD

    对于 ik_smart 歧义裁决原理有兴趣的同学可以看源码中 LexemePath 类的 compareTo 方法。

    4、使用建议

    1. 召回要求高,对分词词元匹配精准的,使用 ik_max_word,并结合 term 查询。

    2. 召回要求低,分词切分要求较低,节省存储,比如日志场景,可以考虑 ik_smart 进行 match_phrase查询。

    3. 索引分词器和搜索分词器原则上保持一致,如果索引使用 ik_max_word 而搜索使用 ik_smart,则有词元匹配失败的可能。

    👉 欢迎加入小哈的星球,你将获得: 专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

    • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17..., 点击查看项目介绍
    • 《从零手撸:前后端分离博客项目(全栈开发)》 2期已完结,演示链接:http://116.62.199.48/;

    截止目前,累计输出 72w+ 字,讲解图 3088+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,Spring Cloud Alibaba 等等,戳我加入学习,解锁全部项目,已有2500+小伙伴加入


    1. 我的私密学习小圈子,从0到1手撸企业实战项目!

    2. SpringBoot 在一次 http 请求中耗费了多少内存?

    3. 面试回答 Redis 是单线程的所以很快,让我回去等通知....

    4. Spring Boot 性能提升的核武器,速度提升 500%!

    最近面试BAT,整理一份面试资料Java面试BATJ通关手册,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。

    获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。

    PS:因公众号平台更改了推送规则,如果不想错过内容,记得读完点一下在看,加个星标,这样每次新文章推送才会第一时间出现在你的订阅列表里。

    “在看”支持小哈呀,谢谢啦

    小哈学Java
    码龄9年,前某厂中台研发。专注于Java领域干货分享,不限于BAT面试, 算法,数据库,Spring Boot, 微服务,高并发, JVM, Docker容器,ELK相关知识,期待与您一同进步。
     最新文章