Adicionar um filtro a uma consulta vetorial no Pesquisa de IA do Azure

Nota

strictPostFilter está atualmente em pré-visualização pública. Esta pré-visualização é fornecida sem um acordo de nível de serviço e não é recomendada para cargas de trabalho em produção. Certas funcionalidades podem não ser suportadas ou podem ter capacidades limitadas. Para mais informações, consulte Termos de Utilização Suplementares para Microsoft Azure Pré-visualizações.

prefilter e postfilter estão geralmente disponíveis na versão estável mais recente da API REST.

No Pesquisa de IA do Azure, pode usar uma expressão filter para adicionar critérios de inclusão ou exclusão a uma consulta vetor. Também pode especificar um modo de filtragem que aplique o filtro:

  • Antes da execução da consulta, conhecido como pré-filtro.
  • Após a execução da consulta, conhecida como pós-filtragem.
  • Depois de os resultados globais de topok serem identificados, conhecido como filtragem pós-estrita (pré-visualização).

Este artigo utiliza REST como ilustração. Para exemplos de código noutras linguagens e soluções de ponta a ponta que incluem consultas vetoriais, consulte o repositório azure-search-vector-samples GitHub.

Também pode usar Search Explorer no portal Azure para consultar o conteúdo do vetor. Na vista JSON, podes adicionar filtros e especificar o modo de filtro.

Como funciona a filtragem em consultas vetoriais

O Pesquisa de IA do Azure utiliza o algoritmo Hierarchical Navigable Small World (HNSW) para pesquisa de Vizinho Aproximado (ANN), armazenando grafos HNSW em múltiplos fragmentos. Cada fragmento contém uma parte do índice completo.

Filtros aplicam-se a filterable campos não vetoriais, sejam string ou numéricos, para incluir ou excluir documentos de pesquisa com base em critérios de filtro. Os campos vetoriais em si não são filtráveis, mas podes usar filtros noutros campos do mesmo índice para restringir os documentos considerados para pesquisa vetorial. Se o seu índice não tiver campos de texto ou numéricos adequados, verifique os metadados do documento que possam ajudar na filtragem, como LastModified ou CreatedBy propriedades.

O vectorFilterMode parâmetro controla onde as operações de filtro são aplicadas durante as fases da pesquisa, o que afeta como os resultados são filtrados para um subconjunto de itens (como por categoria, etiqueta ou outros atributos) e impacta a latência, a recolha e o rendimento. Existem três modos:

  • preFilter aplica o filtro durante a travessia do HNSW em cada fragmento. Este modo maximiza a recordação, mas pode percorrer mais parte do grafo, aumentando a CPU e a latência para filtros altamente seletivos.

  • postFilter executa a travessia e filtragem HNSW em cada fragmento de forma independente, intersecta os resultados ao nível do fragmento e depois agrega o topo k de cada fragmento num topo global k. Este modo pode criar falsos negativos para filtros altamente seletivos ou valores pequenos k .

  • strictPostFilter(pré-visualização) encontra o topo não filtrado global kantes de aplicar o filtro. Este modo tem o maior risco de devolver falsos negativos para filtros altamente seletivos e valores pequenos k .

Para mais informações sobre estes modos, veja Definir o modo de filtro.

Defina um filtro

Os filtros determinam o âmbito das consultas vetoriais e são definidos usando Documents - Search Post (API REST). A menos que queira usar uma funcionalidade de pré-visualização, use a versão estável mais recente das APIs REST do Serviço de Pesquisa para formular o pedido.

Esta API REST fornece:

  • filter relativamente aos critérios.
  • vectorFilterMode para especificar quando o filtro é aplicado durante a consulta vetorial. Para modos suportados, veja Definir o modo de filtro.
POST https://{search-endpoint}/indexes/{index-name}/docs/search?api-version={api-version}
Content-Type: application/json
api-key: {admin-api-key}
    
{
    "count": true,
    "select": "title, content, category",
    "filter": "category eq 'Databases'",
    "vectorFilterMode": "preFilter",
    "vectorQueries": [
        {
            "kind": "vector",
            "vector": [
                -0.009154141,
                0.018708462,
                . . . // Trimmed for readability
                -0.02178128,
                -0.00086512347
            ],
            "fields": "contentVector",
            "k": 50
        }
    ]
}

