向量檢索外掛程式是Elasticsearch團隊自主開發的向量檢索引擎外掛程式,基於阿里巴巴達摩院proxima向量檢索庫實現,能夠協助您快速實現Image Search、視頻指紋採樣、Face Service、語音辨識和商品推薦等向量檢索情境的需求。本文介紹如何使用向量檢索外掛程式。
背景資訊
應用情境
Elasticsearch向量檢索引擎已成熟應用於拍立淘、阿里雲Image Search服務、趣頭條視頻指紋採樣、猜您喜歡、搜尋個人化、CrossMedia搜尋等大規模生產應用情境。
原理
Elasticsearch向量檢索功能基於Elasticsearch外掛程式擴充機制實現,能夠完全相容原生Elasticsearch版本,您無需額外的學習成本即可使用向量檢索引擎。向量索引除了支援即時增量寫入、近即時(Near Real Time,簡稱NRT)搜尋查詢,還具備了所有原生Elasticsearch的分布式能力,同時支援多副本、錯誤恢複等功能。
說明Elasticsearch向量檢索外掛程式不支援通過OSS快照、DataWorks等方式進行資料移轉,建議使用Logstash方式。
演算法說明
在演算法上,目前向量檢索引擎已經支援了hnsw演算法以及linear演算法,適用於單機資料量小(全記憶體)的業務情境。兩種演算法效能對比如下。
表 1. hnsw演算法和linear演算法效能對比 表格中為Elasticsearch 6.7.0版本環境實測資料,測試環境配置如下:
機器配置:資料節點16核64 GB*2 + 100 GB SSD雲端硬碟。
資料集:sift128維float向量。
資料總量:2千萬條。
索引配置:全部採用預設參數。
效能指標
hnsw
linear
top10召回率
98.6%
100%
top50召回率
97.9%
100%
top100召回率
97.4%
100%
延遲(p99)
0.093s
0.934s
延遲(p90)
0.018s
0.305s
說明表中的p表示百分比,例如延遲(p99)表示99%的查詢能在多少秒返回。
前提條件
安裝向量檢索外掛程式(英文名稱為aliyun-knn)。外掛程式的預設安裝情況與Elasticsearch的執行個體版本和核心版本相關,具體說明如下。
Elasticsearch版本
核心版本
外掛程式安裝情況說明
6.7.0
1.2.0以下
需要在外掛程式配置頁面手動安裝向量檢索外掛程式,安裝方法請參見安裝或卸載系統預設外掛程式。
不支援使用script檢索和索引預熱等功能配置。如有需求,建議使用有AliES核心版本的執行個體,詳細資料請參見核心版本發布記錄。
建立向量索引時僅支援預設的
SquaredEuclidean
,不支援通過distance_method
參數指定具體的距離度量函數。
6.8
無
7.4
無
7.7
無
6.7.0
1.2.0及以上
向量檢索外掛程式預設整合在apack外掛程式中(預設已安裝),安裝或卸載向量檢索外掛程式都需對apack外掛程式進行操作。詳細資料請參見使用apack外掛程式的物理複製功能。
支援使用script檢索、索引預熱、擴充函數等擴充功能,但需要將核心版本升級至1.3.0及以上,具體操作請參見升級版本。
如果建立向量索引時遇到解析mapping報錯,請升級核心版本至1.3.0及以上後再重試。
7.10.0
1.4.0及以上
向量檢索外掛程式預設整合在apack外掛程式中(預設已安裝),安裝或卸載向量檢索外掛程式都需對apack外掛程式進行操作。詳細資料請參見使用apack外掛程式的物理複製功能。
核心小版本大於等於1.4.0時,apack外掛程式版本已為最新版本,無需更新。使用時,可通過GET _cat/plugins?v命令擷取外掛程式版本。
其他版本
無
不支援向量檢索功能。
說明核心版本不等同於apack外掛程式版本,使用時可通過GET _cat/plugins?v命令擷取apack外掛程式版本。
完成索引規劃。
演算法
適用情境
是否全記憶體
其他
hnsw
單機資料量小。
對延遲要求高。
對召回率要求高。
是
hnsw是基於“鄰居的鄰居可能是鄰居”的核心思想,它在距離衡量演算法上有一定的限制,需要滿足三角形不等式,即三角形的兩邊之和大於第三邊。例如,對於內積向量空間,由於不滿足三角形不等式,需要轉化為歐式空間或球面空間,才能使用hnsw檢索方法。
建議寫入結束後,在業務低峰期定期forceMerge,有助於降低查詢延遲。
linear
暴力檢索。
召回率100%。
延遲與資料量成正比。
效果對照。
是
無 。
完成叢集規劃。
規劃項
說明
資料節點規格(必須)
16核64 GB及以上。
說明使用aliyun-knn外掛程式構建索引時,需要消耗大量的計算資源。由於小規格叢集極易達到瓶頸,嚴重時會影響叢集穩定性,因此建議您使用16核64 GB及以上規格的叢集。
節點類型
叢集中需要有獨立的專有主節點。
叢集堆外記憶體
大於叢集總向量資料*2。
樣本: 索引中只有一個960維float欄位,索引總文檔數是400,float類型資料佔4位元組記憶體,此索引向量資料記憶體佔用=960*400*4=1.5MB,記憶體空間中堆外記憶體>1.5*2=3MB。
說明如果存在forcemerge操作,考慮到新老資料會出現同時佔用記憶體的情況,請在上面公式的基礎上再乘以2。
64GB及以上記憶體規格,堆外大小≈總記憶體-32G。
寫入限流
向量索引的構建屬於CPU密集型任務,建議業務控制寫入流量不要太高。以16核64 GB的資料節點為例,建議單節點寫入峰值控制在5000tps以內。
同時,由於在向量索引的查詢過程中,會把索引檔案全部載入到系統記憶體,因此建議在業務查詢期間,不要同時進行大批量的寫入,避免因節點記憶體緊張導致分區重啟的情況。
說明以上均為預估,以業務實際的應用情況為準,建議提前進行壓測,並提供充足的記憶體空間。
使用限制
開啟共用彈性儲存功能的6.7版本執行個體不支援向量檢索外掛程式。
在安裝向量檢索外掛程式前,需要確保Elasticsearch執行個體的資料節點規格為16核64 GB及以上。如果不滿足,請先將資料節點規格升級至16核64 GB及以上,詳細資料請參見升配叢集。
使用向量檢索外掛程式的情境中,部分AliES核心的增強功能將不能使用。以物理複製功能為例,如果您使用了物理複製功能,使用向量檢索外掛程式前,需要先關閉該功能,具體操作請參見使用apack外掛程式的物理複製功能。
Elasticsearch向量檢索外掛程式不支援通過OSS快照、DataWorks等方式進行資料移轉,建議使用Logstash方式。
建立向量索引
- 登入目標Elasticsearch執行個體的Kibana控制台,根據頁面提示進入Kibana首頁。登入Kibana控制台的具體操作,請參見登入Kibana控制台。說明 本文以Elasticsearch 6.7.0版本為例,其他版本操作可能略有差別,請以實際介面為準。
- 在左側導覽列,單擊Dev Tools。
在Console中執行如下命令,建立向量索引結構。
PUT test { "settings": { "index.codec": "proxima", "index.vector.algorithm": "hnsw" }, "mappings": { "_doc": { "properties": { "feature": { "type": "proxima_vector", "dim": 2, "vector_type": "float", "distance_method": "SquaredEuclidean" } } } } }
說明在向量索引結構的基礎上,支援添加Elasticsearch支援的其他欄位類型。
如果建立向量索引時遇到解析mapping報錯:
"type": "mapper_parsing_exception", "reason": "Mapping definition for [feature] has unsupported parameters: [distance_method : SquaredEuclidean]
,請及時升級核心版本至最新後重試。
類型
參數
預設值
含義
setting
index.codec
proxima
是否需要底層構建proxima knn索引,可選值如下:
proxima(推薦):底層構建proxima knn索引,支援向量檢索。
null:建立索引的時候,底層不構建proxima knn索引,只構建正排索引。此時,proxima_vector類型欄位僅支援script檢索,不支援hnsw或linear檢索。
說明當資料量較大,且對查詢延遲要求不高的情境,可以把該項配置去掉或設定為null,此時可使用script檢索方式進行knn向量查詢。支援script檢索方式的版本:執行個體版本為6.7.0且apack外掛程式版本≥1.2.1或執行個體版本為7.10.0且apack外掛程式版本≥1.4.0。
index.vector.algorithm
hnsw
指定向量檢索演算法,可選值如下:
hnsw:hnsw演算法。
linear:linear演算法。
index.vector.general.builder.offline_mode
false
指定knn索引構建是否使用離線最佳化模式,可選值如下:
false: 不使用離線最佳化模式。
true: 使用離線最佳化模式,寫入構建的segment片段將會大幅度減少,提升整體寫入吞吐能力。
說明開啟離線最佳化模式需要保證:執行個體版本為6.7.0且apack外掛程式版本≥1.2.1或執行個體版本為7.10.0且apack外掛程式版本≥1.4.0;開啟離線模式構建的索引,不支援使用script檢索資料。
當一次性大量匯入全量資料時,建議開啟離線最佳化模式。
mapping
type
proxima_vector
向量欄位類型。例如:將feature欄位指定為proxima_vector,說明feature為向量欄位。
dim
2
向量維度,必填,僅支援1~2048維。
vector_type
float
向量資料類型,可選值如下:
float:浮點型。
short:短整型。
binary:二進位類型。
其中binary類型為二進位類型,向量資料需要用無符號的32位十進位(uint32)數組表示,且dim必須為32的整數倍。
例如:業務資料為64位二進位1000100100100101111000001001111101000011010010011010011010000100,那麼寫入vector為[-1994006369, 1128900228]。
說明執行個體版本為6.7.0且apack外掛程式版本≥1.2.1或執行個體版本為7.10.0且apack外掛程式版本≥1.4.0,向量資料類型支援以上三種類型。否則,向量資料類型僅支援float類型。
distance_method
SquaredEuclidean(未開方)
距離度量函數,可選值如下:
SquaredEuclidean:歐氏距離(未開方)。
InnerProduct:內積。
Cosine:餘弦相似性。
Hamming:漢明距離(僅支援binary類型)。
說明執行個體版本為6.7.0且apack外掛程式版本≥1.2.1或執行個體版本為7.10.0且apack外掛程式版本≥1.4.0時,支援以上四種距離度量函數。其他版本(6.8,7.4和7.7)執行個體建立向量時僅支援使用預設的SquaredEuclidean,不支援通過
distance_method
參數指定其他距離度量函數。距離度量函數詳細說明請參見距離度量函數。
因Hamming函數實現比較特殊,索引使用hnsw或linear時,不支援標準的knn查詢方式,僅支援script方式,且查詢語句相容script_score。在不同的情境下,您需要根據具體業務測試查詢語句的可用性。
執行如下命令,添加文檔。
POST test/_doc { "feature": [1.0, 2.0] }
說明除binary類型外,其他類型數組長度必須與dim保持一致,而binary類型的向量資料需要轉換成無符號的32位十進位(uint32)數組表示,且dim必須為32的整數倍。
向量查詢
標準檢索
執行如下命令,對文檔進行標準檢索。
GET test/_search { "query": { "hnsw": { "feature": { "vector": [1.5, 2.5], "size": 10 } } } }
常用參數說明如下。
參數
說明
hnsw
向量查詢演算法,與建立索引時指定的algorithm一致。
vector
查詢的向量資料,數組長度必須與建立索引時,mapping指定的dim保持一致。
size
指定召回的文檔數。
說明向量檢索中的size參數與Elasticsearch內建的size參數存在區別,前者控制向量檢索外掛程式knn召回的文檔數,後者控制整個查詢的召迴文檔數。使用時,系統會先通過向量檢索中的size參數召回topN的文檔,然後再由Elasticsearch內建的size參數召回整個查詢的文檔,最終返回結果。
建議向量檢索中的size參數值和Elasticsearch內建的size參數值(預設值為10)保持一致。
說明knn向量檢索還提供進階查詢參數,詳細資料請參見高階參數。
script檢索
script向量檢索僅支援在script_score方式下使用。例如使用script_score對查詢返回的每個文檔進行打分,該分數等於
1/(1+l2Squared(params.queryVector, doc['feature']))
。樣本命令如下。GET test/_search { "query": { "match_all": {} }, "rescore": { "query": { "rescore_query": { "function_score": { "functions": [{ "script_score": { "script": { "source": "1/(1+l2Squared(params.queryVector, doc['feature'])) ", "params": { "queryVector": [2.0, 2.0] } } } }] } } } } }
knn向量script檢索不支援X-Pack提供的函數,僅支援以下函數:
函數
說明
l2Squared(float[] queryVector, DocValues docValues)
歐式演算法函數。
hamming(float[] queryVector, DocValues docValues)
漢明距離函數。
cosineSimilarity(float[] queryVector, DocValues docValues)
cosine(float[] queryVector, DocValues docValues)
餘弦相似性函數。
說明Elasticsearch 6.7版本請使用
cosineSimilarity(float[] queryVector, DocValues docValues)
函數,7.10版本請使用cosine(float[] queryVector, DocValues docValues)
函數。說明使用script檢索功能,需要確保:執行個體版本為6.7.0且apack外掛程式版本≥1.2.1版本或執行個體版本為7.10.0版本且apack外掛程式版本≥1.4.0。您可以通過GET /_cat/plugins?v命令擷取apack外掛程式版本,如果apack外掛程式版本不滿足要求,可提交工單,由阿里雲工程師幫您升級。
函數參數:
float[] queryVector:用於表示查詢向量,可傳入形參和實參。
DocValues docValues:用於指定文檔向量。
script向量檢索不支援處於離線模式(
index.vector.builder.offlineMode = true
)下構建的索引。
索引預熱(降低延遲)
knn索引由於需要進行全記憶體檢索,所以在索引冷載入時會出現查詢延遲較高的情況。因此knn外掛程式提供了索引預熱功能,可以在knn索引提供檢索服務之前,提前對knn索引進行預熱,載入到本地記憶體,從而大大降低冷啟動時的查詢延遲。
所有向量索引實現預熱。
POST _vector/warmup
特定向量索引實現預熱能力。
POST _vector/{indexName}/warmup
說明使用索引預熱功能,需要確保:執行個體版本為6.7.0且apack外掛程式版本≥1.2.1或執行個體版本為7.10.0且apack外掛程式版本≥1.4.0。您可以通過GET _cat/plugins?v命令擷取apack外掛程式版本,如果apack外掛程式版本不滿足要求,可提交工單,由阿里雲工程師幫您升級。
如果叢集中向量索引比較多且資料量比較大,業務只需要對特定索引實現向量檢索,建議只對特定向量使用預熱能力,提升記憶體檢索能力。
向量打分
向量檢索擁有統一的打分公式,而打分機制主要依賴距離度量函數。不同的距離函數直接性的影響檢索排序。
打分公式:
分數= 1/(向量距離函數+1)
向量打分機制預設使用未開方的歐式距離。
在實際應用中,您可以通過查詢分數反推向量間的距離,最佳化向量資料,提升打分。
距離度量函數
不同的距離度量函數對應不同的打分機制,下面是aliyun-knn外掛程式支援的度量演算法:
距離函數 | 定義 | 打分公式 | 應用情境 | 樣本 |
SquaredEuclidean歐氏距離(未開方) | 歐幾裡得度量(euclidean metric)也稱歐氏距離,是一個通常採用的距離定義,指在m維空間中兩個點之間的真實距離,或者向量的自然長度(即該點到原點的距離)。在二維和三維空間中的歐氏距離就是兩點之間的實際距離。 | 以兩個n維向量A = [A1, A2,…, An]和B= [B1, B2,…, Bn]為例:
說明 向量打分預設使用未開方的歐式距離計算。 | 歐氏距離能夠體現個體數值特徵的絕對差異,所以更多的用於需要從維度數值大小中體現差異的分析,例如使用使用者行為指標分析使用者價值的相似性或差異。 | 例如:兩個二維向量[0,0]和[1,2],未開方歐式距離= (1-0)² + (2-0)² = 5。 |
Cosine餘弦相似性 | 餘弦相似性,又稱為餘弦相似性,是通過計算兩個向量的夾角的餘弦值來評估它們之間的相似性。 | 以兩個n維向量A = [A1, A2,…, An]和B= [B1, B2,…, Bn]為例:
| 餘弦相似性更多的是從方向上區分差異,而對絕對的數值不敏感,更多的用於使用者對內容評分來區分興趣的相似性和差異,同時修正了使用者間可能存在的度量標準不統一的問題(因為餘弦相似性對絕對數值不敏感)。 | 例如兩個二維向量[1,1]和[1,0],向量餘弦相似性=0.707。 |
InnerProduct內積 | 以兩個n維向量A = [A1, A2,…, An]和B= [B1, B2,…, Bn]為例:
| 內積同時考慮了兩個向量的夾角及絕對長度。當向量歸一化後,內積與餘弦相似性計算公式等價。 | 例如兩個二維向量[1,1]和[1,5],向量內積=1+5=6。 | |
Hamming漢明距離(僅支援binary類型) | 在資訊理論中,Hamming Distance表示兩個等長字串在對應位置上不同字元的數量,我們以d(x, y)表示字串x和y之間的漢明距離。從另外一個方面看,漢明距離度量了通過替換字元的方式將字串x變成y所需要的最小的替換次數。 | 以兩個n位的二進位編碼x和y為例:
| 典型的用例包括資料通過電腦網路傳輸時的錯誤校正或檢測。它可以用來確定二進位字中不同字元的數目,作為估計誤差的一種方法。 | 例如:1011101與1001001之間的漢明距離是2。 說明 在aliyun-knn外掛程式使用中,binary類型的向量資料需要轉換成無符號的32位十進位(uint32)數組表示,且 |
使用多種距離度量函數,需要確保:執行個體版本為6.7.0且apack外掛程式版本≥1.2.1版本或執行個體版本為7.10.0版本且apack外掛程式版本≥1.4.0。您可以通過GET _cat/plugins?v命令擷取apack外掛程式版本,如果apack外掛程式版本不滿足要求,則knn距離度量函數僅支援SquaredEuclidean歐氏距離(未開方)。如果您需要使用其他距離函數,可提交工單,由阿里雲工程師幫您升級外掛程式版本。
您可以在索引mapping中,通過distance_method參數指定不同的距離度量函數。
熔斷參數
參數 | 描述 | 預設值 |
indices.breaker.vector.native.indexing.limit | 如果堆外記憶體使用量超過該值,寫入操作會被熔斷;等待後台構建完成釋放記憶體後,寫入恢複正常。出現熔斷錯誤表示當前系統記憶體消耗已經過高,建議業務上降低寫入流量。 | 70% |
indices.breaker.vector.native.total.limit | 向量索引後台構建最多使用的堆外記憶體比例。如果實際使用的堆外記憶體超過了這個比例,可能會發生shard重啟的情況。 | 80% |
向量熔斷參數配置屬於叢集配置,可通過GET _cluster/settings命令查看,建議您不要調整熔斷值。
高階參數
參數 | 描述 | 預設值 |
index.vector.hnsw.builder.max_scan_num | 用於控制構圖過程中的近鄰考察範圍,保證最壞情況的效能。 | 100000 |
index.vector.hnsw.builder.neighbor_cnt | hnsw 0層圖每個節點的鄰居數。建議配置為100。該值越大,離線索引儲存消耗越大,圖構建品質越高。 | 100 |
index.vector.hnsw.builder.upper_neighbor_cnt | hnsw上層圖(除0層之外)中每個節點的鄰居上限數。一般建議配置為neighbor_cnt的一半,最大不能超過255。 | 50 |
index.vector.hnsw.builder.efconstruction | 控製圖構建過程中近鄰掃描地區大小,該值越大,離線構圖品質越好,索引構建越慢。建議初始值設定為400。 | 400 |
index.vector.hnsw.builder.max_level | hnsw總層數,包含0層圖和上層圖。例如總共1000萬文檔,scaling_factor為30,那麼層數可以以max level=30為底,取1000萬的對數向上取整,計算得5。 該值對效果影響不大,一般建議初始配置為6。 | 6 |
index.vector.hnsw.builder.scaling_factor | 下層圖是上層圖資料的多少倍,呈指數關係。通常設定在10~100之間。scaling_factor越大,實際產生的圖層數越低。建議初始配置為50。 | 50 |
以上參數需在索引setting中設定使用,並且僅支援在hnsw演算法模型中使用。
參數 | 描述 | 預設值 |
ef | 用於控制線上檢索時,考察的子圖範圍大小。該值越大,召回越高,效能越差。建議取值[100,1000]。 | 100 |
查詢樣本如下。
GET test/_search
{
"query": {
"hnsw": {
"feature": {
"vector": [1.5, 2.5],
"size": 10,
"ef": 100
}
}
}
}
常見問題
Q:如何評估查詢的召回率?
A:可以同時建立兩個索引,一個為hnsw演算法,一個為linear演算法,其他配置相同。用戶端向兩個索引同時推送相同的向量資料,重新整理後,用同樣的查詢向量對比linear索引和hnsw索引召回的文檔ID,交集的文檔ID個數/召回總數,即為待測向量的召回率。
說明交集的文檔ID個數是指兩個索引召回的文檔ID的交集。
Q:叢集寫入期間報錯
circuitBreakingException
,如何處理?A:這個錯誤表明此時系統的堆外記憶體使用量率超過了indices.breaker.vector.native.indexing.limit指定的比例(預設為70%),觸發了寫入的熔斷操作,一般等待背景索引構建任務完成後會自動釋放。建議用戶端寫入時添加錯誤重試機制。
Q:為什麼寫入任務已經停止了,CPU依然在工作?
A:向量索引的構建發生在refresh或flush期間,雖然寫入流量已經停止,但背景向量索引構建任務可能仍然在繼續。等待最後一輪refresh結束後,計算資源就會被釋放。
Q:使用aliyun-knn外掛程式查詢時報錯
class_cast_exception: class org.apache.lucene.index.SoftDeletesDirectoryReaderWrapper$SoftDeletesFilterCodecReader cannot be cast to class org.apache.lucene.index.SegmentReader (org.apache.lucene.index.SoftDeletesDirectoryReaderWrapper$SoftDeletesFilterCodecReader and org.apache.lucene.index.SegmentReader are in unnamed module of loader 'app')
,如何處理?A:關閉索引的物理複製功能,具體操作請參見使用apack外掛程式的物理複製功能。
Q:使用aliyun-knn外掛程式進行向量檢索,速度過慢或出現記憶體熔斷,怎麼辦?
A:aliyun-knn外掛程式進行向量檢索時,使用的是全記憶體型向量,非常消耗記憶體。當索引資料量較大時,由於需要將資料載入到記憶體,因此會出現速度慢或記憶體熔斷的情況。建議索引資料量是機器記憶體的一半,在資料量無法變更的情況下,如果記憶體太小,建議您升配叢集。
Q:關於aliyun-knn外掛程式是否有提供最佳實務文檔供參考?
A:您可以參見阿里雲開發人員社區提供的aliyun-knn外掛程式業務情境和aliyun-knn最佳實務。
Q:knn情境使用
must_not exists
無法過濾出feature欄位為空白的文檔,如何編寫語句過濾出feature欄位為空白的資料?A:knn資料存放區比較特殊,可能會存在個別DSL查詢不相容的情況,您可以使用以下指令碼進行過濾。
GET jx-similar-product-v1/_search { "query": { "bool": { "must": { "script": { "script": { "source": "doc['feature'].empty", "lang": "painless" } } } } } }