P08 — End-to-End Recommendation System

Run it first. There is a companion page that builds this project's machinery as numbered, independently runnable blocks and then assembles them into one measured system: P08 hands-on — block by block (handson/h*.py). Every number on it was produced by running the code. Read it alongside the milestones below.

Medium · 66 hours · Weeks 84–89 · Stage 4 · Python

The bar is higher here. This is your professional domain. A project that would be a strong result in Stage 1 is a mediocre one in Stage 4. Score yourself against what a specialist would expect, not against what a newcomer would achieve.


Table of Contents


The Loop, Instantiated

StepFor this project
1. ProblemGiven a user's history and a catalogue that turns over daily, choose \(k\) items they will engage with — where "engage" is a proxy for a thing you cannot measure
2. ConstraintsCold items appear constantly (news). Cold users are the majority. Feedback is implicit, biased, and delayed. Latency budget is tens of milliseconds
3. Naive designYours. Most people build: average the embeddings of clicked items, ANN search, return top-k
4. Predicted failurePredict what breaks first. Candidates: popularity collapse, duplicate stories, staleness, filter bubble, cold start
5. Minimal implementationExactly the naive design above, measured
6. CorrectnessNo already-seen item; no duplicates; every returned item exists and passes filters
7. InstrumentationPer-stage latency, candidate-set overlap, exposure distribution, freshness distribution
8. BaselineFour baselines: popularity, recency, content-similarity, random. Random is not a joke — it calibrates every other number
9. BottleneckIs quality limited by retrieval recall, by ranking, or by the user representation? Design an experiment that separates them
10. HypothesisEMA profile beats mean profile for users with drifting interests, above a drift rate you specify
11. ModificationEMA with a swept decay rate
12. ExperimentDecay sweep × user segment × drift rate
13. Failure analysisWhich users got worse? Segment before concluding
14. ReportIncluding the metric that improved while the system got worse

Why This Project Matters

You build these professionally. So the value here is not "learn recommenders" — it is learn to evaluate one honestly, which is a genuinely different and rarer skill.

The central hazard: almost every accuracy metric can be improved by recommending popular items, because popular items are popular. A system that quietly collapses onto the head of the catalogue will show a rising NDCG and a falling product. The only defence is a metric suite that includes coverage, novelty, and exposure concentration alongside accuracy, and the discipline to report all of them every time.

The second reason is specific to news, and to you: a news recommender is a system where the catalogue turns over faster than the user model converges. That makes cold start the normal case rather than the exception, and it makes freshness a first-class metric rather than a tie-breaker. Most recommender literature assumes a stable catalogue and does not transfer.


Prerequisites

  • P02 complete — retrieval calls your index, not a library's
  • P01/P13 helpful — you can generate embeddings and reason about their geometry
  • From math.md: §Ranking Metrics (2 h), §Implicit Feedback and Bias (2 h)

Duration and Size

Medium, 66 hours, 6 weeks. Shorter than comparable projects because you start with domain knowledge. That is deliberate, and the exit bar compensates.

TierContentsHours
MVIItem embeddings, mean user profile, ANN retrieval, score-and-sort ranking, seen-filtering, and the four baselines with a full metric suite.32
Standard+ EMA profiles at multiple decay rates, hybrid retrieval, a feature-based reranker, freshness and diversity terms, near-duplicate detection, cold-start handling, calibration measurement.66
ExtensionA two-tower retrieval model trained on your interactions; or a bandit explore/exploit layer with measured regret.+30–45

Central Technical Questions

  1. What is a user's interest, as a mathematical object? A point, a distribution, a set of points, a trajectory? Each choice makes different failures possible.
  2. How fast should a profile forget? Derive the relationship between decay rate and effective memory before you sweep it.
  3. Where does recall actually get lost — retrieval or ranking? Most teams optimise the wrong stage because they never separate them.
  4. What does diversity cost in accuracy, and is the trade favourable? Measure it; do not assume.
  5. How do you evaluate a recommender offline when the logs were produced by a different policy? This is the hardest question in the project and the one with no clean answer.
  6. Which of your improvements is just popularity bias in disguise?

