Realizar a pesquisa por similaridade de vetores no Spanner encontrando os vizinhos mais próximos

Esta página descreve como realizar a pesquisa por similaridade vetorial em Spanner usando a distância do cosseno, a distância euclidiana e o ponto de vetores de produto para encontrar os vizinhos K mais próximos. Antes de ler esta página, é importante que você entenda os seguintes conceitos:

  • Distância euclidiana: mede a menor distância entre dois vetores.
  • Distância de cosseno: mede o cosseno do ângulo entre dois vetores.
  • Produto escalar: calcula o cosseno do ângulo multiplicado pelo produto do vetor correspondente grandes. Se você sabe que todos os embeddings de vetores no conjunto de dados são normalizado, é possível usar DOT_PRODUCT() como uma função de distância.
  • vizinhos mais próximos (KNN, na sigla em inglês): um algoritmo de machine learning supervisionado usado para resolver problemas de problemas de regressão.

É possível usar funções de distância de vetor para realizar a pesquisa de vetor de vizinho mais próximo (KNN) em casos de uso como pesquisa de similaridade ou geração aumentada de recuperação. O Spanner oferece suporte às funções COSINE_DISTANCE(), EUCLIDEAN_DISTANCE() e DOT_PRODUCT(), que operam em embeddings de vetor, permitindo que você encontre a KNN do embedding de entrada.

Por exemplo, depois de gerar e salvar seus dados operacionais do Spanner como embeddings de vetor, você pode fornecer esses embeddings de vetor como um parâmetro de entrada na consulta para encontrar os vetores mais próximos no espaço N-dimensional e pesquisar itens semanticamente semelhantes ou relacionados.

Todas as três funções de distância usam os argumentos vector1 e vector2, que são do tipo array<> e precisam ter as mesmas dimensões e ter o mesmo tamanho. Para mais detalhes sobre essas funções, consulte:

Exemplos

Os exemplos a seguir mostram a pesquisa KNN, a pesquisa KNN em dados particionados e usando um índice secundário com KNN.

Todos os exemplos usam EUCLIDEAN_DISTANCE(). Também é possível usar COSINE_DISTANCE(). Além disso, se todos os embeddings de vetor no seu conjunto de dados forem normalizados, você poderá usar DOT_PRODUCT() como uma função de distância.

Considere uma tabela Documents que tenha uma coluna (DocEmbedding) de representações de texto pré-calculadas da coluna de bytes DocContents.

GoogleSQL

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

PostgreSQL

CREATE TABLE Documents (
UserId       bigint primary key,
DocId        bigint primary key,
Author       varchar(1024),
DocContents  bytea,
DocEmbedding float4[]
);

Supondo que uma embedding de entrada para "baseball, but not professional baseball" seja a matriz [0.3, 0.3, 0.7, 0.7], você pode encontrar os cinco documentos mais próximos que correspondem, com a seguinte consulta:

GoogleSQL

SELECT DocId, DocEmbedding FROM Documents
ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
ARRAY<FLOAT32>[0.3, 0.3, 0.7, 0.8])
LIMIT 5;

PostgreSQL

SELECT DocId, DocEmbedding FROM Documents
ORDER BY spanner.euclidean_distance(DocEmbedding,
'{0.3, 0.3, 0.7, 0.8}'::float4[])
LIMIT 5;

Os resultados esperados deste exemplo:

Documents
+---------------------------+-----------------+
| DocId                     | DocEmbedding    |
+---------------------------+-----------------+
| 24                        | [8, ...]        |
+---------------------------+-----------------+
| 25                        | [6, ...]        |
+---------------------------+-----------------+
| 26                        | [3.2, ...]      |
+---------------------------+-----------------+
| 27                        | [38, ...]       |
+---------------------------+-----------------+
| 14229                     | [1.6, ...]      |
+---------------------------+-----------------+

Exemplo 2: pesquisa de KNN em dados particionados

A consulta no exemplo anterior pode ser modificada adicionando condições à cláusula WHERE para limitar a pesquisa de vetores a um subconjunto dos seus dados. Uma aplicação comum é pesquisar em dados particionados, como linhas que pertencem a um UserId específico.

GoogleSQL

SELECT UserId, DocId, DocEmbedding FROM Documents
WHERE UserId=18
ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
ARRAY<FLOAT32>[0.3, 0.3, 0.7, 0.8])
LIMIT 5;

PostgreSQL

SELECT UserId, DocId, DocEmbedding FROM Documents
WHERE UserId=18
ORDER BY spanner.euclidean_distance(DocEmbedding,
'{0.3, 0.3, 0.7, 0.8}'::float4[])
LIMIT 5;

Os resultados esperados deste exemplo:

Documents
+-----------+-----------------+-----------------+
| UserId    | DocId           | DocEmbedding    |
+-----------+-----------------+-----------------+
| 18        | 234             | [12, ...]       |
+-----------+-----------------+-----------------+
| 18        | 12              | [1.6, ...]      |
+-----------+-----------------+-----------------+
| 18        | 321             | [22, ...]       |
+-----------+-----------------+-----------------+
| 18        | 432             | [3, ...]        |
+-----------+-----------------+-----------------+

Exemplo 3: pesquisa de KNN em intervalos de índices secundários

Se o filtro de cláusula WHERE que você está usando não fizer parte da chave primária da tabela, você pode criar um índice secundário para acelerar a operação com um index-only scan.

GoogleSQL

CREATE INDEX DocsByAuthor
ON Documents(Author)
STORING (DocEmbedding);

SELECT Author, DocId, DocEmbedding FROM Documents
WHERE Author="Mark Twain"
ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
   <embeddings for "book about the time traveling American">)
LIMIT 5;

PostgreSQL

CREATE INDEX DocsByAuthor
ON Documents(Author)
INCLUDE (DocEmbedding);

SELECT Author, DocId, DocEmbedding FROM Documents
WHERE Author="Mark Twain"
ORDER BY spanner.euclidean_distance(DocEmbedding,
   <embeddings for "that book about the time traveling American">)
LIMIT 5;

Os resultados esperados deste exemplo:

Documents
+------------+-----------------+-----------------+
| Author     | DocId           | DocEmbedding    |
+------------+-----------------+-----------------+
| Mark Twain | 234             | [12, ...]       |
+------------+-----------------+-----------------+
| Mark Twain | 12              | [1.6, ...]      |
+------------+-----------------+-----------------+
| Mark Twain | 321             | [22, ...]       |
+------------+-----------------+-----------------+
| Mark Twain | 432             | [3, ...]        |
+------------+-----------------+-----------------+
| Mark Twain | 375             | [9, ...]        |
+------------+-----------------+-----------------+

A seguir