A recommender whose most important finding is that recency-weighted popularity wins

A production-style two-stage recommender over 1.3M products, evaluated on a strict global timeline

AI/ML

Summary

Problem
Recommenders are easy to over-credit when evaluation leaks future data into training. The goal is a ranked, explained list per user that holds up under time-respecting evaluation, for an Amazon-scale catalogue.
My role
Sole developer
Team
Personal project
Status
In progress
Timeline
October 2026
Stack
Python, PyTorch, LightGBM (LambdaMART), FastAPI, Parquet, Redis (adapter), Docker
Next
Fix the re-ranker's training objective, which likely doesn't match how it is used at serving time, and re-measure.

What shipped

  • Showed that recency-weighted popularity (HR@20 0.024 on 20,000 warm users) beat every learned model, including two-tower at 0.00025, by evaluating on a strict global timeline.
  • Raised personalisation sixfold, from 3 to 18 distinct top-1 items over 2,000 users, at a 25% HR@20 cost, by fusing popularity with item co-occurrence.
  • Served 1.33 million items at 33.0 ms p95 for warm users and 310 requests/s in one process by embedding the feature store in memory.
Warm-user HR@20, best source
0.024
Baseline: Two-tower 0.00025, ALS 0.0053
Recency-weighted popularity on 20,000 warm users, exact full-catalogue ranking, one held-out target each (results/evaluation_electronics_0core.json); every learned model's paired-bootstrap difference against it excludes zero
Personalisation from fusion
6× more distinct top-1 items
Baseline: 25% lower HR@20
Shipped fusion of recency popularity with co-occurrence gave 18 distinct top-1 items over 2,000 warm users against 3, at HR@20 0.015 against 0.020 (results/fusion_electronics_0core.json); taken deliberately
Warm-user latency p95
33.0 ms
Baseline: 150 ms target
400 requests at concurrency 8, k=20, one process, 1,334,914 items served (results/load_test_electronics_0core.json); all five latency targets met, 310 requests a second
On this page
Bar chart of RankStack's HR@20 on warm users by source: recency-weighted popularity 0.02395, popularity 0.0069, ALS 0.00525, co-occurrence 0.00295, two-tower 0.00025

Source: results/evaluation_electronics_0core.json in the RankStack repository

Context and constraints

Given a user, the service returns the products they are likely to want next, each with a short explanation and the model version that produced it. The data is Amazon Reviews 2023, Electronics, with 43.4M interactions after validation, and raw files are never committed. Every split, feature and metric is defined against a global timeline, every dataset build writes a manifest of hashes, counts and versions, and CI fails if a documented number drifts from its result file.

Architecture

Retrieval narrows the catalogue to a few hundred candidates. Stage 2 scores them with LambdaMART over 18 point-in-time features, then applies a seen filter, a freshness bonus, a category cap and maximal marginal relevance. Features are served from an embedded in-memory store by default, with a Redis adapter behind the same interface for the distributed case. Every failure mode falls down a degradation ladder that returns a working list rather than an error. Explanations come from templates, with no language model in the request path.

Architecture: RankStackClient, Parquet data and manifests, Recommendation API, Retrieval (recency popularity + co-occurrence, fused), Embedded online feature store, LambdaMART re-ranker and business rules, Template explanations. Connections: Client to Recommendation API; Recommendation API to Embedded online feature store; Recommendation API to Retrieval (recency popularity + co-occurrence, fused); Retrieval (recency popularity + co-occurrence, fused) to LambdaMART re-ranker and business rules; LambdaMART re-ranker and business rules to Template explanations; Parquet data and manifests to Embedded online feature store.a few hundred candidatesload at startupClientclientParquet data and manifestsdata storeRecommendation APIgatewayRetrieval (recency popularity + co-occurrence, fused)modelEmbedded online feature storecacheLambdaMART re-ranker and business rulesmodelTemplate explanationsservice

Request flows

  1. Serve a ranked list: Client → Recommendation API → Retrieval (recency popularity + co-occurrence, fused) → LambdaMART re-ranker and business rules → Template explanations
  2. Load features: Parquet data and manifests → Embedded online feature store

Architecture: RankStack

Components

Request flows

Choose a component to focus it, or a flow to watch its requests. In the view, drag or use the arrow keys to turn it and + or − to zoom. Escape closes.

Engineering decisions

How should train and test be split?
Options
  • Leave-one-out per user
  • Random split
  • A global timeline on absolute timestamps
Chose
One global timeline, with the cut points recorded in every result file and leakage tested automatically
Trade-off
Numbers are lower and harder to compare with papers that use leave-one-out.
Evidence
57% of test users have no training history and 41% target an item unseen at training time, which a per-user split would hide.
What happens when the learned models lose?
Options
  • Drop them quietly
  • Tune until they win
  • Report the loss and ship what won
Chose
Report the loss and ship what won
Trade-off
The headline result is a negative one.
Evidence
Two-tower retrieval scored HR@20 0.00025 and the LambdaMART re-ranker scored −0.0173 against its own stage 1; the hand-written rule layer changed no hit rate at all.
Accuracy or personalisation?
Options
  • Serve recency-weighted popularity alone
  • Fuse it with item-to-item co-occurrence
Chose
Reciprocal-rank fusion of the two
Trade-off
HR@20 falls by 25% (paired difference −0.005, 95% CI −0.0095 to −0.0010).
Evidence
Fusion gives 18 distinct top-1 items across 2,000 warm users instead of 3, so users stop seeing the same few products.
Should a language model write the explanations?
Options
  • Generate explanations with a language model
  • Deterministic templates from the evidence the model used
Chose
Deterministic templates
Trade-off
Explanations are less varied.
Evidence
Every explanation is filled from evidence the ranking actually used, so it can't describe a reason that wasn't there.

Evaluation

Every source is scored by the same harness on the same frozen cohorts, with exact full-catalogue ranking and no sampled negatives.

Source HR@20, warm users HR@20, all users
Recency-weighted popularity 0.02395 0.016055
Popularity 0.0069 0.006733
ALS 0.00525 0.00587
Co-occurrence 0.00295 0.001985
Two-tower 0.00025 0.000604

Against recency-weighted popularity, paired bootstrap over 1,000 resamples gives ALS −0.0187 and two-tower −0.0237, both excluding zero. Nothing learned here beats recency-weighted popularity on this corpus, and that is the result worth stating plainly. A bug that attached every per-item feature to the wrong item was found and fixed; the re-ranking figures were re-measured afterwards, and re-ranking is still worse than stage 1. The results are in results/ in the RankStack repository.

Deployment and operations

400 requests at concurrency 8 met all five latency targets in one process: warm users at 25.1 ms p50 and 33.0 ms p95, cold start at 29.7 ms p95, and the degraded path at 13.9 ms p95, at 310 requests a second over 1,334,914 items. A serving runbook covers every degradation and its alert. CI requires 85% test coverage across unit, property, integration and end-to-end tests.

Results

The service answers within its targets and explains every answer, but two of its three learned components lost their measurements: two-tower retrieval and the LambdaMART re-ranker. The re-ranker’s likely problem is a mismatch between what it was trained on and how it is used. Rather than drop those components quietly, the repository documents them, and the request path serves the fusion that measured best for the trade it makes.

What I'd do next

Retrain the re-ranker on the candidate lists it actually reorders at serving time, then test whether a sequence encoder or an item text tower closes the gap to recency-weighted popularity.

Talk about this project

I'm happy to walk through any decision here in more depth.

pssaikumardangeti@gmail.com