Recuperação de Informação

O que é Recuperação de Informação?

Recuperação de Informação (Information Retrieval, IR) é a disciplina e o conjunto de sistemas para encontrar itens relevantes (documentos, passagens, produtos, imagens, código etc.) em resposta à consulta (query) de um usuário. Exemplos clássicos incluem busca na web, busca em sites e busca em e-commerce. Em sistemas modernos de processamento de linguagem natural (natural language processing, NLP), a IR também é um bloco fundamental para geração aumentada por recuperação (retrieval-augmented generation, RAG), perguntas e respostas e pipelines de análises.

A IR costuma ser formulada como três problemas conectados:

  1. Indexação (indexing): como organizamos um corpus para que possamos pesquisá-lo com eficiência?
  2. Ranqueamento (ranking): dada uma consulta, como pontuamos e ordenamos resultados candidatos por relevância?
  3. Avaliação (evaluation): como medimos se a recuperação é “boa” (offline e online)?

Este artigo foca nos fundamentos dessas três áreas, conectando-as a abordagens modernas densas/semânticas (veja Busca Semântica (Semantic Search) e Modelos de Embeddings (Embedding Models)).

Fundamentos de indexação

Documentos, campos e unidades de recuperação

Antes de construir um índice, defina a unidade de recuperação (retrieval unit):

  • Recuperação em nível de documento (document-level retrieval): retorna documentos inteiros (páginas web, PDFs).
  • Recuperação em nível de passagem (passage-level retrieval): retorna trechos/parágrafos (comum em geração aumentada por recuperação).
  • Recuperação por campos (fielded retrieval): documentos têm campos estruturados (título, corpo, tags). Cada campo pode ser indexado de forma diferente e receber pesos diferentes.

Exemplo prático: para um motor de busca de notícias, você pode indexar title, body e published_date. Uma consulta como “election results” deve atribuir um peso alto a correspondências no título.

Pipeline de processamento de texto

A IR clássica depende de correspondência lexical, então decisões de pré-processamento afetam tanto o tamanho do índice quanto a qualidade da recuperação:

  • Tokenização (tokenization): divisão do texto em termos. Isso é enganadamente complexo para textos multilíngues e domínios com muita pontuação; veja Mergulho Profundo em Tokenização.
  • Normalização (normalization): conversão para minúsculas, normalização Unicode, tratamento de acentos.
  • Palavras de parada (stopwords): opcionalmente remover palavras funcionais frequentes (“the”, “of”). Muitos sistemas modernos mantêm essas palavras porque consultas por frase e modelos de ranqueamento podem se beneficiar.
  • Radicalização/Lematização (stemming/lemmatization): reduz formas flexionadas (“running” → “run”). Ajuda a revocação, pode prejudicar a precisão.
  • Sinônimos / expansão (synonyms / expansion): reescrita opcional no momento da consulta (ou no momento da indexação), especialmente útil em busca de domínio (por exemplo, “heart attack” ↔ “myocardial infarction”).

Essas escolhas devem ser validadas por avaliação, não apenas por intuição.

Índice invertido (indexação esparsa / lexical)

A estrutura clássica para busca textual é o índice invertido (inverted index), que mapeia cada termo para uma lista de ocorrências (postings list) de documentos (e, opcionalmente, posições) que contêm o termo.

  • Dicionário: termos → metadados das ocorrências
  • Lista de ocorrências: para cada termo, uma lista como [(docID, termFrequency, positions...)]

Por que funciona: para uma consulta com termos {t1, t2, ...}, recupere documentos candidatos lendo e mesclando listas de ocorrências desses termos, em vez de varrer todo o corpus.

Exemplo: construindo um índice invertido simples

from collections import defaultdict

docs = {
    1: "information retrieval is about search",
    2: "retrieval models rank documents",
    3: "search engines use inverted indexes",
}

def tokenize(text):
    return [t.lower() for t in text.split()]

index = defaultdict(list)  # term -> list of (doc_id, tf)

for doc_id, text in docs.items():
    tf = defaultdict(int)
    for term in tokenize(text):
        tf[term] += 1
    for term, freq in tf.items():
        index[term].append((doc_id, freq))

# index["retrieval"] -> [(1, 1), (2, 1)]

