概要
本篇主要講解倒排索引的基本原理以及ES常用的幾種分詞器介紹。
倒排索引的建立過程
倒排索引是搜索引擎中常見的索引方法,用來存儲在全文搜索下某個單詞在一個文檔中存儲位置的映射。通過倒排索引,我們輸入一個關鍵詞,可以非常快地獲取包含這個關鍵詞的文檔列表。
我們先看英文的,假設我們有兩個文檔:
- I have a friend who loves smile
- 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架構社區