Here is the thing that still feels a little magical to me about recommender systems: a good one can suggest a film in a genre you have never touched, and be right, without knowing a single thing about what the film is actually about. No plot summary, no cast list, no tags. It works purely from the pattern of who rated what. That is collaborative filtering, and once it clicks it is surprisingly simple.
The starting point is a big table. Rows are users, columns are items, and each cell holds a rating, say 1 to 5 stars. The catch is that almost every cell is empty. Nobody rates more than a tiny sliver of a catalogue, so a real matrix is 95 to 99 percent blank. The entire job is to guess the blanks well, then show each user the handful of items with the highest predicted ratings. That is it. Everything else is just how you fill in the holes.
Find people like you
The oldest approach is user-user filtering. To recommend for you, find the users whose past ratings look most like yours, then borrow what they liked and you have not seen yet. “Look like yours” needs a number, and the number is cosine similarity computed over the items you both rated.
There is one detail that matters more than it looks. Before comparing two users, subtract each one’s own average rating. Some people are generous and hand out fours and fives; some are harsh and cap out at three. If you do not center them, the generous rater looks like they love everything and the harsh one looks like they hate everything, even when they actually agree on which films are better than which. Mean-centered cosine on the shared items is exactly the Pearson correlation, and it runs from +1 for taste twins, through 0, down to -1 for people with opposite taste. That negative end is useful: someone who reliably disagrees with you is just as informative as someone who agrees.
To predict your rating for an item, take your top-k most similar neighbours who actually rated it, and average their opinions weighted by similarity, working in deviations from each neighbour’s mean and adding yours back. Closer neighbours pull harder. Divide by the sum of the absolute similarities so the answer stays on the 1-to-5 scale.
Flip it: item-item
Amazon made a small change that turned out to be a big deal. Instead of finding users like you, find items like the ones you already rated. Two movies are similar if the same people scored them alike. “Customers who bought this also bought that” is item-item filtering.
It wins in practice for two reasons. There are usually fewer items than users, and the relationships between items barely shift week to week, so you can precompute the whole item-to-item similarity table offline and serve recommendations with a fast lookup. And each item tends to have far more ratings than each user, so its similarities are estimated from more data and come out steadier. Less noise, cheaper to serve.
Or learn the tastes directly
Neighbourhood methods stay local. Matrix factorization goes global. The idea: assume every user and every item can be described by a short vector of hidden factors, learned rather than given, and that a rating is roughly the dot product of the two. Fit those vectors with stochastic gradient descent. Walk each known rating, measure the error, nudge both vectors to shrink it, add a little regularization so it does not overfit. Repeat for many passes.
This is the family that won the Netflix Prize. The learned factors self-organize into meaningful axes, often something like sci-fi on one end and romance on the other, and predicting a missing rating is one dot product. In my demo you can watch the training RMSE drop epoch by epoch as the factors separate the tastes apart.
The part nobody escapes
Collaborative filtering has one blind spot it cannot fix on its own: cold start. A brand-new user has no neighbours. A brand-new item has no ratings, so it never gets recommended, so it never gets rated. Real systems patch this by falling back to popularity for newcomers, asking a couple of onboarding questions, and blending in content features so a fresh item can be matched by what it is until the ratings arrive. And most real data is not stars at all but implicit signals, clicks and plays and purchases, which need their own confidence-weighted treatment.
I built an interactive version where you pick a user, toggle between user-user, item-item, and matrix factorization, slide the number of neighbours, and watch the blanks fill in with the drivers highlighted.
Play with it here: https://dev48v.infy.uk/ml/day27-recommender.html
