Elasticsearch 為了搜尋

狼爺發表於2021-03-06

前言

Elasticsearch 是一個開源的搜尋引擎,建立在一個全文搜尋引擎庫 Apache Lucene™ 基礎之上。 Lucene 可以說是當下最先進、高效能、全功能的搜尋引擎庫——無論是開源還是私有。

下面將從索引、相關性、TF−IDF與BM25相關性演算法、查全率跟查準率來分析Elasticsearch的搜尋。

倒排索引

說到倒排索引,就不得不說正排索引。

正排索引,由key查詢實體的過程,使用正排索引,比如我們常用的MySQL索引到資料行的過程。

倒排索引由詞查詢文件的過程,使用倒排索引。這種索引表中的每一項都包括一個屬性值和具有該屬性值的各記錄的地址。將單詞或記錄作為索引,將文件ID作為記錄,這樣便可以方便地通過單詞或記錄查詢到其所在的文件。

兩者的區別就比如一本書中,書本目錄是正排索引,書本附錄中的術語中關聯的頁數是倒排索引。

相關性(relevance)

對於MySQL中的資料,預設返回的索引資料儲存的資料,那Elasticsearch返回的文件的排序順序呢?

預設情況下,返回結果是按相關性倒序排列的。但是什麼是相關性?相關性如何計算?

每個文件都有相關性評分,用一個正浮點數字段 _score 來表示。_score 的評分越高,相關性越高。

查詢語句會為每個文件生成一個 _score 欄位。評分的計算方式取決於查詢型別 不同的查詢語句用於不同的目的: fuzzy 查詢會計算與關鍵詞的拼寫相似程度,terms 查詢會計算找到的內容與關鍵片語成部分匹配的百分比,但是通常我們說的 relevance 是我們用來計算全文字欄位的值相對於全文字檢索詞相似程度的演算法。

相關性的演算法有TF-IDF跟BM25,Elasticsearch5.0之後預設相關性演算法是BM25。

一些術語

索引詞(term)

在Elasticsearch中索引詞(term)是一個能夠被索引的精確值。
如果是英文,忽略大小寫,儲存為小寫格式,忽略一些無效詞(比如the);
如果是中文,忽略一些無效詞(比如“的”)

文字(text)

文字是一段普通的非結構化文字。通常,文字會被分析成一個個的索引詞,儲存在Elasticsearch的索引庫中。
比如文字“I love China”可能會被分析成索引詞“I”,“love”,“China”。具體還得看分詞器。

分析(analysis)

分析是將文字轉換為索引詞的過程,分析的結果依賴於分詞器。

文件(document)

文件是儲存在Elasticsearch中的一個JSON格式的字串。它就像在關聯式資料庫中表的一行。

TF-IDF 演算法

TF(Term Frequence):代表詞頻。公式為 text中某一索引詞w出現的次數 / text所有的term的個數

IDF(Invert Document Frequence):代表逆向文件頻率。公式為 lg(總文件數 / (有索引詞w的文件數+1))

TF−IDF的值為TF∗IDF。

例子:一篇文件總的詞語數是100個,而詞語“中華”出現了3次,詞頻就是3/100=0.03,如果“中華”出現在1000份檔案中,而所有檔案總數是10000000份,逆向檔案頻率就是 lg(10,000,000 / 1,000)=4,最後的TF-IDF的分數為0.03 * 4 = 0.12。

BM25 演算法

BM25 源自 概率相關模型,而不是向量空間模型,BM25 同樣使用詞頻、逆向文件頻率以及欄位長歸一化,但是每個因子的定義都有細微區別。

公式

IDF * ((k + 1) * tf) / (k * (1.0 - b + b * (|d|/avgDl)) + tf)
IDF是根據概率資訊檢索獲得

相比傳統的TF*IDF相關性演算法,在BM25中詞頻的影響降低。詞頻的影響一直在增加,但漸漸地逼近一個值。
BM25 有一個上限,文件裡出現 5 到 10 次的詞會比那些只出現一兩次的對相關度有著顯著影響。但是如圖 TF/IDF 與 BM25 的詞頻飽和度 所見,文件中出現 20 次的詞幾乎與那些出現上千次的詞有著相同的影響。
image

搜尋的效率

如何衡量搜尋的效率呢?用查全率跟查準率。

查全率=(搜尋出的相關文件/系統中的相關文件總量)*100%。代表所有相關文件被檢索的比例。

查準率=(搜尋出的相關文件/檢索出的文件總量)*100%。代表搜尋出來的文件中相關文件的比例。

查全率是衡量搜尋系統和搜尋者搜出相關文件的能力,查準率是衡量搜尋系統和搜尋者拒絕非相關相關文件的能力。兩者合起來,即表示搜尋效率。

查全率 查準率

比如上圖中,期望結果是搜尋出所有的實心圓,結果是搜尋出了部分實心圓跟不想要的空心圓。

分詞器

分詞器 接受一個字串作為輸入,將這個字串拆分成獨立的詞或 語彙單元(token) (可能會丟棄一些標點符號等字元),然後輸出一個 語彙單元流(token stream) 。

有趣的是用於詞彙 識別 的演算法。 whitespace (空白字元)分詞器按空白字元 —— 空格、tabs、換行符等等進行簡單拆分 —— 然後假定連續的非空格字元組成了一個語彙單元。例如:

GET /_analyze?tokenizer=whitespace
You're the 1st runner home!

這個請求會返回如下詞項(terms): You're 、 the 、 1st 、 runner 、 home!

icu_分詞器 和 標準分詞器 使用同樣的 Unicode 文字分段演算法, 只是為了更好的支援亞洲語,新增了泰語、寮國語、中文、日文、和韓文基於詞典的詞彙識別方法,並且可以使用自定義規則將緬甸語和柬埔寨語文字拆分成音節。

中文分詞還可以使用“IK”分詞器。

中文分詞難點,需要根據上下文語義進行分詞。比如“這個蘋果不大好吃”,可以有兩種分詞,“這個蘋果,不大好吃”,“這個蘋果,不大,好吃”,不一樣的分詞,不一樣的結果。

參考資料

相關文章