注:本文若未說明ES版本則為7.10,其他版本會特別標記,由于ES版本不同,部分差異較大,具體請以官方文檔為準
一、向量搜索的核心原理
1.1 向量化表示的本質(zhì)
現(xiàn)代AI技術將文本、圖像等非結(jié)構(gòu)化數(shù)據(jù)轉(zhuǎn)化為高維向量(通常128-1024維),這些向量在數(shù)學空間中攜帶語義特征。如:
- 文本嵌入(Embedding):BERT等模型生成768維向量
- 圖像特征:ResNet提取2048維特征向量
1.2 向量搜索簡介
向量搜索,簡單來說,就是在向量空間中進行的搜索操作。在傳統(tǒng)的文本搜索中,我們通?;陉P鍵詞進行匹配,這種方式對于精確匹配的場景效果較好,但在處理語義理解、模糊匹配等復雜場景時顯得力不從心。而向量搜索則將文本、圖像、音頻等各種數(shù)據(jù)轉(zhuǎn)化為向量表示,通過計算向量之間的相似度來進行搜索。這種方式能夠更好地捕捉數(shù)據(jù)的語義信息,從而實現(xiàn)更精準、更智能的搜索。
例如,對于文本數(shù)據(jù),我們可以使用詞嵌入(Word Embedding)技術將每個單詞映射為一個向量,然后將整個文本的向量表示為其包含單詞向量的某種聚合(如平均、求和等)。在搜索時,將查詢文本也轉(zhuǎn)化為向量,通過計算查詢向量與文檔向量之間的相似度,找出與查詢最相關的文檔。
二、Elasticsearch兩種核心向量搜索方式
Elasticsearch為了滿足向量搜索的需求,在7.0版本新增了兩個字段類型dense_vector
和 sparse_vector
,分別是密集向量和稀疏向量,其中 sparse_vector
在7.6中被棄用,在8.0-8.10版本中是沒有該字段類型的。
2.1 近似kNN(HNSW算法)
在7.10版本中,沒有該方式,因此按照2025/3/1最新版本8.17版本介紹.
k-nearest neighbor(kNN)搜索通過相似性度量所測量等算法查詢向量最近的k個向量。
前置條件
要使用KNN搜索,首先必須要有dense_vector
或 sparse_vector
字段,這些字段的維度一定要和查詢的維度相同,所以一定要確定好維度。關于向量,可以在Elasticsearch中使用自然語言處理(NLP)模型創(chuàng)建這些向量,或者在Elasticsearch外部生成它們,比如使用阿里云的向量模型API獲取文本向量后賦值到搜索參數(shù)中。
- 首先需要顯式映射一個或多個
dense_vector
字段。
在8.0版中添加了對近似kNN搜索的支持。在此之前,dense_vector
字段不支持在映射中啟用索引。如果在8.0之前的版本創(chuàng)建了一個包含dense_vector
字段的索引,那么為了支持近似kNN搜索,必須使用設置index:true
(默認選項)的新字段映射重新索引數(shù)據(jù)。
PUT image-index
{
"mappings": {
"properties": {
"image-vector": {
"type": "dense_vector",
"dims": 3,
"index": true,
"similarity": "l2_norm"
},
"title-vector": {
"type": "dense_vector",
"dims": 5,
"similarity": "l2_norm"
},
"title": {
"type": "text"
},
"file-type": {
"type": "keyword"
}
}
}
}
similarity
的選擇有(詳情可看密集向量字段的參數(shù)):
- l2_norm
- dot_product
- cosine
- max_inner_product
- 創(chuàng)建數(shù)據(jù)
POST image-index/_bulk?refresh=true
{ "index": { "_id": "1" } }
{ "image-vector": [1, 5, -20], "title-vector": [12, 50, -10, 0, 1], "title": "moose family", "file-type": "jpg" }
{ "index": { "_id": "2" } }
{ "image-vector": [42, 8, -15], "title-vector": [25, 1, 4, -12, 2], "title": "alpine lake", "file-type": "png" }
{ "index": { "_id": "3" } }
{ "image-vector": [15, 11, 23], "title-vector": [1, 5, 25, 50, 20], "title": "full moon", "file-type": "jpg" }
...
- 使用knn選項或knn查詢
POST image-index/_search
{
"knn": {
"field": "image-vector",
"query_vector": [-5, 9, -12],
"k": 10,
"num_candidates": 100
},
"fields": [ "title", "file-type" ]
}
- 原理
為了收集結(jié)果,kNN搜索API在每個分片上找到num_candidates
數(shù)量的近似最近鄰候選。搜索計算這些候選向量與查詢向量的相似度,選擇k個最相似的結(jié)果。然后,搜索將來自 每個分片返回全局前k個最近鄰向量。
可以增加num_candidates
以獲得更準確的結(jié)果,但代價是搜索速度變慢。num_candidates
值比較較高的搜索 會從每個分片中考慮更多的候選者。會花費更多的時間,但也會提高搜索真正的k個最接近的向量的概率。
2.2精確暴力搜索
計算查詢向量與所有過濾后的文檔向量的余弦/歐式距離,保證100%召回率但計算成本高。使用精確KNN搜索的話,需要使用帶有vector函數(shù)的script_score
查詢。
- 首先需要顯式映射一個或多個
dense_vector
字段。如果不打算將該字段使用近似kNN搜索,可以將索引映射選項設置為false
。這可以顯著提高索引速度。
PUT product-index
{
"mappings": {
"properties": {
"product-vector": {
"type": "dense_vector",
"dims": 5,
"index": false
},
"price": {
"type": "long"
}
}
}
}
- 創(chuàng)建數(shù)據(jù)
POST product-index/_bulk?refresh=true
{ "index": { "_id": "1" } }
{ "product-vector": [230.0, 300.33, -34.8988, 15.555, -200.0], "price": 1599 }
{ "index": { "_id": "2" } }
{ "product-vector": [-0.5, 100.0, -13.0, 14.8, -156.0], "price": 799 }
{ "index": { "_id": "3" } }
{ "product-vector": [0.5, 111.3, -13.0, 14.8, -156.0], "price": 1099 }
...
- 使用
search
API運行包含向量函數(shù)
的script_score
查詢。
向量函數(shù)的類型有:cosineSimilarity
-計算余弦相似度dotProduct
-計算點積l1 norm
-計算L1距離Hamming
-計算Hamming距離l2 norm
-計算L2距離doc[<field>].vectorValue
-以浮點數(shù)組的形式返回向量的值doc[<field>].magnitude
-返回向量的大小
接下來以cosineSimilarity
為例,這是一個通過計算兩個向量的余弦大小來判斷向量相似度的函數(shù)。我們通過傳入的參數(shù)向量queryVector
,這個名稱是自定義的,但是需要與source
的腳本中params.queryVector
一致,下面方法將會查詢所有文檔
POST product-index/_search
{
"query": {
"script_score": {
"query" : {
"match_all" : {}
},
"script": {
"source": "cosineSimilarity(params.queryVector, 'product-vector') + 1.0",
"params": {
"queryVector": [-0.5, 90.0, -10, 14.8, -156.0]
}
}
}
}
}
但是使用match_all
查詢來全量匹配所有文檔會顯著增加搜索延遲,為了限制傳遞給vector
函數(shù)的匹配文檔的數(shù)量,可以在script_score.query
參數(shù)中指定一個過濾查詢。例如下面示例:
POST product-index/_search
{
"query": {
"script_score": {
"query" : {
"bool" : {
"filter" : {
"range" : {
"price" : {
"gte": 1000
}
}
}
}
},
"script": {
"source": "cosineSimilarity(params.queryVector, 'product-vector') + 1.0",
"params": {
"queryVector": [-0.5, 90.0, -10, 14.8, -156.0]
}
}
}
}
}
2.3 兩種搜索的對比
原理差異
- 精確暴力搜索:在進行搜索時,精確暴力搜索會遍歷數(shù)據(jù)集中的每一個向量,逐一計算查詢向量與它們之間的相似度(如歐幾里得距離、余弦相似度等),然后根據(jù)相似度的大小對所有向量進行排序,最后選取最相似的
k
個向量作為結(jié)果返回。這種方法簡單直接,能夠保證搜索結(jié)果的精確性,但在數(shù)據(jù)量較大時,計算量會非常龐大。 - 近似 k - NN 搜索:近似 k - NN 搜索則采用了一些優(yōu)化策略來減少計算量。例如,利用上述提到的 KD - Tree、HNSW 等索引結(jié)構(gòu),通過構(gòu)建數(shù)據(jù)的層次化表示或圖結(jié)構(gòu),在搜索時能夠快速定位到可能包含相似向量的區(qū)域,而無需遍歷整個數(shù)據(jù)集。以 HNSW 為例,它通過多層圖結(jié)構(gòu),在高層圖中快速跳過大量不相關的向量,僅在底層圖中對局部區(qū)域進行更精確的搜索,從而在保證一定搜索精度的前提下,大大提高了搜索效率。
性能表現(xiàn)
- 搜索時間:精確暴力搜索的搜索時間與數(shù)據(jù)集的大小成正比,數(shù)據(jù)量越大,搜索時間越長。在大規(guī)模數(shù)據(jù)集上,搜索可能需要很長時間才能完成。而近似 k - NN 搜索借助索引結(jié)構(gòu),能夠顯著減少搜索過程中需要計算相似度的向量數(shù)量,搜索時間相對較短,尤其在處理海量數(shù)據(jù)時,優(yōu)勢更為明顯。
- 內(nèi)存使用:精確暴力搜索不需要額外的內(nèi)存來存儲索引結(jié)構(gòu),僅需存儲數(shù)據(jù)集本身。然而,近似 k - NN 搜索需要構(gòu)建和維護索引結(jié)構(gòu),這會占用額外的內(nèi)存空間。例如,HNSW 索引在構(gòu)建過程中會生成多層圖結(jié)構(gòu),每個節(jié)點都需要存儲一定的連接信息,因此會消耗更多的內(nèi)存。
適用場景
- 精確暴力搜索:適用于數(shù)據(jù)集較小、對搜索結(jié)果的精確性要求極高且對搜索時間沒有嚴格限制的場景。例如,在一些數(shù)據(jù)量有限的科研項目中,研究人員可能更關注搜索結(jié)果的準確性,而不太在意搜索時間,此時精確暴力搜索可以滿足需求。
- 近似 k - NN 搜索:在實際應用中,尤其是面對大規(guī)模數(shù)據(jù)集時,近似 k - NN 搜索更為適用。例如,在圖像搜索引擎中,可能需要處理數(shù)百萬甚至數(shù)十億張圖像的向量數(shù)據(jù),如果使用精確暴力搜索,搜索效率將極低。而近似 k - NN 搜索能夠在較短的時間內(nèi)返回近似最優(yōu)的結(jié)果,滿足用戶對實時性的需求
- 用精確搜索的情況
- 公司只有200份合同,需要100%確保找到所有相關文件
- 醫(yī)療診斷系統(tǒng),不能容忍任何漏診可能
- 用近似搜索的情況
- 淘寶有10億商品圖片,用戶想快速找到相似款衣服
- 抖音推薦視頻,允許少量誤差但要求毫秒級響應
精確的強力kNN保證了準確的結(jié)果,但無法很好地擴展大型數(shù)據(jù)集。如果必須要使用精確暴力搜索,您可以通過使用查詢參數(shù)來限制傳遞給函數(shù)的匹配文檔的數(shù)量。如果過濾數(shù)據(jù)到一個小的文檔子集,就可以得到很好的搜索結(jié)果。
三、Elasticsearch支持的其他語義搜索方式
Elasticsearch使用自然語言處理(NLP)和向量搜索提供各種語義搜索功能。使用NLP模型使您能夠從文本中提取文本嵌入。嵌入是提供文本的數(shù)字表示的向量。具有相似含義的內(nèi)容片段具有相似的表示。
Elasticsearch中的semantic_text
字段也支持語義文本搜索。在使用時只需要將文檔的字段設置為semantic_text
, 不需要指定如何為數(shù)據(jù)生成嵌入,或者如何對其進行索引。推理端點自動確定要使用的嵌入生成,索引和查詢。

