Elasticsearch: explorando a pesquisa de k-vizinho mais próximo (kNN)

O interesse na pesquisa vetorial explodiu devido a uma nova geração de modelos de aprendizado de máquina que podem representar vários conteúdos como vetores, incluindo texto, imagens, eventos e muito mais. Frequentemente chamadas de " modelos de incorporação ", essas representações poderosas podem capturar a semelhança entre duas peças de conteúdo de maneiras que vão além de suas características superficiais.

O algoritmo de pesquisa k-Nearest Neighbor (kNN) encontra o vetor em um conjunto de dados que é mais semelhante a um vetor de consulta. Combinado com essas representações vetoriais, a pesquisa kNN abre possibilidades empolgantes para recuperação:

  • Encontre passagens que possam conter a resposta para a pergunta
  • Detecte imagens quase duplicadas em grandes conjuntos de dados
  • Encontre músicas que soem semelhantes a uma determinada música

Espera-se que a pesquisa vetorial se torne uma parte importante da caixa de ferramentas de pesquisa, juntamente com técnicas tradicionais, como pontuação baseada em termos.

Escolha o Algoritmo ANN

Projetar algoritmos de RNA é uma área ativa de pesquisa acadêmica, e há muitos algoritmos promissores para escolher. Eles geralmente fazem concessões diferentes em termos de velocidade de pesquisa, complexidade de implementação e custo de indexação. Felizmente, existe um grande projeto de código aberto chamado ann-benchmarks que testa os principais algoritmos em vários conjuntos de dados e publica resultados comparativos.

O Elasticsearch 8.0 usa um algoritmo ANN chamado Hierachical Navigable Small World graph (HNSW), que organiza vetores em gráficos com base na semelhança entre eles. O HNSW mostra um forte desempenho de pesquisa em vários conjuntos de dados ann-benchmarks e também tem um bom desempenho em nossos próprios testes. Outro benefício do HNSW é que ele é amplamente utilizado na indústria e tem sido implementado em vários sistemas diferentes. Além dos trabalhos acadêmicos originais , existem muitos recursos úteis para entender os detalhes dos algoritmos. Embora o Elasticsearch ANN seja atualmente baseado em HNSW, esse recurso foi projetado de maneira flexível, permitindo incorporar diferentes abordagens no futuro.

A busca pelo vizinho mais próximo é um problema fundamental em ciência de dados e aprendizado de máquina. Dado um conjunto de pontos em um espaço de alta dimensão, o objetivo é encontrar eficientemente os vizinhos mais próximos de um determinado ponto de consulta. O Elasticsearch, um popular mecanismo de busca e plataforma de análise, fornece uma solução poderosa para esse problema usando o algoritmo Hierarchical Navigable Small World (HNSW). Nesta postagem do blog, vamos nos aprofundar em como o HNSW permite a pesquisa eficiente de vizinho mais próximo no Elasticsearch.

HNSW é um algoritmo de vizinho mais próximo aproximado (ANN), o que significa que ele fornece uma aproximação do verdadeiro vizinho mais próximo, não uma garantia. A ideia-chave por trás do HNSW é construir uma hierarquia de nós interconectados, formando um grafo de mundo pequeno. Cada nó no grafo representa um ponto em um espaço de alta dimensão, e as arestas entre os nós representam a similaridade entre os pontos. O gráfico é construído de forma a equilibrar o trade-off entre exploração (encontrar novos pontos possivelmente mais próximos) e exploração (usar pontos descobertos potencialmente próximos).

 

O algoritmo HNSW é dividido em duas fases principais: construção e busca .

Na fase de construção, o algoritmo constrói um grafo de mundo pequeno adicionando nós e arestas ao grafo de maneira gananciosa e iterativa. A cada iteração, o algoritmo seleciona um nó candidato para adicionar ao grafo e o conecta a outros nós que podem ser seus vizinhos mais próximos. O algoritmo usa um conjunto de heurísticas para equilibrar a exploração e a exploração, como manter um conjunto de nós com altos outdegrees (ou seja, nós com muitas arestas para outros nós) para permitir a exploração, além de adicionar arestas para permitir o pioneirismo.

