Encontrar vizinhos mais próximos aproximados, criar índices de vetor e consultar embeddings de vetor

Esta página descreve como encontrar vizinhos mais próximos aproximados (ANN, na sigla em inglês), criar índices de vetor e consultar embeddings de vetor usando as seguintes funções de distância ANN no Spanner:

  • APPROX_COSINE_DISTANCE
  • APPROX_EUCLIDEAN_DISTANCE
  • APPROX_DOT_PRODUCT

Quando um conjunto de dados é pequeno, é possível usar vizinhos mais próximos (KNN) para encontrar os vetores mais próximos. No entanto, à medida que o conjunto de dados cresce, a latência e o custo de uma pesquisa KNN também aumentam. É possível usar ANN para encontrar os vizinhos mais próximos k aproximados com latência e custo significativamente reduzidos.

Vizinhos k-mais próximos aproximados

Em uma pesquisa ANN, os vetores retornados não são os vizinhos mais próximos mais próximos. Ocasionalmente, alguns vetores que não estão entre os vizinhos mais próximos são retornados. Isso é conhecido como perda de recordação. A quantidade de perda de recall aceitável depende do caso de uso, mas, na maioria dos casos, perder um pouco de recall em troca de um melhor desempenho do banco de dados é uma compensação aceitável.

Para mais detalhes sobre as funções de distância aproximada do Spanner, consulte:

Índice vetorial

O Spanner acelera as pesquisas de vetor ANN usando um índice de vetor especializado. Esse índice usa o Vizinho mais próximo escalonável (ScaNN) da Google Research, um algoritmo de vizinho mais próximo altamente eficiente.

O índice de vetor usa uma estrutura baseada em árvore para particionar dados e facilitar pesquisas mais rápidas. O Spanner oferece configurações de árvore de dois e três níveis:

  • Configuração de árvore de dois níveis: os nós de folha (num_leaves) contêm grupos de vetores intimamente relacionados e o centroide correspondente. O nível raiz é composto pelos centroides de todos os nós de folha.
  • Configuração de árvore de três níveis: semelhante em conceito a uma árvore de dois níveis, mas com a introdução de uma camada de ramificação adicional (num_branches), a partir da qual os centroides de nós folhas são particionados para formar o nível raiz (num_leaves).

Além disso, você precisa criar seu índice de vetores com uma métrica de distância específica. Para escolher a métrica de distância mais adequada para seu caso de uso, defina distance_type como COSINE, DOT_PRODUCT ou EUCLIDEAN.

Para mais informações, consulte as instruções VECTOR INDEX.

Limitações

O índice de vetor de spanner tem as seguintes limitações:

  • ALTER VECTOR INDEX não é compatível.

Criar índice vetorial

Para otimizar melhor o índice de vetores para uma boa recuperação e desempenho, recomendamos que você crie o índice de vetores depois que a maioria das linhas com incorporações for gravada no banco de dados. Talvez também seja necessário reconstruir periodicamente o índice de vetores depois de inserir novos dados. Para mais informações, consulte Recriar o índice vetorial.

Para criar um índice vetorial com uma árvore de dois níveis e 1.000 nós de folha em uma tabela Documents com uma coluna de embedding DocEmbedding usando a distância cosine:

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

Para criar um índice de vetores com uma árvore de três níveis e 1.000.000 nós de folhas:

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 a coluna de incorporação for anulável, ela precisará ser declarada com uma cláusula 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);

Consultar embeddings de vetor

Para consultar um índice de vetor, use uma das três funções de distância aproximada:

  • APPROX_COSINE_DISTANCE
  • APPROX_EUCLIDEAN_DISTANCE
  • APPROX_DOT_PRODUCT

As restrições ao usar as funções de distância aproximada incluem:

  • É necessário fornecer uma sugestão de consulta para usar o índice vetorial.
  • É necessário usar uma expressão constante como um argumento da função de distância (por exemplo, um parâmetro ou um literal).
  • A consulta ou subconsulta em que a função de distância aproximada é usada precisa ter uma forma específica: a função de distância precisa ser a única chave ORDER BY, e um limite precisa ser especificado.

Para conferir uma lista detalhada de limitações, consulte a página de referência da função de distância aproximada.

Exemplo

Para pesquisar os 100 vetores mais próximos de [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 a coluna de inserção for anulável:

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

Práticas recomendadas

Siga estas práticas recomendadas para otimizar os índices de vetor e melhorar os resultados da consulta.

Ajustar as opções de pesquisa vetorial

O valor de pesquisa de vetor mais ideal depende do caso de uso, do conjunto de dados de vetor e dos vetores de consulta. Talvez seja necessário realizar um ajuste iterativo para encontrar os melhores valores para sua carga de trabalho específica.

Confira algumas diretrizes úteis para escolher os valores adequados:

  • tree_depth (nível da árvore): se a tabela indexada tiver menos de 10 milhões de linhas, use um tree_depth de 2. Caso contrário, um tree_depth de 3 oferece suporte a tabelas de até 10 bilhões de linhas.

  • num_leaves: use a raiz quadrada do número de linhas no conjunto de dados. Um valor maior pode aumentar o tempo de criação do índice vetorial. Evite definir num_leaves maior que table_row_count/1000, porque isso resulta em folhas muito pequenas e em uma performance ruim.

  • num_leaves_to_search: essa opção especifica quantos nós de folha do índice são pesquisados. Aumentar num_leaves_to_search melhora o recall, mas também aumenta a latência e o custo. Recomendamos usar um número que seja 1% do número total de folhas definidas na instrução CREATE VECTOR INDEX como o valor de num_leaves_to_search. Se você estiver usando uma cláusula de filtro, aumente esse valor para ampliar a pesquisa.

Se o recall aceitável for alcançado, mas o custo da consulta for muito alto, resultando em QPS máxima baixa, tente aumentar num_leaves seguindo estas etapas:

  1. Defina num_leaves como um múltiplo k do valor original (por exemplo, 2 * sqrt(table_row_count)).
  2. Defina num_leaves_to_search como o mesmo múltiplo k do valor original.
  3. Experimente reduzir num_leaves_to_search para melhorar o custo e o QPS mantendo a recuperação.

Melhorar a lembrança

Há várias possibilidades de piora da revogação, incluindo as seguintes:

  • num_leaves_to_search é muito pequeno: pode ser mais difícil encontrar os vizinhos mais próximos de alguns vetores de consulta. Portanto, aumentar num_leaves_to_search para pesquisar mais folhas pode ajudar a melhorar o recall. As consultas recentes podem ter mudado para conter mais desses vetores desafiadores.

  • O índice de vetor precisa ser recriado: a estrutura de árvore do índice de vetor é otimizada para o conjunto de dados no momento da criação e fica estática depois disso. Portanto, se vetores significativamente diferentes forem adicionados após a criação do índice de vetor inicial, a estrutura de árvore poderá ser subótima, levando a uma recuperação mais fraca.

Recriar o índice vetorial

Para recriar o índice de vetores sem inatividade:

  1. Crie um novo índice vetorial na mesma coluna de inserção do índice vetorial atual, atualizando os parâmetros (por exemplo, OPTIONS) conforme apropriado.
  2. Depois que a criação do índice for concluída, mude a dica FORCE_INDEX para apontar para o novo índice e atualizar a consulta de pesquisa vetorial. Isso garante que a consulta use o novo índice de vetor. Talvez também seja necessário retornar num_leaves_to_search na nova consulta.
  3. Descarte o índice vetorial desatualizado.

A seguir