The EMA profile, derived

An exponentially-weighted profile updates as \(u_t = \alpha x_t + (1-\alpha) u_{t-1}\), giving item \(i\) interactions ago a weight \(\alpha(1-\alpha)^i\). The half-life — interactions until an item's weight halves — is \(h = \ln(0.5)/\ln(1-\alpha)\):

αhalf-life (interactions)effective window 1/αweight on last 10 interactions
0.0234.3150.018.3%
0.0513.5120.040.1%
0.106.5810.065.1%
0.203.115.089.3%
0.301.943.397.2%
0.501.002.099.9%

At α=0.5 the profile is essentially "the last two things you clicked". At α=0.02 it is a slow-moving average that will not notice a genuine interest change for a month. The mean profile is the α→0 limit with equal weights, and it can never adapt at all.

Choose α from a stated assumption about how fast interests drift, then test that assumption. In P09 you will simulate populations with known drift rates and can finally check whether your chosen α was right — which is the single best reason to build the simulator.

Recall propagation through the pipeline

Retrieval-then-ranking is a funnel, and the funnel has a ceiling:

\[ \text{recall}_{\text{end-to-end}}@k \le \text{recall}_{\text{retrieval}}@K \]

If retrieval recall@K is 0.90, no ranker — however good — can exceed 0.90 end-to-end. This is obvious once stated and routinely ignored: teams spend quarters on ranking models while the retrieval stage silently caps them.

Your ANN index's recall from P02 is therefore a hard ceiling on your recommender's quality, and E5 measures exactly how much of it you are losing. That linkage between two of your own projects is one of the most satisfying measurements in the journey.

Popularity concentration

If item popularity follows a Zipf distribution with exponent α, the head's share of total engagement mass is:

Zipf αtop 1% of itemstop 10% of items
0.59.4%31.1%
0.830.0%57.1%
1.053.0%76.5%
1.275.1%90.3%

At α=1.0, recommending only the top 1% of the catalogue captures 53% of all engagement. A trivial bestseller list will beat a mediocre personalised model on accuracy metrics, and it will do so while covering 1% of the catalogue. This is why the popularity baseline is mandatory and why coverage is reported alongside NDCG, always. tools/metrics.py computes both.


Architecture

  interactions ──► profile builder ──┬─ mean vector
                                     ├─ EMA vector (α swept)
                                     └─ multi-interest: cluster history, keep top-c centroids
                                              │
   catalogue ──► embeddings ──► ANN index (P02)│
                                              ▼
                            ┌──── retrieval (candidates, K≈500) ────┐
                            │  ANN by profile · ANN per interest    │
                            │  recency pool · popularity pool       │
                            └──────────────┬────────────────────────┘
                                           │  union, dedupe by id
                                           ▼
                            filters: seen · blocked · region · age
                                           ▼
                            ranking: score = w1·sim + w2·freshness
                                             + w3·quality − w4·redundancy
                                           ▼
                            diversity pass (MMR) ──► near-duplicate collapse ──► top-k

Freshness for news is not a tie-breaker. Model it explicitly, e.g. an exponential decay \(f(a) = e^{-a/\tau}\) in article age \(a\), with \(\tau\) a swept parameter. A relevance-only ranker on a news corpus surfaces last month's best article over today's good one, forever.

MMR (maximal marginal relevance) selects greedily: \(\arg\max_i [\lambda \cdot \text{rel}(i) - (1-\lambda)\max_{j \in S}\text{sim}(i,j)]\). One parameter, one line, and it produces the accuracy/diversity frontier of E7.


Showcase — Do This Before You Start

W6 · walkthroughs/w6_popularity.py · ~40 minutes

A working miniature of this project: a bestseller list beating every personalised recommender on NDCG by 2.5x while covering 0.5% of the catalogue.

cd walkthroughs && python3 w6_popularity.py

It is 80-ish lines and it surfaces this project's central surprise in an evening rather than in week six. Run it before committing the weeks.


Implementation Milestones