A fase de busca do algoritmo envolve encontrar os k vizinhos mais próximos de um determinado ponto de consulta no grafo do mundo pequeno. O algoritmo começa com um nó aleatório no grafo e explora iterativamente o grafo, movendo-se para nós vizinhos que podem estar mais próximos do ponto de consulta. A busca termina quando os k vizinhos mais próximos são encontrados ou quando a busca explorou um número predeterminado de nós. Os algoritmos de busca também usam heurísticas para balancear exploração e exploração, como manter um conjunto de nós visitados e usá-los para direcionar buscas para regiões inexploradas do grafo.

HNSW tem várias vantagens sobre outros algoritmos de ANN. Primeiro, ele pode lidar com espaços de alta dimensão com milhões de pontos, tornando-o ideal para uso no Elasticsearch. Em segundo lugar, ele tem poucos requisitos de memória porque pequenos gráficos de mundo podem ser armazenados em estruturas de dados com eficiência de memória, como matrizes e tabelas de hash. Em terceiro lugar, é computacionalmente barato para construir e pesquisar, tornando-o rápido e escalável.

Para visualizar o algoritmo HNSW em ação, confira esta apresentação interativa de Yu. A. Markov, autor do artigo original.

Tipo de campo vetorial denso

O tipo de campo denso_vetor armazena vetores densos de valores numéricos. Os campos de vetores densos são usados ​​principalmente para pesquisas de k-vizinhos mais próximos (kNN). O tipo de vetor_denso não suporta agregação ou classificação.

Você pode adicionar o campo densa_vector como uma matriz numérica baseada em element_type , que é float por padrão:

PUT my-index
{
  "mappings": {
    "properties": {
      "my_vector": {
        "type": "dense_vector",
        "dims": 3
      },
      "my_text" : {
        "type" : "keyword"
      }
    }
  }
}

Podemos escrever alguns documentos usando o seguinte comando:

PUT my-index/_doc/1
{
  "my_text" : "text1",
  "my_vector" : [0.5, 10, 6]
}

PUT my-index/_doc/2
{
  "my_text" : "text2",
  "my_vector" : [-0.5, 10, 10]
}

Nota : Ao contrário da maioria dos outros tipos de dados, os vetores densos são sempre de valor único. Não é possível armazenar vários valores em um campo de vetor_denso.

Para concluir a pesquisa, a conta que você usa deve ter as seguintes permissões de índice :

  • create_index ou manage cria um índice com um campo densa_vector
  • criar, indexar ou gravar para adicionar dados ao índice que você criou
  • ler para pesquisar o índice

Para saber mais sobre como criar essas permissões de usuário, consulte o artigo anterior " Elasticsearch: configurações de segurança do usuário ".

pesquisa do k vizinho mais próximo (kNN)

Uma pesquisa de k vizinhos mais próximos (kNN) localiza os k vetores mais próximos de um vetor de consulta, conforme medido por uma medida de similaridade. Casos de uso comuns para kNN incluem:

  • Classificação de relevância baseada em algoritmos de processamento de linguagem natural (NLP)
  • Recomendações de produtos e mecanismos de recomendação
  • Pesquisa de similaridade para imagens ou vídeos

Para obter um exemplo de NLP e similaridade de imagem, leia a seção " NLP - Natural Language Processing " do meu artigo anterior " Elastic: A Developer's Guide to Getting Started ".

método kNN

O Elasticsearch oferece suporte a dois métodos de pesquisa kNN:

  • KNN exato de força bruta usando consulta script_score com funções vetoriais
  • KNN aproximado usando a opção de pesquisa knn (KNN aproximado)

Na maioria dos casos, você desejará usar kNN aproximado. O kNN aproximado fornece latência mais baixa ao custo de indexação mais lenta e precisão menos que perfeita.

