Recherche dense ou creuse : compromis
Comprenez quand les plongements denses manquent des correspondances exactes de mots-clés et quand BM25 manque des paraphrases sémantiques, ainsi que les raisons pour lesquelles leur combinaison surpasse systématiquement chacune des deux approches utilisée seule.
Recherche dense ou creuse : compromis est une leçon AI Engineering Academy gratuite sur CoddyKit. Ceci est la leçon 1 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Engineering Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Engineering Academy comprend 4 leçons au total.
Certaines parties de cette leçon n'ont pas encore été traduites et s'affichent en anglais.
Two Fundamentally Different Retrieval Signals
Modern retrieval systems rely on two distinct signals: dense retrieval encodes meaning into continuous vector spaces, while sparse retrieval counts exact term occurrences. These signals are complementary, not interchangeable. Understanding their individual strengths and weaknesses is the first step toward building a system that uses both effectively.
How Dense Embeddings Work
Dense retrieval maps both the query and each document into a high-dimensional vector using a neural encoder. Similarity is measured by cosine distance or dot product between vectors. Because the encoder was trained on large text corpora, semantically related phrases end up near each other in vector space even if they share no common words — this is the key advantage of dense retrieval.
from openai import OpenAI
import numpy as np
client = OpenAI()
def embed(text: str) -> list[float]:
resp = client.embeddings.create(
model='text-embedding-3-small',
input=text,
)
return resp.data[0].embedding
def cosine_similarity(a, b):
a, b = np.array(a), np.array(b)
return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
q = embed('How do I cancel my subscription?')
d = embed('Steps to unsubscribe from the service')
print(cosine_similarity(q, d)) # high similarity despite different wordsWhere Dense Retrieval Fails
Dense models struggle with rare terms that were underrepresented during encoder training. A query containing a specific product model number like RTX-4090-Ti-OC, a medical drug name, or a proprietary internal identifier will often fail to match the correct document because the encoder has no learned representation for that token sequence. The vector just lands somewhere unhelpful in embedding space.
How Sparse BM25 Retrieval Works
BM25 (Best Matching 25) is a probabilistic ranking function that scores documents based on how often query terms appear in the document, normalized for document length and dampened by term frequency saturation. It produces a sparse score vector — most dimensions are zero because documents contain only a small fraction of the vocabulary.
# BM25 scoring formula (conceptual)
# score(D, Q) = sum over query terms t of:
# IDF(t) * (tf(t,D) * (k1 + 1)) / (tf(t,D) + k1 * (1 - b + b * |D|/avgdl))
# k1 controls term frequency saturation (typically 1.2-2.0)
# b controls document length normalization (typically 0.75)
# IDF(t) = log((N - df(t) + 0.5) / (df(t) + 0.5))
# N = total documents, df(t) = documents containing term t
# tf(t,D) = frequency of t in document D, |D| = doc length, avgdl = average doc lengthBM25 Strengths: Exact Terms and Jargon
BM25 excels at queries involving exact technical terms, product names, error codes, and numeric identifiers that should match precisely. A query for ORA-01017 (an Oracle error code) will rank documents containing that exact string far above documents that merely discuss database authentication in general terms. This is impossible for a dense model that has never seen that specific code.
from rank_bm25 import BM25Okapi
corpus = [
'Oracle database ORA-01017 invalid username or password logon denied',
'Database authentication and connection troubleshooting guide',
'How to resolve login errors in Oracle and MySQL databases',
]
tokenized_corpus = [doc.lower().split() for doc in corpus]
bm25 = BM25Okapi(tokenized_corpus)
query = 'ORA-01017 error fix'
scores = bm25.get_scores(query.lower().split())
print(dict(zip(range(len(corpus)), scores)))
# doc 0 scores highest because it contains ORA-01017Where BM25 Fails: Paraphrases and Synonyms
BM25 is blind to semantic paraphrasing. A document about 'automobile engine repair' will score zero for a query about 'car motor maintenance' because none of the exact words overlap. This vocabulary mismatch problem, sometimes called the lexical gap, means pure keyword search misses huge amounts of relevant content that simply uses different words to express the same idea.
from rank_bm25 import BM25Okapi
corpus = [
'automobile engine repair and maintenance tips',
'car motor maintenance guide for beginners',
'vehicle powertrain service intervals',
]
tokenized = [doc.split() for doc in corpus]
bm25 = BM25Okapi(tokenized)
scores = bm25.get_scores(['car', 'motor', 'maintenance'])
print(scores)
# doc 1 scores high, doc 0 and 2 score lower despite being semantically relatedBenchmark Evidence: Hybrid Wins Consistently
Benchmarks on BEIR, MS MARCO, and enterprise Q&A datasets consistently show that hybrid retrieval outperforms either dense or sparse alone by 5-15 percent on NDCG@10. The improvement is largest on datasets with a mix of factual lookups (where BM25 helps) and paraphrase queries (where dense embeddings help). No single retrieval method dominates across all query types.
Query Type Analysis: Which Retriever Wins
You can predict which retriever will perform better by analyzing the query type. Dense retrieval wins on conceptual questions, paraphrases, and broad topic queries. BM25 wins on queries containing proper nouns, version numbers, code snippets, acronyms, and rare technical terms. Hybrid always wins when query type is unknown in advance — which is almost always true in production.
# Query type heuristics
def predict_retriever_advantage(query: str) -> str:
tokens = query.split()
has_numbers = any(t[0].isdigit() for t in tokens)
has_uppercase_acronyms = any(t.isupper() and len(t) > 2 for t in tokens)
is_short = len(tokens) <= 4
if has_numbers or has_uppercase_acronyms:
return 'BM25 likely wins (exact terms)'
elif is_short:
return 'Dense likely wins (semantic matching needed)'
else:
return 'Hybrid recommended (mixed signals)'Score Incompatibility Problem
Combining dense and sparse results is non-trivial because their scores are on incompatible scales. Cosine similarity produces values between -1 and 1, while BM25 produces unbounded positive scores that depend on corpus size. You cannot simply add them. The standard solution is to use rank-based fusion rather than score-based fusion — merging ranked lists instead of raw scores.
Practical Decision: When to Use Each
Use dense-only retrieval when your corpus is in a narrow domain with consistent vocabulary and you need semantic generalization across paraphrases. Use BM25-only when queries are primarily lookup-style with exact identifiers and your dataset is small enough that brute-force is feasible. Use hybrid in all production RAG systems where query types vary — the overhead is modest and the recall improvement is significant.
Performance and Infrastructure Trade-offs
Dense retrieval requires GPU-accelerated approximate nearest neighbor search or a vector database, which adds infrastructure cost. BM25 runs entirely on CPU with an inverted index and is extremely fast. Hybrid retrieval requires both infrastructure components plus a fusion step. The added complexity is justified by the recall improvement for most production use cases, but must be weighed against your infrastructure budget.
Quick Check
Test your understanding of dense versus sparse retrieval trade-offs from this lesson.
Lesson Recap
In this lesson you learned: dense retrieval captures semantic meaning but fails on rare exact terms, BM25 sparse retrieval handles exact keywords but misses paraphrases, and hybrid retrieval consistently outperforms either method alone across diverse query types. Their scores are incompatible and must be merged via rank fusion rather than score addition. Next up we implement BM25 keyword search in Python.
Questions Fréquemment Posées
La leçon « Recherche dense ou creuse : compromis » est-elle gratuite ?
Oui — le texte complet de « Recherche dense ou creuse : compromis » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Engineering Academy, passe à CoddyKit PRO. Le cours AI Engineering Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Recherche dense ou creuse : compromis » ?
Comprenez quand les plongements denses manquent des correspondances exactes de mots-clés et quand BM25 manque des paraphrases sémantiques, ainsi que les raisons pour lesquelles leur combinaison surpa… Tu pratiques AI Engineering Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer AI Engineering Academy ?
Aucune expérience préalable n'est requise. AI Engineering Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 4.
Combien de temps prend la leçon « Recherche dense ou creuse : compromis » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon AI Engineering Academy ?
Oui. Chaque leçon AI Engineering Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Recherche dense ou creuse : compromis
- Implémenter une recherche par mots-clés avec BM25
- Fusion réciproque des rangs pour combiner les scores
- Recherche hybride avec Pinecone et pgvector