Encuentra vecinos más cercanos aproximados (ANN) y embeddings de vectores de consulta

En esta página, se describe cómo encontrar vecinos aproximados más cercanos (ANN) y consultar embeddings de vectores con las funciones de distancia de ANN.

Cuando un conjunto de datos es pequeño, puedes usar K-vecinos más cercanos (KNN) para encontrar los vectores k-más cercanos exactos. Sin embargo, a medida que crece tu conjunto de datos, también aumentan la latencia y el costo de una búsqueda de KNN. Puedes usar ANN para encontrar los k-vecinos más cercanos aproximados con una latencia y un costo significativamente reducidos.

En una búsqueda de ANN, los vectores k que se muestran no son los k-vecinos más cercanos verdaderos porque la búsqueda de ANN calcula distancias aproximadas y es posible que no examine todos los vectores del conjunto de datos. En ocasiones, se muestran algunos vectores que no se encuentran entre los k-vecinos más cercanos. Esto se conoce como pérdida de recuperación. La cantidad de pérdida de recuperación que es aceptable para ti depende del caso de uso, pero, en la mayoría de los casos, perder un poco de recuperación a cambio de un mejor rendimiento de la base de datos es una compensación aceptable.

Para obtener más detalles sobre las funciones de distancia aproximada compatibles con Spanner, consulta las siguientes páginas de referencia para tu dialecto de base de datos:

Consulta embeddings de vectores

Spanner acelera las búsquedas de vectores de vecinos aproximados más cercanos (ANN) mediante el uso de un índice de vectores. Puedes usar un índice de vectores para consultar embeddings de vectores. Para consultar embeddings de vectores, debes primero crear un índice de vectores. Luego, puedes usar cualquiera de las tres funciones de distancia aproximada para encontrar el ANN.

Las restricciones cuando se usan las funciones de distancia aproximada incluyen lo siguiente:

  • La función de distancia aproximada debe calcular la distancia entre una columna de incorporación y una expresión constante (por ejemplo, un parámetro o un literal).
  • El resultado de la función de distancia aproximada se debe usar en una cláusula ORDER BY como la única clave de ordenamiento, y se debe especificar un LIMIT después de ORDER BY.
  • La consulta debe filtrar de forma explícita las filas que no están indexadas. En la mayoría de los casos, esto significa que la consulta debe incluir una WHERE <column_name> IS NOT NULL cláusula que coincida con la definición del índice de vectores, a menos que la columna ya esté marcada como NOT NULL en la definición de la tabla.

Para obtener una lista detallada de las limitaciones, consulta la página de referencia de la función de distancia aproximada.

Ejemplos

Considera una tabla Documents que tiene una columna DocEmbedding de embeddings de texto precalculadas de la columna de bytes DocContents y una columna NullableDocEmbedding propagada desde otras fuentes que podrían ser nulas.

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)
);

Para buscar los 100 vectores más cercanos 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;

Para buscar los 100 vectores más cercanos a un embedding que genera una expresión SQL, usa el siguiente patrón. En este ejemplo, la consulta busca los documentos que son más similares a la incorporación de UserId = 1 y 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

Si la columna de incorporación es anulable:

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;

¿Qué sigue?