4 · Content-based filtering
Describe items by their features, average the features of what a user liked into a taste profile, then score unseen items by how close they are. No other users required — and item cold-start vanishes.
Content-based filtering recommends items that resemble — in their own features (genre, tags, text) — the items a user already liked. You build a user profile as the rating-weighted average of the feature vectors of liked items, then rank unseen items by cosine similarity to that profile.
Without this:
Without content features, a brand-new item with zero ratings is invisible to collaborative filtering. Content-based scoring uses features that exist on day one, so a new movie can be recommended the moment it lands — item cold-start solved.
Collaborative filtering looks sideways — 'what did similar users like?'. Content-based filtering looks at the items themselves — 'what is this item like, and does it resemble what you already enjoyed?'. It never needs another user, which is exactly why it cures item cold-start: a brand-new movie has no ratings, but it still has a genre, a cast, and a description on day one.
The recipe has three steps:
- Item feature vectors. Describe each item as a vector of features. The simplest is a genre one-hot: a row with a 1 in every genre the item belongs to. (Real systems add cast, director, tags, TF-IDF of the synopsis — next lesson.)
- User profile. Summarize a user's taste as a single vector in the same feature space. The classic construction is the rating-weighted average of the feature vectors of the items they rated. Crucially, we center the ratings around the user's mean first, so a liked item pulls the profile toward its features and a disliked item pushes the profile away — a 1-star romance should make romance negative in the profile, not just weakly positive.
- Score unseen items by cosine similarity between the profile and each unseen item's feature vector, then rank. Cosine measures direction (which genres), ignoring magnitude, so an item whose genres align with the profile scores high regardless of how many genres it has.
Below we do exactly this for a user who loves sci-fi and dislikes romance, and watch the recommendations fall out — with no second user anywhere in sight.
Python (in browser)
Build a user taste profile from genre one-hots, then cosine-rank unseen items. Sci-fi wins, romance goes negative — all from one user's own history.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Why do we CENTER a user's ratings (subtract their mean) before building the content profile?
- Content-based filtering scores items by their own features (genre one-hot, tags, text), never needing other users.
- A user profile = the (mean-centered) rating-weighted average of liked items' feature vectors, so dislikes get negative weight.
- Rank unseen items by cosine similarity to the profile; this directly solves item cold-start.
Spotify's audio-feature recommendations, news/article recommenders, and 'because you watched X' rows lean on content features for fresh and niche items.
If you remove it: Drop content features and your system goes blind to every new release until it accumulates ratings — a brutal disadvantage for catalogs with constant new arrivals.