Trovare i vicini più prossimi approssimativi (ANN) ed eseguire query sugli incorporamenti vettoriali

Questa pagina descrive come trovare i vicini più prossimi approssimati (ANN) ed eseguire query sugli incorporamenti vettoriali utilizzando le funzioni di distanza ANN.

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

In una ricerca ANN, i k vettori restituiti non sono i k vicini più prossimi perché la ricerca ANN calcola distanze approssimative e potrebbe non esaminare tutti i vettori nel set di dati. Occasionalmente, vengono restituiti alcuni vettori che non rientrano tra i primi k-nearest neighbor. 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 prestazioni del database migliorate è un compromesso accettabile.

Per maggiori dettagli sulle funzioni di distanza approssimativa supportate in Spanner, consulta le seguenti pagine di riferimento per il dialetto del tuo database:

Eseguire query sugli incorporamenti vettoriali

Spanner accelera le ricerche vettoriali approssimative del vicino più prossimo (ANN) utilizzando un indice vettoriale. Puoi utilizzare un indice vettoriale per eseguire query sugli embedding vettoriali. Per eseguire query sugli embedding vettoriali, devi prima creare un indice vettoriale. Puoi quindi utilizzare una delle tre funzioni di distanza approssimativa per trovare il ANN.

Le limitazioni quando si utilizzano le funzioni di distanza approssimativa includono quanto segue:

  • La funzione di distanza approssimativa deve calcolare la distanza tra una colonna di incorporamento e un'espressione costante (ad esempio, un parametro o un valore letterale).
  • L'output della funzione di distanza approssimativa deve essere utilizzato in una clausola ORDER BY come unica chiave di ordinamento e dopo ORDER BY deve essere specificato un LIMIT.
  • La query deve filtrare esplicitamente le righe non indicizzate. Nella maggior parte dei casi, ciò significa che la query deve includere una clausola WHERE <column_name> IS NOT NULL che corrisponda alla definizione dell'indice vettoriale, a meno che la colonna non sia già contrassegnata come NOT NULL nella definizione della tabella.

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

Esempi

Prendi in considerazione una tabella Documents con una colonna DocEmbedding di incorporamenti di testo precalcolati dalla colonna DocContents byte e una colonna NullableDocEmbedding compilata da altre origini che potrebbero essere nulle.

GoogleSQL

CREATE TABLE Documents (
  UserId       INT64 NOT NULL,
  DocId        INT64 NOT NULL,
  Author       STRING(1024),
  DocContents  BYTES(MAX),
  DocEmbedding ARRAY<FLOAT32> NOT NULL,
  NullableDocEmbedding ARRAY<FLOAT32>,
  WordCount    INT64
) PRIMARY KEY (UserId, DocId);

PostgreSQL

CREATE TABLE documents (
  user_id      bigint not null,
  doc_id       bigint not null,
  author       varchar(1024),
  doc_contents bytea,
  doc_embedding float4[] not null,
  nullable_doc_embedding float4[],
  word_count   bigint,
  PRIMARY KEY (user_id, doc_id)
);

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

GoogleSQL

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

PostgreSQL

SELECT doc_id
FROM documents
WHERE word_count > 1000
ORDER BY spanner.approx_euclidean_distance(
  ARRAY[1.0, 2.0, 3.0]::float4[], doc_embedding,
  options=>jsonb'{"num_leaves_to_search": 10}'
)
LIMIT 100;

Per cercare i 100 vettori più vicini a un incorporamento generato da un'espressione SQL, utilizza il seguente pattern. In questo esempio, la query trova i documenti più simili all'embedding di UserId = 1 e DocId = 1:

GoogleSQL

WITH emb AS (
  SELECT DocEmbedding AS value
  FROM Documents
  WHERE UserId = 1 AND DocId = 1
  LIMIT 1
)
SELECT DocId
FROM Documents, emb
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  emb.value, DocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

PostgreSQL

SELECT documents.doc_id
FROM
  documents,
  (
    SELECT doc_embedding AS value
    FROM documents
    WHERE user_id = 1 AND doc_id = 1
    LIMIT 1
  ) vector
WHERE documents.doc_embedding IS NOT NULL
ORDER BY spanner.APPROX_EUCLIDEAN_DISTANCE(documents.doc_embedding,
         vector.value, options=>'{"num_leaves_to_search": 10}'::jsonb)
LIMIT 100

Se la colonna di incorporamento ammette valori nulli:

GoogleSQL

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

PostgreSQL

SELECT doc_id
FROM documents
WHERE nullable_doc_embedding IS NOT NULL AND word_count > 1000
ORDER BY spanner.approx_euclidean_distance(
  ARRAY[1.0, 2.0, 3.0]::float4[], nullable_doc_embedding,
  options=>jsonb'{"num_leaves_to_search": 10}'
)
LIMIT 100;

Aggiungere la distanza ai risultati della query

Negli esempi precedenti, la distanza ANN viene utilizzata per ordinare i risultati della query, ma non viene visualizzata in questi risultati. Per aggiungere la distanza al luogo dei risultati, inserisci la funzione di distanza nella clausola SELECT e assegnale un alias, quindi utilizza questo alias come unica chiave di ordinamento nella clausola ORDER BY.

Esempi

GoogleSQL

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

PostgreSQL

SELECT doc_id,
  spanner.approx_euclidean_distance(
    ARRAY[1.0, 2.0, 3.0]::float4[], nullable_doc_embedding,
    options=>jsonb'{"num_leaves_to_search": 10}'
  ) AS distance
FROM documents
WHERE nullable_doc_embedding IS NOT NULL AND word_count > 1000
ORDER BY distance
LIMIT 100;

Passaggi successivi