Sistemas reais armazenam ocorrências em formatos binários comprimidos e mantêm dados adicionais (comprimentos de documentos, comprimentos de campos, estatísticas de termos).

Índices posicionais e consultas por frase

Se você quiser busca por frase (por exemplo, "information retrieval"), armazene posições (positions) de termos:

  • ocorrências: (docID, [pos1, pos2, ...])

A verificação de frases então passa a ser a validação de se as posições de termos consecutivos se alinham (por exemplo, posições de “retrieval” são exatamente +1 em relação a “information”).

Índices posicionais também ajudam na pontuação por proximidade (proximity scoring) (“termos aparecem perto um do outro”)—frequentemente um sinal forte de relevância.

Compressão de índice e eficiência (por que isso importa)

Em escala de web, listas de ocorrências são enormes, então índices dependem de:

  • Codificação por lacunas (gap (delta) encoding) de docIDs ordenados (armazenar diferenças em vez de IDs absolutos)
  • codificação de inteiros por byte variável (variable-byte) ou por empacotamento de bits (bit-packed)
  • armazenamento baseado em blocos (block-based storage) para saltos eficientes
  • ponteiros de salto (skip pointers) para acelerar interseção em consultas AND

Mesmo se você usar um motor de busca gerenciado (managed search engine) (Elasticsearch/OpenSearch/Solr), entender esses conceitos ajuda a diagnosticar problemas de desempenho (picos de latência, pressão de memória) e ajustar tamanhos de fragmentos (shard sizes) e políticas de mesclagem (merge policies).

Indexação vetorial (vector indexing) (recuperação densa (dense retrieval))

A IR moderna frequentemente recupera por similaridade de embeddings em vez de sobreposição lexical (veja Busca Semântica). Aqui, documentos (ou passagens) são mapeados para vetores, e consultas são embutidas de forma similar. A recuperação se torna uma busca do vizinho mais próximo (nearest neighbor search).

Como a busca exata do vizinho mais próximo é cara em escala, sistemas usam índices de Busca Aproximada de Vizinhos Mais Próximos (Approximate Nearest Neighbor, ANN):

  • Baseados em grafo: HNSW (Hierarchical Navigable Small World)
  • Sistemas de arquivo invertido: IVF (frequentemente em conjunto com quantização de produto (product quantization))
  • Métodos baseados em árvore/partição (menos comuns para embeddings de texto de alta dimensionalidade)

Conclusão prática: índices vetoriais trocam exatidão por velocidade; você ajusta parâmetros para revocação vs latência.

Indexação híbrida

Muitos motores de busca em produção usam recuperação híbrida (hybrid retrieval):

  • índice esparso (sparse index) (BM25) para correspondências lexicais exatas e robustez
  • índice denso (dense index) para correspondência semântica e paráfrases

A abordagem híbrida frequentemente melhora a revocação e a satisfação do usuário, especialmente para consultas curtas e termos específicos de domínio.

Funções de ranqueamento

O ranqueamento é o coração da IR: dados candidatos, compute um escore de relevância e ordene.

A visão de modelo de recuperação

Funções de ranqueamento em IR normalmente são derivadas de uma destas perspectivas:

  • Espaço vetorial / similaridade (vector space / similarity) (similaridade cosseno entre vetores de termos ponderados)
  • Relevância probabilística (probabilistic relevance) (probabilidade de um documento ser relevante dada uma consulta)
  • Modelagem de linguagem (language modeling) (probabilidade de o modelo do documento gerar a consulta)
  • Aprendizado para ranquear (learning to rank, LTR) (treinar um modelo a partir de rótulos de relevância ou cliques)
  • Similaridade neural / por embeddings (neural / embedding similarity) (recuperação densa e codificadores cruzados)

A maioria dos sistemas reais é em múltiplas etapas (multi-stage):

  1. Recuperação de primeira etapa (first-stage retrieval): rápida, alta revocação (BM25 ou ANN denso) → top 100–5000
  2. Reranqueamento (reranking): mais lento, mais preciso (por exemplo, modelo de LTR, codificador cruzado)

TF-IDF e similaridade cosseno (baseline clássico)

Um documento e uma consulta são representados como vetores sobre termos do vocabulário:

  • TF: frequência de termo (term frequency, TF) em um documento
  • IDF: frequência inversa de documento (inverse document frequency, IDF): termos mais raros pesam mais

