🚀 免費嘗試 Zilliz Cloud,完全托管的 Milvus,體驗速度提升 10 倍!立即嘗試

milvus-logo
LFAI
主頁
  • 使用者指南
  • Home
  • Docs
  • 使用者指南

  • 模式與資料欄位

  • 稀疏向量

稀疏向量

稀疏向量是資訊檢索和自然語言處理中重要的資料表示方法。雖然密集向量因其優異的語意理解能力而廣受歡迎,但在需要精確匹配關鍵字或詞組的應用程式時,稀疏向量通常能提供更精確的結果。

概述

稀疏向量是高維向量的一種特殊表示形式,其中大部分元素為零,只有少數維度有非零值。這個特性使得稀疏向量在處理大規模、高維但稀疏的資料時特別有效。常見的應用包括

  • 文字分析:將文件表示為字袋向量,其中每個維度對應一個字,只有在文件中出現的字才有非零值。

  • 推薦系統:使用者與物品互動矩陣,其中每個維度代表使用者對特定物品的評價,大多數使用者只與少數物品互動。

  • 影像處理:局部特徵表示法,只針對影像中的關鍵點,產生高維度的稀疏向量。

如下圖所示,密集向量通常表示為連續陣列,其中每個位置都有一個值 (例如[0.3, 0.8, 0.2, 0.3, 0.1])。相反地,稀疏向量只儲存非零元素及其索引,通常以鍵值對來表示 (例如:[{2: 0.2}, ..., {9997: 0.5}, {9999: 0.7}])。這種表示方式可大幅減少儲存空間並提昇計算效率,尤其是在處理極高維數據 (例如 10,000 維) 時。

Spare vector representation 稀疏向量表示法

稀疏向量可以使用各種方法產生,例如文字處理中的TF-IDFTermFrequency-Inverse Document Frequency)和BM25。此外,Milvus 也提供方便的方法來協助產生和處理稀疏向量。詳情請參閱嵌入

對於文字資料,Milvus 也提供全文檢索功能,讓您可以直接在原始文字資料上執行向量檢索,而無需使用外部嵌入模型來產生稀疏向量。如需詳細資訊,請參閱全文檢索。

向量化之後,資料可以儲存在 Milvus 中進行管理和向量檢索。下圖說明基本流程。

Use sparse vector in Milvus 在 Milvus 中使用稀疏向量

除了稀疏向量,Milvus 也支援密集向量和二進位向量。密集向量是捕捉深層語意關係的理想選擇,而二進位向量則在快速相似性比較和內容重複刪除等情況下表現優異。如需詳細資訊,請參閱密集向量二進位向量

在 Milvus 中使用稀疏向量

Milvus 支援以下列任何一種格式來表示稀疏向量。

  • 稀疏矩陣 (使用scipy.sparse class)

    from scipy.sparse import csr_matrix
    
    # Create a sparse matrix
    row = [0, 0, 1, 2, 2, 2]
    col = [0, 2, 2, 0, 1, 2]
    data = [1, 2, 3, 4, 5, 6]
    sparse_matrix = csr_matrix((data, (row, col)), shape=(3, 3))
    
    # Represent sparse vector using the sparse matrix
    sparse_vector = sparse_matrix.getrow(0)
    
    
  • 字典清單 (格式為{dimension_index: value, ...})

    # Represent sparse vector using a dictionary
    sparse_vector = [{1: 0.5, 100: 0.3, 500: 0.8, 1024: 0.2, 5000: 0.6}]
    
    
    SortedMap<Long, Float> sparseVector = new TreeMap<>();
    sparseVector.put(1L, 0.5f);
    sparseVector.put(100L, 0.3f);
    sparseVector.put(500L, 0.8f);
    sparseVector.put(1024L, 0.2f);
    sparseVector.put(5000L, 0.6f);
    
    
  • 元組迭代器清單 (格式如[(dimension_index, value)])

    # Represent sparse vector using a list of tuples
    sparse_vector = [[(1, 0.5), (100, 0.3), (500, 0.8), (1024, 0.2), (5000, 0.6)]]
    
    

新增向量領域

要在 Milvus 中使用稀疏向量,請在建立集合時定義一個欄位來儲存稀疏向量。這個過程包括

  1. 設定datatype 為支援的稀疏向量資料類型,SPARSE_FLOAT_VECTOR

  2. 不需要指定維度。

