BM25 vs Dense Embeddings: Lessons from Building a Hybrid Search System

Sep 30, 2025

intro

when i started this project, i just wanted to know: “can modern neural embeddings beat BM25 in search?”

BM25 is old-school keyword matching. embeddings are new-school semantic vectors. i thought the answer would be simple: embeddings crush BM25.

spoiler: nope. the real story is more interesting. BM25 is stubbornly strong, embeddings bring new strengths, and the best results come when you let them work together.

Code (Colab).


1. meeting the dataset: SciFact

the SciFact dataset is like a tiny search engine for scientific fact-checking.

crucial insight: this is a many-to-many mapping. one claim can be backed by several papers, and one paper can support multiple claims.

most claims only have 1–5 relevant papers. so if you miss those few, your recall@10 drops to zero.


2. BM25: the keyword warrior

BM25 is from the 1990s, but it’s still everywhere — ElasticSearch, Lucene, even early Google-style engines.

how it works (in human terms):

so “E. coli” in your query is gold, because it’s rare and discriminative. “the” or “study” barely moves the needle.

👉 i think of BM25 as the literalist friend: it listens only for the exact words you use, but it’s very good at remembering them.

my results on SciFact:

krecall@knDCG@k
50.710.61
100.770.63
200.810.64
1000.880.66

pretty solid! BM25 nails queries where claims and papers share exact terms.


3. dense embeddings: the semantic explorer

dense embeddings are the opposite: instead of words, they work in meaning space.

so even if two texts have no words in common, they can still be “close” in meaning.

example:

👉 i think of dense models as the paraphrase friend: they don’t care about exact words, they care about what you meant.

results with FAISS HNSW:

krecall@knDCG@k
50.830.74
100.870.75
200.900.76
1000.970.77

a big improvement over BM25.


4. pause: recall@k and nDCG explained

metrics confused me at first, so i broke them down with analogies.

so recall = “found it?” and nDCG = “found it quickly?”

dense embeddings improved both, which told me they were finding relevant papers and ranking them earlier.


5. HNSW: the shortcut graph

dense embeddings are great, but searching them with FAISS Flat = scanning everything = slow at scale.

enter HNSW (Hierarchical Navigable Small World graphs). it’s like building a maze of shortcuts through the embedding space.

two dials to tune:

👉 i think of HNSW as the friend who knows shortcuts in your city:

when i swept M ∈ 64 and efSearch ∈ 256, i saw:


6. RRF fusion: why not both?

dense beat BM25 overall, but BM25 sometimes nailed rare keywords dense missed.

so i fused them with Reciprocal Rank Fusion (RRF):

intuition:

👉 i think of RRF as the group project rule: if either partner says “this doc is important,” it gets noticed.

results:

krecall@knDCG@k
50.780.69
100.840.71
200.890.73
1000.970.75

hybrid recall@100 = 0.973, slightly better than dense alone. nDCG sits nicely between the two.


7. error analysis: when hybrid wins

in some queries, hybrid beat both baselines.

pattern:

it’s like two friends searching with different styles, but together they cover more ground.


8. latency: making it practical

so hybrid is not just accurate, it’s also practical for real systems.


9. what i learned


closing

i started with: “can embeddings beat BM25?” i ended with: “why not both?”

for me, the best part was realizing old and new methods aren’t rivals — they’re teammates.

if you’re a student curious about IR: pick a dataset like SciFact, try BM25, try embeddings, build an HNSW index, then fuse them with RRF. you’ll learn a ton about how search engines actually work.