Acerca da pesquisa híbrida

A pesquisa vetorial suporta a pesquisa híbrida, um padrão de arquitetura popular na obtenção de informações (IR) que combina a pesquisa semântica e a pesquisa por palavras-chave (também denominada pesquisa baseada em tokens). Com a pesquisa híbrida, os programadores podem tirar partido do melhor das duas abordagens, oferecendo efetivamente uma qualidade de pesquisa superior.

Esta página explica os conceitos de pesquisa híbrida, pesquisa semântica e pesquisa baseada em tokens, e inclui exemplos de como configurar a pesquisa baseada em tokens e a pesquisa híbrida:

Por que motivo a pesquisa híbrida é importante?

Conforme descrito na vista geral da pesquisa vetorial, a pesquisa semântica com a pesquisa vetorial pode encontrar itens com semelhança semântica através de consultas.

Os modelos de incorporação, como as incorporações do Vertex AI, criam um espaço vetorial como um mapa dos significados do conteúdo. Cada incorporação de texto ou multimodal é uma localização no mapa que representa o significado de algum conteúdo. Como exemplo simplificado, quando um modelo de incorporação recebe um texto que aborda filmes durante 10%, música durante 2% e atores durante 30%, pode representar este texto com uma incorporação [0.1, 0.02, 0.3]. Com a pesquisa vetorial, pode encontrar rapidamente outras incorporações na sua vizinhança. Esta pesquisa pelo significado do conteúdo chama-se pesquisa semântica.

Ilustração de termos de pesquisa semânticos relativos entre si num mapa de significados.
Termos de pesquisa semânticos.

A pesquisa semântica com incorporações e a pesquisa vetorial podem ajudar a tornar os sistemas de TI tão inteligentes quanto os bibliotecários experientes ou os funcionários das lojas. As incorporações podem ser usadas para associar diferentes dados empresariais aos respetivos significados; por exemplo, consultas e resultados da pesquisa; textos e imagens; atividades do utilizador e produtos recomendados; textos em inglês e textos em japonês; ou dados de sensores e condições de alerta. Com esta capacidade, existe uma grande variedade de exemplos de utilização para incorporações.

A pesquisa semântica não abrange todos os requisitos possíveis para aplicações de obtenção de informações, como a geração aumentada de obtenção (RAG). A pesquisa semântica só pode encontrar dados que o modelo de incorporação consiga compreender. Por exemplo, as consultas ou os conjuntos de dados com números de produtos ou SKUs arbitrários, nomes de produtos novos que foram adicionados recentemente e nomes de código proprietários da empresa não funcionam com a pesquisa semântica porque não estão incluídos no conjunto de dados de preparação do modelo de incorporação. Estes dados são denominados dados "fora do domínio".

Nestes casos, teria de combinar a pesquisa semântica com a pesquisa baseada em palavras-chave (também denominada baseada em tokens) para formar uma pesquisa híbrida. Com a pesquisa híbrida, pode tirar partido da pesquisa semântica e baseada em tokens para alcançar uma qualidade de pesquisa superior.

Um dos sistemas de pesquisa híbrida mais populares é a Pesquisa Google. O serviço incorporou a pesquisa semântica em 2015 com o modelo RankBrain, além do respetivo algoritmo de pesquisa de palavras-chave baseado em tokens. Com a introdução da pesquisa híbrida, a Pesquisa Google conseguiu melhorar significativamente a qualidade da pesquisa ao satisfazer os dois requisitos: pesquisa por significado e pesquisa por palavra-chave.

Anteriormente, a criação de um motor de pesquisa híbrido era uma tarefa complexa. Tal como acontece com a Pesquisa Google, tem de criar e operar dois tipos diferentes de motores de pesquisa (pesquisa semântica e pesquisa baseada em tokens) e unir e classificar os resultados dos mesmos. Com o suporte de pesquisa híbrida na pesquisa vetorial, pode criar o seu próprio sistema de pesquisa híbrida com um único índice de pesquisa vetorial, personalizado de acordo com os requisitos da sua empresa.

