A 5-minute guided walkthrough · no experience required

Taste-Profile Recommender Matrix Factorization POC

You finish a show, tap “More Like This,” and either nod because it gets you — or roll your eyes and go find something to watch yourself. This demo is about that gap, and whether a machine can close it having never been told a single thing about genres, categories, or what anyone actually likes.

Why it matters: get this wrong often enough and people quietly stop opening the app for recommendations at all — they scroll past whatever surfaces and go search for something themselves, or worse, open a competitor's app instead. Get it right and someone finds their next favorite show in ten seconds instead of twenty minutes of scrolling. Below, meet Jordan — a real (synthetic) person in this dataset who has rated exactly 3 titles, and strong opinions about all 3 — and watch whether the model reads them correctly using nothing but those 3 numbers. Then verify the aggregate claim yourself against 2,105 people the model has never seen.

Step 1
Meet Jordan
Watch the model guess a stranger's taste cold — no genres, no hints, just 3 star ratings.
Step 2
Grab the real equation
Put your thumb on the actual math and watch a prediction move in real time.
Step 3
Did the model get Jordan right?
Get the verdict, then pull your own evidence and watch the accuracy claim prove itself.
Reference — what this demo claims (see Step 3 to verify it yourself)

Citation

Koren, Y., Bell, R., and Volinsky, C. 2009. Matrix Factorization Techniques for Recommender Systems. IEEE Computer 42, 8 (2009), 30-37. DOI: 10.1109/MC.2009.263.
🏆 Not a toy built for this demo: this is the exact family of method — regularized matrix factorization with per-user and per-item bias terms — that won the $1M Netflix Prize (2006–2009) for a 10%+ improvement in recommendation accuracy over Netflix's own production system at the time. Koren, Bell & Volinsky's 2009 paper above is the write-up of that winning approach.

Methodology

Matrix factorization predicts how a user would rate an item by breaking that rating into four learned pieces, then adding them together: (1) the GLOBAL AVERAGE rating across everyone, (2) a USER BIAS -- some users rate everything higher or lower than average, (3) an ITEM BIAS -- some items get rated higher or lower than average regardless of who's watching, and (4) a PERSONALIZED MATCH -- each user and each item is assigned a vector of 20 numbers ('latent factors'), learned purely from the ratings data with no hand-labeled genres or categories, and the match score is how well the user's vector and the item's vector point in the same direction. A user whose vector points toward 'the scifi/action corner' of that 20-dimensional space gets high match scores on items whose vectors point the same way -- even though the model was never told what 'scifi' or 'action' means. Below, five independently-manipulable controls put both that four-term equation AND the falsification proof itself in your hands: 'Drag the taste vector' places the gold dot at a real user's real learned position on 2 of the model's 20 latent dimensions and recomputes the personalized-match term live as you drag it; the AXIS PICKER lets you choose *which* 2 of the 20 dimensions get plotted, so you aren't stuck with one fixed projection -- every one of the 20 is a real, explorable axis; the USER BIAS slider and ITEM BIAS slider let you override those two additive terms directly and watch the predicted rating -- and the ranked list -- recompute live; and 'Verify the proof yourself' lets you draw random pairs from the ACTUAL held-out set the proof below was scored on and watch your own running RMSE converge onto that pre-registered number as you draw more -- the same Law-of-Large-Numbers pattern that makes a coin's observed frequency converge to its true probability. All five read from the SAME formula and the SAME learned numbers the server uses for real recommendations and the real proof, not a canned example.

Proof panel

Held-out metric
0.8719 RMSE (held-out, lower is better)
Threshold
1.1867 RMSE (popularity-only baseline -- must beat this to prove real taste-profile signal, not just popularity bias)
Outcome
PASS
Held-out RMSE, computed on 2,105 (user,item) pairs never used during training -- see "Drag the taste vector" further down the walkthrough for how a single prediction is actually computed, live.