#MilestoneHoursDone when
1Dataset: real or realistic articles with timestamps, categories, text; interaction log generator or real logs6Documented, with its popularity Zipf α measured
2Embeddings + the P02 index over the catalogue4Index recall@K measured on this corpus
3The four baselines: random, popularity, recency, content-similarity5All four scored on the full metric suite. Do this before anything clever
4Mean-profile retrieval + ranking5Beats (or fails to beat) the baselines — report either way
5EMA profiles, α ∈ {0.02…0.5}5Half-life table reproduced empirically
6Full metric suite via tools/metrics.py4recall, precision, NDCG, MRR, coverage, novelty, Gini, ILD, freshness, calibration
7Filters: seen, blocked, region, max age4Correct and measured for selectivity — links straight to P03's E3
8Near-duplicate detection and collapse6Measured duplicate rate before/after
9Freshness term with swept τ4Freshness/accuracy frontier plotted
10MMR diversity with swept λ5Accuracy/diversity frontier plotted
11Feature-based reranker (GBDT over sim, freshness, popularity, category match)8Beats score-and-sort, or is honestly reported as not doing so
12Cold-start paths: new user, new item5Both measured separately from the warm case
13Experiments + report5All rows filled

Concepts To Study

  • Implicit feedback: clicks are not ratings; absence of a click is not a negative
  • Position and presentation bias: the strongest predictor of a click is where the item was shown
  • Two-stage retrieval/ranking and the recall ceiling
  • User representation: mean, EMA, multi-interest clustering, sequence models
  • Content-based vs collaborative vs hybrid, and why news is content-heavy
  • Cold start: item cold start dominates in news; user cold start dominates in acquisition
  • Freshness modelling: decay functions, and half-life as a product decision
  • Diversity: MMR, determinantal point processes (know they exist), intra-list distance
  • Filter bubbles and feedback loops: the recommender shapes the data that trains its successor
  • Ranking metrics: NDCG's discount as a modelling assumption, MAP, MRR — and which fits a feed
  • Beyond-accuracy metrics: coverage, novelty, serendipity, Gini
  • Calibration: does the topic mix of recommendations match the user's history mix?
  • Off-policy evaluation: IPS, capped IPS, doubly robust — enough to know why naive offline replay is biased

Primary-Source Readings

Budget: 10 hours.

ReadingWhyHours
Covington, P., Adams, J., Sargin, E. Deep Neural Networks for YouTube Recommendations. RecSys 2016Two-stage architecture; the "example age" feature is the freshness lesson1.5
Steck, H. Calibrated Recommendations. RecSys 2018Why accuracy-optimal recommendations are miscalibrated, with a fix1.5
Carbonell, J., Goldstein, J. The Use of MMR... SIGIR 1998MMR, in four pages0.5
Chaney, A. J. B., Stewart, B. M., Engelhardt, B. E. How Algorithmic Confounding in Recommendation Systems Increases Homogeneity and Decreases Utility. RecSys 2018The feedback loop, simulated. Sets up P09 directly2
Wu, F. et al. MIND: A Large-scale Dataset for News Recommendation. ACL 2020News-specific evaluation and its pitfalls1.5
Cañamares, R., Castells, P. Should I Follow the Crowd? A Probabilistic Analysis of the Effectiveness of Popularity in Recommender Systems. SIGIR 2018Why popularity baselines are so hard to beat, analysed1.5
Hu, Y., Koren, Y., Volinsky, C. Collaborative Filtering for Implicit Feedback Datasets. ICDM 2008The implicit-feedback formulation1.5

Experiments