from pymilvus import MilvusClient, DataType

client = MilvusClient(uri="http://localhost:19530")

client.drop_collection(collection_name="my_sparse_collection")

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="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR)

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;

import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());
        
CreateCollectionReq.CollectionSchema schema = client.createSchema();
schema.setEnableDynamicField(true);
schema.addField(AddFieldReq.builder()
        .fieldName("pk")
        .dataType(DataType.VarChar)
        .isPrimaryKey(true)
        .autoID(true)
        .maxLength(100)
        .build());

schema.addField(AddFieldReq.builder()
        .fieldName("sparse_vector")
        .dataType(DataType.SparseFloatVector)
        .build());

import { DataType } from "@zilliz/milvus2-sdk-node";

const schema = [
  {
    name: "metadata",
    data_type: DataType.JSON,
  },
  {
    name: "pk",
    data_type: DataType.Int64,
    is_primary_key: true,
  },
  {
    name: "sparse_vector",
    data_type: DataType.SparseFloatVector,
  }
];


export primaryField='{
    "fieldName": "pk",
    "dataType": "VarChar",
    "isPrimary": true,
    "elementTypeParams": {
        "max_length": 100
    }
}'

export vectorField='{
    "fieldName": "sparse_vector",
    "dataType": "SparseFloatVector"
}'

export schema="{
    \"autoID\": true,
    \"fields\": [
        $primaryField,
        $vectorField
    ]
}"

在這個範例中,新增了一個名為sparse_vector 的向量欄位,用來儲存稀疏向量。這個欄位的資料類型是SPARSE_FLOAT_VECTOR

為向量欄位設定索引參數

為稀疏向量建立索引的過程與為密集向量建立索引的過程相似,但在指定的索引類型 (index_type) 、距離度量 (metric_type) 和索引參數 (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",
    metric_type="IP",
    params={"inverted_index_algo": "DAAT_MAXSCORE"},
)

import io.milvus.v2.common.IndexParam;
import java.util.*;

List<IndexParam> indexes = new ArrayList<>();
Map<String,Object> extraParams = new HashMap<>();
extraParams.put("inverted_index_algo": "DAAT_MAXSCORE");
indexes.add(IndexParam.builder()
        .fieldName("sparse_vector")
        .indexName("sparse_inverted_index")
        .indexType(IndexParam.IndexType.SPARSE_INVERTED_INDEX)
        .metricType(IndexParam.MetricType.IP)
        .extraParams(extraParams)
        .build());

const indexParams = await client.createIndex({
    index_name: 'sparse_inverted_index',
    field_name: 'sparse_vector',
    metric_type: MetricType.IP,
    index_type: IndexType.SPARSE_INVERTED_INDEX,
    params: {
      inverted_index_algo: 'DAAT_MAXSCORE',
    },
});

export indexParams='[
        {
            "fieldName": "sparse_vector",
            "metricType": "IP",
            "indexName": "sparse_inverted_index",
            "indexType": "SPARSE_INVERTED_INDEX",
            "params":{"inverted_index_algo": "DAAT_MAXSCORE"}
        }
    ]'

在上面的範例中

  • index_type:要為稀疏向量場建立的索引類型。有效值:

    • SPARSE_INVERTED_INDEX:稀疏向量的通用反演索引。

    從 Milvus 2.5.4 起,SPARSE_WAND 已經被廢棄。取而代之,建議使用"inverted_index_algo": "DAAT_WAND" 以達到等效,同時保持相容性。

  • metric_type:用來計算稀疏向量之間相似性的度量。有效值:

    • IP (Inner Product):使用點乘積測量相似性。

    • BM25:通常用於全文搜尋,著重於文字相似性。

      如需詳細資訊,請參閱公制類型全文檢索。

  • params.inverted_index_algo:用於建立和查詢索引的演算法。有效值:

    • "DAAT_MAXSCORE" (預設):使用 MaxScore 演算法的最佳化 Document-at-a-Time (DAAT) 查詢處理。MaxScore 可跳過可能影響最小的詞彙和文件,為高 k 值或包含許多詞彙的查詢提供更好的效能。為了達到這個目的,MaxScore 會根據最大影響分數,將詞彙分為必要和非必要兩組,並將重點放在對 top-k 結果有貢獻的詞彙上。

    • "DAAT_WAND":使用 WAND 演算法優化 DAAT 查詢處理。WAND 利用最大影響分數跳過非競爭性文件,評估較少的命中文件,但每次命中的開銷較高。這使得 WAND 對於 k 值較小的查詢或較短的查詢更有效率,在這些情況下跳過是較可行的。

    • "TAAT_NAIVE":Basic Term-at-a-Time (TAAT) 查詢處理。雖然與DAAT_MAXSCOREDAAT_WAND 相比較慢,但TAAT_NAIVE 提供了獨特的優勢。DAAT 演算法使用快取的最大影響分數,不論全域集合參數 (avgdl) 有何改變,這些分數都會保持靜態,而TAAT_NAIVE 則不同,它會動態地適應這些改變。