O kNN exato e de força bruta garante resultados precisos, mas não se adapta bem a grandes conjuntos de dados. Usando essa abordagem, a consulta script_score precisa verificar cada documento correspondente para calcular a função vetorial, o que torna a pesquisa mais lenta. No entanto, você pode melhorar a latência usando a consulta para limitar o número de documentos correspondentes passados ​​para a função. Você pode usar esse método para obter um bom desempenho de pesquisa se filtrar seus dados para um pequeno conjunto de documentos.

Exatamente kNN

Para executar uma pesquisa kNN exata, use a consulta script_score com funções vetoriais.

1) Mapeie explicitamente um ou mais campos de vetor denso. Se você não pretende usar este campo para kNN aproximado (kNN aproximado), omita a opção de mapeamento de índice ou defina-a como falsa. Isso pode melhorar significativamente a velocidade de indexação.

Primeiro, precisamos indexar nossos dados no Elasticsearch e especificar os campos que queremos usar para pesquisas de vizinhos mais próximos. Aqui está um exemplo:

PUT product-index
{
  "mappings": {
    "properties": {
      "product-vector": {
        "type": "dense_vector",
        "dims": 5,
        "index": false
      },
      "price": {
        "type": "long"
      }
    }
  }
}

2) Vamos escrever os dados:

POST product-index/_bulk?refresh=true
{ "index": { "_id": "1" } }
{ "product-vector": [230.0, 300.33, -34.8988, 15.555, -200.0], "price": 1599 }
{ "index": { "_id": "2" } }
{ "product-vector": [-0.5, 100.0, -13.0, 14.8, -156.0], "price": 799 }
{ "index": { "_id": "3" } }
{ "product-vector": [0.5, 111.3, -13.0, 14.8, -156.0], "price": 1099 }
...

3) Use a API de pesquisa para executar consultas script_score que incluam funções vetoriais.

Dica : Para limitar o número de documentos correspondentes passados ​​para a função de vetor, recomendamos que você especifique uma consulta de filtro no parâmetro script_score.query. Você pode usar uma consulta match_all com este parâmetro para corresponder a todos os documentos, se desejar , no entanto, a correspondência de todos os documentos aumentará significativamente a latência da pesquisa.

POST product-index/_search
{
  "query": {
    "script_score": {
      "query" : {
        "bool" : {
          "filter" : {
            "range" : {
              "price" : {
                "gte": 1000
              }
            }
          }
        }
      },
      "script": {
        "source": "cosineSimilarity(params.queryVector, 'product-vector') + 1.0",
        "params": {
          "queryVector": [-0.5, 90.0, -10, 14.8, -156.0]
        }
      }
    }
  }
}

Para obter um exemplo completo, você pode ler o artigo " Elasticsearch: Vector-Based Scoring " em detalhes.

kNN aproximado - kNN aproximado

Aviso : as pesquisas aproximadas de kNN têm requisitos de recursos específicos em comparação com outros tipos de pesquisas. Em particular, todos os dados vetoriais devem caber no cache da página do nó para serem válidos. Consulte o Guia de ajuste de pesquisa kNN aproximado para obter instruções importantes sobre configuração e dimensionamento .

Para executar uma pesquisa kNN aproximada, use a opção knn para pesquisar o campo de vetor_denso habilitado para índice.

1) Mapeie explicitamente um ou mais campos de vetor denso. A pesquisa kNN aproximada requer as seguintes opções de mapeamento:

  • O valor do índice é verdadeiro.
  • semelhança . Esse valor determina a medida de similaridade usada para pontuar documentos com base na similaridade entre os vetores de consulta e de documento. Consulte a documentação do parâmetro de similaridade para obter uma lista de métricas disponíveis.
PUT image-index
{
  "mappings": {
    "properties": {
      "image-vector": {
        "type": "dense_vector",
        "dims": 3,
        "index": true,
        "similarity": "l2_norm"
      },
      "title": {
        "type": "text"
      },
      "file-type": {
        "type": "keyword"
      }
    }
  }
}

2) Escreva alguns dados

POST image-index/_bulk?refresh=true
{ "index": { "_id": "1" } }
{ "image-vector": [1, 5, -20], "title": "moose family", "file-type": "jpg" }
{ "index": { "_id": "2" } }
{ "image-vector": [42, 8, -15], "title": "alpine lake", "file-type": "png" }
{ "index": { "_id": "3" } }
{ "image-vector": [15, 11, 23], "title": "full moon", "file-type": "jpg" }
...

