Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
Skip to content
búsqueda de texto

Construindo um Índice Invertido em Elixir: do Zero ao TF-IDF

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Para construir um índice invertido em Elixir, associe cada termo aos documentos em que aparece e guarde, para cada documento, a frequência desse termo. Depois, use essa estrutura para encontrar candidatos e ordená-los com TF-IDF. Neste tutorial, a convenção de pontuação será tf × log(N / df): simples de calcular e explicar, mas apenas uma das variantes possíveis.

O que o índice invertido armazena

Uma coleção de documentos costuma ser vista como documentos que contêm termos. O índice invertido muda a direção: para cada termo, guarda os documentos que o contêm. A documentação do Elasticsearch resume: “An inverted index is a data structure that maps each token to the documents that contain it.” (documentação de índice invertido).

Para busca booleana simples, cada termo poderia apontar apenas para uma lista de IDs. Para classificar resultados, é útil guardar também a frequência do termo em cada documento. Índices podem ainda armazenar posições, necessárias para recursos como busca de frases. Esses metadados não são a mesma coisa: a frequência do termo, ou tf, conta ocorrências dentro de um documento; a frequência documental, ou df, conta quantos documentos do corpus contêm o termo.

Defina uma coleção e a normalização

Comece com documentos identificados de forma única. Para um exemplo pequeno, uma lista em memória é suficiente:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
documents = [
  %{id: 1, text: "Elixir constrói ferramentas de busca"},
  %{id: 2, text: "Busca textual usa termos e índices"},
  %{id: 3, text: "Elixir usa índices invertidos"}
]

A tokenização precisa ser igual na indexação e na consulta. Esta versão didática converte texto para minúsculas e separa por sequências que não sejam letras ou números, preservando letras Unicode e algarismos:

def tokenize(text) do
  text
  |> String.downcase()
  |> String.split(~r/[^p{L}p{N}]+/u, trim: true)
end

Essa regra é uma simplificação, não um analisador linguístico de português. Ela não remove palavras comuns, não reduz flexões à mesma raiz e não decide como tratar equivalências de acentos. Hífens são separadores; stemming, stop words, normalização Unicode e outras convenções podem exigir regras próprias. Se adaptar a regex ou a normalização, use exatamente a mesma versão na consulta.

Construa postings com frequências

Use um mapa com o formato %{termo => %{id_documento => frequência}}. O mapa interno é a posting list do termo: cada ID aparece no máximo uma vez por termo, com sua frequência como valor. Assim, contar quantos documentos contêm um termo é tão simples quanto obter o tamanho desse mapa.

def index_document(%{id: id, text: text}, index) do
  text
  |> tokenize()
  |> Enum.frequencies()
  |> Enum.reduce(index, fn {term, tf}, acc ->
    postings = Map.get(acc, term, %{})
    Map.put(acc, term, Map.put(postings, id, tf))
  end)
end

def build_index(documents) do
  Enum.reduce(documents, %{}, fn document, index ->
    index_document(document, index)
  end)
end

Enum.frequencies/1 transforma os tokens de um documento em pares de termo e frequência; a redução externa combina esses pares no índice. A documentação do Apache Lucene descreve a relação entre termos e documentos e as estatísticas armazenadas para busca baseada em termos (Index File Formats, Lucene 3.0.3). Essa página é antiga: serve aqui como referência conceitual, não como especificação do formato atual do Lucene.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

IDs devem ser únicos. Se a coleção contiver dois documentos com o mesmo ID, o segundo substituirá a frequência do primeiro naquele termo; valide a entrada se esse caso for possível. Uma coleção vazia produz %{}, sem postings.

Use o índice para recuperar candidatos

Recuperação e classificação são etapas distintas. Primeiro determine quais documentos podem ser resultado; depois some os pesos dos termos da consulta para ordenar esses candidatos. A escolha entre OR e AND muda o conjunto recuperado, não apenas sua ordem.

Consulta OR: qualquer termo pode corresponder

Na consulta OR, inclua um documento se ele aparecer nas postings de pelo menos um termo. Ao percorrer os termos normalizados da consulta, some a contribuição de cada termo encontrado:

def score_or(index, query, document_count) do
  query
  |> tokenize()
  |> Enum.reduce(%{}, fn term, scores ->
    postings = Map.get(index, term, %{})
    df = map_size(postings)

    if df == 0 do
      scores
    else
      idf = :math.log(document_count / df)

      Enum.reduce(postings, scores, fn {id, tf}, acc ->
        Map.update(acc, id, tf * idf, &(&1 + tf * idf))
      end)
    end
  end)
  |> Enum.sort_by(fn {_id, score} -> score end, :desc)
end