建立集合

稀疏向量和索引設定完成後,就可以建立包含稀疏向量的集合。以下範例使用 create_collection方法來建立一個名為my_sparse_collection 的集合。

client.create_collection(
    collection_name="my_sparse_collection",
    schema=schema,
    index_params=index_params
)

import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;

MilvusClientV2 client = new MilvusClientV2(ConnectConfig.builder()
        .uri("http://localhost:19530")
        .build());
        
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
        .collectionName("my_sparse_collection")
        .collectionSchema(schema)
        .indexParams(indexes)
        .build();
client.createCollection(requestCreate);

import { MilvusClient } from "@zilliz/milvus2-sdk-node";

const client = new MilvusClient({
    address: 'http://localhost:19530'
});

await client.createCollection({
    collection_name: 'my_sparse_collection',
    schema: schema,
    index_params: indexParams
});

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d "{
    \"collectionName\": \"my_sparse_collection\",
    \"schema\": $schema,
    \"indexParams\": $indexParams
}"

插入資料

建立集合後,插入包含稀疏向量的資料。

sparse_vectors = [
    {"sparse_vector": {1: 0.5, 100: 0.3, 500: 0.8}},
    {"sparse_vector": {10: 0.1, 200: 0.7, 1000: 0.9}},
]

client.insert(
    collection_name="my_sparse_collection",
    data=sparse_vectors
)

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.milvus.v2.service.vector.request.InsertReq;
import io.milvus.v2.service.vector.response.InsertResp;

List<JsonObject> rows = new ArrayList<>();
Gson gson = new Gson();
{
    JsonObject row = new JsonObject();
    SortedMap<Long, Float> sparse = new TreeMap<>();
    sparse.put(1L, 0.5f);
    sparse.put(100L, 0.3f);
    sparse.put(500L, 0.8f);
    row.add("sparse_vector", gson.toJsonTree(sparse));
    rows.add(row);
}
{
    JsonObject row = new JsonObject();
    SortedMap<Long, Float> sparse = new TreeMap<>();
    sparse.put(10L, 0.1f);
    sparse.put(200L, 0.7f);
    sparse.put(1000L, 0.9f);
    row.add("sparse_vector", gson.toJsonTree(sparse));
    rows.add(row);
}

InsertResp insertR = client.insert(InsertReq.builder()
        .collectionName("my_sparse_collection")
        .data(rows)
        .build());

const data = [
  { sparse_vector: { "1": 0.5, "100": 0.3, "500": 0.8 } },
  { sparse_vector: { "10": 0.1, "200": 0.7, "1000": 0.9 } },
];
client.insert({
  collection_name: "my_sparse_collection",
  data: data,
});


curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
    "data": [
        {"sparse_vector": {"1": 0.5, "100": 0.3, "500": 0.8}},
        {"sparse_vector": {"10": 0.1, "200": 0.7, "1000": 0.9}}        
    ],
    "collectionName": "my_sparse_collection"
}'

## {"code":0,"cost":0,"data":{"insertCount":2,"insertIds":["453577185629572534","453577185629572535"]}}

若要使用稀疏向量執行相似性搜尋,請準備查詢向量和搜尋參數。

# Prepare search parameters
search_params = {
    "params": {"drop_ratio_search": 0.2},  # Additional optional search parameters
}

# Prepare the query vector
query_vector = [{1: 0.2, 50: 0.4, 1000: 0.7}]

