使用Elasticsearch做向量空間內的相似性搜索


Elasticsearch做文本檢索是基於文本之間的相似性的。在Elasticsearch 5.0中,Elasticsearch將默認算法由TF / IDF切換爲Okapi BM25,該算法用於對與查詢相關的結果進行評分。對Elasticsearch中的 TF / IDFOkapi BM25感興趣的可以直接查看Elastic的官方博客。(簡單的說,TF/IDF和BM25的本質區別在於,TF/IDF是一個向量空間模型,而BM25是一個概率模型,他們的簡單對比可以查看這個文章

TF/IDF中是一個向量空間模型,它將查詢的每個term都視爲向量模型的一個維度。通過該模型可將查詢定義爲一個向量,而Elasticsearch存儲的文檔定義另一個。然後,將兩個向量的標量積視爲文檔與查詢的相關性。標量積越大,相關性越高。而BM25屬於概率模型,但是,它的公式和TF/IDF並沒有您所期望的那麼大。兩種模型都將每個term的權重定義爲一些idf函數和某些tf函數的乘積,然後將其作爲整個文檔對給定查詢的分數(相關度)彙總。

但是,如果我們想根據更抽象的內容(例如單詞的含義或寫作風格)來查找類似的文檔,該怎麼辦?這是Elasticsearch的密集矢量字段數據類型(dense vector)和矢量字段的script-score queries發揮作用的地方。

什麼是Word Embeddings

因爲計算機不認識字符串,所以我們需要將文字轉換爲數字。Word Embedding就是來完成這樣的工作。 其定義是:A Word Embedding format generally tries to map a word using a dictionary to a vector。

如下圖,計算機用ASC碼來識別bread(麪包)和toast(吐司),從ASC II碼上,這兩個單詞沒有任何的相關性,但如果我們將它們在大量的語料中的上下文關係作爲向量維度進行分析,就可以看到,他們通常都和butter(黃油)、breakfast(早餐)等單詞同時出現。

或許,我們的搜索空間裏面沒有面包這個詞出現,但如果用戶搜索了麪包,我們可以嘗試給他同時返回吐司相關的信息,這些或許會對用戶有用
在這裏插入圖片描述
因此,如上圖,我們把beard和toast映射爲:

bread [0.80 0.11 .0.05 0.93 0.20 ...]
toast [0.76 0.22 0.15 0.95 0.12 ...]

對應的向量數組,即爲bread和toast的 Word Embeddings。當然,Word Embeddings的生成可以採用不同的算法,這裏不做詳述。

索引Word Embeddings

Word Embeddings是詞的矢量表示,通常用於自然語言處理任務,例如文本分類或情感分析。相似的詞傾向於在相似的上下文中出現。Word Embeddings將出現在相似上下文中的單詞映射爲具有相似值的矢量表示。這樣,單詞的語義被一定程度上得以保留。

爲了演示矢量場的使用,我們將經過預訓練的GloVeWord Embeddings導入到Elasticsearch中。該glove.6B.50d.txt文件將詞彙表的400000個單詞中的每一個映射到50維向量。摘錄如下所示。

public 0.034236 0.50591 -0.19488 -0.26424 -0.269 -0.0024169 -0.42642 -0.29695 0.21507 -0.0053071 -0.6861 -0.2125 0.24388 -0.45197 0.072675 -0.12681 -0.36037 0.12668 0.38054 -0.43214 1.1571 0.51524 -0.50795 -0.18806 -0.16628 -2.035 -0.023095 -0.043807 -0.33862 0.22944 3.4413 0.58809 0.15753 -1.7452 -0.81105 0.04273 0.19056 -0.28506 0.13358 -0.094805 -0.17632 0.076961 -0.19293 0.71098 -0.19331 0.019016 -1.2177 0.3962 0.52807 0.33352
early 0.35948 -0.16637 -0.30288 -0.55095 -0.49135 0.048866 -1.6003 0.19451 -0.80288 0.157 0.14782 -0.45813 -0.30852 0.03055 0.38079 0.16768 -0.74477 -0.88759 -1.1255 0.28654 0.37413 -0.053585 0.019005 -0.30474 0.30998 -1.3004 -0.56797 -0.50119 0.031763 0.58832 3.692 -0.56015 -0.043986 -0.4513 0.49902 -0.13698 0.033691 0.40458 -0.16825 0.033614 -0.66019 -0.070503 -0.39145 -0.11031 0.27384 0.25301 0.3471 -0.31089 -0.32557 

爲了導入這些映射,我們創建了一個words索引,並在索引mapping中指定dense_vector爲vector字段的類型。然後,我們遍歷GloVe文件中的行,並將單詞和向量分批批量插入該索引中。之後,例如可以使用GET請求檢索“ early”一詞(/words/_doc/early):

{
  "_index": "words",
  "_type": "_doc",
  "_id": "early",
  "_version": 15,
  "_seq_no": 503319,
  "_primary_term": 2,
  "found": true,
  "_source": {
    "word": "early",
    "vector": [0.35948,-0.16637,-0.30288,-0.55095,-0.49135,0.048866,-1.6003,0.19451,-0.80288,0.157,0.14782,-0.45813,-0.30852,0.03055,0.38079,0.16768,-0.74477,-0.88759,-1.1255,0.28654,0.37413,-0.053585,0.019005,-0.30474,0.30998,-1.3004,-0.56797,-0.50119,0.031763,0.58832,3.692,-0.56015,-0.043986,-0.4513,0.49902,-0.13698,0.033691,0.40458,-0.16825,0.033614,-0.66019,-0.070503,-0.39145,-0.11031,0.27384,0.25301,0.3471,-0.31089,-0.32557,-0.51921]
  }
}

評分的餘弦相似度

對於 GloVe Word Embeddings,兩個詞向量之間的餘弦相似性可以揭示相應詞的語義相似性。從Elasticsearch 7.2開始,餘弦相似度可作爲預定義函數使用,可用於文檔評分。

要查找與表示形式相似的單詞,[0.1, 0.2, -0.3]我們可以向發送POST請求/words/_search,在此我們將預定義cosineSimilarity函數與查詢向量和存儲文檔的向量值一起用作函數自變量,以計算文檔分數。請注意,由於分數不能爲負,因此我們需要在函數的結果上添加1.0。

{
  "size": 1,
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "source": "cosineSimilarity(params.queryVector, doc['vector'])+1.0",
        "params": {
          "queryVector": [0.1, 0.2, -0.3]  
        }
      }
    }
  }
}