Uma forma comum:

  • tfidf(t, d) = tf(t,d) * log(N / df(t))

Pontue com similaridade cosseno entre vetores de consulta e documento.

TF-IDF é conceitualmente simples e ainda útil, mas a busca lexical moderna normalmente usa BM25.

BM25 (o “carro-chefe” do ranqueamento lexical)

BM25 é uma função de ranqueamento forte e amplamente usada, derivada de IR probabilística. Ela melhora em relação a TF-IDF ao adicionar saturação da frequência de termo (term frequency saturation) e normalização do comprimento do documento (document length normalization).

Uma forma comum de pontuação do BM25:

[ \text{score}(q,d)=\sum_{t\in q} \text{IDF}(t)\cdot \frac{tf(t,d)\cdot (k_1+1)}{tf(t,d)+k_1\cdot (1-b+b\cdot \frac{|d|}{\text{avgdl}})} ]

Onde:

  • tf(t,d) é a contagem do termo
  • |d| é o comprimento do documento, avgdl é o comprimento médio dos documentos
  • k1 controla a saturação de TF (frequentemente ~1.2–2.0)
  • b controla a normalização por comprimento (frequentemente ~0.75)

Intuição prática

  • Repetir um termo 50 vezes não é 50× mais relevante do que vê-lo algumas vezes → saturação.
  • Documentos muito longos naturalmente contêm mais termos → normalização para que documentos longos não sejam favorecidos injustamente.

Exemplo mínimo de BM25 (ilustrativo)

import math
from collections import Counter

def bm25_score(query_terms, doc_terms, df, N, avgdl, k1=1.5, b=0.75):
    score = 0.0
    doc_len = len(doc_terms)
    tf = Counter(doc_terms)

    for t in query_terms:
        if t not in tf:
            continue
        # a common IDF variant (with smoothing)
        idf = math.log(1 + (N - df.get(t, 0) + 0.5) / (df.get(t, 0) + 0.5))
        denom = tf[t] + k1 * (1 - b + b * doc_len / avgdl)
        score += idf * (tf[t] * (k1 + 1)) / denom
    return score

Implementações reais também tratam:

  • frequência de termo na consulta (query term frequency) (às vezes)
  • BM25 por campos (fielded BM25) (pesos diferentes para título/corpo)
  • bônus de proximidade de termos (term proximity boosts)

Probabilidade da consulta (abordagem de modelagem de linguagem)

No modelo de probabilidade da consulta (query likelihood model), cada documento define um modelo de linguagem (language model), e você pontua o quão provável seria esse modelo gerar a consulta:

[ \text{score}(q,d) = \sum_{t \in q} \log P(t \mid d) ]

Como muitos termos da consulta não aparecerão em um documento, você precisa de suavização (smoothing) (por exemplo, suavização de Dirichlet (Dirichlet smoothing)). Embora seja menos comum do que BM25 em produtos mainstream, a modelagem de linguagem oferece uma lente teórica útil e influenciou muitos modelos posteriores.

Aprendizado para Ranquear (LTR)

Em vez de fórmulas desenhadas à mão, o Aprendizado para Ranquear treina um modelo para pontuar documentos usando atributos (features) como:

  • escore BM25, atributos de TF-IDF
  • correspondências de campos (correspondência no título, correspondência em texto âncora)
  • recência, popularidade, sinais do tipo PageRank (PageRank-like signals)
  • sinais derivados de cliques (cuidadosamente desenviesados (de-biased))
  • atributos de similaridade densa

Objetivos de LTR geralmente são:

  • pontual (pointwise) (prever rótulo de relevância)
  • pareado (pairwise) (preferir doc A em vez de doc B)
  • por lista (listwise) (otimizar métricas de ranqueamento como NDCG)

LTR moderno frequentemente usa árvores com boosting de gradiente (gradient-boosted trees) (por exemplo, LambdaMART) devido à eficiência e ao forte desempenho.

Ranqueamento neural e reranqueamento

Ranqueamento neural normalmente aparece em dois lugares:

  1. Recuperação densa (bi-codificadores (bi-encoders)): embutir consulta e documentos; pontuar por produto escalar (dot product) / similaridade cosseno.
  2. Reranqueamento neural (codificadores cruzados (cross-encoders)): alimentar [query, document] em conjunto em um transformer (transformer) e prever relevância.