Neste exemplo, a imersão vetorial dirige-se ao contentVector campo, e os critérios do filtro aplicam-se a category, um campo de texto filtrável. Como o preFilter modo é utilizado, o filtro é aplicado antes do motor de busca executar a consulta, pelo que apenas documentos da Databases categoria são considerados durante a pesquisa vetorial.

Definir o modo de filtro

O vectorFilterMode parâmetro determina quando e como o filtro é aplicado em relação à execução da consulta vetorial. Pode usar os seguintes modos:

  • preFilter (recomendado)
  • postFilter
  • strictPostFilter (pré-visualização)

Nota

preFilter é o padrão para índices criados após aproximadamente 15 de outubro de 2023. Para índices criados antes desta data, postFilter é o padrão. Para usar preFilter e as outras funcionalidades vetoriais avançadas, como compressão vetorial, você deve recriar o seu índice.

Pode testar a compatibilidade enviando uma consulta vetorial na versão "vectorFilterMode": "preFilter" da API REST ou posterior com 2023-10-01-preview. Se a consulta falhar, o seu índice não suporta preFilter.

O pré-filtragem aplica filtros antes da execução da consulta, o que reduz o conjunto de candidatos para o algoritmo de pesquisa vetorial. Os resultados superioresk são então selecionados deste conjunto filtrado.

Numa consulta vetorial, preFilter é o modo predefinido porque favorece a recordação e a qualidade em detrimento da latência.

Como funciona este modo

  1. Em cada fragmento, aplique o predicado do filtro durante a travessia do HNSW, expandindo o grafo até que k candidatos sejam encontrados.

  2. Produza os resultados locais pré-filtrados de topo de cada fragmentok.

  3. Agregue os resultados filtrados num conjunto global dos melhores k resultados.

Efeito deste modo

A travessia expande a superfície de pesquisa para encontrar mais candidatos filtrados, especialmente se o filtro for seletivo. Isto produz os resultados máximosk mais semelhantes em todos os fragmentos. Cada fragmento identifica os k resultados que satisfazem o predicado do filtro.

A pré-filtragem garante que os resultados k são retornados se existirem no índice. Para filtros altamente seletivos, isto pode fazer com que uma parte significativa do grafo seja percorrida, aumentando o custo de computação e a latência, ao mesmo tempo que reduz o rendimento. Se o seu filtro for altamente seletivo (tem muito poucas correspondências), considere usar exhaustive: true para realizar uma pesquisa exaustiva.

Diagrama dos pré-filtros.

Tabela comparativa

Modo Recuperação (resultados filtrados) Custo computacional Risco de falsos negativos Quando usar
preFilter Muito alto Maior (aumenta com a seletividade e complexidade do filtro) Sem risco Padrão recomendado para todos os cenários, especialmente quando a recordação é crítica (domínios de pesquisa sensíveis), ao usar filtros seletivos ou ao usar pequenos k.
postFilter Médio a alto (diminui com a seletividade do filtro) Semelhante ao não filtrado, mas aumenta com a complexidade do filtro Moderado (pode falhar partidas por fragmento) Uma opção para filtros que não são demasiado seletivos e para consultas mais altask .
strictPostFilter Menor (diminui mais rapidamente com a seletividade do filtro) Semelhante ao não filtrado Maior (pode devolver resultados zero para filtros seletivos ou pequenos k) Uma opção para aplicações de pesquisa facetada onde a obtenção de mais resultados após a aplicação do filtro afeta a experiência do utilizador mais do que o risco de falsos negativos. Não uses com pequenos k.

Testes de benchmark de pré-filtragem e pós-filtragem

Importante

Esta secção aplica-se ao pré-filtro e pós-filtro, não ao pós-filtro estrito.

Para compreender as condições em que um modo de filtro tem um desempenho melhor do que o outro, realizámos uma série de testes para avaliar os resultados das consultas em índices pequenos, médios e grandes.

  • Pequeno (100.000 documentos, índice de 2,5 GB, 1.536 dimensões)
  • Médio (1 milhão de documentos, índice de 25 GB, 1.536 dimensões)
  • Grande (1 mil milhões de documentos, índice de 1,9 TB, 96 dimensões)

Para as cargas de trabalho pequenas e médias, usámos um serviço Standard 2 (S2) com uma partição e uma réplica. Para a grande carga de trabalho, usámos um serviço Standard 3 (S3) com 12 partições e uma réplica.

