We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

BM25

While TF-IDF is a good start, there's a keyword ranking algorithm called Okapi BM25 that performs even better. BM25 addresses three problems with basic TF-IDF:

  1. Better IDF calculation: More stable scoring for rare and common terms
  2. Term frequency saturation: Prevents terms from dominating by appearing too often
  3. Document length normalization: Accounts for long and short documents

The IDF Problem

In basic TF-IDF, IDF is calculated as log(N / df):

  • N = total number of documents in the collection
  • df = document frequency (how many documents contain this term)
  • log = logarithm function (reduces the impact of very large numbers)

The problems with this formula are:

  • Division by zero: When df = 0 (we "solved" this by adding 1 to both the numerator and denominator)
  • Unstable rare terms: Very rare terms get extremely high scores
  • Zero scores: Terms that appear in every document can bottom out at 0

BM25 IDF Solution

BM25 uses a more stable IDF formula:

IDF = log((N - df + 0.5) / (df + 0.5) + 1)

Let's break down the reasoning behind each piece, starting with the core ratio:

(N - df + 0.5) / (df + 0.5)

This compares the documents that don't contain the term against documents that do contain it.

  • Numerator (N - df + 0.5): Documents without the term, plus smoothing
  • Denominator (df + 0.5): Documents with the term, plus smoothing

What Is Smoothing?

The smoothing part of this formula is where we add 0.5 to the numerator and denominator, then add 1 to the division result.

  • Why add 0.5? To prevent division by 0. The fancy term for this is Laplace smoothing.
  • Why add 1? The final + 1 ensures IDF is always positive, which handles some edge cases.

Assignment

In this lesson we'll focus on the first improvement offered by BM25: better IDF calculation.

  1. bm25_idf_parser = subparsers.add_parser(
        "bm25idf", help="Get BM25 IDF score for a given term"
    )
    bm25_idf_parser.add_argument("term", type=str, help="Term to get BM25 IDF score for")
    
    1. print(f"BM25 IDF score of '{args.term}': {bm25idf:.2f}")
      

Run and submit the CLI tests.