#ExperimentSweepPredict first
E1Baselinesrandom / popularity / recency / content-simPredict the popularity baseline's NDCG. You will underestimate it
E2Profile typemean vs EMA(α ∈ {0.02…0.5})Optimal α, overall and by user segment
E3Profile type × history length× {1–3, 4–10, 11–50, 50+ interactions}An interaction: predict where mean beats EMA
E4Candidate-set size K{50,100,500,1000,5000}Where does more retrieval stop helping?
E5Retrieval recall ceilingANN efSearch ∈ {16…256}End-to-end quality vs retrieval recall — the linkage to P02
E6Freshness weight τ{1 h, 6 h, 24 h, 7 d, ∞}Accuracy/freshness frontier
E7Diversity λ{0, 0.3, 0.5, 0.7, 1.0}Accuracy/ILD frontier; predict the elbow
E8Deduplicationon/offDuplicate rate, and its effect on perceived quality
E9Rerankerscore-and-sort vs GBDTLift, and which feature carries it
E10Cold startnew users (<3 interactions), new items (<1 h old)Measured separately; the aggregate hides both
E11Popularity-bias auditevery configuration aboveCoverage and Gini per config. Which "wins" are just head collapse?
E12Calibrationtopic distribution of recs vs historyPredict the direction of the miscalibration
E13Latency budgetper stage: retrieval / filter / rank / diversifyPredict which stage dominates. It is usually not the one you think
E14Stabilitysame user, consecutive requestsHow much does the list churn? Excessive churn is a real product bug

E11 is the experiment that makes this a Stage 4 project. For every configuration you evaluate, record coverage and Gini next to NDCG. Then find at least one config where NDCG improved and coverage collapsed, and write it up. That is the honest- evaluation skill the project exists to build.

E5 is the linkage experiment. Sweep your ANN's efSearch, measure retrieval recall and end-to-end NDCG at each point, and plot them together. You will find a knee beyond which better retrieval buys nothing — that knee is your correct operating point, and almost nobody computes it.


Benchmarks and Metrics

All computed by tools/metrics.py. Report the whole suite for every configuration. A table with only NDCG is a rejected result.

FamilyMetrics
Accuracyrecall@k, precision@k, NDCG@k, MRR — k always stated
Cataloguecoverage, Gini of exposure, novelty (bits)
List qualityintra-list diversity, duplicate rate, freshness distribution
Fitcalibration error between recommendation and history topic mixes
Servingp50/p95/p99 end-to-end and per stage
Stabilityrank correlation between consecutive requests for an unchanged user
Segmentedevery accuracy metric, split by history length and by user activity decile

Segmented reporting is not optional. An aggregate metric on a Zipf-distributed user population is dominated by heavy users. A change that helps the top decile and harms everyone else looks like a win in aggregate, and it is the most common way a recommender gets worse while its dashboard improves.


Correctness Tests

  1. Never recommend a seen item. Zero, across the full evaluation.
  2. No duplicate ids within one list.
  3. Every returned item exists and satisfies every active filter.
  4. Exactly k items returned, or fewer with an explicit reason logged.
  5. Determinism: same user state + same seed → same list.
  6. Metric implementations verified against hand-computed examples — the worked cases in metrics.py's demo.
  7. No future leakage: an item published after the request time can never appear. This is the label-leakage bug of recommenders and it inflates offline metrics enormously.
  8. Empty history produces a sensible cold-start list, not a crash.
  9. Empty candidate set after filtering degrades gracefully.
  10. Profile update is order-dependent for EMA and order-independent for mean. Test both properties — getting this backwards is a real bug.

Failure Tests

InjectionRequired behaviour
Embedding service returns zeros for 10% of itemsDetected, not silently ranked at the origin
Item published in the future (clock skew)Rejected by the freshness filter
User with 10,000 interactionsProfile build stays within the latency budget
Catalogue with 90% near-duplicatesDedup keeps the list usable
All candidates filtered outGraceful fallback, logged
Interaction log with duplicated eventsProfile not double-weighted
Stale index (30 minutes behind)Freshness degrades measurably — quantify it; it is P15's question
One category comprising 80% of the catalogueDiversity and calibration must respond
Adversarial engagement (a bot clicking one topic)Profile poisoning; measure how fast, then bound it