Os índices tinham uma construção idêntica: um campo de chave, um campo vetorial, um campo de texto e um campo numérico filtrável. O índice seguinte é definido usando a 2023-11-01 sintaxe.

def get_index_schema(self, index_name, dimensions):
    return {
        "name": index_name,
        "fields": [
            {"name": "id", "type": "Edm.String", "key": True, "searchable": True},
            {"name": "content_vector", "type": "Collection(Edm.Single)", "dimensions": dimensions,
              "searchable": True, "retrievable": True, "filterable": False, "facetable": False, "sortable": False,
              "vectorSearchProfile": "defaulthnsw"},
            {"name": "text", "type": "Edm.String", "searchable": True, "filterable": False, "retrievable": True,
              "sortable": False, "facetable": False},
            {"name": "score", "type": "Edm.Double", "searchable": False, "filterable": True,
              "retrievable": True, "sortable": True, "facetable": True}
        ],
      "vectorSearch": {
        "algorithms": [
            {
              "name": "defaulthnsw",
              "kind": "hnsw",
              "hnswParameters": { "metric": "euclidean" }
            }
          ],
          "profiles": [
            {
              "name": "defaulthnsw",
              "algorithm": "defaulthnsw"
            }
        ]
      }
    }

Nas consultas, utilizámos um filtro idêntico tanto para as operações de pré-filtro como para as de pós-filtro. Utilizámos um filtro simples para garantir que as variações de desempenho se deviam ao modo de filtragem, e não à complexidade do filtro.

Os resultados foram medidos em consultas por segundo (QPS).

Concluições

  • A pré-filtragem é quase sempre mais lenta do que a pós-filtragem, exceto em índices pequenos onde o desempenho é aproximadamente igual.

  • Em conjuntos de dados maiores, a pré-filtragem é ordens de grandeza mais lenta.

  • Porque é que o pré-filtro é o padrão se quase sempre é mais lento? A pré-filtragem garante que k os resultados são devolvidos se existirem no índice, onde o viés favorece a recordação e a precisão em detrimento da velocidade.

  • Utilize a pós-filtragem se:

    • Valorize a velocidade em vez da seleção (a pós-filtragem pode obter menos de k resultados).

    • Use filtros que não sejam excessivamente seletivos.

    • Para que o desempenho de pré-filtragem não seja aceitável, deve-se ter índices de tamanho suficiente.

Detalhes

  • Dado um conjunto de dados com 100.000 vetores em 1.536 dimensões:

    • Ao filtrar mais de 30% do conjunto de dados, o pré-filtragem e o pós-filtragem foram comparáveis.

    • Ao filtrar menos de 0,1% do conjunto de dados, o pré-filtragem foi cerca de 50% mais lento do que o pós-filtragem.

  • Dado um conjunto de dados com 1 milhão de vetores em 1.536 dimensões:

    • Ao filtrar mais de 30% do conjunto de dados, o pré-filtro foi cerca de 30% mais lento.

    • Ao filtrar menos de 2% do conjunto de dados, a pré-filtragem foi cerca de sete vezes mais lenta.

  • Dado um conjunto de dados com 1 mil milhões de vetores em 96 dimensões:

    • Ao filtrar mais de 5% do conjunto de dados, a pré-filtragem foi cerca de 50% mais lenta.

    • Ao filtrar menos de 10% do conjunto de dados, a pré-filtragem foi cerca de sete vezes mais lenta.

O gráfico seguinte mostra o QPS relativo ao pré-filtro, calculado como o pré-filtro QPS dividido pelo pós-filtro QPS.

Gráfico que mostra o desempenho de QPS para índices pequenos, médios e grandes do QPS relativo.

O eixo vertical representa o desempenho relativo do pré-filtragem em comparação com o pós-filtragem, expresso como uma razão de QPS (consultas por segundo). Por exemplo:

  • Um valor de 0.0 significa que a pré-filtragem é 100% mais lenta do que a pós-filtragem.
  • Um valor de 0.5 significa que a pré-filtragem é 50% mais lenta.
  • Um valor de 1.0 significa que pré-filtragem e pós-filtragem são equivalentes.

O eixo horizontal representa a taxa de filtragem, ou seja, a percentagem de documentos candidatos após a aplicação do filtro. Por exemplo, uma taxa de 1.00% significa que os critérios do filtro selecionaram um por cento do corpus de pesquisa.