1. 程式人生 > >倒排索引與分詞

倒排索引與分詞

倒排索引

正排索引:文件ID到文件內容、單詞的關聯關係
倒排索引:單詞到文件ID的關聯關係
這裡寫圖片描述
倒排索引查詢流程:(以查詢包含“搜尋引擎”的文件為例)

  • 通過倒排索引獲得“搜尋引擎”對應的文件ID有1和3
  • 通過正排索引查詢1和3的完整內容
  • 返回使用者最終結果

倒排索引是搜尋引擎的核心,主要包含兩部分:

單詞詞典(Term Dictionary)(一般由B+Tree實現)

  • 記錄所有文件的單詞,一般都比較大
  • 記錄單詞到倒排列表的關聯資訊

倒排列表(Posting List): 記錄單詞對應的文件集合,由倒排索引項(Posting)組成

倒排索引項(Posting)主要包含如下資訊:

  • 文件ID,用於獲取原始資訊
  • 單詞頻率(TF,Term Frequency),記錄該單詞在該文件中的出現次數,用於後續相關性算分
  • 位置(Position),記錄單詞在文件中的分詞位置(多個),用於做詞語搜尋(Phrase Query)
  • 偏移(Offset),記錄單詞在文件的開始和結束位置,用於做高亮顯示
    這裡寫圖片描述

分詞

官方文件

分詞是指將文字轉換成一系列單詞(term or token)的過程,也可以叫做文字分析,在es裡面成為Analysis.

分詞器是es中專門處理分詞的元件,英文為Analyzer,它的元件如下:

  • Character Filters: 針對原始文字進行處理,比如去除html特殊標記符
  • Tokenizer: 將原始文字按照一定規則切分為單詞
  • Token Filters: 針對tokenizer處理的單詞進行在加工,比如轉小寫、刪除、或新增等處理

Analyze API

es提供了一個測試分詞的api介面,方便驗證分詞效果,endpoint是_analyze

  • 可以直接指定analyzer進行測試
  • 可以直接指定索引中的欄位進行測試
  • 可以自定義分詞器進行測試

直接指定analyzer進行測試:

POST _analyze
{
  "analyzer": "standard",   #analyzer:分詞器
  "text": "hello world"     #text:測試文字
}

直接指定索引中的欄位進行測試:

POST test_index/_analyze
{
  "field": "username",   #field:測試欄位
  "text": "hello world"
}

自定義分詞器進行測試:

POST _analyze
{
  "tokenizer": "standard",
  "filter": ["lowercase"],  #自定義analyzer
  "text": "Hello World"     
}

自帶分詞器

Standard: 按詞切分,支援多語言;小寫處理
Simple: 按照非字母切分;小寫處理
Whitespace: 按照空格切分
Stop: Stop World指語氣助詞等修飾性的詞語,比如the、an、的、這等等,相比simple多了stop word 處理
Keyword: 不分詞,直接將輸入作為一個單詞輸出
pattern: 通過正則表示式自定義分隔符;預設是\W+,即非字元的符號作為分隔符
Language: 提供了30+常見語言的分詞器

測試:

POST _analyze
{
  "analyzer": "standard",
  "text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone"
}

中文分詞

中文分詞指的是將一個漢字序列切分成一個一個單獨的詞,在英文中,單詞之間是以空格作為自然分界符,漢語中詞沒有一個形式上的分界符。

常用分詞系統:
IK

  • 實現中英文單詞的切分,支援ik_smart、ik_maxword等模式
  • 可自定義詞庫,支援熱更新分詞詞典
  • python中最流行的分詞系統,支援分詞和詞性標註
  • 支援繁體分詞、自定義詞典、並行分詞等

基於自然語言處理的分詞系統:
Hanlp:由一系列模型與演算法組成的java工具包,目標是普及自然語言處理在生產環境中的應用
THULAC:THU Lexical Analyzer for Chinese,由清華大學自然語言處理與社會人文計算實驗室研製推出的一套中文此法分析工具包,具有中文分詞和詞性標註功能

IK分詞器的安裝使用:
安裝:

./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.0.0/elasticsearch-analysis-ik-6.0.0.zip
對應es版本,在叢集所有節點上安裝,然後重啟服務

ik分詞器基礎知識:
兩種analyzer,ik_smartik_max_word,可以根據需求選擇,但一般是選用ik_max_word
ik_max_word:會將文字做最細粒度的拆分。例如,會將“中華人民共和國國歌”差分為“中華人民共和國,中華人民,中華,華人,人民共和國,人民,共和國,共和,國,國歌”,會窮盡各種可能的組合

ik_smart:會做最粗粒度的拆分。例如,會將“中華人民共和國國歌”差分為“中華人民共和國,國歌”
這裡寫圖片描述

使用ik分詞:(需要在mapping中指定)

PUT myikindex
{
  "mappings": {
    "doc":{
      "properties": {
        "test":{
          "analyzer": "ik_max_word",
          "type": "text"
        }
      }
    }
  }
}   

