前言:

网上很多的文章都建议在使用IK分词器的时候,建立索引的时候使用ik_max_word模式;搜索的时候使用ik_smart模式。理由是max_word模式分词的结果会包含smart分词的结果,这样操作会使得搜索的结果很全面。

但在实际的应用中,我们会发现,有些时候,max_word模式的分词结果并不能够包含smart模式下的分词结果。

下面,我们就看一个简单的测试实例:

假设我们现在要分别在max_word模式和smart模式下搜索“2022年”,搜索结果如下表所示:

max_word模式

0 - 4 : 2022 | ARABIC

4 - 5 : 年 | COUNT

smart模式

0 - 5 : 2022年 | TYPE_CQUAN

我们会发现max_word模式下的分词结果并没有覆盖smart模式下的分词结果。这是什么原因导致的呢?

下面,我们通过分析IK分词器的处理流程来寻找答案。

IK分词器实现原理剖析

IKAnalyzer中包含3个子分词器

LetterSegmenter

处理英文字母和阿拉伯数字的分词器

CN_QuantifierSegmenter

处理中文数量词的分词器

CJKSegmenter

处理中文和日韩字符的分词器

下面我们看下这三个子分词器分别是如何工作的。

英文字符及阿拉伯数字子分词器

假设我们现在对“111aaa222bbb”这个字符串在max_word模式下进行分词处理,其分词的处理结果如下所示:

0 - 12 : 111aaa222bbb | LETTER

0 - 3 : 111 | ARABIC

3 - 6 : aaa | ENGLISH

6 - 9 : 222 | ARABIC

9 - 12 : bbb | ENGLISH

上面的第一列代表的是词元的起始位移和偏移量,中间列是词元字符信息,第三列是词元属性。

现在我们将上面的字符串做一些改变,对字符串“111aaa@222bbb”进行分词处理,我们会发现其分词结果如下所示:

0 - 13 : 111aa@a222bbb | LETTER

0 - 3 : 111 | ARABIC

3 - 5 : aa | ENGLISH

6 - 7 : a | ENGLISH

7 - 10 : 222 | ARABIC

10 - 13 : bbb | ENGLISH

现在我们再将上面的字符串做一些修改,变成“111aaa 222bbb”,那么我们会得到如下的分词结果:

0 - 6 : 111aaa | LETTER

0 - 3 : 111 | ARABIC

3 - 6 : aaa | ENGLISH

7 - 13 : 222bbb | LETTER

7 - 10 : 222 | ARABIC

10 - 13 : bbb | ENGLISH

上面的三个字符串除了中间的字符外其他地方是一样的,但是得到的分词结果却是有很多相同。

从分词结果中,我们可以看到LetterSegmenter会拆分出三种词性的词元,分别是LETTER(数字英文混合),ARABIC(数字),ENGLISH(英文)。这三种不同的词元属性分别对应了三种不同的处理流程。

细心的同学可能还会有一个疑问,字符串"111aaa@222bbb"与字符串"111aaa 222bbb"解析出来的混合词元不一样。这是因为@字符是英文字符的链接符号,但空格并不是。

中文数量词子分词器

主要分为数词处理流程和量词处理流程两部分组成,处理流程比较简单,这里不再进行详细叙述。

中文-日韩文子分词器

主要是根据处理词典中的词库进行分词处理,那么如果我们要处理的词语在词库中并不存在的话,会出现什么情况呢?

因为IK分词是一个基于词典的分词器,只有包含在词典的词才能被正确切分,IK解决分词歧义只是根据几条最佳的分词实践规则,并没有用到任何概率模型,也不具有新词发现的功能。

因此,如果我们要处理的文本在词库中不存在的时候,就会被切分成单个字符的模式。

分词歧义裁决器

我们尝试一下在smart模式下对"111aaa 222bbb"进行分词处理,我们会得到如下的分词结果:

0 - 6 : 111aaa | LETTER

7 - 13 : 222bbb | LETTER

那么为什么smart模式下的分词结果会和max_word模式下的分词结果不同呢?通过阅读IK分词器的源代码,我们会发现IK分词器下的smart模式主要是通过IKArbitrator这个类来实现的。

这个类是分词结果的歧义处理类。在了解IKArbitrator这个类的处理流程之前,我们需要先了解两个数据结构,Lexeme,QuickSortSet和LexemePath。

