Inteligência artificial, sem ruído.
Tutoriais3 min

Como reproduzir BM25, Dense Retrieval e SPLADE em um MacBook de 16 GB

Reprodução prática de três baselines de retrieval no MS MARCO e NFCorpus: crashes, correções e a verificação manual de scores que todo time de RAG deveria fazer.

Como reproduzir BM25, Dense Retrieval e SPLADE em um MacBook de 16 GB

Por que reproduzir baselines de retrieval importa

Times que constroem sistemas RAG dizem coisas como “dense retrieval supera BM25” ou “busca híbrida funciona melhor” o tempo todo — e na maioria das vezes, ninguém envolvido realmente reproduziu o baseline que está sendo comparado. Abdullahi Dattijo decidiu fazer diferente: rodou BM25, dense retrieval (BGE-base) e SPLADE v3 em um MacBook de 16 GB, usando os benchmarks MS MARCO (8,8 milhões de passagens) e NFCorpus, e verificou cada número contra os scores oficiais publicados.

Todos os números bateram. Mas chegar lá exigiu lidar com crash de memória, conflito entre bibliotecas nativas e um modelo com acesso restrito que uma chave de API sozinha não resolve. Nada disso aparece na documentação oficial.

BM25 no MS MARCO: o crash de memória

O primeiro exercício parece simples: construir um índice Lucene invertido sobre 8.841.823 passagens do MS MARCO e buscar com BM25. O problema é que o script bin/run.sh do Anserini define 192 GB de heap máximo para a JVM. Em uma máquina com 16 GB, o sistema operacional mata o processo silenciosamente após indexar 3,4 milhões de documentos — sem exceção Java, apenas um “Killed: 9” no terminal.

A solução foi reduzir o heap para 10 GB e as threads de 9 para 4. A indexação completa rodou em 2 minutos e 16 segundos e produziu um MRR@10 de 0,1874 — idêntico ao valor oficial do leaderboard do MS MARCO para “BM25 (Lucene8, tuned)” até o último dígito.

Dense retrieval e o crash entre duas bibliotecas

A recuperação densa com BGE-base-en-v1.5 (BAAI, licença MIT) usa um índice HNSW pré-construído. Rodar com mais de uma thread de busca causou segmentation fault — a JVM e o PyTorch carregam cada um sua própria cópia da runtime de threading OpenMP, e no macOS as duas colidem. A correção é uma variável de ambiente: export KMP_DUPLICATE_LIB_OK=TRUE mais execução single-thread.

O resultado foi MRR@10 de 0,3521 — uma melhoria de 87,9% sobre o BM25. A diferença é substancial, mas vem com custo: o modelo precisa ser treinado em centenas de milhares de pares query-documento rotulados, enquanto o BM25 não precisa de nenhum dado de treino.

SPLADE: entre os dois mundos

O SPLADE v3 (Naver, licença CC BY-NC-SA 4.0) produz vetores esparsos como o BM25, mas com pesos determinados por uma rede neural treinada, não por uma fórmula fixa. No NFCorpus (3.633 documentos), o nDCG@10 ficou em 0,3624 — acima do BM25 (0,3218) e abaixo do BGE-base (0,3808).

O atrito aqui foi de acesso: o modelo é gated no Hugging Face, e um token de API válido não basta — é preciso solicitar acesso na página do modelo. Além disso, a codificação do corpus levou 68 minutos em CPU, contra 4 minutos do BGE-base. Sem GPU, é um custo real a se considerar.

A verificação que qualquer time de RAG deveria fazer

O ponto mais valioso do artigo não são os números, mas a verificação manual: Dattijo reconstruiu o vetor de pesos BM25 de um documento direto do índice, computou o produto escalar contra o vetor da query e obteve 17,949487686157227 — idêntico ao score retornado pelo Lucene. A mesma verificação funciona para dense retrieval e SPLADE.

Se você mantém um sistema de retrieval ou RAG em produção, a recomendação é clara: antes de trocar prompts, modelos ou adicionar agentes, verifique se os documentos certos estão sendo encontrados. Reproduza um baseline conhecido, verifique um score query-documento manualmente e confirme se sua métrica reflete o comportamento que interessa ao produto. Se você não consegue explicar um resultado recuperado, não pode confiar em mil deles.


Descubra mais sobre noticiAI

Assine para receber nossas notícias mais recentes por e-mail.

R
Sobre o autorRedação Noticiai

Equipe editorial dedicada a explicar inteligência artificial com clareza, independência e contexto.