自定義分詞

當自帶的分詞無法滿足需求時,可以自定義分詞,通過自定義Character Filters、TokenizerToken Filters實現。

(1)Character Filters:在Tokenizer之前對原始文字進行處理,比如增加、刪除或替換字元等,自帶的如下:

  • HTML Strip去除html標籤和轉換html實體
  • Mapping進行字元替換操作
  • Pattern Replace進行正則匹配替換

會影響後續tokenizer解析的postion和offset資訊

POST _analyze
{
  "tokenizer": "keyword",
  "char_filter": ["html_strip"],
  "text": "<p>I&apos;m so <b>happy</b>!</p>"
}

(2)Tokenizer: 將原始文字按照一定規則切分為單詞(term or token),自帶的如下:

  • standard: 按照單詞進行分割
  • letter: 按照非字元類進行分割
  • whitespace: 按照空格進行分割
  • UAX URL Email: 按照standard分割,但不會分割郵箱和url
  • NGram和Edge NGram: 連詞分割
  • Path Hierarchy: 按照檔案路徑進行切割
POST _analyze
{
  "tokenizer": "path_hierarchy",
  "text": "/one/two/three"
}

(3)Token Filters:針對tokenizer處理的單詞進行在加工,比如轉小寫、刪除、或新增等處理,自帶的如下:

  • lowercase: 將所有term轉換為小寫
  • stop: 刪除stop words
  • NGram和Edge NGram連詞分割
  • Synonym新增近義詞的term
POST _analyze
{
  "tokenizer": "standard",
  "text": "a Hello,world!",
  "filter": [
    "stop",
    "lowercase",
    {
      "type": "ngram",
      "min_gram": 4,
      "max_gram": 4
    }
    ]
}

自定義分詞的api:

自定義分詞需要在索引的配置中設定:

PUT test_index
{
  "settings": {
    "analysis": {
      "char_filter": {},
      "tokenizer": {},
      "filter": {},
      "analyzer": {}
    }
  }
}   
# "analysis": 分詞配置,可以自定義char_filter、tokenizer、filter、analyzer

示例1:
這裡寫圖片描述
這裡寫圖片描述

#配置索引
PUT test_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_custom_abalyzer": {
          "type": "custom",
          "char_filter": ["html_strip"],
          "tokenizer": "standard",
          "filter": [
              "lowercase",
              "asciifolding"
            ]
        }
      }
    }
  }
}   

#測試
POST test_index/_analyze
{
  "analyzer": "my_custom_abalyzer",
  "text": "Is this <b>a box</b>?"
}

示例2:
這裡寫圖片描述
這裡寫圖片描述

#配置索引 
PUT test_index3
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_custom_analyzer": {
          "type": "custom",
          "char_filter": ["emoticons"],
          "tokenizer": "punctuation",
          "filter": [
              "lowercase",
              "english_stop"
            ]
        }
      },
      "char_filter": {
       "emoticons": {
         "type": "mapping",
         "mappings": [
             ":) => _happy_",
             ":( => _sad_"
           ]
        }
      },
      "tokenizer": {
        "punctuation": {
          "type": "pattern",
          "pattern": "[.,!?]"
       }
      },
      "filter": {
        "english_stop": {
          "type": "stop",
          "stopwords": "_english_"
        }
      }
    }
  }
}
#驗證 
POST test_index3/_analyze
{
  "analyzer": "my_custom_analyzer",
  "text": "I'm a :) person,and you?"
}

分詞使用說明:

分詞會在如下兩個時機使用:

  • 建立或更新文件時(Index Time),會對相應的文件進行分詞處理
  • 查詢時(Search Time),會對查詢語句就行分詞

索引時分詞是通過配置Index Mapping 中每個欄位的analyzer屬性實現的,如下:

PUT test_index
{
  "mappings": {
    "doc": {
      "properties": {
        "title": {
          "type": "text",
          "analyzer": "whitespace"
        }
      }
    }
  }
}   
# "analyzer"指定分詞器

查詢時分詞的指定方式有如下幾種:

  • 查詢的時候通過analyzer指定分詞器
  • 通過index mapping 設定search_analyzer實現
POST test_index/_search
{
  "query":{
    "match": {
      "message": {
        "query": "hello",
        "analyzer": "standard"
      }
    }
  }
}


PUT test_index
{
  "mappings": {
    "doc": {
      "properties": {
        "title": {
          "type": "text",
          "analyzer": "whitespace"
          , "search_analyzer": "standard"
        }
      }
    }
  }
}

一般不需要特別指定查詢時的分詞器,直接使用索引時的分詞器即可,否則會出現無法匹配的情況。

分詞的使用建議:

  1. 明確欄位是否需要分詞,不需要分詞的欄位就將type 設定為keyword,可以節省空間和提高寫效能
  2. 善用_analyze API,檢視文件的具體分詞結果
  3. 動手測試