Como funciona a pesquisa baseada em tokens na pesquisa vetorial? Depois de dividir o texto em tokens (como palavras ou subpalavras), pode usar algoritmos de incorporação esparsos populares, como BM25, BM25 ou SPLADE, para gerar incorporação esparsa para o texto.

Uma explicação simplificada das incorporações esparsas é que são vetores que representam o número de vezes que cada palavra ou subpalavra aparece no texto. As incorporações esparsas típicas não têm em conta a semântica do texto.

Incorporações esparsas
Incorporações esparsas.

Podem existir milhares de palavras diferentes usadas em textos. Assim, esta incorporação tem normalmente dezenas de milhares de dimensões, com apenas algumas dimensões a terem valores diferentes de zero. É por isso que são chamadas incorporações "esparsas". A maioria dos respetivos valores são zeros. Este espaço de incorporação esparso funciona como um mapa de palavras-chave, semelhante a um índice de livros.

Neste espaço de incorporação esparsa, pode encontrar incorporações semelhantes analisando a vizinhança de uma incorporação de consulta. Estas incorporações são semelhantes em termos de distribuição das palavras-chave usadas nos respetivos textos.

Ilustração de palavras-chave semelhantes localizadas próximas num mapa de significado.
Termos de pesquisa de tokens.

Este é o mecanismo básico da pesquisa baseada em tokens com incorporações esparsas. Com a pesquisa híbrida na pesquisa vetorial, pode misturar incorporações densas e esparsas num único índice vetorial e executar consultas com incorporações densas, incorporações esparsas ou ambas. O resultado é uma combinação de pesquisa semântica e resultados da pesquisa baseados em tokens.

A pesquisa híbrida também oferece uma latência de consulta mais baixa em comparação com um motor de pesquisa baseado em tokens com um design de índice invertido. Tal como na pesquisa vetorial para a pesquisa semântica, cada consulta com incorporações densas ou esparsas termina em milissegundos, mesmo com milhões ou milhares de milhões de itens.

Para explicar como usar a pesquisa baseada em tokens, as secções seguintes incluem exemplos de código que geram incorporações esparsas e criam um índice com elas na pesquisa vetorial.

Para experimentar este código de exemplo, use o bloco de notas: Combinar pesquisa semântica e por palavras-chave: um tutorial de pesquisa híbrida com a Vertex AI Vector Search.

O primeiro passo é preparar um ficheiro de dados para criar um índice para incorporações esparsas, com base no formato de dados descrito em Formato e estrutura dos dados de entrada.

No JSON, o ficheiro de dados tem o seguinte aspeto:

{"id": "3", "sparse_embedding": {"values": [0.1, 0.2], "dimensions": [1, 4]}}
{"id": "4", "sparse_embedding": {"values": [-0.4, 0.2, -1.3], "dimensions": [10, 20, 30]}}

Cada item deve ter uma propriedade sparse_embedding com propriedades values e dimensions. As incorporações esparsas têm milhares de dimensões com alguns valores diferentes de zero. Este formato de dados funciona de forma eficiente porque contém apenas os valores diferentes de zero com as respetivas posições no espaço.

Prepare um conjunto de dados de amostra

Como conjunto de dados de exemplo, vamos usar o conjunto de dados da Google Merch Shop, que tem cerca de 200 linhas de produtos com a marca Google.

0                          Google Sticker
1                    Google Cloud Sticker
2                       Android Black Pen
3                   Google Ombre Lime Pen
4                    For Everyone Eco Pen
                      ...
197        Google Recycled Black Backpack
198    Google Cascades Unisex Zip Sweater
199    Google Cascades Womens Zip Sweater
200         Google Cloud Skyline Backpack
201       Google City Black Tote Backpack

Prepare um vetorizador TF-IDF

Com este conjunto de dados, vamos preparar um vetorizador, um modelo que gera incorporações esparsas a partir de um texto. Este exemplo usa o TfidfVectorizer no scikit-learn, que é um vetorizador básico que usa o algoritmo TF-IDF.

from sklearn.feature_extraction.text import TfidfVectorizer

# Make a list of the item titles
corpus = df.title.tolist()

