Recommender Systems
- Collaborative filtering dates to Tapestry (Xerox PARC, 1992) and was popularized by the Netflix Prize (2006-2009)
- Recommender systems drive 35% of Amazon purchases and 75% of Netflix views
- Matrix factorization (SVD) and deep learning embeddings are now the dominant approaches
Recommender systems predict what items a user will like based on past behavior (collaborative filtering) or item/user attributes (content-based filtering), or both (hybrid). Matrix factorization decomposes the user-item interaction matrix into user and item embedding vectors.
- Matrix factorization directly generalizes to word2vec, BERT embeddings, and two-tower neural networks
- The cold-start problem (new users/items) connects to few-shot learning
- Content-based filtering is essentially feature-based regression/classification on item attributes
- Distinguish memory-based collaborative filtering (user-user, item-item) from model-based matrix factorization, and explain why MF generalizes better on sparse data.
- Derive the ALS and SGD updates for explicit-feedback matrix factorization with bias terms.
- Explain why implicit feedback needs a confidence-weighted MSE (Hu-Koren-Volinsky) or a pairwise BPR loss instead of pointwise log-loss.
- State the offline ranking metrics precision@k, recall@k, NDCG@k, and MAP, and derive NDCG.
- Describe the production retrieval-then-ranking pipeline (two-tower retrieval feeding a ranking model) and the offline-online metric mismatch.
TL;DR
Recommender systems predict what a user will like next from a sparse history of interactions. Memory-based collaborative filtering (user-user and item-item) [1] recommends by similarity in the rating matrix; it is interpretable but does not generalize across sparsity. Matrix factorization [2] learns latent factors such that , fit by SGD or ALS, and was the engine of the Netflix Prize era. Implicit feedback (clicks, views) requires a different objective: the Hu-Koren-Volinsky weighted-MSE confidence model [3] or Bayesian Personalized Ranking [4], which optimizes a pairwise log-loss between observed and sampled-negative items and is an AUC surrogate. Factorization machines [5] generalize MF to arbitrary feature interactions. Modern production systems use a two-tower retrieval model feeding a ranking model [6], with sequential models (SASRec [7], BERT4Rec) capturing temporal order. Evaluate with precision@k, recall@k, NDCG@k [8], MAP, and AUC; the offline-online gap is real and the only thing that matters in production is the A/B test.
Intuition
A user-item interaction matrix is the central object. It is overwhelmingly sparse (most users have rated a tiny fraction of items) and the recommendation problem is to predict the missing entries.
Three families of solution:
- Memory-based CF. Compute pairwise similarities (cosine, Pearson) between rows (users) or columns (items) and predict by weighted average of similar users’ ratings or similar items’ ratings. Interpretable, no training, but does not extrapolate.
- Model-based / matrix factorization. Learn low-rank latent factors , with . Latent factors generalize across sparsity: a user’s preference for “subtle European thrillers” is encoded in even if she has rated only one such film.
- Hybrid / content-based. Blend MF with content features (genre, embeddings, side information). Mitigates cold-start and is the basis of all modern systems.
Memory-based CF can only recommend items that some similar user has rated; on long-tail items it has no signal at all. Matrix factorization compresses the user-item history into a low-dimensional latent space where similarity is geometric, not co-occurrence-based. A new item with only a handful of ratings still gets a useful , and the decomposition transfers preferences across sparsely-overlapping items [2].
Math
Matrix factorization with biases: the Netflix-prize objective
Model
where is the global mean rating, and are user and item biases (some users rate uniformly higher; some items are simply more popular), and are latent factors. Typical is 20 to 200.
Objective (explicit feedback, observed entries only)
Let . Minimize
The sum over (not all of ) is the key: missing entries are missing, not zero. Treating them as zero is a standard mistake on explicit feedback.
SGD updates
For one observation with prediction error :
These are standard squared-loss SGD updates with regularization. Cheap per step, embarrassingly parallel across pairs.
ALS: closed-form per-block updates
When all are fixed, the loss is a quadratic in each alone (the user-blocks decouple). Setting the gradient to zero:
where stacks the rows of for the items user rated. By symmetry, the updates are analogous with . Alternating (fix , solve for ; fix , solve for ) gives ALS [2]. Each update is a small ridge regression, embarrassingly parallel across users (or items), and the loss is non-increasing across alternations.
When to choose SGD vs ALS
- SGD is faster on dense data and easy to extend to non-quadratic losses.
- ALS parallelizes trivially across users (and across items) and is the right choice on Spark / distributed settings.
- For implicit feedback below, ALS-style closed-form updates are essential because the loss sums over all (user, item) pairs.
Implicit feedback: the Hu-Koren-Volinsky confidence model
Setup
In the implicit setting we observe interaction counts (clicks, plays, watch time) but no explicit “dislike” signal. We cannot fit directly because the absence of an interaction is ambiguous: “user did not see it” versus “user saw and ignored”.
Hu, Koren, and Volinsky [3] introduce two derived quantities per pair:
where is the “preference” (binary) and is the confidence (always , larger when the user interacted more). Typical .
Objective
Minimize the confidence-weighted squared loss over all :
Unobserved pairs () are included with and : low-confidence negative signal.
Closed-form ALS update
Because the loss sums over all pairs, the per-user update has a closed form in per user (with a clever reformulation that keeps it tractable):
where . The trick: , and only the second term varies per user, with supported only on the observed items.
This is the standard “implicit ALS” algorithm. It is the engine of the Spark ALS recommender and many production systems.
Bayesian Personalized Ranking: pairwise log-loss as an AUC surrogate
Motivation
Pointwise log-loss treats every (user, item) example independently and is dominated by the (vast) majority of unobserved-pair zeros. We do not actually care about predicting probabilities accurately; we care about ranking relevant items above irrelevant ones.
Triplets and the BPR loss
For each user , sample triplets where is an observed (positive) item and is a sampled negative (typically uniform over unobserved items). Define and minimize
where and are the model parameters [4].
Why this is an AUC surrogate
The empirical AUC of a per-user ranking is
Replacing the indicator with gives a smooth lower bound, and taking the negative log yields the BPR loss. So BPR is directly optimizing a smooth approximation to per-user AUC.
SGD update (matrix factorization variant)
For a triplet and the MF model :
BPR with negative sampling scales linearly per epoch in the number of observed interactions.
Factorization machines
Factorization machines [5] generalize MF to arbitrary feature vectors (which may concatenate user-id one-hots, item-id one-hots, side features, contexts, etc.). The order-2 FM is
with each pairwise interaction parameterized by a low-rank inner product rather than a free coefficient. The trick: the pairwise sum has a closed-form rewrite
so each prediction is , not . FMs subsume MF (with one-hot user and item ids) and have a clean field-aware extension (FFM, 2016) that took several display-ad CTR competitions.
Modern production: retrieval and ranking are different model classes
Industrial recommenders almost always look like this:
| Stage | Goal | Model class | Latency target |
|---|---|---|---|
| Candidate generation (retrieval) | Pull a few hundred plausible items from a catalog of millions | Two-tower / dual-encoder, approximate nearest neighbor (FAISS, ScaNN), embedding-based retrieval | p50 ~10ms; offline ANN index update daily. |
| Ranking | Score the few hundred candidates by predicted engagement | Wide-and-deep, DLRM, gradient-boosted trees over candidate features, sequential transformers | p50 ~50-200ms; trained on hours-old logs. |
| Re-ranking / business logic | Diversify, dedupe, apply business rules, exploration | Heuristics + bandits + MMR-style diversification | p50 ~10ms. |
Retrieval must score every item in the catalog (or a sub-linear approximation) for a query, so it is forced into a model with separable user and item embeddings: the user tower and item tower run independently, the score is an inner product, and an ANN index serves it in milliseconds.
Ranking sees only a few hundred candidates per request and can afford a joint model that mixes user, item, and context features arbitrarily (cross-features, sequence transformers, gradient-boosted trees on top). The two-stage design is what makes it possible to serve recommendations from a catalog of items at p99 latency below 200ms [6].
Sequential models such as SASRec [7] (causal self-attention over a user’s interaction history) and BERT4Rec (masked-token prediction over the same) are the modern ranking-stage workhorses. They model that the order of interactions carries signal that bag-of-items encodings discard.
Evaluation: ranking metrics
Precision@k, recall@k, NDCG@k, MAP
Precision and recall at k
Let be the top- items the model returns for user , and the held-out relevant items.
Average over users. Cheap and intuitive but ignore order within the top-.
Discounted cumulative gain (DCG)
Assign each item rank in the prediction a relevance grade . The discounted gain is
The numerator rewards highly-relevant items disproportionately; the denominator down-weights lower positions [8].
Normalized DCG
where is the ideal DCG (the same formula on the optimally-sorted relevance list). NDCG ranges in and is comparable across users with different numbers of relevant items.
Mean Average Precision (MAP)
Per user, compute precision at each rank where a relevant item appears, average over those ranks (Average Precision = AP), and then average AP over users:
MAP rewards getting relevant items high in the list and is the standard metric in IR.
Hit Rate@k
, averaged over users. Brutal but useful: did we put any relevant item in the top-?
AUC
Per user, the fraction of (relevant, irrelevant) pairs the model orders correctly. This is the metric BPR optimizes a smooth surrogate of.
The hard truth of recommender systems: a 5% lift in offline NDCG can translate to anywhere from -1% to +3% in online clickthrough or watch time, depending on how the model interacts with feedback loops, position bias, exploration, and presentation. The only way to know is to A/B test. Offline evals are useful for ranking candidate models, not for picking the production champion.
How the families compare
| Method | Handles cold start (new user/item)? | Handles implicit feedback? | Scales to 100M users? | Captures latent factors? | Eval metric |
|---|---|---|---|---|---|
| User-User CF | no (needs neighbors) | with similarity tweaks | no (n^2 similarity) | no (rating averages) | precision@k, recall@k |
| Item-Item CF | partial (precomputed item-item table works for new users with any history) | with similarity tweaks | with truncation, yes | no | precision@k, recall@k |
| Matrix Factorization (explicit) | no (no factors for new ids) | no (use HKV instead) | yes (ALS / SGD) | yes | RMSE, NDCG |
| Implicit ALS (HKV) | no for ids; yes if combined with content | yes (its design goal) | yes (Spark ALS) | yes | NDCG, recall@k, AUC |
| BPR-MF | no for ids | yes (designed for it) | yes (SGD with negative sampling) | yes | AUC, NDCG |
| Two-tower retrieval | yes (content features in the towers) | yes | yes (linear in n + ANN serving) | yes (learned embeddings) | recall@k |
Implementations
The numpy version implements explicit-feedback MF with biases via SGD on a
hard-coded ratings matrix. The PyTorch version is the modern equivalent with
gradient-based training. The surprise-style sketch shows the classic
single-machine API (note: surprise does not run under Pyodide, so the
sketch is shown for shape comparison only).
import numpy as np
def fit_mf_sgd(ratings, k=8, n_epochs=50, lr=0.01, lam=0.05, seed=0):
"""Explicit-feedback matrix factorization with biases. ratings is a
list of (u, i, r) tuples."""
rng = np.random.default_rng(seed)
users = np.array([r[0] for r in ratings])
items = np.array([r[1] for r in ratings])
vals = np.array([r[2] for r in ratings], dtype=float)
n_u = users.max() + 1
n_i = items.max() + 1
mu = vals.mean()
bu = np.zeros(n_u)
bi = np.zeros(n_i)
P = 0.1 * rng.normal(size=(n_u, k))
Q = 0.1 * rng.normal(size=(n_i, k))
for epoch in range(n_epochs):
order = rng.permutation(len(ratings))
for j in order:
u, i, r = users[j], items[j], vals[j]
pred = mu + bu[u] + bi[i] + P[u] @ Q[i]
e = r - pred
bu[u] += lr * (e - lam * bu[u])
bi[i] += lr * (e - lam * bi[i])
P_u_old = P[u].copy()
P[u] += lr * (e * Q[i] - lam * P[u])
Q[i] += lr * (e * P_u_old - lam * Q[i])
return mu, bu, bi, P, Q
# Tiny example: 4 users, 5 items, sparse ratings on a 1-5 scale.
ratings = [
(0, 0, 5), (0, 1, 3), (0, 3, 4),
(1, 0, 4), (1, 2, 5), (1, 4, 2),
(2, 1, 5), (2, 3, 4), (2, 4, 4),
(3, 0, 2), (3, 2, 3), (3, 4, 5),
]
mu, bu, bi, P, Q = fit_mf_sgd(ratings, k=4, n_epochs=200)
print("predicted ratings (4x5):")
print(np.round(mu + bu[:, None] + bi[None, :] + P @ Q.T, 2))Run a tiny in-memory MF fit live on a 4-user / 5-item ratings matrix and inspect the predicted full matrix:
(no output yet)
Real-world uses
| Company / system | Stage | What changed historically |
|---|---|---|
| Netflix Prize (2006-2009) | Rating prediction | Popularized matrix factorization with biases; the SVD++ winner combined MF with implicit-feedback signals. |
| YouTube recommendations | Two-stage retrieval + ranking | Covington-Adams-Sargin 2016: candidate-generation deep neural network feeds a ranking deep neural network; explicit hyperparameter for "freshness". |
| Amazon product recommendations | Item-item CF + content | Item-item table famously precomputed offline; "customers who bought this also bought" remains a useful fallback even in the deep era. |
| Spotify Discover Weekly | Hybrid (CF + audio embeddings + NLP on playlist titles) | Combats cold-start by leaning on audio content embeddings for new tracks. |
| TikTok For You | Sequential ranking | Heavy use of sequential transformers and short-feedback signals (watch time, completion rate, replays). |
Pitfalls
Pure ID-based matrix factorization has no factors for users or items unseen at training time. Any system that needs to recommend on day 1 - new users, new products, new content - must either fall back to popularity, use content features in the embedding model (two-tower with content towers), or run an explicit exploration policy (a contextual bandit). “We will deal with cold start later” is the line that kills many recsys launches.
Models trained on logged interactions overweight already-popular items because users were exposed to them more. Without correction (inverse propensity weighting, exploration, off-policy evaluation), the model recommends popular items, which generates more popular-item interactions, which trains the next model. The feedback loop is the most under-discussed hazard in production recsys.
The classic Netflix-prize metric (RMSE on a 1-5 scale) optimizes pointwise calibration but is essentially uncorrelated with what users actually do (click, watch, buy). Modern systems train on implicit feedback with a ranking loss (BPR, listwise softmax, sampled softmax) and evaluate on NDCG@k or recall@k. Always report a ranking metric in addition to (or instead of) a regression metric.
A 5% lift in offline NDCG can map to anywhere from -1% to +3% in online A/B test impact. The mismatch comes from selection bias in the logs, position bias, freshness, presentation effects, and feedback loops. Treat offline evals as a way to filter candidate models and run an A/B test before declaring victory.
Pop quiz
- 1. In the matrix factorization model r_hat_ui = mu + b_u + b_i + p_u . q_i, what role does b_u play?
- 2. Why does implicit-feedback MF use a confidence-weighted loss summed over ALL pairs, while explicit MF sums only over observed pairs?
- 3. What is BPR a smooth surrogate for?
- 4. Why are retrieval and ranking different model classes in production?
- 5. NDCG@k is preferred over precision@k when...
Interview Q&A
Derive ALS for explicit-feedback matrix factorization.onsite
Setup. With observed entries , minimize
(Biases factor cleanly into the same updates; we omit them here for clarity.)
Block structure. With fixed, decomposes as a sum over users:
The user blocks decouple. Let stack the for and the corresponding ratings. The user- subproblem is
Closed-form ridge solution.
Symmetrically,
Algorithm. Alternate (fix , solve all in parallel; fix , solve all in parallel) until convergence. Each block update is monotone non-increasing in , so converges [2].
Cost. Each user update is a ridge solve, per user. Across users, embarrassingly parallel.
Why does matrix factorization beat memory-based collaborative filtering on sparse data?onsite
Coverage. Memory-based CF predicts from a weighted average of neighbors (similar users or similar items) who have rated item . On long-tail items, there may be zero such neighbors, so the model has no signal at all. MF, on the other hand, has and for every user and item, and the latent factors generalize across items the user has not interacted with.
Generalization through compression. MF compresses the sparse rating matrix into two -rank tables ( with ). The -regularized loss explicitly trades fit for parsimony; the model only “spends” capacity on signals supported by enough data. Memory-based CF has no such regularization and treats every co-occurrence at face value.
Latent factors are interpretable as themes. A user who has rated only a handful of films but tends toward “subtle European thrillers” gets a that aligns with that subspace. New films in that subspace get a that scores well against even though no neighbor has rated them.
Empirical evidence. The Netflix Prize was won by ensembles dominated by MF variants (SVD++, timeSVD); pure user-user / item-item CF placed far behind [2][9].
Why do we use BPR loss instead of pointwise log-loss for implicit feedback?onsite
Pointwise log-loss problems. Treating each pair as a binary classification example with is dominated by the (vast) majority of unobserved pairs. The model spends most of its capacity learning to predict for the bulk of the catalog, which is not the same as ranking observed items above unobserved ones.
The right objective is per-user AUC. What we actually want is, for each user with positive set and negative set :
The indicator is non-differentiable; replace it with and take to get the BPR loss [4]:
So BPR is a smooth, differentiable surrogate for AUC, which is exactly the ranking quantity we care about.
Practical advantage. BPR with negative sampling is per epoch (one negative sampled per positive), not .
What strategies handle the cold-start problem?onsite
Three families, often combined:
- Content features in the embedding model. A two-tower model where the user tower includes demographics / context and the item tower includes content features (text embeddings, image embeddings, genre, language, …) produces meaningful embeddings for new users and new items because the towers have learned how content features map to the latent space. This is the modern default.
- Popularity prior + diversification. For brand-new users, fall back to popular items in their language and region; mix in a diversity prior so the user is exposed to multiple sub-domains and the next-step model gets signal.
- Exploration via contextual bandits. Treat recommendation as a bandit: every recommendation collects observations that update the policy. UCB or Thompson sampling on user-item context features explicitly trades exploitation against exploration. This is what makes “discovery” surfaces (Spotify Discover Weekly, YouTube’s mixers) work.
In practice all three coexist: a two-tower retrieval model with content features handles structural cold start, a popularity prior handles unauthenticated traffic, and bandit-style exploration ensures the model keeps learning about niche content that is undersampled in the logs.
A weekend project to internalize the recsys stack:
- Implement explicit-feedback MF with biases via SGD (the numpy
CodeCompareblock) on the MovieLens 100K dataset and reach RMSE under 0.93 with and tuned . - Switch to implicit feedback by binarizing ratings ( for any rating) and implement HKV-style ALS. Compare to BPR-MF on NDCG@10 and recall@10.
- Build a tiny two-tower model in PyTorch where the user tower takes (user id, age bucket, country) and the item tower takes (item id, genre one-hot). Train with sampled-softmax. Verify it produces non-trivial recommendations for cold-start users.
- Stretch: implement a popularity baseline and verify your MF beats it on NDCG@10 - many production recsys efforts do not.
References
- Sarwar, B., Karypis, G., Konstan, J., and Riedl, J. (2001). Item-based collaborative filtering recommendation algorithms. Proc. 10th International World Wide Web Conference (WWW), 285-295. doi:10.1145/371920.372071
- Koren, Y., Bell, R., and Volinsky, C. (2009). Matrix factorization techniques for recommender systems. Computer 42(8), 30-37. doi:10.1109/MC.2009.263
- Hu, Y., Koren, Y., and Volinsky, C. (2008). Collaborative filtering for implicit feedback datasets. Proc. 8th IEEE International Conference on Data Mining (ICDM), 263-272. doi:10.1109/ICDM.2008.22
- Rendle, S., Freudenthaler, C., Gantner, Z., and Schmidt-Thieme, L. (2009). BPR: Bayesian personalized ranking from implicit feedback. Proc. 25th Conference on Uncertainty in Artificial Intelligence (UAI), 452-461. arXiv:1205.2618
- Rendle, S. (2010). Factorization machines. Proc. IEEE International Conference on Data Mining (ICDM), 995-1000. doi:10.1109/ICDM.2010.127
- Covington, P., Adams, J., and Sargin, E. (2016). Deep neural networks for YouTube recommendations. Proc. 10th ACM Conference on Recommender Systems (RecSys), 191-198. doi:10.1145/2959100.2959190
- Kang, W.-C. and McAuley, J. (2018). Self-attentive sequential recommendation. Proc. IEEE International Conference on Data Mining (ICDM), 197-206. doi:10.1109/ICDM.2018.00035
- Järvelin, K. and Kekäläinen, J. (2002). Cumulated gain-based evaluation of IR techniques. ACM Transactions on Information Systems 20(4), 422-446. doi:10.1145/582415.582418
- Hastie, T., Tibshirani, R., and Friedman, J. (2009). The Elements of Statistical Learning, 2nd ed.. Springer. https://hastie.su.domains/ElemStatLearn/