Recommender systems from the ground up
Collaborative filtering
Collaborative filtering needs no item features. It finds neighbours in the interaction matrix and lets the crowd stand in for the description.
The matrix
Put users in rows and items in columns. Each cell is a rating or a count, and most cells are empty. Collaborative filtering (using the behaviour of other users) works from that matrix alone. Two users are similar if they rated the same items in a similar way. Two items are similar if the same users touched them.
The empty cells define the problem. A full matrix would make the job easy and pointless: if everyone had rated everything, sorting by rating would be enough. The recommender exists because the matrix is sparse (almost all of it is empty), and the empty cells are the ones the product wants to fill.
The values in the cells matter. A rating is a level. A count is an amount. A binary event is a fact. Mix them in one matrix, and the score becomes hard to read and easy to misuse. Comparing a cosine built on counts with one built on ratings has the same fault. Pick one meaning per matrix and say so.
User-based and item-based
User-based CF finds people like you and recommends what they liked. Item-based CF finds items like the ones you liked and recommends those. Item-based is usually more stable. Item neighbourhoods change more slowly than people do, and catalogues often hold fewer items than users.
Both are neighbourhood methods: pick k neighbours, weight what they say by similarity, and score the unseen items. The value of k is a trade between noise and over-smoothing. A handful of neighbours is noisy. Thousands of them turn every user into the global average.
The score is usually a weighted average, not a vote. For user-based CF, the score for an unseen item is the neighbours’ ratings weighted by similarity. Each user’s mean is often subtracted first, so a generous rater does not dominate. Mean-centring is the gap between “people like you rated this highly” and “people like you rate everything highly”.
Item-based neighbours with Surprise
from surprise import Dataset, KNNBasic
data = Dataset.load_builtin("ml-100k")
trainset = data.build_full_trainset()
algo = KNNBasic(k=40, sim_options={"name": "cosine", "user_based": False})
algo.fit(trainset)
print(algo.predict("196", "302").est)Neighbours in a binary matrix
Run the item-item scorer and inspect the neighbours of each liked item. Then add a third liked item and see whether the recommendations become more or less diverse.
Similarity, and what it hides
Cosine on the rating vectors, Jaccard on binary events, or Pearson correlation on centred ratings. Each answers a slightly different question. Jaccard ignores how much. Pearson ignores level and compares shape. Cosine sits in between and is the usual default.
All of them are inflated by popularity. Two items co-occur because both are popular, not because they belong together. Divide by the items’ base rates (how often each item appears at all). Without that step the ranking rediscovers the head of the catalogue and calls it a neighbourhood.
Shrinkage is the standard repair. Multiply a similarity by the number of shared users, divided by that number plus a constant. An overlap of one then counts for little, and an overlap of fifty counts for almost all of it. The constant is a prior: how much evidence you need before you trust a fluke.
A co-occurrence is not a cause. Two items bought together may simply both be cheap, both be promoted, or both be shown on the same page. Check the base rate before trusting the edge.
Sparsity is the real problem
A catalogue of a million items and a user with twenty interactions gives a matrix that is almost all empty. Only a tiny fraction of one percent is filled. Neighbourhoods computed on that little overlap are noisy. Many users have no usable neighbour at all, so the system quietly falls back to popularity.
The next chapter moves to latent factors for that reason. Latent factors (a short vector per user and per item) pool evidence across the whole matrix. They do not rely on direct overlap. Coverage — the share of users and items that can be scored at all — matters as much as accuracy. It is the number a neighbourhood model usually loses on.
Sparse data also makes evaluation noisy. A user with three test interactions can move the mean by a third. A single lucky hit can look like a breakthrough. Report the number of users behind a metric. Do not compare two models on a split where the gap is smaller than the variation between users. A confidence interval on the mean is cheap to compute. It is often wider than the gap between two candidates, which is a useful check before a release.
A weighted vote from neighbours
Run the prediction for the missing item, then lower k to one neighbour and see how much the estimate moves. A prediction built on one person is an anecdote.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What does collaborative filtering use as its signal?