# Initialize TfidfVectorizer
vectorizer = TfidfVectorizer()

# Fit and Transform
vectorizer.fit_transform(corpus)

A variável corpus contém uma lista dos 200 nomes de artigos, como "Google Sticker" ou "Chrome Dino Pin". Em seguida, o código transmite-os ao vetorizador chamando a função fit_transform(). Com isto, o vetorizador fica pronto para gerar incorporações esparsas.

O vetorizador TF-IDF tenta atribuir um peso mais elevado às palavras de assinatura no conjunto de dados (como "Camisas" ou "Dino") em comparação com palavras triviais (como "O", "um" ou "de") e contabiliza quantas vezes essas palavras de assinatura são usadas no documento especificado. Cada valor de uma incorporação esparsa representa uma frequência de cada palavra com base nas contagens. Para mais informações sobre o TF-IDF, consulte o artigo Como funcionam o TF-IDF e o TfidfVectorizer?.

Neste exemplo, usamos a tokenização básica ao nível da palavra e a vetorização TF-IDF para simplificar. No desenvolvimento de produção, pode escolher quaisquer outras opções de tokenização e vetorização para gerar incorporações esparsas com base nos seus requisitos. Para os tokenizadores, em muitos casos, os tokenizadores de subpalavras têm um bom desempenho em comparação com a tokenização ao nível da palavra e são escolhas populares. Para os vetorizadores, o BM25 é popular como uma versão melhorada do TF-IDF. O SPLADE é outro algoritmo de vetorização popular que usa alguma semântica para a incorporação esparsa.

Obtenha uma incorporação esparsa

Para facilitar a utilização do vetorizador com a Vector Search, vamos definir uma função de wrapper, get_sparse_embedding():

def get_sparse_embedding(text):

  # Transform Text into TF-IDF Sparse Vector
  tfidf_vector = vectorizer.transform([text])

  # Create Sparse Embedding for the New Text
  values = []
  dims = []
  for i, tfidf_value in enumerate(tfidf_vector.data):
    values.append(float(tfidf_value))
    dims.append(int(tfidf_vector.indices[i]))
  return {"values": values, "dimensions": dims}

Esta função transmite o parâmetro "text" ao vetorizador para gerar uma incorporação esparsa. Em seguida, converta-o no formato {"values": ...., "dimensions": ...} mencionado anteriormente para criar um índice esparso de pesquisa vetorial.

Pode testar esta função:

text_text = "Chrome Dino Pin"
get_sparse_embedding(text_text)

Isto deve gerar a seguinte incorporação esparsa:

{'values': [0.6756557405747007, 0.5212913389979028, 0.5212913389979028],
 'dimensions': [157, 48, 33]}

Crie um ficheiro de dados de entrada

Para este exemplo, vamos gerar incorporações esparsas para todos os 200 artigos.

items = []
for i in range(len(df)):
  id = i
  title = df.title[i]
  sparse_embedding = get_sparse_embedding(title)
  items.append({"id": id, "title": title, "sparse_embedding": sparse_embedding})

Este código gera a seguinte linha para cada item:

{
  'id': 0,
  'title': 'Google Sticker',
  'sparse_embedding': {
    'values': [0.933008728540452, 0.359853737603667],
    'dimensions': [191, 78]
  }
}

Em seguida, guarde-os como um ficheiro JSONL "items.json" e carregue-os para um contentor do Cloud Storage.

# output as a JSONL file and save to bucket
with open("items.json", "w") as f:
  for item in items:
    f.write(f"{item}\n")
! gcloud storage cp items.json $BUCKET_URI

Crie um índice de incorporação esparsa no Vector Search

Em seguida, vamos criar e implementar um índice de incorporação esparsa na pesquisa vetorial. Este é o mesmo procedimento documentado no início rápido da pesquisa vetorial.

# create Index
my_index = aiplatform.MatchingEngineIndex.create_tree_ah_index(
  display_name = f"vs-hybridsearch-index-{UID}",
  contents_delta_uri = BUCKET_URI,
  dimensions = 768,
  approximate_neighbors_count = 10,
)

