Trovare i vicini più prossimi approssimativi, creare indici vettoriali ed eseguire query sugli incorporamenti vettoriali

Questa pagina descrive come trovare i vicini più vicini approssimativi (ANN), creare indici di vettori e eseguire query sugli embedding di vettori utilizzando le seguenti funzioni di distanza ANN in Spanner:

  • APPROX_COSINE_DISTANCE
  • APPROX_EUCLIDEAN_DISTANCE
  • APPROX_DOT_PRODUCT

Quando un set di dati è piccolo, puoi utilizzare la tecnica K-Nearest Neighbor (KNN) per trovare i vettori k-nearest esatti. Tuttavia, con l'aumento del set di dati, aumentano anche la latenza e il costo di una ricerca KNN. Puoi utilizzare le ANN per trovare i vicini più prossimi approssimativi con latenza e costi notevolmente ridotti.

Vicini k più prossimi approssimativi

In una ricerca ANN, i vettori restituiti da k non sono i veri vicini più prossimi di k. A volte vengono restituiti alcuni vettori che non sono tra i k più vicini. Questo fenomeno è noto come perdita di richiamo. La quantità di perdita di richiamo accettabile dipende dal caso d'uso, ma nella maggior parte dei casi, perdere un po' di richiamo in cambio di un miglioramento delle prestazioni del database è un compromesso accettabile.

Per ulteriori dettagli sulle funzioni di distanza approssimativa di Spanner, consulta:

Indice vettoriale

Spanner accelera le ricerche di vettori ANN utilizzando un indice vettoriale specializzato. Questo indice sfrutta Scalable Nearest Neighbor (ScaNN) di Google Research, un algoritmo di ricerca del vicino più prossimo altamente efficiente.

L'indice di vettori utilizza una struttura basata su albero per partizionare i dati e semplificare le ricerche. Spanner offre configurazioni ad albero sia a due che a tre livelli:

  • Configurazione ad albero a due livelli: i nodi foglia (num_leaves) contengono gruppi di vettori correlati e il loro centroide corrispondente. Il livello radice è costituito dai centroidi di tutti i nodi foglia.
  • Configurazione ad albero a tre livelli: simile per concetto a un albero a due livelli, ma con l'introduzione di un livello aggiuntivo di rami (num_branches), da cui i centroidi dei nodi a foglia vengono ulteriormente suddivisi per formare il livello principale (num_leaves).

Inoltre, devi creare l'indice di vettori con una metrica di distanza specifica. Puoi scegliere la metrica della distanza più adatta al tuo caso d'uso impostando distance_type su COSINE, DOT_PRODUCT o EUCLIDEAN.

Per ulteriori informazioni, consulta le dichiarazioni VECTOR INDEX.

Limitazioni

L'indice di vettori Spanner presenta le seguenti limitazioni:

  • ALTER VECTOR INDEX non è supportato.

Crea indice di vettori

Per ottimizzare al meglio l'indice di vettori in modo da ottenere un buon recupero e un buon rendimento, ti consigliamo di creare l'indice di vettori dopo che la maggior parte delle righe con gli embedding è stata scritta nel database. Potresti anche dover ricostruire periodicamente l'indice del vettore dopo aver inserito nuovi dati. Per ulteriori informazioni, consulta Rigenerare l'indice del vettore.

Per creare un indice vettoriale con un albero a due livelli e 1000 nodi a foglia in una tabella Documents con una colonna di embedding DocEmbedding utilizzando la distanza di cosine:

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(DocEmbedding)
  OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);

Per creare un indice di vettori con un albero a tre livelli e 1000000 nodi foglia:

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(NullableDocEmbedding)
  WHERE NullableDocEmbedding IS NOT NULL
  OPTIONS (distance_type = 'COSINE', tree_depth = 3, num_branches=1000, num_leaves = 1000000);

Se la colonna di incorporamento è nullable, devi dichiararla con una clausola WHERE column_name IS NOT NULL:

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(NullableDocEmbedding)
  WHERE NullableDocEmbedding IS NOT NULL
  OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);

Esegui query sugli embedding vettoriali

Per eseguire una query su un indice di vettori, utilizza una delle tre funzioni di distanza approssimativa:

  • APPROX_COSINE_DISTANCE
  • APPROX_EUCLIDEAN_DISTANCE
  • APPROX_DOT_PRODUCT

