6 · User-user collaborative filtering
'Users like you also liked…'. Find the target user's nearest neighbors by their rating vectors, then predict an unseen rating as the similarity-weighted average of what those neighbors thought.
User-user CF needs no item features at all: treat each user as their row of the rating matrix, find the users whose taste is most similar (after removing each person's rating-scale bias by mean-centering), and predict an unseen rating as the similarity-weighted average of those neighbors' opinions.
Without this:
Skip mean-centering and a generous user who rates everything 4-5 looks similar to everyone, while a harsh 1-2 user looks similar to no one — similarity ends up measuring rating habits instead of taste.
Content-based methods (Chapter 2) needed item features. Collaborative filtering (CF) throws features away and learns purely from the matrix: it assumes that if you and I agreed on the items we both rated, we'll agree on the ones only one of us has seen. That is the whole idea behind 'users like you also liked'.
The simplest CF is user-user (memory-based) filtering. To predict how user u would rate item i:
- Treat each user as a vector — their row of the rating matrix.
- Measure similarity between
uand every other user (we'll use cosine). - Keep
u's top-k neighbors who actually rated itemi. - Predict
R[u,i]as the similarity-weighted average of those neighbors' ratings.
There is one essential preprocessing step: mean-centering. People use the rating scale differently — an easy grader's 4 means what a tough grader's 2 means. If you compare raw rows, you mostly measure rating habits, not taste. So before computing similarity (and before averaging), subtract each user's own mean from their observed ratings. The prediction formula on centered ratings is:
pred(u,i) = mean(u) + Σ_v sim(u,v)·(R[v,i] − mean(v)) / Σ_v |sim(u,v)|
The mean(u) puts the answer back on u's personal scale, and the neighbors contribute their deviations from their own means. Below we build a tiny rating matrix, hide one true entry, and reconstruct it from neighbors.
Python (in browser)
User-user CF: mean-center rows, cosine-similarity to find neighbors, predict a held-out rating as the similarity-weighted average of neighbors' centered ratings.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Why do we mean-center each user's ratings BEFORE computing user-user similarity?
- User-user CF predicts an unseen rating from the similarity-weighted ratings of the target user's nearest neighbors — no item features needed.
- Mean-center each user's row first to strip out rating-scale bias, then add the target's mean back in the prediction.
- It is 'memory-based': it keeps the full matrix and computes neighbors on demand — interpretable but hard to scale to millions of users.
The original GroupLens/MovieLens recommenders were user-user CF, and 'people similar to you' modules still use the same neighbor logic today.
If you remove it: Without neighbor-based CF you'd fall back to popularity for everyone — losing the personalization that comes free from the matrix.