Lexeme是分词器中解析出来的词元结果,其主要的字段包括:

// 词元的起始位移
private int offset;
// 词元的相对起始位置
private int begin;
// 词元的长度
private int length;
// 词元文本
private String lexemeText;
// 词元类型
private int lexemeType;

QuickSortSet是IK分词器中用来对词元进行排序的集合。其中的排序规则是词元相对起始位置小的优先;相对起始位置相同的情况下,词元长度大的优先。

LexemePath继承了QuickSortSet,其代表的是词元链。在IK分词器的smart模式下,会出现多个词元链的候选集。

那么,我们怎么选择最优的词元链呢?选择的关键就在LexemePath的compareTo方法中。

  public int compareTo(LexemePath o) {
// 比较有效文本长度
if (this.payloadLength > o.payloadLength) {
return -1;
} else if (this.payloadLength < o.payloadLength) {
return 1;
} else {
// 比较词元个数,越少越好
if (this.size() < o.size()) {
return -1;
} else if (this.size() > o.size()) {
return 1;
} else {
// 路径跨度越大越好
if (this.getPathLength() > o.getPathLength()) {
return -1;
} else if (this.getPathLength() < o.getPathLength()) {
return 1;
} else {
// 根据统计学结论,逆向切分概率高于正向切分,因此位置越靠后的优先
if (this.pathEnd > o.pathEnd) {
return -1;
} else if (pathEnd < o.pathEnd) {
return 1;
} else {
// 词长越平均越好
if (this.getXWeight() > o.getXWeight()) {
return -1;
} else if (this.getXWeight() < o.getXWeight()) {
return 1;
} else {
// 词元位置权重比较
if (this.getPWeight() > o.getPWeight()) {
return -1;
} else if (this.getPWeight() < o.getPWeight()) {
return 1;
}
}
}
}
}
}
return 0;
}

接下来,我们看看IK分词器是如何选择出最优词元链的?其主要处理流程如下:

总结:

现在回到前言部分中我们提及的问题,经过我们对IK分词器处理流程地分析,我们很容易得到答案。

这是因为2022是数词,而年是量词。在max_word模式下,数词和量词不会进行合并处理。但是在smart模式下,数词和量词会进行合并处理。

通过阅读IK分词器的源码,我们会发现它并没有采用任何先进的算法模型,但是该分词器依然被广泛地被使用。

IK分词器被广泛使用,从某种意义上说明,很多真实的应用场景,并不需要使用那些先进而复杂的深度学习算法模型。低成本的浅层特征模型也仍然可以达到十分具有竞争力的准确率和召回率。

加入技术交流群,请扫描下方二维码。

IK分词器实现原理剖析 —— 一个小问题引发的思考的更多相关文章

  1. 一个小BUG引发的思考。(论开发与测试之间的那点事)

    标题不是“一个馒头引发的血案”. 言归正传:今天上午测试的时候,发现了一个BUG,如图: 一个用肉眼就能发现的BUG.原因当然是因为开发同事没有自测试,流入到了测试人员这里了. 无非是开发同事不严谨造 ...

  2. Elasticsearch之文档的增删改查以及ik分词器

    文档的增删改查 增加文档 使用elasticsearch-head查看 修改文档 使用elasticsearch-head查看 删除文档 使用elasticsearch-head查看 查看文档的三种方 ...

  3. 【ELK】【docker】【elasticsearch】2.使用elasticSearch+kibana+logstash+ik分词器+pinyin分词器+繁简体转化分词器 6.5.4 启动 ELK+logstash概念描述

    官网地址:https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#docker-cli-run-prod ...

  4. IK分词器原理与源码分析

    原文:http://3dobe.com/archives/44/ 引言 做搜索技术的不可能不接触分词器.个人认为为什么搜索引擎无法被数据库所替代的原因主要有两点,一个是在数据量比较大的时候,搜索引擎的 ...

  5. IK分词器 原理分析 源码解析

    IK分词器在是一款 基于词典和规则 的中文分词器.本文讲解的IK分词器是独立于elasticsearch.Lucene.solr,可以直接用在java代码中的部分.关于如何开发es分词插件,后续会有文 ...

  6. [Linux]Linux下安装和配置solr/tomcat/IK分词器 详细实例一.

    在这里一下讲解着三个的安装和配置, 是因为solr需要使用tomcat和IK分词器, 这里会通过图文教程的形式来详解它们的安装和使用.注: 本文属于原创文章, 如若转载,请注明出处, 谢谢.关于设置I ...

  7. ElasticSearch6.5.0 【安装IK分词器】

    不得不夸奖一下ES的周边资源,比如这个IK分词器,紧跟ES的版本,卢本伟牛逼!另外ES更新太快了吧,几乎不到半个月一个小版本就发布了!!目前已经发了6.5.2,估计我还没怎么玩就到7.0了. 下载 分 ...

  8. Elasticsearch入门之从零开始安装ik分词器

    起因 需要在ES中使用聚合进行统计分析,但是聚合字段值为中文,ES的默认分词器对于中文支持非常不友好:会把完整的中文词语拆分为一系列独立的汉字进行聚合,显然这并不是我的初衷.我们来看个实例: POST ...

  9. Restful认识和 IK分词器的使用

    什么是Restful风格 Restful是一种面向资源的架构风格,可以简单理解为:使用URL定位资源,用HTTP动词(GET,POST,DELETE,PUT)描述操作. 使用Restful的好处: 透 ...