Para usar o índice, tem de criar um ponto final de índice. Funciona como uma instância de servidor que aceita pedidos de consulta para o seu índice.

# create IndexEndpoint
my_index_endpoint = aiplatform.MatchingEngineIndexEndpoint.create(
  display_name = f"vs-quickstart-index-endpoint-{UID}",
  public_endpoint_enabled = True
)

Com o ponto final de índice, implemente o índice especificando um ID do índice implementado exclusivo.

DEPLOYED_INDEX_ID = f"vs_quickstart_deployed_{UID}"
# deploy the Index to the Index Endpoint
my_index_endpoint.deploy_index(
    index = my_index, deployed_index_id = DEPLOYED_INDEX_ID
)

Depois de aguardar pela implementação, temos tudo pronto para executar uma consulta de teste.

Execute uma consulta com um índice de incorporação esparsa

Para executar uma consulta com um índice de incorporação esparsa, tem de criar um objeto HybridQuery para encapsular a incorporação esparsa do texto da consulta, como no exemplo seguinte:

from google.cloud.aiplatform.matching_engine.matching_engine_index_endpoint import HybridQuery

# create HybridQuery
query_text = "Kids"
query_emb = get_sparse_embedding(query_text)
query = HybridQuery(
  sparse_embedding_dimensions=query_emb['dimensions'],
  sparse_embedding_values=query_emb['values'],
)

Este código de exemplo usa o texto "Crianças" para a consulta. Agora, execute uma consulta com o objeto HybridQuery.

# build a query request
response = my_index_endpoint.find_neighbors(
  deployed_index_id=DEPLOYED_INDEX_ID,
  queries=[query],
  num_neighbors=5,
)

# print results
for idx, neighbor in enumerate(response[0]):
  title = df.title[int(neighbor.id)]
  print(f"{title:<40}")

Isto deve gerar um resultado semelhante ao seguinte:

Google Blue Kids Sunglasses
Google Red Kids Sunglasses
YouTube Kids Coloring Pencils
YouTube Kids Character Sticker Sheet

Dos 200 artigos, o resultado contém os nomes dos artigos que têm a palavra-chave "Kids".

Este exemplo combina a pesquisa baseada em tokens com a pesquisa semântica para criar uma pesquisa híbrida na pesquisa vetorial.

Como criar um índice híbrido

Para criar um índice híbrido, cada item deve ter "embedding" (para incorporação densa) e "sparse_embedding":

items = []
for i in range(len(df)):
  id = i
  title = df.title[i]
  dense_embedding = get_dense_embedding(title)
  sparse_embedding = get_sparse_embedding(title)
  items.append(
    {"id": id, "title": title,
      "embedding": dense_embedding,
      "sparse_embedding": sparse_embedding,}
  )
items[0]

A função get_dense_embedding() usa a API Vertex AI Embedding para gerar a incorporação de texto com 768 dimensões. Isto gera incorporações densas e esparsas no seguinte formato:

{
  "id": 0,
  "title": "Google Sticker",
  "embedding":
    [0.022880317643284798,
    -0.03315234184265137,
    ...
    -0.03309667482972145,
    0.04621824622154236],
  "sparse_embedding": {
    "values": [0.933008728540452, 0.359853737603667],
    "dimensions": [191, 78]
  }
}

O resto do processo é igual ao do Exemplo: como usar a pesquisa baseada em tokens: carregue o ficheiro JSONL para o contentor do Cloud Storage, crie um índice de pesquisa vetorial com o ficheiro e implemente o índice no ponto final do índice.

Execute uma consulta híbrida

Depois de implementar o índice híbrido, pode executar uma consulta híbrida:

# create HybridQuery
query_text = "Kids"
query_dense_emb = get_dense_embedding(query_text)
query_sparse_emb = get_sparse_embedding(query_text)
query = HybridQuery(
  dense_embedding=query_dense_emb,
  sparse_embedding_dimensions=query_sparse_emb['dimensions'],
  sparse_embedding_values=query_sparse_emb['values'],
  rrf_ranking_alpha=0.5,
)