The prediction equation — plain English and formal notation, together

In plain English: the star rating this model predicts for one user and one item is just four numbers added together — the average rating everyone gives, how much higher or lower this user tends to rate things, how much higher or lower this item tends to get rated, and how well this user's learned taste vector points in the same direction as this item's. Formally, in the same breath:

r^ui =μ+ bu+ bi+ qiTpu
The row of symbols above is real MathML, not a picture — view-source (or a screen reader) sees the identical expression, copy-pasteable, not a screenshot standing in for it.
TermPlain EnglishFormal
Global average The average star rating across every rating in the whole training set, regardless of who gave it or what it was for. μμ
User bias How much higher or lower this user rates things than that average — some people are just harsher or more generous graders, independent of taste. bubu
Item bias How much higher or lower this item gets rated than average, independent of who's rating it — a widely-loved item vs. a widely-panned one. bibi
Personalized match How well this user's latent taste vector (20 numbers, learned purely from ratings, with no hand-labeled genres) points in the same direction as this item's — their dot product. qiTpuqiTpu
Read the full derivation — the objective function, regularization, and the exact update rule (for a technical reader)

The four terms above aren't fit one at a time — they're learned jointly by minimizing one number: the total squared prediction error over every known rating, plus an L2 regularization penalty that keeps every learned number small so the model doesn't overfit to any single rating.

L= (u,i)K (ruir^ui)2 +λ ( bu2+ bi2+ qi2+ pu2 )

L is minimized with stochastic gradient descent: for every known rating, visited in random order, each term nudges itself toward making THAT ONE prediction more accurate, scaled by a learning rate γ and pulled back toward zero by the same λ. This run used γ=0.01, λ=0.05, over 60 full passes ("epochs") through the training data — pu in the qi update below is the value from before this interaction's own update (Koren et al. 2009's simultaneous-update formulation).

e=ruir^ui bubu+γ(eλbu) bibi+γ(eλbi) qiqi+γ(epuλqi) pupu+γ(eqiλpu)

The Proof panel's headline number is RMSE (root-mean-square error), computed on the held-out set T — 2105 (user, item, rating) triples the model never trained on: the square root of the average squared gap between predicted and actual rating.

RMSE= 1|T| (u,i,r)T (ruir^ui)2

This is exactly the risk this run's falsification proof guards against: popularity bias — a model that just learned "recommend whatever's already popular" can look accurate on many real datasets, because popular items collect denser, less noisy rating signal. The pre-registered threshold in the Proof panel above is a popularity-only baseline's own RMSE, computed on this same held-out set with zero leakage; this model beats it on data it never trained on, which a pure-popularity model structurally could not do.

⇩ Download the full technical write-up (.md) — citation, every formula above, and this run's real hyperparameters, in one self-contained file
Step 1 of 3

Meet Jordan

Jordan has rated exactly 3 titles: 5 stars for a scifi pick, 4 stars for an action film, 1 star for a romance. That's the entire signal the model gets — it has never been told what any of those genre words mean, only 3 numbers next to 3 IDs. So here's the question this whole walkthrough is really asking: can it figure Jordan out from that alone?

👆 Click a user in the list on the left. Jordan — the gold-outlined “scenario” entry at the top — is pre-selected for you: their known ratings appear on the left of the panel that opens, and the model's top unrated picks appear on the right.

Users

Select a user to view their ratings and recommendations.

You're now looking at one real prediction end to end. Check the “genre affinity” table at the bottom of that panel and the payoff box beneath it — the model was never told what a genre is, yet its picks cluster exactly where Jordan already rates highly. That's one person. Step 2 shows you exactly how that number gets computed, term by term; Step 3 checks whether the pattern holds up across everyone else.

Next: Grab the real equation →
Step 2 of 3

Look inside one prediction

So how did the model actually reason about Jordan? Not with a genre lookup — it doesn't have one. Every predicted rating is just 4 numbers added together. Below, you move each one by hand, on Jordan's real learned position, and watch a real prediction change live — the exact same formula the server just used to build the recommendation list you saw in Step 1.