Le limitazioni relative all'utilizzo delle funzioni di distanza approssimativa includono quanto segue:

  • Devi fornire un suggerimento di query per utilizzare l'indice vettoriale.
  • Devi utilizzare un'espressione costante come argomento della funzione di distanza (ad esempio un parametro o un valore letterale).
  • La query o la sottoquery in cui viene utilizzata la funzione di distanza approssimativa deve avere una forma specifica: la funzione di distanza deve essere l'unica chiave ORDER BY e deve essere specificato un limite.

Per un elenco dettagliato delle limitazioni, consulta la pagina di riferimento della funzione di distanza approssimativa.

Esempio

Per cercare i 100 vettori più vicini a [1.0, 2.0, 3.0]:

SELECT DocId
FROM Documents@{FORCE_INDEX=DocEmbeddingIndex}
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], DocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

Se la colonna di incorporamento ammette valori nulli:

SELECT DocId
FROM Documents@{FORCE_INDEX=DocEmbeddingIndex}
WHERE NullableDocEmbedding IS NOT NULL
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], NullableDocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

Best practice

Segui queste best practice per ottimizzare gli indici di vettori e migliorare i risultati delle query.

Ottimizza le opzioni di ricerca vettoriale

Il valore di ricerca di vettori più ottimale dipende dal caso d'uso, dal set di dati di vettori e dai vettori di query. Potresti dover eseguire l'ottimizzazione iterativa per trovare i valori migliori per il tuo carico di lavoro specifico.

Ecco alcune linee guida utili da seguire per scegliere valori appropriati:

  • tree_depth (livello dell'albero): se la tabella da indicizzare ha meno di 10 milioni di righe, utilizza un valore tree_depth pari a 2. In caso contrario, un tree_depth di 3 supporta tabelle fino a circa 10 miliardi di righe.

  • num_leaves: utilizza la radice quadrata del numero di righe nel set di dati. Un valore più elevato può aumentare il tempo di compilazione dell'indice di vettori. Evita di impostare un valore di num_leaves superiore a table_row_count/1000, in quanto si ottengono foglie troppo piccole e un rendimento scadente.

  • num_leaves_to_search: questa opzione specifica il numero di nodi foglia dell'indice sottoposti a ricerca. L'aumento di num_leaves_to_search migliora il richiamo, ma aumenta anche la latenza e i costi. Ti consigliamo di utilizzare un numero pari all'1% del numero totale di foglie definito nell'istruzione CREATE VECTOR INDEX come valore per num_leaves_to_search. Se utilizzi una clausola di filtro, aumenta questo valore per ampliare la ricerca.

Se viene raggiunto un livello di recupero accettabile, ma il costo delle query è troppo elevato, con un QPS massimo ridotto, prova ad aumentare num_leaves seguendo questi passaggi:

  1. Imposta num_leaves su un multiplo k del suo valore originale (ad esempio 2 * sqrt(table_row_count)).
  2. Imposta num_leaves_to_search come lo stesso multiplo k del valore originale.
  3. Prova a ridurre num_leaves_to_search per migliorare il costo e il QPS mantenendo il richiamo.

Migliorare il ricordo

Esistono diverse possibilità di peggioramento del richiamo, tra cui:

  • num_leaves_to_search è troppo piccolo: potresti riscontrare difficoltà a trovare i vicini più prossimi per alcuni vettori di query, quindi aumentare num_leaves_to_search per cercare più foglie può contribuire a migliorare il richiamo. Le query recenti potrebbero aver iniziato a contenere più di questi vettori problematici.

  • L'indice di vettori deve essere ricostruito: la struttura ad albero dell'indice di vettori è ottimizzata per il set di dati al momento della creazione ed è statica in seguito. Pertanto, se vengono aggiunti vettori notevolmente diversi dopo la creazione dell'indice iniziale dei vettori, la struttura ad albero potrebbe non essere ottimale, con un conseguente peggioramento del recupero.

Ricostruisci l'indice di vettori

Per ricostruire l'indice di vettori senza tempi di inattività:

  1. Crea un nuovo indice vettoriale nella stessa colonna di embedding dell'indice vettoriale corrente, aggiornando i parametri (ad es. OPTIONS) in base alle esigenze.
  2. Al termine della creazione dell'indice, modifica l'suggerimento FORCE_INDEX in modo che indichi il nuovo indice per aggiornare la query di ricerca di vettori. In questo modo, la query utilizzerà il nuovo indice di vettori. Potresti anche dover eseguire nuovamente l'accordatura num_leaves_to_search nella nuova query.
  3. Elimina l'indice vettoriale obsoleto.

Passaggi successivi