Para o texto da consulta "Kids", gere incorporações densas e esparsas para a palavra e encapsule-as no objeto HybridQuery. A diferença em relação ao HybridQuery anterior são dois parâmetros adicionais: dense_embedding e rrf_ranking_alpha.

Desta vez, vamos imprimir as distâncias para cada artigo:

# print results
for idx, neighbor in enumerate(response[0]):
  title = df.title[int(neighbor.id)]
  dense_dist = neighbor.distance if neighbor.distance else 0.0
  sparse_dist = neighbor.sparse_distance if neighbor.sparse_distance else 0.0
  print(f"{title:<40}: dense_dist: {dense_dist:.3f}, sparse_dist: {sparse_dist:.3f}")

Em cada objeto neighbor, existe uma propriedade distance que tem a distância entre a consulta e o item com a incorporação densa, e uma propriedade sparse_distance que tem a distância com a incorporação esparsa. Estes valores são distâncias invertidas, pelo que um valor mais elevado significa uma distância mais curta.

Ao executar uma consulta com HybridQuery, recebe o seguinte resultado:

Google Blue Kids Sunglasses             : dense_dist: 0.677, sparse_dist: 0.606
Google Red Kids Sunglasses              : dense_dist: 0.665, sparse_dist: 0.572
YouTube Kids Coloring Pencils           : dense_dist: 0.655, sparse_dist: 0.478
YouTube Kids Character Sticker Sheet    : dense_dist: 0.644, sparse_dist: 0.468
Google White Classic Youth Tee          : dense_dist: 0.645, sparse_dist: 0.000
Google Doogler Youth Tee                : dense_dist: 0.639, sparse_dist: 0.000
Google Indigo Youth Tee                 : dense_dist: 0.637, sparse_dist: 0.000
Google Black Classic Youth Tee          : dense_dist: 0.632, sparse_dist: 0.000
Chrome Dino Glow-in-the-Dark Youth Tee  : dense_dist: 0.632, sparse_dist: 0.000
Google Bike Youth Tee                   : dense_dist: 0.629, sparse_dist: 0.000

Além dos resultados da pesquisa baseados em tokens que têm a palavra-chave "Crianças", também estão incluídos resultados da pesquisa semântica. Por exemplo, "Google White Classic Youth Tee" é incluído porque o modelo de incorporação sabe que "Youth" e "Kids" são semanticamente semelhantes.

Para unir os resultados da pesquisa semântica e baseada em tokens, a pesquisa híbrida usa a Reciprocal Rank Fusion (RRF). Para mais informações sobre a RRF e como especificar o parâmetro rrf_ranking_alpha, consulte o artigo O que é a Reciprocal Rank Fusion?.

Reclassificação

O RRF oferece uma forma de unir a classificação dos resultados da pesquisa semântica e baseada em tokens. Em muitos sistemas de recomendação ou obtenção de informações de produção, os resultados passam por algoritmos de classificação de precisão adicionais, denominados reclassificação. Com a combinação da obtenção rápida ao nível do milissegundo com a pesquisa vetorial e a reclassificação de precisão nos resultados, pode criar sistemas de várias fases que oferecem uma maior qualidade de pesquisa ou desempenho de recomendações.

Pipeline de avaliação de milhões de itens e, em seguida, reclassificação de centenas de itens para produzir um número menor de recomendações.
Reclassificação.

A API Vertex AI Ranking oferece uma forma de implementar a classificação com base na relevância genérica entre o texto da consulta e os textos dos resultados da pesquisa com o modelo pré-preparado. O TensorFlow Ranking também oferece uma introdução sobre como criar e preparar modelos de aprendizagem para classificação (LTR) para reclassificação avançada que podem ser personalizados para vários requisitos empresariais.

Comece a usar a pesquisa híbrida

Os seguintes recursos podem ajudar a começar a usar a pesquisa híbrida na pesquisa vetorial.

Recursos de pesquisa híbrida

Recursos do Vector Search

Conceitos adicionais

As secções seguintes descrevem o TF-IDF e o TfidfVectorizer, a Reciprocal Rank Fusion e o parâmetro alfa mais detalhadamente.

