Vettore sparso
I vettori sparsi rappresentano parole o frasi utilizzando embedding vettoriali in cui la maggior parte degli elementi è pari a zero, con un solo elemento non nullo che indica la presenza di una parola specifica. I modelli vettoriali sparsi, come SPLADEv2, superano i modelli densi nella ricerca di conoscenza fuori dal dominio, nella consapevolezza delle parole chiave e nell'interpretabilità. Sono particolarmente utili nel reperimento di informazioni, nell'elaborazione del linguaggio naturale e nei sistemi di raccomandazione, dove la combinazione di vettori sparsi per il richiamo con un modello denso per il ranking può migliorare significativamente i risultati del reperimento.
In Milvus, l'uso di vettori sparsi segue un flusso di lavoro simile a quello dei vettori densi. Si tratta di creare una collezione con una colonna di vettori sparsi, inserire i dati, creare un indice ed eseguire ricerche di similarità e query scalari.
In questa esercitazione si apprende come:
- Preparare le incorporazioni di vettori sparsi;
- Creare una collezione con un campo vettoriale sparse;
- Inserire entità con incorporazioni vettoriali rade;
- Indicizzare la collezione ed eseguire una ricerca ANN sui vettori sparsi.
Per vedere i vettori sparsi in azione, consultare hello_sparse.py.
note
Attualmente, il supporto per i vettori sparsi è una funzione beta nella versione 2.4.0, con l'intenzione di renderla generalmente disponibile nella versione 3.0.0.Preparare le incorporazioni di vettori sparsi
Per utilizzare i vettori sparsi in Milvus, preparare le incorporazioni vettoriali in uno dei formati supportati:
Matrici sparse: Utilizzare la famiglia di classi scipy.sparse per rappresentare le incorporazioni rade. Questo metodo è efficiente per gestire dati su larga scala e ad alta dimensionalità.
Elenco di dizionari: Rappresenta ogni incorporamento sparso come un dizionario, strutturato come
{dimension_index: value, ...}
, dove ogni coppia chiave-valore rappresenta l'indice della dimensione e il valore corrispondente.Esempio:
{2: 0.33, 98: 0.72, ...}
Elenco di iterabili di tuple: Simile all'elenco di dizionari, ma utilizza un iterabile di tuple,
[(dimension_index, value)]
, per specificare solo le dimensioni non nulle e i loro valori.Esempio:
[(2, 0.33), (98, 0.72), ...]
L'esempio seguente prepara le incorporazioni rade generando una matrice rada casuale per 10.000 entità, ciascuna con 10.000 dimensioni e una densità di sparsità di 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
# }
# }
note
Le dimensioni dei vettori devono essere di tipo Python int
o numpy.integer
e i valori di tipo Python float
o numpy.floating
.
Per generare embeddings, si può anche usare il pacchetto model
integrato nella libreria PyMilvus, che offre una serie di funzioni di embedding. Per maggiori dettagli, consultare Embeddings.
Creare un insieme con un campo vettoriale rado
Per creare un insieme con un campo vettoriale rado, impostate il tipo di dati del campo vettoriale rado su DataType.SPARSE_FLOAT_VECTOR. A differenza dei vettori densi, non è necessario specificare una dimensione per i vettori sparsi.
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)
Per i dettagli sui parametri comuni delle collezioni, fare riferimento a create_collection() .
Inserire entità con incorporazioni vettoriali rade
Per inserire entità con incorporazioni vettoriali rade, è sufficiente passare l'elenco delle entità al metodo insert()
al metodo.
# Insert entities
client.insert(collection_name="test_sparse_vector", data=entities)
Indicizzare la collezione
Prima di eseguire ricerche di somiglianza, creare un indice per la collezione. Per ulteriori informazioni sui tipi di indice e sui parametri, consultare add_index() e 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)
Per la creazione di indici su vettori sparsi, tenere presente quanto segue:
index_type
: Il tipo di indice da costruire. Opzioni possibili per i vettori sparsi:SPARSE_INVERTED_INDEX
: Un indice invertito che mappa ogni dimensione ai suoi vettori non nulli, facilitando l'accesso diretto ai dati rilevanti durante le ricerche. Ideale per insiemi di dati con dati radi ma ad alta dimensionalità.SPARSE_WAND
: Utilizza l'algoritmo Weak-AND (WAND) per escludere rapidamente i candidati improbabili, concentrando la valutazione su quelli con un potenziale di classificazione più elevato. Tratta le dimensioni come termini e i vettori come documenti, accelerando le ricerche in grandi insiemi di dati sparsi.
metric_type
: Per i vettori sparsi è supportata solo la metrica di distanzaIP
(Prodotto Interno).params.drop_ratio_build
: Parametro dell'indice utilizzato specificamente per i vettori sparsi. Controlla la percentuale di valori piccoli del vettore che vengono esclusi durante il processo di indicizzazione. Questo parametro consente di regolare con precisione il compromesso tra efficienza e accuratezza, ignorando i valori piccoli durante la costruzione dell'indice. Ad esempio, sedrop_ratio_build = 0.3
, durante la costruzione dell'indice, tutti i valori di tutti i vettori sparsi vengono raccolti e ordinati. Il 30% più piccolo di questi valori non viene incluso nell'indice, riducendo così il carico di lavoro computazionale durante la ricerca.
Per ulteriori informazioni, consultare Indice in-memory.
Esecuzione della ricerca RNA
Dopo che la raccolta è stata indicizzata e caricata in memoria, utilizzare il metodo search()
per recuperare i documenti pertinenti in base alla query.
# 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}}
Quando si configurano i parametri di ricerca, tenere presente quanto segue:
params.drop_ratio_search
: Il parametro di ricerca utilizzato specificamente per i vettori sparsi. Questa opzione consente di regolare con precisione il processo di ricerca, specificando il rapporto tra i valori più piccoli del vettore di query da ignorare. Aiuta a bilanciare la precisione della ricerca e le prestazioni. Più piccolo è il valore impostato perdrop_ratio_search
, meno questi valori piccoli contribuiscono al punteggio finale. Ignorando alcuni valori piccoli, è possibile migliorare le prestazioni della ricerca con un impatto minimo sulla precisione.
Eseguire interrogazioni scalari
Oltre alla ricerca RNA, Milvus supporta anche query scalari su vettori sparsi. Queste query consentono di recuperare i documenti in base a un valore scalare associato al vettore sparse. Per maggiori informazioni sui parametri, consultare query().
Filtrare le entità con scalar_field maggiore di 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}}]
Filtra le entità in base alla chiave primaria:
# 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'}]
Limiti
Quando si utilizzano vettori sparsi in Milvus, si devono considerare i seguenti limiti:
Attualmente, per i vettori sparsi è supportata solo la metrica della distanza IP.
Per i campi vettoriali sparsi, sono supportati solo i tipi di indice SPARSE_INVERTED_INDEX e SPARSE_WAND.
Attualmente, la ricerca per intervallo, la ricerca per raggruppamento e la ricerca per iteratore non sono supportate per i vettori sparsi.
DOMANDE FREQUENTI
Quale metrica di distanza è supportata per i vettori sparsi?
I vettori sparsi supportano solo la metrica di distanza Inner Product (IP) a causa dell'elevata dimensionalità dei vettori sparsi, che rende impraticabili la distanza L2 e la distanza coseno.
Potete spiegare la differenza tra SPARSE_INVERTED_INDEX e SPARSE_WAND e come posso scegliere tra i due?
SPARSE_INVERTED_INDEX è un indice invertito tradizionale, mentre SPARSE_WAND utilizza l'algoritmo Weak-AND per ridurre il numero di valutazioni della distanza IP completa durante la ricerca. SPARSE_WAND è in genere più veloce, ma le sue prestazioni possono diminuire con l'aumentare della densità dei vettori. Per scegliere, è necessario condurre esperimenti e benchmark in base al set di dati e al caso d'uso specifici.
Come scegliere i parametri drop_ratio_build e drop_ratio_search?
La scelta di drop_ratio_build e drop_ratio_search dipende dalle caratteristiche dei dati e dai requisiti di latenza/throughput e precisione della ricerca.
Quali tipi di dati sono supportati per le incorporazioni rade?
La parte della dimensione deve essere un intero a 32 bit senza segno e la parte del valore può essere un numero a virgola mobile a 32 bit non negativo.
La dimensione di un incorporamento sparse può essere un qualsiasi valore discreto all'interno dello spazio uint32?
Sì, con un'eccezione. La dimensione di un incorporamento sparse può essere qualsiasi valore nell'intervallo
[0, maximum of uint32)
. Ciò significa che non è possibile utilizzare il valore massimo di uint32.Le ricerche sui segmenti crescenti vengono condotte attraverso un indice o con la forza bruta?
Le ricerche sui segmenti crescenti vengono condotte attraverso un indice dello stesso tipo dell'indice del segmento sigillato. Per i nuovi segmenti crescenti prima che l'indice sia costruito, si usa una ricerca a forza bruta.
È possibile avere vettori sparsi e densi in un'unica collezione?
Sì, grazie al supporto di più tipi di vettore, è possibile creare collezioni con colonne di vettori sia sparse che dense ed eseguire ricerche ibride su di esse.
Quali sono i requisiti per inserire o ricercare embeddings sparsi?
Le incorporazioni rade devono avere almeno un valore non nullo e gli indici dei vettori devono essere non negativi.