Lab 05
Evaluating Information Retrieval Systems
Overview of the lab
Lab 04 answered "How do we rank documents?" Lab 05 answers "How do we know whether that ranking is good?" The aim is not only to calculate the standard metrics, but to understand what user behaviour each one approximates. The systems compared are TF-IDF and BM25 from Lab 04, on Cranfield.
- Why evaluation? Relevance judgments and the offline setup.
- Accuracy, Precision and Recall: why accuracy is not enough, and precision and recall at a cutoff \(k\).
- Why rank position matters: RR and MRR, AP and MAP.
- Graded relevance: DCG and nDCG.
- Live evaluation on Cranfield with
ir_measures. - Different metrics can disagree: metric choice and per-query analysis.
- Summary, and an optional exercise.
Learning objectives
By the end you should be able to explain:
- why retrieval evaluation requires relevance judgments;
- why accuracy is not a useful measure for retrieval;
- the difference between Precision and Recall, and why rank-aware metrics are needed;
- the meaning of Reciprocal Rank and MRR, and of Average Precision and MAP;
- how graded relevance leads to DCG and nDCG;
- why different metrics can produce different conclusions;
Why evaluation?
Two systems return these rankings for the same query. Which system is better? Without knowing which documents are relevant: we do not know. We need relevance judgments.
Try it: be the assessor
Click documents to mark them relevant. The measures (explained below) update for both systems; the better value is highlighted.
Relevance judgments
For each query–document pair, a human assessor assigns a label: relevant or not relevant, or a grade such as 0 = not relevant, 1 = somewhat relevant, 2 = relevant, 3 = highly relevant. Once we have ranking + relevance, we can calculate metrics.
The offline evaluation setup
from a fixed test set
TF-IDF, BM25, …
a run
qrels
one number per query, then the mean
The metric is a summary of ranking quality under a particular definition of relevance.
Accuracy, Precision and Recall
For one query, every document in the collection falls into one of four cells: the system retrieved it or not, and it is relevant or not.
| relevant | not relevant | |
|---|---|---|
| retrieved | TP, true positive: a hit | FP, false positive: noise the user reads |
| not retrieved | FN, false negative: a miss | TN, true negative: correctly left out |
Accuracy, and why it is not enough
The share of documents the system classified correctly, as retrieved-and-relevant or left-out-and-not-relevant. It is the natural measure for classification, where the classes are of similar size.
- ❗ In retrieval, almost every document is not relevant. A Cranfield query has on average 7.2 relevant documents among 1,400 (1,612 relevant judgments for 225 queries). For a query with 7 relevant documents, a system that returns nothing leaves the other 1,393 correctly out and gets accuracy \(1393 / 1400 = 99.5\%\).
- On the Web, with billions of pages and a handful of relevant ones, the useless empty system scores 99.9999…%. Accuracy is dominated by the true negatives, which the user never sees and does not care about.
- So retrieval evaluation ignores TN and looks only at what the user sees (TP, FP) and what the user needs (TP, FN): precision and recall.
Try it: accuracy vs precision and recall
One query: set the collection size, how many documents are relevant, how many the system retrieves and how many of those are relevant.
A toy ranking
A toy ranking used throughout: D1 (relevant), D2 (not), D3 (relevant), D4 (not), D5 (relevant), written R N R N R. The collection contains exactly these three relevant documents: D1, D3, D5. For ranked retrieval both measures are computed at a cutoff \(k\): we look only at the top \(k\) results (P@1, P@5, P@10, …).
Precision
Of the documents we retrieved, how many are relevant? Precision looks only at what the system returned: how clean is the result list?
- Example: a search engine shows 10 results and 4 of them are relevant: \(P@10 = 4/10 = 0.4\). Six of the ten results the user reads are wasted.
- Documents below the cutoff do not matter for P@k, and neither do relevant documents the system never returned.
The toy ranking R N R N R, precision at every cutoff:
| rank \(k\) | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| document | D1 R | D2 N | D3 R | D4 N | D5 R |
| relevant in top \(k\) | 1 | 1 | 2 | 2 | 3 |
| \(P@k\) | 1/1 = 1.00 | 1/2 = 0.50 | 2/3 = 0.67 | 2/4 = 0.50 | 3/5 = 0.60 |
Try it: precision at k
Move the cutoff \(k\): the bracket marks the retrieved set, the documents the user sees. Click a document to switch it between relevant and not relevant.
Recall
Of all relevant documents that exist, how many did we retrieve? Recall looks at the whole collection: how much of what the user needs did we find? Its denominator counts relevant documents the system never showed, which is why it needs relevance judgments beyond the returned list.
- Example: a lawyer needs every court decision on a topic. The archive holds 50 relevant decisions and the search finds 40 of them: \(Recall = 40/50 = 0.8\), however many irrelevant ones came along.
- If the collection held 2 more relevant documents that the ranking never reaches, the same ranking would only get \(Recall@5 = 3/5\): nothing about the list changed, only the denominator.
The toy ranking R N R N R, with 3 relevant documents in the collection:
| rank \(k\) | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| document | D1 R | D2 N | D3 R | D4 N | D5 R |
| relevant in top \(k\) | 1 | 1 | 2 | 2 | 3 |
| \(Recall@k\) | 1/3 = 0.33 | 1/3 = 0.33 | 2/3 = 0.67 | 2/3 = 0.67 | 3/3 = 1.00 |
Try it: recall at k
The whole collection of 30 documents; the toy ranking returns D1–D5 in order. Move \(k\) to retrieve more of the ranking, and add relevant documents that the ranking never reaches.
retrieved (top \(k\)) relevant, found relevant, missed
Precision vs recall
- They answer different questions. With 10 relevant documents: system A retrieves 2, both relevant: Precision 1.0, Recall 0.2. System B retrieves 100, 10 of them relevant: Precision 0.1, Recall 1.0. Neither measure is universally more important.
- Retrieval depth: as we retrieve more and more documents, recall tends to increase and precision often decreases. This is a tendency, not a guarantee for every single step: a relevant document at rank \(k\) raises P@k.
Try it: both on one ranking
Click a rank to switch it between relevant (R) and not relevant (N); use + and − to change the length, and set how many relevant documents exist in total. The plot shows P@k and Recall@k; the shaded columns are relevant ranks. The same playground computes RR and AP, used in Part III.
P@k Recall@k
Why rank position matters
Ranking A is R R R N N N N N N N, ranking B is N N N N N N N R R R. Both have \(P@10 = 0.3\), yet ranking A is clearly preferable for most users (load both in the playground above). We need rank-aware metrics.
Reciprocal Rank and MRR
\(r\) is the rank of the first relevant result: at rank 1, RR = 1; at rank 2, 0.5; at rank 5, 0.2. With no relevant result, RR = 0. RR is for one query, MRR is the mean across queries.
- Useful when the user mainly wants one good result: navigational search, entity lookup, question answering, some FAQ tasks.
- But MRR ignores everything after the first relevant document.
Average Precision and MAP
\(R\) is the number of relevant documents for the query and \(rel(k)\) is 1 if the document at rank \(k\) is relevant. In words: every time we meet a relevant document, record the precision at that point; then average those values over all \(R\) relevant documents. AP is for one query, MAP the mean across queries.
Worked example, R N R N R: \(P@1 = 1\), \(P@3 = 2/3\), \(P@5 = 3/5\), so \(AP = \frac{1 + 2/3 + 3/5}{3} \approx 0.756\). For rankings A and B above, AP is 1.0 and about 0.216, and RR is 1 and 0.125: the rank-aware metrics separate what P@10 could not.
Recap
| Metric | Main question |
|---|---|
| Precision@k | How many of the top \(k\) are relevant? |
| Recall@k | How much of the relevant material did we find? |
| RR | Where is the first relevant result? |
| MRR | How early is the first relevant result, on average? |
| AP | Are relevant documents consistently ranked high? |
| MAP | Mean AP across queries |
So far relevance has been binary. What if some relevant documents are much better than others?
Graded relevance: DCG and nDCG
Cranfield has graded relevance: a document may be of minimum interest, useful, highly relevant, or a complete answer. Binary metrics collapse all of these into "relevant", which loses information: a ranking that puts a complete answer first and one that puts a document of minimum interest first get the same P@1. DCG (Järvelin & Kekäläinen, 2002) measures a graded ranking by imagining a user who reads the list from the top.
Three ideas in one formula
- Gain: each document is worth something to the user, and more if its grade is higher. With the gain \(2^{rel} - 1\): grade 0 → 0, 1 → 1, 2 → 3, 3 → 7, 4 → 15. The exponent makes one highly relevant document worth more than several marginal ones: a grade-3 document (7) is worth more than two grade-2 documents (3 + 3).
- Discount: the user reads from the top, and every rank further down is less likely to be read. So the gain at rank \(i\) is divided by \(\log_2(i + 1)\): rank 1 by 1 (full value), rank 2 by 1.58, rank 3 by 2, rank 4 by 2.32, rank 10 by 3.46. The logarithm makes the penalty grow slowly: a document at rank 10 still keeps 29% of its value.
- Cumulative: add up the discounted gains of the top \(k\) documents. The total is the value the user collects by reading the top \(k\).
\(rel_i\) is the grade of the document at rank \(i\); \(IDCG@k\) is the DCG@k of the ideal ranking, all judged documents sorted by grade, highest first.
A worked example
The system ranks four documents with grades [3, 0, 2, 1].
| rank \(i\) | grade | gain \(2^{rel} - 1\) | discount \(\log_2(i+1)\) | gain / discount |
|---|---|---|---|---|
| 1 | 3 | 7 | 1.000 | 7.000 |
| 2 | 0 | 0 | 1.585 | 0.000 |
| 3 | 2 | 3 | 2.000 | 1.500 |
| 4 | 1 | 1 | 2.322 | 0.431 |
| DCG@4 | 8.931 | |||
- Is 8.931 good? A raw DCG cannot tell: it depends on how much relevant material the query has. Ten grade-3 documents give \(DCG@10 = 31.8\); a query with a single grade-1 document can never exceed 1.
- So normalize: the best possible order of the same documents is [3, 2, 1, 0], with \(IDCG@4 = 7 + 3/1.585 + 1/2 + 0 = 9.393\). Then \(nDCG@4 = 8.931 / 9.393 = 0.951\): 95% of the best achievable. The system lost a little because the grade-2 and grade-1 documents came one rank too late.
- The same documents in the worst order, [0, 1, 2, 3]: \(DCG@4 = 0 + 1/1.585 + 3/2 + 7/2.322 = 5.146\), \(nDCG@4 = 0.548\). Same documents, same precision, very different nDCG: the order of the grades is what it measures.
- \(nDCG@k\) is always between 0 and 1, and 1 exactly for an ideal ranking; after normalization it can be averaged over queries.
Try it: compute nDCG
Type the grades of a ranking, top first. The tables show every term of the sum; "Sort to ideal" turns the ranking into the ideal one.
💡 Not every nDCG uses \(2^{rel} - 1\). ir_measures' default nDCG@10 (the trec_eval definition) uses the grade itself as the gain; the formula above is nDCG(dcg='exp-log2')@10. With [3, 0, 2, 1] the two give 0.951 and 0.930. Always report which one you used.
Live evaluation on Cranfield
Cranfield (ir_datasets) has graded judgments. For binary metrics we treat relevance ≥ 1 as relevant; for nDCG we keep the grades (a grade of −1 counts as 0).
qrels.py Python
import ir_datasets
from collections import Counter
dataset = ir_datasets.load("cranfield")
queries = list(dataset.queries_iter())
qrels = list(dataset.qrels_iter())
Counter(q.relevance for q in qrels)
# Counter({3: 734, 2: 387, 4: 363, -1: 225, 1: 128})
Using ir_measures
Use an established evaluation library (ir_measures) rather than implementing every metric yourself. It reads TREC-style run files, query_id Q0 doc_id rank score system, with the top 100 results of each system for every query, made with the two search functions from Lab 04:
write_runs.py Python
# search_tfidf and search_bm25 from Lab 04, return (doc_id, title, score)
def write_run(path, system_name, search_fn, k=100):
with open(path, "w", encoding="utf-8") as f:
for q in queries:
for rank, (doc_id, title, score) in enumerate(search_fn(q.text, k=k), start=1):
f.write(f"{q.query_id} Q0 {doc_id} {rank} {score:.8f} {system_name}\n")
write_run("lab04_tfidf.run", "tfidf", search_tfidf)
write_run("lab04_bm25.run", "bm25", search_bm25)
evaluate.py Python
# pip install ir_measures
import ir_measures
from ir_measures import P, Recall, RR, AP, nDCG
tfidf_run = ir_measures.read_trec_run("lab04_tfidf.run")
bm25_run = ir_measures.read_trec_run("lab04_bm25.run")
metrics = [
P(rel=1) @ 10,
Recall(rel=1) @ 100,
RR(rel=1),
AP(rel=1),
nDCG @ 10,
]
tfidf_scores = ir_measures.calc_aggregate(metrics, qrels, tfidf_run)
bm25_scores = ir_measures.calc_aggregate(metrics, qrels, bm25_run)
print(tfidf_scores)
# {AP: 0.2737..., nDCG@10: 0.3170..., RR: 0.5100..., P@10: 0.2284..., R@100: 0.7006...}
print(bm25_scores)
# {AP: 0.2405..., nDCG@10: 0.2822..., RR: 0.5044..., P@10: 0.1991..., R@100: 0.6512...}
See it: TF-IDF vs BM25 on all 225 queries
Computed in the page from the same runs; the values are identical to ir_measures' output above (checked query by query). The last row is nDCG with the \(2^{rel} - 1\) gain from Part IV.
💡 Do not focus only on which row has the larger number. Ask instead: which metric changed? What user behaviour does that metric represent? Is the difference at rank 10, at rank 100, or across the whole ranking? And here: BM25 is lower than TF-IDF on every metric. "More sophisticated" is not automatically better; the setup matters. text.lower().split() keeps punctuation attached: 5,486 of the 12,246 distinct tokens contain it, like thickness,, which then does not match thickness. With a word tokenizer (\b\w+\b) the same BM25 reaches MAP 0.262 and P@10 0.219, closer to TF-IDF but still below it on Cranfield.
Different metrics can disagree
- System A has a higher P@10, system B a higher Recall@100: no contradiction, they are strong in different ways.
- System A has a higher MRR, system B a higher MAP: this can happen, because MRR cares about the first relevant result and MAP about all of them.
Metric choice follows the task
| Task | What matters | Typical metrics |
|---|---|---|
| Web search | the early ranks | P@5, P@10, nDCG@10 |
| Legal or medical literature search | finding as much relevant material as possible | Recall@100, Recall@1000 |
| Navigational search | one correct result, quickly | MRR |
| General ad-hoc retrieval | the whole ranking | MAP, nDCG |
Per-query analysis
Averages can hide failures. A higher MAP for one system does not mean it wins on every query: some queries may strongly prefer one system, others the other. Serious IR evaluation combines aggregate metrics + per-query analysis + error analysis.
Try it: every Cranfield query
Each dot is one query: TF-IDF's score on the x-axis, BM25's on the y-axis. Dots above the diagonal are queries where BM25 is better. Click a dot (or choose a query) to compare the two top-10 lists with their relevance grades; the default is the query where the systems differ most in AP.
Summary: key takeaways
| Metric | Best interpretation |
|---|---|
| P@k | How clean are the top results? |
| Recall@k | How much relevant material did we retrieve? |
| MRR | How quickly did we find the first relevant result? |
| MAP | Are relevant documents ranked high throughout the ranking? |
| nDCG@k | Are the most relevant documents ranked highest? |
- A retrieval score is meaningless without an evaluation setup.
- Accuracy is useless for retrieval: almost every document is not relevant. Precision and Recall measure different retrieval goals.
- Rank position matters.
- Different metrics encode different assumptions about user needs.
Optional exercise
Evaluate TF-IDF and BM25 on Cranfield with P@10, Recall@100, MRR, MAP and nDCG@10. Then choose two queries where the systems differ strongly and inspect the rankings manually (the per-query plot above helps). The purpose is not a large report, but to connect a metric difference to actual ranking behaviour.