01 — Evaluation Metrics for Retrieval, Ranking, and Recommendation
“You can’t improve what you don’t measure, and you’ll regret what you measure wrong.”
Introduction
Recommender systems are evaluated at three layers: retrieval (did we surface relevant candidates?), ranking (is the order good?), and business (did users do something valuable?). A great team measures all three, knows the failure modes of each metric, and aligns them with product goals.
This post is a tour of the metrics you’ll actually see in interview loops, design docs, and dashboards — what they reward, what they hide, and when to use which.
1. The Layered Evaluation Mental Model
flowchart TB
A[Offline: Held-Out Logs] --> B[Retrieval Metrics<br/>Recall@K, HitRate@K]
A --> C[Ranking Metrics<br/>NDCG, MAP, MRR, AUC]
A --> D[Calibration & Loss<br/>LogLoss, ECE]
E[Online: Live Traffic] --> F[A/B Test KPIs<br/>CTR, CVR, DAU, Revenue]
E --> G[Guardrails<br/>Latency, Diversity, Coverage]
A -.bias.-> E
D --> C
A subtle but crucial point: offline metrics correlate with online metrics, they do not equal them. Selection bias, position bias, and freshness gaps mean a 5% NDCG lift may translate to 0% CTR lift — or worse.
2. Retrieval Metrics
Retrieval is set-based: “did relevant items appear in the top-K candidates I returned?” Order matters less than coverage.
2.1 Hit Rate (HR@K) / Recall@K
\[\text{HR@K} = \frac{1}{|U|}\sum_{u \in U} \mathbb{1}[\text{relevant item} \in \text{top-K}_u]\]For implicit feedback with one held-out item per user, HR@K is equivalent to Recall@K.
2.2 Coverage
What fraction of the catalog ever appears in top-K? Low coverage = popularity bias.
\[\text{Coverage@K} = \frac{|\bigcup_u \text{TopK}_u|}{|I|}\]2.3 Pros & Cons
| Metric | Pros | Cons |
|---|---|---|
| HR@K / Recall@K | Simple, intuitive, fast to compute | Ignores rank order; favors popular items |
| Precision@K | Useful when slate size is small | Affected by ground-truth sparsity |
| Coverage | Catches popularity bias | Doesn’t measure quality |
3. Ranking Metrics
3.1 Mean Average Precision (MAP)
For a user $u$ with $m$ relevant items:
\[\text{AP}_u = \frac{1}{m}\sum_{k=1}^{N} P(k) \cdot \text{rel}(k), \quad \text{MAP} = \frac{1}{|U|}\sum_u \text{AP}_u\]3.2 Mean Reciprocal Rank (MRR)
\[\text{MRR} = \frac{1}{|U|}\sum_u \frac{1}{\text{rank}_u^*}\]where $\text{rank}_u^*$ is the rank of the first relevant item. Used heavily in search and Q&A.
3.3 Normalized Discounted Cumulative Gain (NDCG)
\[\text{DCG@K} = \sum_{i=1}^{K} \frac{2^{\text{rel}_i}-1}{\log_2(i+1)}, \quad \text{NDCG@K} = \frac{\text{DCG@K}}{\text{IDCG@K}}\]NDCG is the gold standard for graded relevance (e.g., ratings, dwell time buckets).
3.4 AUC / ROC-AUC
Probability that a random positive ranks above a random negative. GAUC (group-wise AUC, averaged per user) is what you actually want in RecSys — vanilla AUC mixes across users and can be misleading.
3.5 Pros & Cons
| Metric | Pros | Cons |
|---|---|---|
| MAP | Robust, handles multiple positives | Binary relevance only |
| MRR | Great for “first hit” problems | Ignores positions 2..K |
| NDCG | Graded relevance, smooth | Sensitive to gain function choice |
| AUC | Stable, threshold-free | Insensitive to top-of-list errors |
| GAUC | Per-user AUC — what users feel | Slow on long-tail users |
4. Calibration & Probability Quality
Pointwise scores need to be calibrated so downstream auctions (ads) and slate optimization (diversity) work.
- LogLoss (binary cross-entropy): rewards calibrated probabilities.
- Expected Calibration Error (ECE): bins predictions and compares predicted vs observed frequency.
- Brier Score: $\frac{1}{N}\sum (p_i - y_i)^2$.
Production teams plot reliability diagrams weekly — they’ll catch drift no AUC delta will.
5. Business / Online Metrics
| Metric | What it captures |
|---|---|
| CTR | Click-through rate (engagement, often confounded by position) |
| CVR | Conversion rate (purchase, signup) |
| DAU/MAU | Retention proxy |
| Session length | Engagement depth |
| Revenue / GMV | Direct value |
| Time-to-action | Surfacing speed |
| Long-term value (LTV) | True north — hard to measure |
Trap: optimizing CTR alone is a one-way ticket to clickbait. Multi-objective + counterfactual + holdouts (see Blog 17, Blog 20).
6. Beyond Accuracy: Diversity, Novelty, Serendipity
- Intra-list diversity (ILD): mean pairwise distance among top-K items.
- Novelty: $-\log(\text{popularity})$.
- Serendipity: relevant and unexpected.
- Catalog coverage, long-tail share as fairness proxies.
See Blog 19 for the full treatment.
7. Code: Compute the Big Six on MovieLens
# pip install pandas numpy scikit-learn
import numpy as np
import pandas as pd
from sklearn.metrics import roc_auc_score, log_loss
# --- 1. Load MovieLens 100K ratings (download from https://grouplens.org/datasets/movielens/100k/)
ratings = pd.read_csv("u.data", sep="\t",
names=["user", "item", "rating", "ts"])
# Implicit positives: rating >= 4
ratings["label"] = (ratings["rating"] >= 4).astype(int)
# Toy "model": predict probability proportional to global popularity
pop = ratings.groupby("item")["label"].mean().to_dict()
ratings["score"] = ratings["item"].map(pop)
# --- 2. Metric implementations
def hit_rate_at_k(df, k=10):
return (
df.sort_values("score", ascending=False)
.groupby("user")
.head(k)
.groupby("user")["label"].max()
.mean()
)
def ndcg_at_k(df, k=10):
def _ndcg(g):
g = g.sort_values("score", ascending=False).head(k)
rel = g["label"].values
gains = (2**rel - 1) / np.log2(np.arange(2, len(rel)+2))
ideal = np.sort(rel)[::-1]
idcg = (2**ideal - 1) / np.log2(np.arange(2, len(ideal)+2))
return gains.sum() / (idcg.sum() + 1e-9)
return df.groupby("user").apply(_ndcg).mean()
def mrr(df):
def _mrr(g):
g = g.sort_values("score", ascending=False).reset_index(drop=True)
hits = g.index[g["label"] == 1]
return 0 if len(hits) == 0 else 1 / (hits[0] + 1)
return df.groupby("user").apply(_mrr).mean()
def gauc(df):
aucs = []
for _, g in df.groupby("user"):
if g["label"].nunique() < 2:
continue
aucs.append(roc_auc_score(g["label"], g["score"]))
return np.mean(aucs)
# --- 3. Report
print(f"HR@10: {hit_rate_at_k(ratings, 10):.4f}")
print(f"NDCG@10: {ndcg_at_k(ratings, 10):.4f}")
print(f"MRR: {mrr(ratings):.4f}")
print(f"GAUC: {gauc(ratings):.4f}")
print(f"LogLoss: {log_loss(ratings['label'], ratings['score'].clip(1e-6, 1-1e-6)):.4f}")
8. Choosing the Right Metric
| Product surface | Primary offline | Primary online |
|---|---|---|
| E-commerce ranking | NDCG@K, GAUC | CTR, CVR, GMV |
| Feed (Twitter/TikTok) | MRR, NDCG | Session time, retention |
| Search | NDCG@10, MRR | CTR, query reformulation rate |
| Email/Push | Recall@K, calibrated CTR | Open rate, unsubscribes |
| Ads | LogLoss, calibrated AUC | eCPM, advertiser ROI |
9. Production Tips
- Always slice metrics: by country, device, user cohort, item freshness.
- Long-tail evaluation: report metrics on users with <5 interactions separately.
- Counterfactual eval (Blog 17) before risking online traffic.
- Holdout users, not interactions — random interaction holdout leaks future info.
- Multiple time windows: 7-day vs 28-day metrics catch novelty effects.
10. Common Pitfalls
- Reporting AUC instead of GAUC.
- Computing NDCG without normalizing per-user IDCG.
- Random splits instead of time-based splits.
- Optimizing CTR while CVR drops.
- Ignoring position bias — a model can “win” because it puts items higher (Blog 17).
- Comparing models on different candidate sets.
11. Public Datasets to Practice
- MovieLens 100K/1M/25M — ratings — https://grouplens.org/datasets/movielens/
- Amazon Reviews 2018 — reviews+meta — https://nijianmo.github.io/amazon/
- Yelp Open Dataset — https://www.yelp.com/dataset
- RetailRocket — clicks/views/purchases — https://www.kaggle.com/datasets/retailrocket/ecommerce-dataset
- Criteo CTR — binary clicks — https://ailab.criteo.com/download-criteo-1tb-click-logs-dataset/
12. Further Reading
- Cremonesi et al., Performance of Recommender Algorithms on Top-N Tasks (RecSys 2010)
- Hu, Koren, Volinsky, Collaborative Filtering for Implicit Feedback Datasets (2008)
- Yang et al., Streaming Recommender Systems (KDD 2018)
- Diaz et al., Evaluating Stochastic Rankings with Expected Exposure (CIKM 2020)