大家在使用 ES 作全文检索,经常会遇到库里有数据,但是无法检索出来,以至于遗漏掉部分重要信息。这里我们将场聚焦于使用 IK 分词器,同时使用 ik_smart 分词模式,那么常见的一种方案是进行词库的远程更新,对新数据生效。在舆情监测场景下,客户需要对已有的品牌词、主体词进行监测,丢数据是一种比较尴尬的存在。下面主要介绍在追求极高检索召回率目标下的一些尝试和总结。
分词器
有些同学可能认为,我没有指明分词器,但是也能进行正常索引和检索。下面就简单介绍一些常见的分词器:
a) standard - 标准分词器
该分词器是 ES 默认分词器,具有极好的普适性。英文的处理能力同于StopAnalyzer. 支持中文采用的方法为单字切分。他会将词汇单元转换成小写形式,并去除停用词和标点符号。
b) Whitespace 分词器
仅仅是去除空格,对字符没有lowcase化,不支持中文;并且不对生成的词汇单元进行其他的规范化处理。
c) IK Analyzer
IK 分为 ik_max_word 和 ik_smart 两种分词策略。根据 IK 的文档,二者区别如下:
ik_max_word:会将文本做最细粒度的拆分,例如「中华人民共和国国歌」会被拆分为「中华人民共和国、中华人民、中华、华人、人民共和国、人民、人、民、共和国、共和、和、国国、国歌」,会穷尽各种可能的组合;
ik_smart:会将文本做最粗粒度的拆分,例如「中华人民共和国国歌」会被拆分为「中华人民共和国、国歌」;
基本可以满足常见的中文分词场景。
检索误差分析
我们以 ik_smart 举例:
index_v1/_analyze/ POST{"analyzer": "ik_smart","text": "微贷网费用有哪些"}分词结果: ["微" 、"贷"、"网费"、"用"、"有"、"哪"、"些"]分 析: 如果此时检索 "微贷网" 就无法命中该条数据。(Phrase查询,且slop=0) ) 此时的优化方案为扩充词库 "微贷网",此时便可以正确分词。此类问题,我们归咎于是因为分词不精准导致的。
此时一个直观的解决思路是 按字分词,我们将 IK 对应的默认词库拿掉,就达到按字分词的目的。
index_v1/_analyze/ POST{"analyzer": "ik_smart","text": "微贷网费用有哪些"}分词结果:{"tokens":[{"token":"微","start_offset":0,"end_offset":1,"type":"CN_CHAR","position":0},{"token":"贷","start_offset":1,"end_offset":2,"type":"CN_CHAR","position":1},{"token":"网","start_offset":2,"end_offset":3,"type":"CN_CHAR","position":2},{"token":"费","start_offset":3,"end_offset":4,"type":"CN_CHAR","position":3},{"token":"用","start_offset":4,"end_offset":5,"type":"CN_CHAR","position":4},{"token":"有","start_offset":5,"end_offset":6,"type":"CN_CHAR","position":5},{"token":"哪","start_offset":6,"end_offset":7,"type":"CN_CHAR","position":6},{"token":"些","start_offset":7,"end_offset":8,"type":"CN_CHAR","position":7}]}分 析: 此时 phrase 查询 "微贷网",便可以精准查询。
如果仅仅到这里,使用标准分词器也是可以的。按字分词 + Phrase 查询可以保证较高的检索召回率。但是我们不要忘记还有停用词的概念,也会影响检索的精准度。我们考虑 Phrase 查询:"苏宁"
index_v1/_analyze/ POST{"analyzer": "standard","text": "江苏、宁夏"}分词结果:{"tokens":[{"token":"江","start_offset":0,"end_offset":1,"type":"<IDEOGRAPHIC>","position":0},{"token":"苏","start_offset":1,"end_offset":2,"type":"<IDEOGRAPHIC>","position":1},{"token":"宁","start_offset":3,"end_offset":4,"type":"<IDEOGRAPHIC>","position":2},{"token":"夏","start_offset":4,"end_offset":5,"type":"<IDEOGRAPHIC>","position":3}]}分 析:可以看到标点符号的确被过滤掉,不会建索引。所以该文档会检索到。但是不符合自然语义。
此时如果基于 standard 分词器去改造,就需要重新编译 ES源码。而对IK进行改造,就显的容易些。
{"analyzer": "ik_smart","text": "江苏、宁夏"}分词结果:{"tokens": [{"token": "江","start_offset": 0,"end_offset": 1,"type": "CN_CHAR","position": 0},{"token": "苏","start_offset": 1,"end_offset": 2,"type": "CN_CHAR","position": 1},{"token": "、","start_offset": 2,"end_offset": 3,"type": "OTHER_CJK","position": 2},{"token": "宁","start_offset": 3,"end_offset": 4,"type": "CN_CHAR","position": 3},{"token": "夏","start_offset": 4,"end_offset": 5,"type": "CN_CHAR","position": 4}]}分析:可以看到 "、" 被保留,此时 Phrase 查询,就不会被检索出来。
通过对 IK 进行改造,保留部分停用词,提高检索准确率。
收益
在进行按字分词且保留特殊字符模式下,召回和准确率直线提升,也减轻了词典的运维和维护工作,从而能将注意力集中在系统开发和维护中。
总结
这种模式在一定场景下能解决检索遇到的问题。但是会产生以下问题:
检索效率降低
phrase 查询要比Terms查询效率低10倍,而又是基于字的分词,效率自然会更低。而随后的基于 bigram 查询优化具有一定的性能提升。
噪音数据变得多
即便是基于词典的分词模式,也是有一定先验知识存在。按字分词,只能机械的按照规则进行拆分。可想而知,在某些场景下检索出的数据是不符合预期的。搜索 "更美"(更美APP简称),"今天变的更美丽"这样的文档都会检索出来。当然这个还涉及实体识别、相关度等一系列NLP相关任务。
希望能对大家产生一些帮助!!!