Query vazia, texto que só gera separadores ou termos desconhecidos resulta em uma lista vazia. O teste df == 0 evita uma divisão por zero: um termo sem posting não tem documentos candidatos e não contribui para a pontuação. Para construir uma função de consulta reutilizável, passe também a regra OR/AND explicitamente, em vez de deixar a semântica implícita.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Consulta AND: todos os termos precisam corresponder

Para AND, intersecte os conjuntos de IDs das postings dos termos normalizados antes de pontuar. Se qualquer termo não tiver posting, a interseção é vazia. Depois da interseção, some as contribuições TF-IDF dos termos da consulta somente para os IDs restantes. Essa separação torna explícito que um documento pode ter uma pontuação alta para um termo, mas ainda assim ser excluído por não conter outro termo obrigatório.

Escolha e interprete a fórmula TF-IDF

Neste exemplo, a contribuição de um termo t para um documento d é:

tf(t,d) × log(N / df(t))

  • tf(t,d) é a frequência armazenada na posting daquele documento.
  • N é a quantidade de documentos no corpus indexado.
  • df(t) é o número de documentos com posting para o termo.
  • log é o logaritmo natural usado por :math.log/1 no exemplo.

Un documento que repite el término aumenta su contribución, mientras que un término presente en muchos documentos recibe menos peso. Si el término aparece en todos los documentos, log(N / df) = 0 y no distingue documentos en esta variante. En una consulta con varios términos, la función suma las contribuciones; la magnitud final depende de la normalización elegida para tf y de si se normaliza por longitud del documento.

Esta es una convención didáctica, no la única fórmula de TF-IDF ni una afirmación sobre el cálculo actual de Lucene. La API TFIDFSimilarity de Apache Lucene 7.2.0 documenta una variante con frecuencia transformada por raíz cuadrada, IDF suavizado basado en docCount y docFreq, y un factor de normalización por longitud (API TFIDFSimilarity 7.2.0). Las convenciones y versiones importan: no traslade esa fórmula concreta a este ejemplo sin cambiar y explicar el cálculo.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Compruebe la puntuación con cálculos manuales

Con los tres documentos de ejemplo, las postings relevantes son:

  • elixir: documentos 1 y 3; df = 2.
  • busca: documentos 1 y 2; df = 2.
  • índices: documentos 2 y 3; df = 2.
  • usa: documentos 2 y 3; df = 2.

Para esta colección, cada uno de esos términos tiene peso IDF log(3 / 2). La consulta Elixir normaliza al término elixir y devuelve los documentos 1 y 3; como tf = 1 en ambos, empatan. La consulta busca elixir bajo OR devuelve los documentos 1, 2 y 3. El documento 1 contiene ambos términos y suma dos contribuciones; los documentos 2 y 3 contienen uno cada uno y reciben una. Bajo AND, solo el documento 1 contiene ambos términos.

Enum, Stream y el tamaño de la colección

Para unos pocos documentos en memoria, Enum mantiene el recorrido directo: consume enumerables y produce resultados de forma inmediata. Stream compone transformaciones perezosas y puede evitar materializar etapas intermedias si el origen es grande. La documentación oficial de Elixir explica ambos comportamientos y el uso de streams con recursos (módulo Enum y funciones relacionadas).

La pereza no convierte por sí sola este mapa en un índice apto para colecciones ilimitadas: el índice final sigue ocupando memoria. Para documentos leídos de archivos, elija APIs que cierren el recurso correctamente, incluso si el procesamiento se interrumpe. También tendrá que decidir dónde persistir el índice, cómo actualizar postings y qué hacer con eliminaciones; esas decisiones quedan fuera del ejemplo en memoria.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Dónde encaja frente a BM25

TF-IDF es una buena forma de aprender la relación entre frecuencia local y rareza en el corpus, pero no debe presentarse como el patrón predeterminado de producción. La documentación de Elasticsearch indica que BM25 es su similitud por defecto y lo describe como una variación de TF-IDF (configuración de similitud de Elasticsearch). El comportamiento efectivo puede depender de la versión y de la configuración del índice.

Aspecto TF-IDF del ejemplo BM25 según la documentación de Elasticsearch
Frecuencia del término La contribución crece linealmente con el tf sin transformación. La frecuencia se satura: repetir un término sigue influyendo, pero cada repetición adicional aporta menos.
Longitud del documento La fórmula elegida no normaliza por longitud. Incluye normalización por longitud, con ajustes configurables.
Uso por defecto Convención pedagógica de este tutorial; no se afirma como valor por defecto de un motor. La documentación actual de Elasticsearch lo identifica como predeterminado; una configuración o versión concreta puede diferir.

El siguiente paso, si se necesita un buscador real, no es solo cambiar una fórmula: hacen falta decisiones sobre analizadores de texto, persistencia, actualizaciones, recuperación OR/AND, relevancia y configuración de la plataforma de búsqueda.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.