Pick a depth. Each prompt opens in your AI pre-loaded with the lesson. Click a row to preview the prompt.
Before you build anything clever, build the dumbest possible recommender: rank items by global popularity and show the same top-N list to everyone. This baseline is embarrassingly strong — popular items are popular because lots of people like them, so 'most popular' captures a real, if generic, signal. It's also nearly free to compute and impossible to overfit. The entire point of building a personalized model is to beat this baseline by a meaningful margin; if your matrix factorization or deep ranker can't clear the popularity bar on held-out data, something is broken — a bug, a leak, or a case where personalization genuinely doesn't help this catalog. Every module from here on will implicitly or explicitly compare against this baseline, so building it now and measuring it honestly is not optional busywork.
The demo builds a tiny synthetic interaction dataset with a real (but weak) personalization signal, computes a popularity-only recommender, and measures recall@10 for both so you have a concrete number to beat.
import random
from collections import Counter
random.seed(3)
N_USERS, N_ITEMS = 300, 100
# Ground truth: each user has a hidden "taste cluster" that boosts a subset
# of items, layered on top of overall popularity.
popularity = {i: random.paretovariate(1.5) for i in range(N_ITEMS)}
clusters = {u: random.randint(0, 4) for u in range(N_USERS)}
cluster_boost = {c: random.sample(range(N_ITEMS), 10) for c in range(5)}
def sample_interactions(u, n=15):
weights = [popularity[i] * (3 if i in cluster_boost[clusters[u]] else 1) for i in range(N_ITEMS)]
return set(random.choices(range(N_ITEMS), weights=weights, k=n))
train, test = {}, {}
for u in range(N_USERS):
seen = sample_interactions(u, 20)
seen = list(seen)
random.shuffle(seen)
split = int(len(seen) * 0.7)
train[u], test[u] = set(seen[:split]), set(seen[split:])
# --- Baseline: global popularity from train interactions only ---
counts = Counter()
for u, items in train.items():
counts.update(items)
top_popular = [i for i, _ in counts.most_common(10)]
def recall_at_k(recs, held_out):
if not held_out:
return None
hits = len(set(recs) & held_out)
return hits / len(held_out)
recalls = [recall_at_k(top_popular, test[u]) for u in range(N_USERS) if test[u]]
print(f"popularity baseline recall@10: {sum(recalls)/len(recalls):.3f}")
# --- "Personalized": recommend the boosted items for the user's cluster ---
def personalized_recs(u):
return cluster_boost[clusters[u]][:10]
recalls_p = [recall_at_k(personalized_recs(u), test[u]) for u in range(N_USERS) if test[u]]
print(f"cluster-personalized recall@10: {sum(recalls_p)/len(recalls_p):.3f}")python3 main.py