稀疏向量
稀疏向量使用向量嵌入來表示單字或詞組,其中大部分元素為零,只有一個非零元素表示特定單字的存在。稀疏向量模型(例如SPLADEv2)在域外知識搜尋、關鍵字感知和可解釋性上優於密集模型。它們在資訊檢索、自然語言處理和推薦系統中特別有用,在這些系統中,結合用於召回的稀疏向量和用於排序的大型模型可以顯著改善檢索結果。
在 Milvus 中,稀疏向量的使用遵循與密集向量相似的工作流程。它包括建立具有稀疏向量列的集合、插入資料、建立索引,以及進行相似性檢索和標量查詢。
在本教程中,您將學習如何
- 準備稀疏向量嵌入;
- 建立具有稀疏向量欄位的集合;
- 使用稀疏向量嵌入插入實體;
- 索引集合並在稀疏向量上執行 ANN 搜尋。
若要觀看稀疏向量的實作,請參考hello_sparse.py。
注意事項
目前,稀疏向量的支援是 2.4.0 中的測試版功能,計劃在 3.0.0 中普及。準備稀疏向量嵌入
要在 Milvus 中使用稀疏向量,請準備其中一種支援格式的向量嵌入:
稀疏矩陣:利用scipy.sparse類族來表示您的稀疏嵌入。這種方法對於處理大規模、高維數據非常有效。
辭典清單:將每個稀疏嵌入表示為字典,結構為
{dimension_index: value, ...},其中每個 key-value 對表示維度索引及其對應值。範例:
{2: 0.33, 98: 0.72, ...}Tuples 的迭代清單:類似於字典清單,但使用元組迭代,
[(dimension_index, value)],僅指定非零維度及其值。範例:
[(2, 0.33), (98, 0.72), ...]
以下範例準備稀疏嵌入,方法是為 10,000 個實體產生隨機稀疏矩陣,每個實體有 10,000 個維度,稀疏密度為 0.005。
# Prepare entities with sparse vector representation
import numpy as np
import random
rng = np.random.default_rng()
num_entities, dim = 10000, 10000
# Generate random sparse rows with an average of 25 non-zero elements per row
entities = [
{
"scalar_field": rng.random(),
# To represent a single sparse vector row, you can use:
# - Any of the scipy.sparse sparse matrices class family with shape[0] == 1
# - Dict[int, float]
# - Iterable[Tuple[int, float]]
"sparse_vector": {
d: rng.random() for d in random.sample(range(dim), random.randint(20, 30))
},
}
for _ in range(num_entities)
]
# print the first entity to check the representation
print(entities[0])
# Output:
# {
# 'scalar_field': 0.520821523849214,
# 'sparse_vector': {
# 5263: 0.2639375518635271,
# 3573: 0.34701499565746674,
# 9637: 0.30856525997853057,
# 4399: 0.19771651149001523,
# 6959: 0.31025067641541815,
# 1729: 0.8265339135915016,
# 1220: 0.15303302147479103,
# 7335: 0.9436728846033107,
# 6167: 0.19929870545596562,
# 5891: 0.8214617920371853,
# 2245: 0.7852255053773395,
# 2886: 0.8787982039149889,
# 8966: 0.9000606703940665,
# 4910: 0.3001170013981104,
# 17: 0.00875671667413136,
# 3279: 0.7003425473001098,
# 2622: 0.7571360018373428,
# 4962: 0.3901879090102064,
# 4698: 0.22589525720196246,
# 3290: 0.5510228492587324,
# 6185: 0.4508413201390492
# }
# }
注意
向量維度必須是 Pythonint 或numpy.integer 類型,而值必須是 Pythonfloat 或numpy.floating 類型。
要產生 embeddings,您也可以使用內建在 PyMilvus 函式庫中的model 套件,它提供了一系列的 embedding 函式。詳情請參閱嵌入。
使用稀疏向量場建立集合
要使用稀疏向量場建立集合,請將稀疏向量場的資料類型設定為DataType.SPARSE_FLOAT_VECTOR。與密集向量不同,稀疏向量不需要指定維度。
from pymilvus import MilvusClient, DataType
# Create a MilvusClient instance
client = MilvusClient(uri="http://localhost:19530")
# Create a collection with a sparse vector field
schema = client.create_schema(
auto_id=True,
enable_dynamic_fields=True,
)
schema.add_field(field_name="pk", datatype=DataType.VARCHAR, is_primary=True, max_length=100)
schema.add_field(field_name="scalar_field", datatype=DataType.DOUBLE)
# For sparse vector, no need to specify dimension
schema.add_field(field_name="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR) # set `datatype` to `SPARSE_FLOAT_VECTOR`
client.create_collection(collection_name="test_sparse_vector", schema=schema)
關於常見集合參數的詳細資訊,請參閱create_collection()。
插入具有稀疏向量嵌入的實體
要插入具有稀疏向量內嵌的實體,只要將實體清單傳給 insert()方法。
# Insert entities
client.insert(collection_name="test_sparse_vector", data=entities)
為集合建立索引
在執行相似性搜尋之前,請先為集合建立索引。有關索引類型和參數的詳細資訊,請參閱add_index()和create_index ()。
# Index the collection
# Prepare index params
index_params = client.prepare_index_params()
index_params.add_index(
field_name="sparse_vector",
index_name="sparse_inverted_index",
index_type="SPARSE_INVERTED_INDEX", # the type of index to be created. set to `SPARSE_INVERTED_INDEX` or `SPARSE_WAND`.
metric_type="IP", # the metric type to be used for the index. Currently, only `IP` (Inner Product) is supported.
params={"drop_ratio_build": 0.2}, # the ratio of small vector values to be dropped during indexing.
)
# Create index
client.create_index(collection_name="test_sparse_vector", index_params=index_params)
對於在稀疏向量上建立索引,請注意下列事項:
index_type:要建立的索引類型。稀疏向量的可能選項:SPARSE_INVERTED_INDEX:倒轉索引,將每個維度映射到其非零向量,方便在搜尋時直接存取相關資料。適用於稀疏但高維數據的資料集。SPARSE_WAND:利用 Weak-AND (WAND) 演算法快速繞過不可能的候選項目,並將評估重點放在具有較高排名潛力的候選項目上。將維度視為詞彙,將向量視為文件,加快大型稀疏資料集的搜尋速度。
metric_type:稀疏向量只支援IP(Inner Product) 距離公制。params.drop_ratio_build:專門用於稀疏向量的索引參數。它控制在索引過程中排除小向量值的比例。此參數可透過在建立索引時忽略小值來微調效率與精確度之間的權衡。舉例來說,如果drop_ratio_build = 0.3,在索引建構過程中,所有稀疏向量的所有值都會被收集和排序。這些值中最小的 30% 不會包含在索引中,因此可以減少搜尋時的計算工作量。
如需詳細資訊,請參閱「記憶體內索引」。
執行 ANN 搜尋
在文集建立索引並載入記憶體後,使用 search()方法根據查詢擷取相關文件。
# Load the collection into memory
client.load_collection(collection_name="test_sparse_vector")
# Perform ANN search on sparse vectors
# for demo purpose we search for the last inserted vector
query_vector = entities[-1]["sparse_vector"]
search_params = {
"metric_type": "IP",
"params": {"drop_ratio_search": 0.2}, # the ratio of small vector values to be dropped during search.
}
search_res = client.search(
collection_name="test_sparse_vector",
data=[query_vector],
limit=3,
output_fields=["pk", "scalar_field"],
search_params=search_params,
)
for hits in search_res:
for hit in hits:
print(f"hit: {hit}")
# Output:
# hit: {'id': '448458373272710786', 'distance': 7.220192909240723, 'entity': {'pk': '448458373272710786', 'scalar_field': 0.46767865218233806}}
# hit: {'id': '448458373272708317', 'distance': 1.2287548780441284, 'entity': {'pk': '448458373272708317', 'scalar_field': 0.7315987515699472}}
# hit: {'id': '448458373272702005', 'distance': 0.9848432540893555, 'entity': {'pk': '448458373272702005', 'scalar_field': 0.9871869181562156}}
配置搜尋參數時,請注意下列事項:
params.drop_ratio_search:專門用於稀疏向量的搜尋參數。此選項允許透過指定查詢向量中最小值的忽略比例來微調搜尋過程。它有助於平衡搜尋精確度與效能。drop_ratio_search設定的值越小,這些小值對最終得分的貢獻就越少。透過忽略一些小值,可以在對精確度影響最小的情況下提高搜尋效能。
執行標量查詢
除了 ANN 搜尋外,Milvus 也支援稀疏向量的標量查詢。這些查詢允許您根據與稀疏向量相關的標量值來檢索文件。有關參數的詳細資訊,請參閱query()。
過濾scalar_field大於 3 的實體:
# Perform a query by specifying filter expr
filter_query_res = client.query(
collection_name="test_sparse_vector",
filter="scalar_field > 0.999",
)
print(filter_query_res[:2])
# Output:
# [{'pk': '448458373272701862', 'scalar_field': 0.9994093623822689, 'sparse_vector': {173: 0.35266244411468506, 400: 0.49995484948158264, 480: 0.8757831454277039, 661: 0.9931875467300415, 1040: 0.0965644046664238, 1728: 0.7478245496749878, 2365: 0.4351981580257416, 2923: 0.5505295395851135, 3181: 0.7396837472915649, 3848: 0.4428485333919525, 4701: 0.39119353890419006, 5199: 0.790219783782959, 5798: 0.9623121619224548, 6213: 0.453134149312973, 6341: 0.745091438293457, 6775: 0.27766478061676025, 6875: 0.017947908490896225, 8093: 0.11834774166345596, 8617: 0.2289179265499115, 8991: 0.36600416898727417, 9346: 0.5502803921699524}}, {'pk': '448458373272702421', 'scalar_field': 0.9990218525410719, 'sparse_vector': {448: 0.587817907333374, 1866: 0.0994109958410263, 2438: 0.8672442436218262, 2533: 0.8063794374465942, 2595: 0.02122959867119789, 2828: 0.33827054500579834, 2871: 0.1984412521123886, 2938: 0.09674275666475296, 3154: 0.21552987396717072, 3662: 0.5236313343048096, 3711: 0.6463911533355713, 4029: 0.4041993021965027, 7143: 0.7370485663414001, 7589: 0.37588241696357727, 7776: 0.436136394739151, 7962: 0.06377989053726196, 8385: 0.5808192491531372, 8592: 0.8865005970001221, 8648: 0.05727503448724747, 9071: 0.9450633525848389, 9161: 0.146037295460701, 9358: 0.1903032660484314, 9679: 0.3146636486053467, 9974: 0.8561339378356934, 9991: 0.15841573476791382}}]
依據主鍵過濾實體:
# primary keys of entities that satisfy the filter
pks = [ret["pk"] for ret in filter_query_res]
# Perform a query by primary key
pk_query_res = client.query(
collection_name="test_sparse_vector", filter=f"pk == '{pks[0]}'"
)
print(pk_query_res)
# Output:
# [{'scalar_field': 0.9994093623822689, 'sparse_vector': {173: 0.35266244411468506, 400: 0.49995484948158264, 480: 0.8757831454277039, 661: 0.9931875467300415, 1040: 0.0965644046664238, 1728: 0.7478245496749878, 2365: 0.4351981580257416, 2923: 0.5505295395851135, 3181: 0.7396837472915649, 3848: 0.4428485333919525, 4701: 0.39119353890419006, 5199: 0.790219783782959, 5798: 0.9623121619224548, 6213: 0.453134149312973, 6341: 0.745091438293457, 6775: 0.27766478061676025, 6875: 0.017947908490896225, 8093: 0.11834774166345596, 8617: 0.2289179265499115, 8991: 0.36600416898727417, 9346: 0.5502803921699524}, 'pk': '448458373272701862'}]
限制
在 Milvus 中使用稀疏向量時,請考慮下列限制:
常見問題
稀疏向量支援什麼距離指標?
由於稀疏向量的高維度,使得 L2 距離和余弦距離不切實際,因此稀疏向量只支援 Inner Product (IP) 距離公制。
您能解釋 SPARSE_INVERTED_INDEX 和 SPARSE_WAND 之間的差異,以及該如何選擇嗎?
SPARSE_INVERTED_INDEX是一種傳統的倒轉索引,而SPARSE_WAND則使用Weak-AND演算法來減少搜尋過程中完整 IP 距離評估的次數。SPARSE_WAND通常較快,但其效能會隨著向量密度的增加而下降。要在兩者之間做出選擇,請根據您的特定資料集和使用個案進行實驗和基準測試。
我應該如何選擇 drop_ratio_build 和 drop_ratio_search 參數?
drop_ratio_build和drop_ratio_search的選擇取決於您資料的特性,以及您對搜尋延遲/吞吐量和精確度的要求。
稀疏嵌入支援哪些資料類型?
維度部分必須是無符號 32 位元整數,而值部分可以是非負 32 位元浮點數。
稀疏嵌入的維度可以是 uint32 空間內的任何離散值嗎?
可以,但有一個例外。稀疏嵌入的維度可以是
[0, maximum of uint32)範圍內的任何值。這表示您不能使用 uint32 的最大值。對成長中的區段進行搜尋時,是透過索引還是暴力搜尋?
對成長中區段的搜尋是透過與封存區段索引相同類型的索引進行。對於索引建立前的新成長區段,會使用暴力搜尋。
是否可以在單一集合中同時擁有稀疏向量和密集向量?
可以,透過多重向量類型支援,您可以建立同時具有稀疏和密集向量列的集合,並對它們執行混合搜尋。
插入或搜尋稀疏內嵌向量有什麼要求?
稀疏內嵌必須至少有一個非零值,向量索引必須是非負數。