Controls 1–4: four independently-manipulable terms, one live equation

Select a user above to load their real learned taste-vector position.
1Drag the taste vector · 2Pick the axes

👆 Drag the gold dot below — it's the user you picked in Step 1, plotted on 2 of the model's 20 learned taste dimensions. Watch the ranked list on the right reorder as you move it.

Factor A: 0.000 · Factor B: 0.000

👆 Try the two “Factor” dropdowns below to plot a completely different pair of the model's 20 dimensions — the dot jumps to this user's real position on whichever 2 you pick.

Axes (any 2 of the model's 20 learned dims): Factor A Factor B
Live top 5, recomputed as you drag or slide anything below
#ItemGenrePredicted
Pinned item — exact live decomposition
Click any dot on the plot to pin an item here; its predicted rating recomputes as you drag or slide.
Click an item dot to pin it.
34 Perturb the bias terms — 2 more real regression parameters

👆 Click any dot on the plot to pin an item, then drag the two sliders below. “User bias” shifts every item's score by the same amount; “Item bias” moves only the pinned item. Watch the top-5 list and the pinned item's math respond instantly.

These sliders move the SAME b_u / b_i terms shown in the equation above, live. User bias shifts every item's score by the same amount (watch the whole list rise, fall, and clip at 1/5). Item bias moves only the pinned item and can push it into or out of the top 5.
+0.00 real: —
pin an item first

Try pinning a different item (click any dot), or nudge both sliders to see the top-5 list and the pinned item's math respond instantly. Step 3 lets you confirm the accuracy number these controls imply.

Next: Verify the proof yourself →
Step 3 of 3

Did the model get Jordan right — and does it hold up?

Jordan's verdict Scroll back to Step 1's genre-affinity table for the receipts, but here's the short version: Jordan rated exactly 3 titles — 5 stars for scifi, 4 stars for action, 1 star for romance — and that's the entire signal the model got. Its top-10 unrated picks for Jordan land overwhelmingly in scifi and action, not romance. It got Jordan right, using nothing but 3 numbers. One person isn't proof of anything on its own, though — the question below is whether that pattern is real signal or a lucky guess.

The Proof panel up in Reference reports one number, computed once, offline, over the full 2,105-pair held-out set — the aggregate version of the same question Jordan just answered. Don't take that on faith either: draw from that exact same set right now and watch your own number converge onto it.

👆 Start small: click “Draw 1 pair” below to see one real held-out example scored live. Once you've seen a single draw, click “Draw 100 pairs” a few times — watch the green dot on the RMSE track below walk toward the gold “pre-registered” line. That's the Law of Large Numbers happening in front of you, not asserted.

5Verify the proof yourself — draw from the real held-out set

The Proof panel above reports one number, computed once, offline, over the full 2,105-pair held-out set (rows the model never trained on). Don't take that on faith: draw random pairs from that exact same held-out set right now, score each with the exact same predict() function every other control on this page uses, and watch YOUR running RMSE below converge onto the pre-registered number as your sample grows — the same Law-of-Large-Numbers pattern that makes a coin's observed flip frequency converge onto its true probability, grounded here in this demo's real falsification-gate result instead of a toy.
Drawn so far: 0 / held-out pairs (sampled with replacement)
Running RMSE (your sample) vs. the pre-registered proof
Your running RMSE: · Pre-registered (full 2,105): · Threshold to beat:
Observed |error| histogram (grows with every draw)
Last 8 draws
UserItemActualPredicted|error|
No draws yet -- click "Draw 1 pair" above.

If your running RMSE (green dot) lands left of the red dashed “threshold” line, this draw sample already confirms the model beats the popularity baseline — the exact bar the falsification gate had to clear.

↑ Back to the top — jump to any step, or explore the raw JSON API in the README.