Codificadores cruzados são mais precisos, mas caros; eles geralmente são aplicados aos top K candidatos. Essa abordagem se conecta a Arquitetura Transformer (Transformer Architecture) e ao treinamento moderno de embeddings em Modelos de Embeddings.

Pipelines de recuperação em duas etapas (e em múltiplas etapas)

Uma arquitetura comum em produção:

  • Geração de candidatos (candidate generation) (rápida):
    • BM25 top 2000
    • ANN denso top 2000
    • união/mesclagem → 4000 candidatos
  • Reranqueamento (mais lento):
    • LTR / codificador cruzado reranquear top 200–500
  • Pós-processamento:
    • desduplicação, diversidade, regras de negócio
    • filtros (idioma, segurança, controle de acesso)

Essa estrutura equilibra latência, custo e relevância.

Fundamentos de avaliação de recuperação

A avaliação responde: os usuários estão obtendo o que precisam? A avaliação em IR é sutil porque relevância é parcialmente subjetiva, a intenção da consulta varia e vieses de interação podem distorcer sinais.

Julgamentos de relevância e coleções de teste

A avaliação offline normalmente precisa de:

  • Um conjunto de consultas (tópicos)
  • Um corpus de documentos
  • julgamentos de relevância (relevance judgments): para cada consulta, quais documentos são relevantes (binário ou graduado)

Metodologia clássica:

  • Formação de pool (pooling): pegar os top resultados de múltiplos sistemas, julgar o pool e assumir que documentos não julgados são não relevantes (uma aproximação).
  • Medir sistemas no conjunto julgado.

Esta é a base da avaliação no estilo TREC e permanece influente.

Métricas de matriz de confusão: precisão e revocação

Para uma consulta:

  • Precisão = relevantes recuperados / recuperados
  • Revocação = relevantes recuperados / relevantes disponíveis

Em recuperação ranqueada (ranked retrieval), normalmente nos importamos com Precision@k e Recall@k porque usuários só veem os primeiros resultados.

Exemplo prático: se os top 10 resultados contêm 7 documentos relevantes, então Precision@10 = 0.7.

Métricas sensíveis ao ranking (as mais usadas em IR)

Ranking Recíproco Médio (Mean Reciprocal Rank, MRR)

Útil quando o usuário quer uma boa resposta (QA, navegação):

  • rank recíproco (reciprocal rank) = 1 / (rank of first relevant result)
  • MRR = média do rank recíproco ao longo das consultas

Se o primeiro resultado relevante está na posição 1 → 1.0; na posição 5 → 0.2.

Precisão Média (Average Precision, AP) e Média da Precisão Média (Mean Average Precision, MAP)

AP recompensa recuperar documentos relevantes cedo:

[ AP = \frac{1}{R}\sum_{k=1}^{n} P@k \cdot rel(k) ]

Onde rel(k)=1 se o item na posição k é relevante, senão 0; R é o total de documentos relevantes para aquela consulta.

MAP é a média de AP ao longo das consultas.

MAP é comum em IR acadêmica; assume relevância binária e pode ser rígida quando julgamentos são incompletos.

NDCG (Ganho Cumulativo Descontado Normalizado (Normalized Discounted Cumulative Gain, NDCG))

NDCG suporta relevância graduada (graded relevance) (por exemplo, 0=irrelevante, 1=relacionado, 2=muito relevante) e desconta posições mais baixas:

[ DCG@k = \sum_{i=1}^{k} \frac{2^{rel_i}-1}{\log_2(i+1)} ]

Normalize pelo DCG ideal (ordenado pela melhor relevância) para obter NDCG@k em [0,1].

NDCG é amplamente usada em busca na web e ranqueamento de recomendadores porque corresponde à atenção do usuário concentrada no topo.

Revocação@k (Recall@k)

Especialmente importante para recuperação alimentando modelos downstream (por exemplo, geração aumentada por recuperação): se o recuperador não trouxer a evidência necessária, o gerador não consegue usá-la.

  • Recall@k pergunta: recuperamos a evidência relevante em algum lugar no top k?

Em cenários de geração aumentada por recuperação, você pode acompanhar Recall@50 ou Acerto@k (Hit@k) (pelo menos um acerto relevante).

