Step 1: More like this
Marrow & Page, an online bookshop, wants a "more like this" row under every book and a "recommended for you" row on
the home page. You have its 300 books (books.json) and 950 customers' clicks (sessions.json). Start with the
text: embed each book, and books whose embeddings point the same way are alike.
1. Write book_text(book) in recommender.py: "<title> by <author>. <blurb>".
2. Write more_like_this(E, i, k): the k books most similar to book i. The rows of E are unit length, so
E @ E[i] is every book's cosine similarity to i. Leave out i itself and books out of stock (IN_STOCK).
3. Run. It embeds the catalogue (cached after the first run) and shows two books' neighbours.
recommender.py, the file you edit83 lines
"""Marrow & Page's "more like this" and "recommended for you", built from embeddings and from what people read."""
import numpy as np
import harness as H
BOOKS, SESSIONS = H.load()
IX = {b["id"]: i for i, b in enumerate(BOOKS)}
IN_STOCK = np.array([b["in_stock"] for b in BOOKS])
NEW = np.array([b["added"] == "2026-09" for b in BOOKS]) # added this month: nobody has clicked them yet
def zscore(x):
return (x - x.mean()) / (x.std() + 1e-9)
# ---------- Step 1: more like this ----------
def book_text(book):
"""The text to embed for a book: "<title> by <author>. <blurb>"."""
# TODO (Step 1): "<title> by <author>. <blurb>".
raise NotImplementedError("Step 1: write book_text()")
def more_like_this(E, i, k=5):
"""The k row indices most similar to book i by cosine similarity (E rows are unit length): not i itself, and only
books in stock (IN_STOCK). Most similar first."""
# TODO (Step 1): E @ E[i], then leave out i and books out of stock; the k best, best first.
raise NotImplementedError("Step 1: write more_like_this()")
# ---------- Step 2: measure it offline ----------
def rank(scores, history, k=10):
"""The k best row indices by score, leaving out the history (row indices) and books out of stock."""
raise NotImplementedError("rank() arrives in Step 2")
def evaluate(score_fn, only_new=None, k=10):
"""{"hit", "mrr", "n"} over SESSIONS["test"]: score_fn(history row indices) gives a score per book; a hit is the
target in rank(..., k); mrr averages 1/position (0 for a miss). only_new=True or False keeps only the customers
whose target is (or is not) a NEW book."""
raise NotImplementedError("evaluate() arrives in Step 2")
def clicks():
"""How many times each book was clicked in SESSIONS["train"], as an array in row order."""
raise NotImplementedError("clicks() arrives in Step 2")
def profile(E, history, decay=0.8):
"""Scores from a taste profile: the history's embeddings summed with weight decay**age (the last click has age
0, weight 1), then the dot product of every book with that profile."""
raise NotImplementedError("profile() arrives in Step 2")
# ---------- Step 3: what people read together ----------
def cooccurrence():
"""A books x books matrix: C[a, b] = the number of train sessions containing both a and b (a != b), divided by
sqrt(clicks[a] * clicks[b]), 0 where either has no clicks. The diagonal is 0."""
raise NotImplementedError("cooccurrence() arrives in Step 3")
# ---------- Step 4: new books ----------
def blend(co_scores, emb_scores, counts, k=3):
"""w * zscore(co_scores) + (1 - w) * zscore(emb_scores), per book, where w = counts / (counts + k): a book with
many clicks is judged by who read it, a book nobody has clicked by its text."""
raise NotImplementedError("blend() arrives in Step 4")
# ---------- Step 5: lists people want ----------
def mmr(scores, E, history, k=10, lam=0.7, pool=50):
"""Maximal marginal relevance: from the top `pool` of rank(scores, history, pool), pick k one at a time, each time
the book with the highest lam * relevance - (1 - lam) * (its highest similarity to a book already picked).
Relevance is the score rescaled to 0..1 over the pool (max 1, min 0)."""
raise NotImplementedError("mmr() arrives in Step 5")
def cap_authors(ranked, k=10, per_author=2):
"""The first k of ranked (row indices, best first) keeping at most per_author books by any one author."""
raise NotImplementedError("cap_authors() arrives in Step 5")books.jsonharness.pysessions.jsontry_it.py