結果,我們得到單詞“ rites”的分數約爲1.5:

{
    "took": 103,
    "timed_out": false,
    "hits": {
        "total": {
            "value": 10000,
            "relation": "gte"
        },
        "max_score": 1.5047596,
        "hits": [
            {
                "_index": "words",
                "_type": "_doc",
                "_id": "rites",
                "_score": 1.5047596,
                "_source": {
                    "word": "rites",
                    "vector": [0.82594,0.55036,-2.5851,-0.52783,0.96654,0.55221,0.28173,0.15945,0.33305,0.41263,0.29314,0.1444,1.1311,0.0053411,0.35198,0.094642,-0.89222,-0.85773,0.044799,0.59454,0.26779,0.044897,-0.10393,-0.21768,-0.049958,-0.018437,-0.63575,-0.72981,-0.23786,-0.30149,1.2795,0.22047,-0.55406,0.0038758,-0.055598,0.41379,0.85904,-0.62734,-0.17855,1.7538,-0.78475,-0.52078,-0.88765,1.3897,0.58374,0.16477,-0.15503,-0.11889,-0.66121,-1.108]
                }
            }
        ]
    }
}

爲了更加容易探索Word Embeddings,我們使用Spring Shell在Kotlin中構建了一個包裝器。它接受一個單詞作爲輸入,從索引中檢索相應的向量,然後執行script_score查詢以顯示相關度最高結果:

shell:>similar --to "cat"
{"word":"dog","score":1.9218005}
{"word":"rabbit","score":1.8487821}
{"word":"monkey","score":1.8041081}
{"word":"rat","score":1.7891964}
{"word":"cats","score":1.786527}
{"word":"snake","score":1.779891}
{"word":"dogs","score":1.7795815}
{"word":"pet","score":1.7792249}
{"word":"mouse","score":1.7731668}
{"word":"bite","score":1.77288}
{"word":"shark","score":1.7655175}
{"word":"puppy","score":1.76256}
{"word":"monster","score":1.7619764}
{"word":"spider","score":1.7521701}
{"word":"beast","score":1.7520056}
{"word":"crocodile","score":1.7498653}
{"word":"baby","score":1.7463862}
{"word":"pig","score":1.7445586}
{"word":"frog","score":1.7426511}
{"word":"bug","score":1.7365949}

該項目代碼可以在github.com/schocco/es-vectorsearch上找到。

侷限性

目前 dense vector 數據類型還是experimental狀態。其存儲的向量不應超過1024維(Elasticsearch <7.2 的維度限制是500)。
直接使用餘弦相似度來計算文檔評分是相對昂貴的,應與filter一起使用以限制需要計算分數的文檔數量。對於大規模的向量相似性搜索,您可能需要研究更具體的項目,例如FAISS庫,以“使用GPU進行十億規模的相似性搜索”。

通過抽象屬性搜索

我們使用Word Embeddings來證明如何使用Elasticsearch來做向量空間的相似性,但相同的概念也應適用於其他領域。我們可以將圖片映射到對圖片樣式進行編碼的向量表示中,以搜索以相似樣式繪製的圖片:
在這裏插入圖片描述
通過餘弦相似性去進行向量搜索:
在這裏插入圖片描述

如果我們可以將諸如口味或樣式之類的抽象屬性映射到向量空間,那麼我們還可以搜索與給定查詢項共享該抽象屬性的新食譜,服裝或傢俱。
由於機器學習的最新進展,許多易於使用的開源庫都支持創建特定於域的Embeddings,而Elasticsearch中對向量字段的script score query支持,讓我們能輕鬆實現特定於域的相似性搜索的又一步。

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