The lexical side: BM25 and hybrid search
Semantic search, brilliant on paraphrases, misses an exact invoice number. How do you marry vectors to the world of exact words, without one score scale crushing the other?
In the previous chapter, we made the index durable and verified it: it survives restarts, and a round-trip oracle proves we read it back faithfully. All the search we have built since the beginning of this course rests on a solid foundation. But it also rests on one thing alone: meaning, captured by vectors. And there are queries where meaning, precisely, is not enough.
Imagine your personal document base, and this query: “invoice 8831”. You are not looking for a document that vaguely talks about billing. You are looking for invoice number 8831 - that exact one. Yet to a semantic engine, “8831” is just another point in space, a neighbor of “8830” and “2207”. It will hand you a pile of very similar invoices, unable to tell the right one apart. Semantic search just ran into what it cannot see: the letter.
Two blind spots, not one
The reflex would be to say: “let’s go back to good old keyword search - it finds the exact number.” True, but we would fall back into the trap that opened this course. Keyword search is blind to meaning: ask it “cancel a settlement,” it will never find a document titled “procedure for refunding an order,” because not a single word is shared. That is the vocabulary mismatch Vocabulary mismatch A situation where two texts discuss the same thing using entirely different words. For example, 'cancel a payment' and 'refund procedure' share no words: a purely lexical search treats them as unrelated, whereas semantic search brings them together. : two texts can be about the same thing using entirely different words.
So we have two blind spots, symmetrical and complementary:
- Semantic search excels at paraphrases and synonyms, but floats on exact identifiers, rare proper nouns, literal references. It sees meaning, not letters.
- Lexical search (by words) pins the exact token every time, but is deaf to synonyms. It sees letters, not meaning.
The idea of this chapter is to refuse the choice. Rather than pitting the two worlds against each other, we will make them collaborate: that is hybrid search Hybrid search A search strategy that combines a semantic signal (meaning proximity between vectors) and a lexical signal (exact word matching, e.g. BM25), then fuses their rankings. It aims to cover the complementary blind spots of both approaches: synonyms for meaning, exact identifiers for the letter. . But before marrying two scores, we first need to know how to build a good one on the lexical side. That is the role of BM25.
BM25, the score of the letter
How do you rate how well a document “fits” a keyword query? The basic intuition: the more a query word appears in the document, the more relevant that document is. This counting is called term frequency Term frequency The number of times a term appears in a given document (TF). The higher it is, the more the document seems to be about that term, but BM25 saturates this gain: the tenth occurrence adds far less than the first. . But this raw intuition leads to three pitfalls, and BM25 BM25 The reference lexical scoring function in information retrieval (Best Matching 25). It rates how relevant a document is to a keyword query by combining three ingredients: saturated term frequency, term rarity (IDF) and a document-length normalization. Source: Robertson & Zaragoza, 2009 (for “Best Matching 25”) is precisely the formula that corrects all three. Let us walk through them one by one.
First pitfall: not all words are equal. If your query is “the consulting contract,” the word “the” appears in almost every document: it discriminates nothing. The word “consulting,” on the other hand, is rare and therefore very informative. BM25 weights each term by its rarity through the inverse document frequency Inverse document frequency A weight that measures how rare a term is across the corpus (IDF). It grows when the term appears in few documents: a rare, discriminating word weighs heavily, while a common word found everywhere weighs almost nothing. Source: Spärck Jones, 1972 , or IDF:
This equation reads: the weight of term is the logarithm of a quantity that grows when , the number of documents containing , is small. In other words, a term present in few documents (like “8831”) receives a large weight; a term present everywhere receives a tiny one. The rare speaks loudly, the commonplace goes quiet.
Second pitfall: the tenth occurrence is not worth the first. A document containing “consulting” five times is more relevant than one containing it only once. But is it five times more relevant? No. The gain must taper off: going from one to two occurrences counts a lot, going from nine to ten counts almost nothing. BM25 makes term frequency saturate via a parameter , which controls the speed at which this ceiling is reached.
Third pitfall: long documents cheat. A very long document mechanically has more chances of containing any given word, without being any more relevant. BM25 corrects this bias with length normalization Length normalization A correction that stops long documents from cheating. At equal term frequency, a document longer than the corpus average is mechanically more likely to contain any given word; BM25 penalizes it through the parameter b (0 = no correction, 1 = full correction). , dosed by a parameter : at equal frequency, a document longer than average is penalized. Everything comes together in a single formula:
Read it as follows: the score of document for query is the sum, over each term in the query, of its rarity weight multiplied by a saturated frequency. In the numerator, is the number of times appears in . In the denominator, the term compares the document length to the average corpus length: that is what penalizes long documents. Typical values are and .
The fusion trap
We now know how to produce two scores for the same document: a lexical BM25 score, and a semantic score via cosine similarity (the one from chapter 1). The temptation is immediate: add them up, and the best of both worlds will emerge.
That is a trap, and a subtle one. The two scores do not live on the same scale. Cosine similarity is bounded in . BM25 is not bounded: it grows with term rarity and count, and commonly reaches values of 2, 10, 15. Adding (an excellent cosine) to (a strong BM25) gives : the cosine is drowned, it no longer matters. The naive sum does not merge two signals - it lets the noisiest one crush the other. In practice, it behaves almost like a purely lexical search in disguise.
Fusion by rank: RRF
The remedy is almost insolently elegant: if the problem comes from score scales, then let us stop using scores altogether. Let us use only ranks. Whether you are first with a cosine of or first with a BM25 of , you are first, full stop. That is the idea behind Reciprocal Rank Fusion Reciprocal rank fusion A method for fusing several rankings (RRF). A document's score is the sum, over each ranking, of 1 / (k + rank), with k often set to 60. Because it uses only ranks and never raw scores, it is scale-invariant: ideal for fusing a bounded cosine and an unbounded BM25. Source: Cormack et al., 2009 (RRF).
This equation reads: the fused score of document is the sum, over each ranking (here the lexical and the semantic), of the inverse of , where is the rank of in ranking . Being ranked high (small rank) gives a large contribution; being ranked low gives a small one. The constant , classically set to , dampens the gap between the very top positions so that a single ranking cannot decide everything on its own.
The beauty of this formula is that it never reads a raw score. Multiply all BM25 values by a thousand: the ranks do not move, so the fused result does not move. RRF is scale-invariant by construction. A document that gets rewarded must be well ranked in both worlds at once; that is exactly what we expect from a good fusion.
Your turn to fuse
The component below puts the three rankings side by side on a small document corpus: the lexical ranking (BM25), the semantic ranking (cosine), and the hybrid ranking. Switch between the two queries, and especially switch the fusion method between RRF and naive sum to see BM25’s scale take over.
Query
Fusion method
Lexical (BM25)
- #1top2.234
Invoice FR-2024-8831, consulting services
- #20.693
Invoice FR-2024-2207, annual hosting
- #30.693
Invoice FR-2023-1180, software license
- #40.000
Cancel or get a refund for an order
- #50.000
Settlement of internal staff rules
- #60.000
Expense report, trip to Lyon
- zero score: no shared token
Semantic (cosine)
- #1top1.000
Invoice FR-2024-8831, consulting services
- #20.998
Invoice FR-2024-2207, annual hosting
- #30.990
Invoice FR-2023-1180, software license
- #40.788
Settlement of internal staff rules
- #50.643
Expense report, trip to Lyon
- #60.407
Cancel or get a refund for an order
Hybrid
- #1top0.033
Invoice FR-2024-8831, consulting services
- #20.032
Invoice FR-2024-2207, annual hosting
- #30.032
Invoice FR-2023-1180, software license
- #40.031
Settlement of internal staff rules
- #50.031
Cancel or get a refund for an order
- #60.031
Expense report, trip to Lyon
What you observe
Identifier query: BM25 isolates d1 decisively, while cosine makes d1, d2 and d3 nearly indistinguishable. Meaning cannot tell three almost identical invoices apart; the rare token 8831 can.
Three questions to ask yourself while playing:
- On the “invoice 8831” query, compare the lexical column and the semantic column. Which one places the right document far ahead? Which one hesitates between three invoices nearly tied? Why?
- Switch to “cancel the settlement”. Which document does the lexical column put at the top, and is it the right one? What does the semantic column do?
- Still on the paraphrase, switch fusion from RRF to naive sum. Does the document at the top of the hybrid column change? Which of the two fusion modes keeps the right document, and why does the other get it wrong?
Exercises
In one sentence
Semantic search and lexical search have symmetrical blind spots (one misses exact identifiers, the other misses synonyms); they are reconciled through hybrid search, on the condition of fusing not the raw scores (whose incompatible scales let BM25 crush the cosine) but the ranks, via reciprocal rank fusion, scale-invariant by construction.
1. Why can a purely semantic search miss the query 'invoice 8831'?
2. What is the IDF factor in BM25 for?
3. Why fuse with RRF rather than by summing scores?
Toward chapter 8
We now know how to retrieve the right documents: by meaning, by letters, and by their robust fusion. But retrieving is not answering. If you ask your document base a real question, you do not want a list of documents to read yourself - you want a written answer, grounded in those documents. That is the whole challenge of the final chapter: connecting our search engine to a language model. We will give it, in context, the best passages surfaced by hybrid search, and it will compose the answer by drawing on them. That is the architecture known as retrieval-augmented generation, RAG, where everything we have built finally converges.
Sources
- Robertson, S. & Zaragoza, H. (2009). “The Probabilistic Relevance Framework: BM25 and Beyond.” Foundations and Trends in Information Retrieval 3(4), 333-389. DOI 10.1561/1500000019
- Spärck Jones, K. (1972). “A Statistical Interpretation of Term Specificity and its Application in Retrieval.” Journal of Documentation 28(1), 11-21. DOI 10.1108/eb026526
- Cormack, G. V., Clarke, C. L. A. & Büttcher, S. (2009). “Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods.” SIGIR ‘09, 758-759. DOI 10.1145/1571941.1572114
Further reading
- Manning, C. D., Raghavan, P. & Schütze, H. (2008). Introduction to Information Retrieval. Cambridge University Press. Free online version