3) Execute a pesquisa com a opção knn

POST image-index/_search
{
  "knn": {
    "field": "image-vector",
    "query_vector": [-5, 9, -12],
    "k": 10,
    "num_candidates": 100
  },
  "fields": [ "title", "file-type" ]
}

Dos documentos retornados, _score é determinado pela similaridade entre os vetores de consulta e documento. Consulte similaridade para obter mais informações sobre como as pontuações de pesquisa kNN são calculadas .

Nota : O suporte para pesquisa kNN aproximada foi adicionado na versão 8.0. Antes disso, os campos de vetor_denso não suportavam a indexação habilitada no mapa. Se você criou um índice com um campo densa_vector antes de 8.0, para oferecer suporte a pesquisas kNN aproximadas, você deve mapear os dados de reindexação com um novo campo que define o índice e define index: true.

Ajustando kNN aproximado para velocidade ou precisão

Para coletar resultados, a API de pesquisa kNN localiza num_candidates os candidatos vizinhos mais próximos aproximados em cada estilhaço. A pesquisa calcula a semelhança desses vetores candidatos com o vetor de consulta, selecionando os k resultados mais semelhantes de cada estilhaço. A pesquisa, então, mescla os resultados de cada fragmento para retornar os k vizinhos globais mais próximos.

Você pode aumentar num_candidates para obter resultados mais precisos ao custo de tornar a pesquisa mais lenta. Buscas com valores altos de num_candidates consideram mais candidatos de cada shard. Isso leva mais tempo, mas a busca tem uma probabilidade maior de encontrar os verdadeiros k primeiros vizinhos mais próximos.

Novamente, você pode reduzir num_candidates para acelerar a pesquisa, mas os resultados podem ser menos precisos.

KNN aproximado usando vetores de bytes

A API de pesquisa kNN aproximada oferece suporte a vetores de valores de bytes, além de vetores de valores de ponto flutuante. Pesquise o campo densa_vector usando a opção knn com element_type definido como byte e indexação habilitada.

1) Mapeie explicitamente um ou mais campos de vetor denso, defina element_type como byte e habilite a indexação.

PUT byte-image-index
{
  "mappings": {
    "properties": {
      "byte-image-vector": {
        "type": "dense_vector",
        "element_type": "byte",
        "dims": 2,
        "index": true,
        "similarity": "cosine"
      },
      "title": {
        "type": "text"
      }
    }
  }
}

2) Indexe seus dados, certificando-se de que todos os valores vetoriais sejam inteiros no intervalo [-128, 127].

POST byte-image-index/_bulk?refresh=true
{ "index": { "_id": "1" } }
{ "byte-image-vector": [5, -20], "title": "moose family" }
{ "index": { "_id": "2" } }
{ "byte-image-vector": [8, -15], "title": "alpine lake" }
{ "index": { "_id": "3" } }
{ "byte-image-vector": [11, 23], "title": "full moon" }

3) Execute a pesquisa com a opção knn, certificando-se de que o valor query_vector seja um número inteiro no intervalo [-128, 127].

POST byte-image-index/_search
{
  "knn": {
    "field": "byte-image-vector",
    "query_vector": [-5, 9],
    "k": 10,
    "num_candidates": 100
  },
  "fields": [ "title" ]
}

Pesquisa kNN filtrada

A API de pesquisa kNN oferece suporte à restrição de pesquisas usando filtros. A pesquisa retornará os k principais documentos que também correspondem à consulta do filtro.

A solicitação a seguir executa uma pesquisa kNN aproximada filtrada pelo campo de tipo de arquivo:

POST image-index/_search
{
  "knn": {
    "field": "image-vector",
    "query_vector": [54, 10, -2],
    "k": 5,
    "num_candidates": 50,
    "filter": {
      "term": {
        "file-type": "png"
      }
    }
  },
  "fields": ["title"],
  "_source": false
}

