• Elasticsearch系列---倒排索引原理与分词器


    概要

    本篇主要讲解倒排索引的基本原理以及ES常用的几种分词器介绍。

    倒排索引的建立过程

    倒排索引是搜索引擎中常见的索引方法,用来存储在全文搜索下某个单词在一个文档中存储位置的映射。通过倒排索引,我们输入一个关键词,可以非常快地获取包含这个关键词的文档列表。

    我们先看英文的,假设我们有两个文档:

    1. I have a friend who loves smile
    2. love me, I love you

    为了建立倒排索引,我们先按最简单的用空格把每个单词分开,可以得到如下结果:
    *表示该列文档中有这个词条,为空表示没有该词条

    Term doc1 doc2
    I * *
    have *
    a *
    friend *
    who *
    loves *
    smile *
    love *
    me *
    you *

    如果我们要搜索 I love you,我们只需要查找包含每个词条的文档:

    Term doc1 doc2
    I * *
    love *
    you *

    两个文档都能匹配上,如果按命中词条数量来算,doc2比doc1更匹配。

    这个是倒排索引最简化的表达方式,在ES的倒排索引存储结果中,还会记录每个词条在文档中出现的位置。

    期望的分词处理

    我们再看一下这个索引的建立过程,loves和love有区别吗?没有,都是爱的意思,一个是第三人称单数,一个是原形。如果能将一些语法的区别处理掉,这样的搜索结果是不是更切合实际需求?
    例如:

    • loves提取词干处理成love
    • a,have之类的无实义的词,直接屏蔽掉
    • 等等

    现在索引看上去成这样:

    Term doc1 doc2
    friend *
    love * *
    smile *
    me *
    you *

    这样是不是精简了很多?
    这个过程叫normalization,在建立倒排索引的时候,会执行一系列的操作,对拆分出的各个单词进行相应的处理,以提升后面搜索的时候能够搜索到相关联的文档的概率,如时态的转换,单复数的转换,同义词的转换,大小写的转换等。

    分词器登场

    分词器的作用就是把整篇文档,按一定的语义切分成一个一个的词条,目标是提升文档的召回率,并降低无效数据的噪音。

    recall召回率,也叫可搜索性,指搜索的时候,增加能够搜索到的结果的数量。
    降噪:指降低文档中一些低相关性词条对整体搜索排序结果的干扰。

    文档的分词过程包含以下几步:

    • 字符过滤器

    对字符串进行预处理,如HTML标签清洗Love --> Love,I & you --> I and you等等。

    • 分词器

    把字符串切分成单个的词条,如英文的按空格和标点切分,中文的按词语切分,针对不同的语言,有不同的分词器,有相对简单的标准分词器,也有特别复杂的中文分词器,里面包含了非常复杂的切分逻辑如:

    I Love you --> I/Love/you

    我和我的祖国 --> 我/和/我的/祖国

    • Token过滤器
      将分词器得到的词条进一步的处理,如改变词条(英文词干提取loves --> love),删除无实际意义的词条(英文的a, and, this,中文的"的","了","吗"),增加词条(补充同义词)

    分词器非常重要,好的分词器可以显著提升召回率,不恰当的分词器得到的结果可能会对搜索产生歧义,最后处理好的结果再拿去建立倒排索引。

    常见分词器介绍

    Elasticsearch自身提供了内置的分词器,也允许使用第三方的分词器。

    内置分词器

    • 标准分词器standard analyzer

    ES默认分词器,根据Unicode联盟定义的单词边界划分文本,删除绝大部分标点,最后将词条小写。

    • 简单分词器simple analyzer

    在任何不是字母的地方分隔文本,将词条小写

    • 空格分词器whitespace analyzer

    在空格的地方划分文本

    • 语言分词器language analyzer

    特定的语言的分词器,如english,英语分词器,维护了一组英语停用词and、the之类的,用于删除词条,针对英文语法规则,有提取单词词干的能力。

    内置的分词器主要是对英文的支持效果比较好,中文则需要使用外部的分词器。

    外部分词器

    • IK中文分词器ik_max_word
      会将文本做最细粒度的拆分;尽可能多的拆分出词语。
      如南京市长江大桥 --> 南京市/南京/市长/长江大桥/长江/大桥

    • IK中文分词器ik_smart
      会做最粗粒度的拆分;已被分出的词语将不会再次被其它词语占有
      如南京市长江大桥 --> 南京市/长江大桥

    • 中日韩文分词器cjk
      支持亚洲语言中文,日文,韩文
      如南京市长江大桥 --> 南京/京市/市长/长江/江大/大桥

    • 阿里中文分词器aliws
      阿里自研的中文分词器
      如南京市长江大桥 --> 南京/市/长江/大桥

    外部分词器众多,开源也有很多,有针对不同语言,不同领域的,各位可以结合自身业务的特点,挑选适合自己的分词器,这里就不一一介绍了,有兴趣自己可以去了解一下。

    集成分词器

    以Elasticsearch 6.3.1版本为例,集成IK分词器,其他的分词器过程也类似,在ES的bin目录下执行插件安装命令即可:
    ./elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.3.1/elasticsearch-analysis-ik-6.3.1.zip

    其中install后面的那个的地址是 elasticsearch-analysis-ik 的github release对应ES版本的下载地址。

    安装成功后,ES启动日志就能看到如下信息:
    [2019-11-27T12:17:15,255][INFO ][o.e.p.PluginsService] [node-1] loaded plugin [analysis-ik]

    测试分词效果

    ES有analyze API来查看文本是如何被分词的,可用来做学习和调试用,请求命令如下:

    GET /_analyze
    {
      "analyzer": "ik_max_word",
      "text": "南京市长江大桥"
    }
    
    

    响应结果:

    {
      "tokens": [
        {
          "token": "南京市",
          "start_offset": 0,
          "end_offset": 3,
          "type": "CN_WORD",
          "position": 0
        },
        {
          "token": "南京",
          "start_offset": 0,
          "end_offset": 2,
          "type": "CN_WORD",
          "position": 1
        },
        {
          "token": "市长",
          "start_offset": 2,
          "end_offset": 4,
          "type": "CN_WORD",
          "position": 2
        },
        {
          "token": "长江大桥",
          "start_offset": 3,
          "end_offset": 7,
          "type": "CN_WORD",
          "position": 3
        },
        {
          "token": "长江",
          "start_offset": 3,
          "end_offset": 5,
          "type": "CN_WORD",
          "position": 4
        },
        {
          "token": "大桥",
          "start_offset": 5,
          "end_offset": 7,
          "type": "CN_WORD",
          "position": 5
        }
      ]
    }
    

    小结

    本篇主要介绍了倒排索引的基本思路,展示了简化后的结构,并阐述了分词处理的基本步骤。目前市面上流行的分词器组件特别多,开源的社区也非常活跃,各位可根据实际的项目需求背景,挑选适合的进行集成 ,注意版本号的兼容性问题即可。

    专注Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区
    Java架构社区

  • 相关阅读:
    P12 向量组03--极大线性无关组
    超导体
    Matlab中disp、fprintf和sprintf有什么区别?
    点击word页面自动弹出信息检索很烦人
    Postman 官方中文版 v7.25.3
    无法为更新定位行。一些值可能已在最后一次读取后已更改解决办法
    Everything文件查找工具
    Delphi的DataSource事件
    Delphi中inherited问题
    delphi窗体继承
  • 原文地址:https://www.cnblogs.com/huangying2124/p/12081933.html
Copyright © 2020-2023  润新知