Expected Difficulties

  1. You will beat the baselines by less than you expect, or not at all. See the Zipf table. This is the expected outcome and it is a legitimate result; report it and diagnose it rather than tuning until the number looks better.
  2. Offline evaluation is biased by the logging policy. Your logs record what the old system showed. Items never shown have no positives and score as failures. Read the off-policy material, state the bias in the report, and note that P09 exists precisely to escape it.
  3. Metric selection will tempt you. Decide the primary metric before running the sweep and write it down.
  4. Segment or be fooled. Always.
  5. Freshness and accuracy fight, and the accuracy metric always wins offline because offline data cannot express "this was stale when shown". Note the limitation explicitly; it is another P09 motivation.
  6. The domain-expertise trap: you will be tempted to jump to the sophisticated design. Build the naive one and measure it first — the whole method depends on it, and knowing the answer in advance is exactly when the discipline matters most.

Scope Boundaries

In scope: content-based and hybrid retrieval, profile construction, ranking, diversity, freshness, dedup, cold start, offline evaluation, a full metric suite.

Out of scope: training a large neural ranker; real user traffic; a production serving stack; multi-objective optimisation beyond a weighted sum; sequence models (extension); a feature store; real-time profile updates (P07 does that, and P15 integrates it).


Deliverables

  1. recsys/ — pipeline, four baselines, metric suite, sweep runner
  2. REPORT.md centred on E11 (the popularity-bias audit) and E5 (the recall ceiling)
  3. A comparison matrix: every configuration × every metric, one table. This is the portfolio artifact
  4. Notebook entries for E2, E5, E11
  5. A written statement of the offline-evaluation bias, and what P09 will do about it

Exit Criteria

  • All four baselines implemented and scored on the full suite
  • Your system compared against all four, with an honest verdict per metric
  • E2 + E3 complete: EMA vs mean, swept, segmented by history length
  • E5 complete: retrieval recall ceiling measured, knee identified
  • E11 complete: at least one configuration documented where accuracy rose and coverage fell
  • All metrics reported segmented, not only aggregate
  • Cold-start paths measured separately for new users and new items
  • Latency budget measured per stage
  • REPORT.md written with a falsified prediction and the offline-bias limitation stated

Extension Ideas

  • Two-tower retrieval trained on your interactions, compared against content embeddings at equal latency.
  • Bandit exploration (Thompson sampling or LinUCB) with measured regret and coverage effects.
  • Sequence model (GRU4Rec-style) as the profile, compared against EMA. The direct test of "is a learned sequence better than an exponential average", which is a question with a real answer in your domain.
  • Off-policy evaluation with capped IPS, compared against naive replay — quantifying the bias you flagged.

Connections

Backward: P02 is retrieval and sets the recall ceiling. P03 provides filtered search and the selectivity regime. P01/P13 provide embeddings.

Forward:

  • P09: the simulator evaluates this pipeline under known ground truth, escaping offline bias. Keep the pipeline's interface stable and swappable
  • P10: A/B testing over the same pipeline
  • P15: the serving path of the integrated system

References

  • Covington, P., Adams, J., Sargin, E. Deep Neural Networks for YouTube Recommendations. RecSys 2016.
  • Steck, H. Calibrated Recommendations. RecSys 2018.
  • Carbonell, J., Goldstein, J. The Use of MMR, Diversity-Based Reranking for Reordering Documents and Producing Summaries. SIGIR 1998.
  • Chaney, A. J. B., Stewart, B. M., Engelhardt, B. E. How Algorithmic Confounding in Recommendation Systems Increases Homogeneity and Decreases Utility. RecSys 2018.
  • Cañamares, R., Castells, P. Should I Follow the Crowd? SIGIR 2018.
  • Hu, Y., Koren, Y., Volinsky, C. Collaborative Filtering for Implicit Feedback Datasets. ICDM 2008.
  • Wu, F. et al. MIND: A Large-scale Dataset for News Recommendation. ACL 2020.
  • Joachims, T., Swaminathan, A., Schnabel, T. Unbiased Learning-to-Rank with Biased Feedback. WSDM 2017.
  • Ricci, F., Rokach, L., Shapira, B. (eds.) Recommender Systems Handbook, 3rd ed. Springer, 2022.
  • Kunaver, M., Požrl, T. Diversity in recommender systems — A survey. Knowledge-Based Systems 123, 2017.