Observação : Os filtros são aplicados durante uma pesquisa kNN aproximada para garantir que k documentos correspondentes sejam retornados. Isso contrasta com os métodos de pós-filtragem, que aplicam filtros após a conclusão da pesquisa aproximada de kNN. A desvantagem da pós-filtragem é que às vezes ela retorna menos de k resultados, mesmo quando há documentos correspondentes suficientes.

Combinando kNN aproximado com outros recursos

Você pode realizar pesquisas híbridas fornecendo a opção knn com a consulta:

POST image-index/_search
{
  "query": {
    "match": {
      "title": {
        "query": "mountain lake",
        "boost": 0.9
      }
    }
  },
  "knn": {
    "field": "image-vector",
    "query_vector": [54, 10, -2],
    "k": 5,
    "num_candidates": 50,
    "boost": 0.1
  },
  "size": 10
}

A pesquisa encontra as correspondências de vetor top globais k = 5, combina-as com as correspondências na consulta de correspondência e, finalmente, retorna os 10 resultados de maior pontuação. knn e correspondência de consulta são combinados por disjunção , como se você tivesse um valor booleano entre eles ou algo entre eles. Os principais resultados do vetor k representam os vizinhos globais mais próximos em todos os estilhaços de índice.

A pontuação para cada ocorrência é a soma de knn e a pontuação da consulta. Você pode especificar um valor de aumento para dar peso a cada pontuação na soma. No exemplo acima, a pontuação seria calculada como

score = 0.9 * match_score + 0.1 * knn_score

A opção knn também pode ser usada com agregação . Normalmente, o Elasticsearch calcula um agregado sobre todos os documentos correspondentes à pesquisa. Assim, para uma busca kNN aproximada, a agregação é calculada nos k documentos mais recentes. Se a pesquisa também incluir uma consulta, a agregação será calculada sobre o conjunto combinado de knn e correspondências de consulta.

Considerações de índice

Para pesquisas kNN aproximadas, o Elasticsearch armazena valores vetoriais densos para cada segmento como um gráfico HNSW . A indexação de vetores para pesquisas kNN aproximadas pode levar muito tempo porque a construção desses gráficos é muito cara. Pode ser necessário aumentar o valor do tempo limite para solicitações de clientes para indexação e solicitações em massa. O Approximate kNN Tuning Guide contém orientações importantes sobre o desempenho da indexação e como a configuração do índice afeta o desempenho da pesquisa.

Além de seus parâmetros de ajuste de tempo de busca, o algoritmo HNSW possui parâmetros de tempo de índice, que compensam entre o custo de construção do grafo, velocidade de busca e precisão. Ao definir o mapeamento de vetor denso, você pode usar o parâmetro index_options para ajustar esses parâmetros

PUT image-index
{
  "mappings": {
    "properties": {
      "image-vector": {
        "type": "dense_vector",
        "dims": 3,
        "index": true,
        "similarity": "l2_norm",
        "index_options": {
          "type": "hnsw",
          "m": 32,
          "ef_construction": 100
        }
      }
    }
  }
}

Limitações da pesquisa kNN aproximada

  • Não é possível executar procuras kNN aproximadas em campos de vetor denso em mapas aninhados .
  • A opção ccs_minimize_roundtrips não é suportada ao usar a pesquisa kNN na pesquisa entre clusters .
  • O Elasticsearch usa o algoritmo HNSW para dar suporte a pesquisas kNN eficientes. Como a maioria dos algoritmos kNN, HNSW é uma aproximação que sacrifica a precisão do resultado pela velocidade de pesquisa. Isso significa que os resultados retornados nem sempre são os k vizinhos mais próximos verdadeiros.

Observação : as pesquisas kNN aproximadas sempre usam o tipo de pesquisa dfs_query_then_fetch para que as correspondências top-k globais sejam coletadas em estilhaços. Você não pode definir explicitamente search_type ao executar uma pesquisa kNN.

Supongo que te gusta

Origin blog.csdn.net/UbuntuTouch/article/details/129816427
Recomendado
Clasificación