在本範例中,drop_ratio_search 是專門針對稀疏向量的可選參數,允許在搜尋過程中微調查詢向量中的小值。例如,使用{"drop_ratio_search": 0.2} 時,查詢向量中最小的 20% 值會在搜尋過程中被忽略。

然後,使用search 方法執行相似性搜尋。

res = client.search(
    collection_name="my_sparse_collection",
    data=query_vector,
    limit=3,
    output_fields=["pk"],
    search_params=search_params,
)

print(res)

# Output
# data: ["[{'id': '453718927992172266', 'distance': 0.6299999952316284, 'entity': {'pk': '453718927992172266'}}, {'id': '453718927992172265', 'distance': 0.10000000149011612, 'entity': {'pk': '453718927992172265'}}]"]

import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.SparseFloatVec;
import io.milvus.v2.service.vector.response.SearchResp;

Map<String,Object> searchParams = new HashMap<>();
searchParams.put("drop_ratio_search", 0.2);

SortedMap<Long, Float> sparse = new TreeMap<>();
sparse.put(10L, 0.1f);
sparse.put(200L, 0.7f);
sparse.put(1000L, 0.9f);

SparseFloatVec queryVector = new SparseFloatVec(sparse);

SearchResp searchR = client.search(SearchReq.builder()
        .collectionName("my_sparse_collection")
        .data(Collections.singletonList(queryVector))
        .annsField("sparse_vector")
        .searchParams(searchParams)
        .topK(3)
        .outputFields(Collections.singletonList("pk"))
        .build());
        
System.out.println(searchR.getSearchResults());

// Output
//
// [[SearchResp.SearchResult(entity={pk=453444327741536759}, score=1.31, id=453444327741536759), SearchResp.SearchResult(entity={pk=453444327741536756}, score=1.31, id=453444327741536756), SearchResp.SearchResult(entity={pk=453444327741536753}, score=1.31, id=453444327741536753)]]

client.search({
    collection_name: 'my_sparse_collection',
    data: {1: 0.2, 50: 0.4, 1000: 0.7},
    limit: 3,
    output_fields: ['pk'],
    params: {
        drop_ratio_search: 0.2
    }
});

curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
-d '{
    "collectionName": "my_sparse_collection",
    "data": [
        {"1": 0.2, "50": 0.4, "1000": 0.7}
    ],
    "annsField": "sparse_vector",
    "limit": 3,
    "searchParams":{
        "params":{"drop_ratio_search": 0.2}
    },
    "outputFields": ["pk"]
}'

## {"code":0,"cost":0,"data":[{"distance":0.63,"id":"453577185629572535","pk":"453577185629572535"},{"distance":0.1,"id":"453577185629572534","pk":"453577185629572534"}]}

有關相似性搜尋參數的詳細資訊,請參閱基本 ANN 搜尋

限制

在 Milvus 中使用稀疏向量時,請考慮下列限制:

  • 目前,稀疏向量只支援IPBM25(用於全文檢索)距離度量。稀疏向量的高維度使得 L2 和余弦距離不切實際。

  • 對於稀疏向量欄位,只支援SPARSE_INVERTED_INDEX索引類型。

  • 稀疏向量支援的資料類型:

    • 維度部分必須是無符號的 32 位元整數;
    • 值部分可以是非負 32 位元浮點數。
  • 稀疏向量必須符合下列插入和搜尋的要求:

    • 向量中至少有一個值是非零的;
    • 向量索引是非負數。

常見問題

  • 稀疏嵌入的維度可以是 uint32 空間內的任何離散值嗎?

    可以,但有一個例外。稀疏嵌入的維度可以是[0, maximum of uint32) 範圍內的任何值。這表示您不能使用 uint32 的最大值。

  • 對成長中的區段進行搜尋時,是透過索引還是暴力搜尋?

    對成長中區段的搜尋是透過與封存區段索引相同類型的索引進行。對於索引建立前的新成長區段,會使用暴力搜尋。

  • 是否可以在單一集合中同時擁有稀疏向量和密集向量?

    可以,透過多重向量類型支援,您可以建立同時具有稀疏和密集向量列的集合,並對它們執行混合搜尋。

免費嘗試托管的 Milvus

Zilliz Cloud 無縫接入,由 Milvus 提供動力,速度提升 10 倍。

開始使用
反饋

這個頁面有幫助嗎?