Como funcionam o TF-IDF e o TfidfVectorizer?

A função fit_transform() executa dois processos importantes do algoritmo TF-IDF:

  • Fit: o vetorizador calcula a frequência inversa do documento (IDF) para cada termo no vocabulário. O IDF reflete a importância de um termo em todo o corpus. Os termos raros recebem pontuações de IDF mais elevadas:

    IDF(t) = log_e(Total number of documents / Number of documents containing term t)

  • Transform:

    • Tokenização: divide os documentos em termos individuais (palavras ou expressões)
    • Cálculo da frequência do termo (TF): conta a frequência com que cada termo aparece em cada documento com:

      TF(t, d) = (Number of times term t appears in document d) / (Total number of terms in document d)

    • Cálculo de TF-IDF: combina o TF de cada termo com o IDF pré-calculado para criar uma pontuação de TF-IDF. Esta pontuação representa a importância de um termo num documento específico em relação ao corpus completo.

      TF-IDF(t, d) = TF(t, d) * IDF(t)

      O vetorizador TF-IDF tenta atribuir um peso superior às palavras de assinatura no conjunto de dados, como "Camisas" ou "Dino", em comparação com palavras triviais, como "O", "um" ou "de", e contabiliza quantas vezes essas palavras de assinatura são usadas no documento especificado. Cada valor de uma incorporação esparsa representa uma frequência de cada palavra com base nas contagens.

O que é a Reciprocal Rank Fusion?

Para unir os resultados da pesquisa semântica e baseada em tokens, a pesquisa híbrida usa a Reciprocal Rank Fusion (RRF). O RRF é um algoritmo para combinar várias listas classificadas de itens numa única classificação unificada. É uma técnica popular para unir resultados da pesquisa de diferentes fontes ou métodos de obtenção, especialmente em sistemas de pesquisa híbridos e modelos de linguagem (conteúdo extenso).

No caso da pesquisa híbrida da pesquisa vetorial, a distância densa e a distância esparsa são medidas em espaços diferentes e não podem ser comparadas diretamente entre si. Assim, a RRF funciona eficazmente para unir e classificar os resultados dos dois espaços diferentes.

Veja como funciona o RRF:

  1. Classificação recíproca: para cada item numa lista classificada, calcule a respetiva classificação recíproca. Isto significa tomar o inverso da posição (classificação) do artigo na lista. Por exemplo, o item classificado em primeiro lugar recebe uma classificação recíproca de 1/1 = 1, e o item classificado em segundo lugar recebe 1/2 = 0,5.
  2. Soma das classificações recíprocas: soma as classificações recíprocas de cada item em todas as listas classificadas. Isto dá uma pontuação final para cada item.
  3. Ordenar por pontuação final: ordene os itens pela respetiva pontuação final por ordem descendente. Os itens com as pontuações mais elevadas são considerados os mais relevantes ou importantes.

Em resumo, os itens com classificações mais elevadas nos resultados densos e esparsos são apresentados na parte superior da lista. Assim, o artigo "Óculos de sol azuis para crianças da Google" está na parte superior, uma vez que tem classificações mais elevadas nos resultados da pesquisa densos e esparsos. Os itens como "Google White Classic Youth Tee" têm uma classificação baixa, uma vez que só têm classificações no resultado da pesquisa denso.

Como se comporta o parâmetro alfa

O exemplo de como usar a pesquisa híbrida define o parâmetro rrf_ranking_alpha como 0,5 quando cria o objeto HybridQuery. Pode especificar um peso na classificação dos resultados da pesquisa densos e esparsos através dos seguintes valores para rrf_ranking_alpha:

  • 1 ou não especificado: a pesquisa híbrida usa apenas resultados da pesquisa densos e ignora os resultados da pesquisa esparsos.
  • 0: a pesquisa híbrida usa apenas resultados da pesquisa esparsos e ignora os resultados da pesquisa densos.
  • 0 para 1: a pesquisa híbrida une os resultados de vetores densos e esparsos com o peso especificado pelo valor. 0,5 significa que vão ser unidas com o mesmo peso.