通過工作示例了解什么是向量數據庫、它們如何實現 “相似性” 搜索以及它們可以在明顯的 LLM 空間之外的哪些地方使用。除非你一直生活在巖石下,否則你可能聽說過諸如生成式人工智能和大型語言模型(LLM)之類的術語。 除此之外,你很有可能聽說過向量數據庫,它為 LLMs 的查詢提供上下文。 有沒有想過它們是什么以及它們在明顯的 LLM 領域之外有何用處? 好吧,請繼續閱讀以了解這項令人興奮的新技術,構建您自己的向量數據庫并思考如何在你的項目中利用它,包括但不限于 LLMs。
以值匹配為中心的搜索的局限性
首先,讓我們看看到底缺少什么而引發了對不同類型數據庫技術的需求。 這是與搜索數據有關。 當你在數據庫中聽到 “搜索” 這個詞時,你可能會立即想到正常的以數值或關鍵字為中心的搜索,例如:
- 相等:其中 customer_id = 123
- 比較:年齡大于 25 歲
- 通配符:客戶名稱以 “Mc” 開頭,例如 “McDonald”
有時,這些以價值為中心的搜索也相互依存,例如
其中年齡 (age)?> 25 且郵政編碼 (zipcode) = ‘12345’
現代數據庫技術在過去幾十年中不斷發展,提高了此類搜索的效率,我將其稱為 “以值為中心的搜索”,其中評估特定值以在查詢中進行過濾。 雖然它們在許多情況下都可以工作,可以說在幾乎所有與業務相關的應用程序中,但請考慮如下:
給我找一個像麗莎 (Lisa) 一樣的客戶
請注意所使用的過濾器:它并沒有詢問姓名為 “Lisa” 的客戶; 只是像她這樣的人,即與麗莎相似的人。 相似是什么意思? 這是一個很難回答的問題。 這不是名字,因為類似的客戶可能被命名為 Alice、Bob 或 Chris。 難道是他們的年齡? 可能吧。 假設麗莎的年齡是 40 歲。40 歲的顧客最相似。 25 歲的客戶相似度會降低,55 歲的客戶也同樣不相似。
讓我們思考一下。 考慮這三位顧客各自的年齡。

如果我們畫一個圖表,將 Lisa 的余額放在中間,然后繪制其他的圖表,它將如下圖所示。 他們的年齡與 40 歲(麗莎的年齡)的距離顯示了他們距離該目標有多遠。 在本例中,我們表明 Bob 最相似,Charlie 最不相似,而 Alice 更相似一些。
年齡只是客戶的一方面。 在尋找 “像麗莎” 這樣的人時,我們可能會想到更多的屬性; 不只是一個。 其中一個屬性可以是客戶的凈資產,如下所示,添加到原始表中:

如果 Lisa 的凈資產是10萬,這些客戶之間會有什么新的相似之處? 我們可以創建一個以年齡和凈資產為兩個軸的二維圖表,如下圖所示。

然而,由于后者以千為單位,而前者以兩位數為單位,因此圖表將不成比例。 為了獲得相同的比例,我們需要將這些絕對值轉換為一些相對值以進行比較。 年齡從 20 歲到 80 歲不等,即相差 60 歲。因此,Alice 與 Lisa 的年齡距離為 (40–20)/60 = 0.33。 同樣,凈資產的分布范圍為 50 到 200,即 150。同樣,Bob 的凈資產距離為 (200–100)/150 = 0.67。

我們發現 Bon 的檔案不再與麗莎 “相似”。 為了找到復合距離,我們可以在二維圖上計算它們之間的距離,例如:
Composite Distance = Square Root of (Square of (Age Distance) + Square of (Net Worth Distance))
使用該公式,我們計算與 Lisa 的復合距離。