Um cálculo concreto de métrica (exemplo simplificado)

Suponha que, para uma consulta, os top 5 resultados tenham rótulos de relevância:

[2, 1, 0, 2, 0] (graduado), e calculamos DCG@5:

  • rank 1: (2^2−1)/log2(2) = 3/1 = 3
  • rank 2: (2^1−1)/log2(3) = 1/1.585 = 0.631
  • rank 3: 0
  • rank 4: 3/log2(5)= 3/2.322 = 1.292
  • rank 5: 0

DCG@5 ≈ 4.923

Para obter NDCG@5, compute a ordenação ideal [2,2,1,0,0] e divida pelo DCG correspondente.

Avaliação offline vs online

Métricas offline são essenciais para iteração rápida, mas são proxies. O comportamento online pode diferir devido a:

  • qualidade de trechos (snippets)
  • confiança do usuário e viés de posição
  • reformulação de consultas (query reformulation) e sessões em múltiplos passos
  • efeitos de latência e disponibilidade

Métodos de avaliação online incluem:

  • testes A/B (A/B tests): randomizar usuários em controle vs tratamento, medir taxa de cliques (click-through rate), taxa de sessões bem-sucedidas (successful session rate), tempo até o sucesso (time to success), retenção (retention).
  • intercalação (interleaving): misturar resultados de dois ranqueadores em uma lista para comparar com menos amostras.

Dados de clique devem ser interpretados com cuidado devido a:

  • viés de posição (position bias) (resultados no topo recebem mais cliques independentemente da relevância)
  • viés de apresentação (presentation bias) (snippets, títulos)
  • viés de seleção (selection bias) (usuários não examinam todos os resultados)

Abordagens de remoção de vieses (debiasing approaches) (modelos de clique (click models), intervenções aleatorizadas (randomized interventions)) frequentemente são necessárias em pipelines sérios de LTR.

Armadilhas comuns de avaliação

  • Incompatibilidade de métrica: otimizar NDCG@10 quando o produto precisa “encontrar qualquer resposta rápido” (MRR) ou “recuperar evidência para downstream” (Recall@50).
  • Julgamentos incompletos: documentos relevantes não julgados contados como não relevantes podem distorcer métricas do tipo MAP.
  • Overfitting a consultas de benchmark: melhorias não generalizam.
  • Ignorar latência/custo: um pequeno ganho em NDCG pode não valer 10× mais computação.
  • Pré-processamento com vazamento: diferenças de tokenização/normalização entre indexação e consulta podem prejudicar silenciosamente a revocação.

Para uma perspectiva mais ampla sobre escolha de métricas e trade-offs em sistemas de NLP, veja Avaliação para NLP.

Juntando tudo: uma pilha prática de recuperação

Uma pilha típica moderna para recuperação de texto em uma aplicação de NLP:

  1. Ingestão (ingestion) e pré-processamento
    • limpar texto, dividir em passagens
    • tokenizar/normalizar de forma consistente
  2. Indexação
    • construir índice invertido para BM25
    • computar embeddings e construir índice ANN (opcional)
  3. Geração de candidatos
    • BM25 top N
    • ANN top N
    • mesclar + desduplicar
  4. Reranqueamento
    • modelo de LTR ou codificador cruzado transformer sobre top K
  5. Avaliação
    • offline: NDCG@10, MRR, Recall@k (dependente da tarefa)
    • online: testes A/B, orçamentos de latência, monitoramento de deriva (drift)

Esse pipeline se conecta naturalmente a Representação de Texto (Text Representation) (como texto vira atributos/vetores) e Modelos de Embeddings (como representações densas são treinadas e avaliadas).

Principais conclusões

  • Indexação determina o que pode ser recuperado com eficiência: índices invertidos para busca lexical; estruturas ANN para busca vetorial; abordagens híbridas combinam ambos.
  • Ranqueamento geralmente é em múltiplas etapas: BM25/recuperação densa para revocação, depois reranqueamento para precisão.
  • Avaliação deve corresponder à noção de sucesso do produto: use métricas sensíveis ao ranking (MRR, NDCG, MAP) e meça revocação quando a recuperação alimenta modelos downstream.
  • Sistemas fortes de IR equilibram relevância, latência, robustez e manutenibilidade—não apenas uma única métrica em um benchmark.