Recommender Systems Cheat Sheet
Compares content-based filtering, collaborative filtering, and matrix factorization approaches, with code examples and ranking metrics for evaluation.
Recommendation Approaches
Core paradigms for building a recommender.
- Content-based filtering- Recommends items similar to what a user liked before, based on item features (genre, tags, description)
- Collaborative filtering (user-based)- Recommends items liked by users with similar rating patterns
- Collaborative filtering (item-based)- Recommends items similar to ones the user already rated highly, based on co-rating patterns
- Matrix factorization- Decomposes the user-item rating matrix into low-rank latent user and item factor matrices (e.g., SVD, ALS)
- Hybrid- Combines content-based and collaborative signals to handle cold-start and sparsity
- Cold-start problem- Difficulty recommending for new users/items with no interaction history
Content-Based Similarity
Recommend items with similar TF-IDF text features.
from sklearn.feature_extraction.text import TfidfVectorizerfrom sklearn.metrics.pairwise import cosine_similaritytfidf = TfidfVectorizer(stop_words='english')tfidf_matrix = tfidf.fit_transform(items_df['description'])sim_matrix = cosine_similarity(tfidf_matrix)similar_idx = sim_matrix[item_index].argsort()[::-1][1:11] # top 10, excluding itselfrecommendations = items_df.iloc[similar_idx]['title']
Matrix Factorization with SVD
Factorize a sparse ratings matrix using scikit-learn's TruncatedSVD.
from sklearn.decomposition import TruncatedSVDimport numpy as np# ratings_matrix: rows = users, cols = items, 0 = missing ratingsvd = TruncatedSVD(n_components=20, random_state=42)user_factors = svd.fit_transform(ratings_matrix)item_factors = svd.components_.Tpredicted_ratings = user_factors @ item_factors.Ttop_items_for_user = np.argsort(predicted_ratings[user_id])[::-1][:10]
Evaluation Metrics
Rating-prediction accuracy vs. ranking quality.
- RMSE / MAE- Measures error between predicted and actual ratings; standard for explicit-rating tasks
- Precision@K- Fraction of the top-K recommended items the user actually interacted with
- Recall@K- Fraction of all relevant items that appear in the top-K recommendations
- NDCG- Rewards ranking relevant items higher, discounting relevance by position in the list
- Coverage- Fraction of the catalog the system is capable of recommending; guards against always recommending the same popular items
Implicit Feedback with ALS
Factorize click/purchase counts (not explicit ratings) using confidence-weighted Alternating Least Squares.
import implicitfrom scipy.sparse import csr_matrix# user_item: sparse matrix of raw interaction counts (clicks, buys)confidence = (1 + 40 * user_item).astype('double') # alpha=40 confidence scalingmodel = implicit.als.AlternatingLeastSquares( factors=64, regularization=0.05, iterations=20)model.fit(confidence)# Recommend for a single user (returns item ids + scores)item_ids, scores = model.recommend( userid=42, user_items=confidence[42], N=10, filter_already_liked_items=True)
Bayesian Personalized Ranking Loss
Pairwise ranking objective that directly optimizes for correct item ordering instead of rating error.
import torchimport torch.nn.functional as Fdef bpr_loss(user_emb, pos_item_emb, neg_item_emb): pos_scores = (user_emb * pos_item_emb).sum(dim=-1) neg_scores = (user_emb * neg_item_emb).sum(dim=-1) # maximize margin between observed (pos) and sampled unobserved (neg) items return -F.logsigmoid(pos_scores - neg_scores).mean()# training stepoptimizer.zero_grad()loss = bpr_loss(u_vec, pos_vec, neg_vec) + 1e-5 * (u_vec.norm() + pos_vec.norm())loss.backward()optimizer.step()
Two-Tower Retrieval Model
Separately encode users and items into a shared embedding space for fast approximate nearest-neighbor serving.
import torch.nn as nnclass TwoTowerModel(nn.Module): def __init__(self, n_users, n_items, dim=64): super().__init__() self.user_tower = nn.Embedding(n_users, dim) self.item_tower = nn.Embedding(n_items, dim) def forward(self, user_ids, item_ids): u = F.normalize(self.user_tower(user_ids), dim=-1) i = F.normalize(self.item_tower(item_ids), dim=-1) return (u * i).sum(dim=-1) # cosine similarity score# Serving: precompute item_tower(all_items) offline, index with ANN (Faiss/ScaNN),# then at request time only run the user tower + a nearest-neighbor lookup.
Beyond-Accuracy Objectives
Metrics that catch failure modes rating-error and ranking metrics miss.
- Diversity- Intra-list dissimilarity among the top-K items; guards against recommending near-duplicates
- Novelty- How unexpected/unpopular the recommended items are, weighted by inverse item popularity
- Serendipity- Relevant items the user would not have discovered through obvious means; combines novelty with relevance
- Exposure bias / feedback loop- Logged interactions are biased toward what the system already recommended, inflating offline metrics for popular items
- Popularity bias- Tendency of collaborative models to over-recommend already-popular items, starving the long tail
- Calibration- Whether the distribution of recommended categories matches the user's historical interest distribution
MMR Re-Ranking for Diversity
Maximal Marginal Relevance trades off relevance and diversity after an initial ranking pass.
import numpy as npdef mmr_rerank(candidates, relevance, sim_matrix, k=10, lambda_=0.7): selected, remaining = [], list(range(len(candidates))) while remaining and len(selected) < k: def mmr_score(i): max_sim = max([sim_matrix[i][j] for j in selected], default=0) return lambda_ * relevance[i] - (1 - lambda_) * max_sim best = max(remaining, key=mmr_score) selected.append(best) remaining.remove(best) return [candidates[i] for i in selected]
Optimizing for RMSE alone doesn't guarantee good rankings -- always pair rating-error metrics with a top-K ranking metric like Precision@K or NDCG before shipping a recommender.