我們可能會發現 Alice?距離 Lisa 的距離可能比 Bob 要近,而且和 Charlie 距離是最遠。 只需添加一個維度即可顯著改變相似性。 考慮添加另一個維度,例如 “孩子的數量”,使其成為 3 維圖,這可能會進一步改變物體與麗莎的距離。 實際上,對象有數百個屬性可供比較。 將所有這些都寫在紙上是不可能的。 但希望你能了解多維空間中兩點之間的距離。 距離越小,點越相似,0 表示在所有維度上完全相同。
點的屬性被捕獲為向量。 在上面的例子中,向量的維度將是 [Age,Net Worth]; 所以我們將按如下方式表示這些值。
代表 Lisa 的向量是 [40,100000]。 點之間的距離通常表示為歐幾里德距離,如下面二維空間的函數 d() 所示。 資料來源:維基百科。
運用 Elasticsearch 作為向量數據并計算距離
在上面,我們通過一個詳細的例子描述了如何把數據轉換為向量,并計算向量直接的距離。事實上,如果我們通過手動的方式來計算,就顯得非常麻煩。Elasticsearch 作為全球下載量最多的向量數據庫,我們可以很方便地利用它來幫我們進行計算向量之間的相似性。下面,我們來通過 Elasticsearch 來實現向量之間的相似性。
首先,我們為向量的索引定義一個 mapping:
PUT my-index
{"mappings": {"properties": {"my_vector": {"type": "dense_vector","dims": 2,"similarity": "l2_norm"},"name" : {"type" : "keyword"}}}
}
請注意,在上面,我們定義了一個叫做 dense_vector 的數據類型。這個就是我們的向量數據類型。它的維度為 2。我們可以詳細參考 Elastic 官方文檔來了解這個數據類型。my_vector 的相似性,我們使用 l2_norm 來定義 similarity,它表明是歐幾里得距離。請詳細參閱文檔。
我們通過如下的命令來寫入數據到 Elasticsearch:
POST my-index/_bulk?refresh=true
{ "index" : { "_id" : "1" } }
{ "name" : "Alice", "my_vector": [20,100000] }
{ "index" : { "_id" : "2" } }
{ "name" : "Bob", "my_vector": [40,200000] }
{ "index" : {"_id" : "3" } }
{ "name" : "Charlie", "my_vector": [80,50000] }
我們可以通過如下的命令來查看寫入的數據:
GET my_index/_search?filter_path=**.hits
上面的命令返回的響應為:
"hits": {"hits": [{"_index": "my_index","_id": "1","_score": 1,"_source": {"name": "Alice","my_vector": [20,100000]}},{"_index": "my_index","_id": "2","_score": 1,"_source": {"name": "Bob","my_vector": [40,200000]}},{"_index": "my_index","_id": "3","_score": 1,"_source": {"name": "Charlie","my_vector": [80,50000]}}]}
}
我們可以通過 Elasticsearch 來計算我們搜索對象 Lisa 的距離。搜索的結果將返回在我們的向量數據庫中最近的向量。它們是按照距離的大小進行排序的。在上面的向量中,我們想找到一個最相近的 Lisa,而它的向量為 [40, 100000]。我們可以通過如下的方法來搜索我們的向量:
接下來,我們使用 Elasticsearch 的 knn search 端點來進行搜索:
POST my-index/_search?filter_path=**.hits
{"knn": {"field": "my_vector","query_vector": [40, 100000],"k": 10,"num_candidates": 100}
}
上面的搜索結果是:
{"hits": {"hits": [{"_index": "my-index","_id": "1","_score": 0.0024937657,"_source": {"name": "Alice","my_vector": [20,100000]}},{"_index": "my-index","_id": "3","_score": 3.9999976e-10,"_source": {"name": "Charlie","my_vector": [80,50000]}},{"_index": "my-index","_id": "2","_score": 1e-10,"_source": {"name": "Bob","my_vector": [40,200000]}}]}
如上所示,我們看到的結果是 Alice 排名是第一的,而緊隨其后的是 Charlie。而我們之前認為的 Bob 是排在最后的一個。Bob 的距離是最遠的,這個和之前的推送方法有一定的誤差,比如相對計算的方法不同。
更多有關 Elasticsearch 向量搜索的內容,請詳細閱讀文章 “AI”。