随机推荐

  1. freeswitch拨打分机号

    概述 电话语音服务中,有一种稍微复杂的场景,就是总机分机的落地场景,客户拨打总机号码之后,需要再拨打分机号转接到指定的话机. 分机号的拨打一般在总机接通之后,会有语音提示,总机收号之后转接分机. 分机 ...

  2. VM Ware 给Centos虚拟机配置静态IP

    前言:在大家的日常运维工作中,肯定多多少少都会用到虚拟机,但是默认情况下VMware创建的虚拟机每次启动都有可能更换IP这就对我们的工作造成了一定的麻烦,下面我就给大家介绍下,如何给centos虚拟机 ...

  3. Oracle 用户密码中包括了“@”字符串的错误提示解决方法

    Oracle 用户密码设置了带有"@"符号,正常登陆总是无法登陆,提示无法解析的连接字符串错误 解决办法:1:修改密码:修改密码使密码中不包括@符号:2:增加转义即可,在密码前后增 ...

  4. datax开启hana支持以及dolphinscheduler开启datax任务

    datax开启hana支持以及dolphinscheduler开启datax任务 前面(@,@) 前段时间因为要做异构数据导入导出,所以搜了下,发现这类工具收费的居多,使用起来未必趁手~ 于是我找了下 ...

  5. c# SerialPort HEX there is no data received

    C#窗口程序进行串口通信,按照串口通信协议,设置com口,波特率,停止位,校验位,数据位,本地虚拟串口调试ok,但是和外设调试时,发送HEX模式数据命令,没有数据返回, 所以关键问题在于HEX模式,发 ...

  6. NC20583 [SDOI2016]齿轮

    题目链接 题目 题目描述 现有一个传动系统,包含了N个组合齿轮和M个链条.每一个链条连接了两个组合齿轮u和v,并提供了一个传动比x : y. 即如果只考虑这两个组合齿轮,编号为u的齿轮转动x圈,编号为 ...

  7. 千万不要把Request传递到异步线程里面!有坑!

    你好哇,我是歪歪. 前几天在网上冲浪的时候看到一篇技术文章,讲的是他把一个 request 请求传递到了线程池里面,然后遇到了一个匪夷所思的情况. 他写了这篇文章,把自己针对这个问题的探索过程分享了出 ...

  8. 企业运维实践-还不会部署高可用的kubernetes集群?使用kubeadm方式安装高可用k8s集群v1.23.7

    关注「WeiyiGeek」公众号 设为「特别关注」每天带你玩转网络安全运维.应用开发.物联网IOT学习! 希望各位看友[关注.点赞.评论.收藏.投币],助力每一个梦想. 文章目录: 0x00 前言简述 ...

  9. linux 邮件

    通过命令行发送邮件 [root@ProxyServer ~]# mail -s "test" justin@51cto.com dfdafd dfadf fadf EOT [roo ...

  10. python闭包函数及装饰器简介

    目录: 闭包函数简介 闭包函数的实际应用 装饰器简介 装饰器初期-完整版 装饰器语法糖 闭包函数简介 1.定义在函数内部的函数(函数的嵌套) 2.内部函数运用外部函数局部名称空间中的变量名 注:函数名 ...