翻譯 警告:此功能處于測試階段,可能會發(fā)生變化。設計和代碼不如官方GA功能成熟,并且按原樣提供,沒有任何保證。Beta版功能不受正式GA功能的支持SLA的限制。使用時需要注意風險
使用
- 創(chuàng)建Index Mapping
默認是使用預配置的.elser-2-elasticsearch
端點,使用以下API請求設置semantic_text
:
PUT semantic-embeddings
{
"mappings": {
"properties": {
"content": {
"type": "semantic_text"
}
}
}
}
- 加載數(shù)據(jù)
將文檔添加到索引時,Elasticsearch 會自動使用指定的推理端點計算嵌入,并將其存儲在 semantic_text
字段中。您無需手動生成嵌入向量。以下是索引文檔的示例:
PUT semantic-embeddings/_doc/1
{
"content": "Elasticsearch 8.17 introduces semantic search capabilities."
}
- 語義搜索
GET semantic-embeddings/_search
{
"query": {
"semantic": {
"field": "content",
"query": "如何在跑步時避免肌肉酸痛?"
}
}
}
使用嵌入豐富數(shù)據(jù)集之后,可以使用語義搜索查詢數(shù)據(jù)。在語義查詢類型中提供semantic_text
字段名稱和查詢文本。用于為semantic_text
字段生成嵌入的推理端點將用于處理查詢文本。
通過上述步驟,您可以在 Elasticsearch 8.17 中利用 semantic_text
字段實現(xiàn)高效的語義搜索功能。該功能使搜索更加智能,能夠理解查詢的意圖和上下文,從而提供更相關的搜索結(jié)果。
向量搜索典型應用場景
- 基于自然語言處理(NLP)算法的相關性排序
- 產(chǎn)品推薦和推薦引擎
- 圖像或視頻的相似性搜索
轉(zhuǎn)自https://www.cnblogs.com/renyi537/p/18867146
該文章在 2025/5/12 9:53:53 編輯過