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架構社區

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章