Where we left off
Lesson 19 showed exactly why this course's hand-rolled sparse scorer degrades at scale: idf() rescans the whole corpus on every call, with nothing cached. rank_bm25 is a small, real BM25 implementation that fixes precisely that, same algorithm from Lesson 5, precomputed statistics instead of live rescans.
The code, piece by piece
from rank_bm25 import BM25Okapi
bm25 = BM25Okapi(tokenized_corpus)Construction does the expensive part once: every document's length, the corpus average length, and every term's document frequency, computed and cached immediately. This is the missing piece from Lesson 19's hand-rolled version, none of that was ever cached, it was recomputed inside the scoring loop itself.
scores = bm25.get_scores(query_tokens)Every query after construction reuses that cached work. This is the whole reason rank_bm25 doesn't show the same runaway growth Lesson 19 measured, the expensive part already happened, once, up front.
Checkpoint
rank_bm25: a real, small BM25 implementation, same algorithm as Lesson 5, with document statistics precomputed once at construction instead of rescanned on every call.BM25Okapi(tokenized_corpus): the expensive setup step, done once.bm25.get_scores(query_tokens): the cheap, repeatable query step, this is what actually runs per question in a real system.
If anything here still feels unclear, ask before moving to Lesson 21.