Compressing a Sparse Table of Preferences
A rating table can be enormous and mostly empty. Small vectors for readers and guides can share information across its observed entries.
15 sections · 5 questions · 2 coding challenges
The arrow keys move between sections too.
Sharing Information Through Latent Factors
BOTIE
The coordinates are learned to fit the objective. They should not automatically be labelled with human interests such as humour or difficulty unless additional evidence supports that interpretation.
Sources for this history
Do We Need a Separate Rule for Every Reader and Guide?
Botie’s rating table grows whenever a reader joins or a guide appears. Most reader-guide pairs have no rating, so storing a learned prediction independently for every pair would miss the shared structure.
Matrix factorisation gives each reader and item a short learned vector. Their dot product predicts a rating, and observed ratings teach both sets of vectors.
The factors are supplied for inspection; missing ratings are not observed zeros.
Step 1 of 3
Give each side a short vector
Coordinates must align between reader and item factors.
| Latent coordinate | Reader | Guide |
|---|---|---|
| Coordinate 1 | 1 | 2 |
| Coordinate 2 | 2 | 1 |
Coordinate 1
- Reader
- 1
- Guide
- 2
Coordinate 2
- Reader
- 2
- Guide
- 1
Step 2 of 3
Predict by combining coordinates
The dot product gives one pair’s score.
Reader vector: [1, 2] Guide vector: [2, 1] Prediction = (1 × 2) + (2 × 1) = 4 Observed rating: 5
Step 3 of 3
Update only from an observed rating
The prediction error moves both vectors.
Residual = 4 − 5 = −1 Reader gradient = −1 × [2, 1] = [−2, −1]
Shared factors can describe preferences without filling every missing rating
A shared coordinate system lets one reader vector score every candidate row.
How does one reader vector score several guides?
The reader and guide factors use the same coordinates. Change one factor and follow which candidate scores increase, decrease or remain unchanged.
BOTIE
Predicted scores in guide order
- 04
- 16
Follow the operations
1. Guide factor rows
2 1 0 3 2. Reader factors
- 01
- 12
3. Matrix times vector
- 04
- 16
4. Result
- 04
- 16
Factors must already be fitted from training observations. Scores are not probabilities.
1. Define the Two Factor Matrices
Store one vector per reader in U and one per guide in V. Their matrix product with V transposed gives a full score table. Factor width is a modelling choice that controls capacity.
A rating model often also includes a global mean and reader/item biases. We omit those here to isolate the factor interaction, not because offsets are unimportant.
Latent coordinates can rotate without changing predictions, so they do not have unique meanings.
2. Fit Observed Entries Only
We know the ratings at some positions in the table. A mask identifies those training entries, and the squared-error objective compares predictions with the ratings there. Unobserved positions have no rating target. A regularization penalty can also discourage excessively large factors.
Clicks require a different interpretation: an absent click might mean the item was never seen. We need to choose an objective or confidence scheme appropriate to that feedback before reusing the factor model.
Prediction = dot([1,2], [2,1]) = 4
Observed rating = 5
Residual = 4 - 5 = -1The residual is defined as prediction minus observation throughout this lesson.
3. Update Both Vectors From the Same Old State
For half squared error on one pair, the reader gradient is residual times the old item vector, and the item gradient is residual times the old reader vector. Make copies before updating either in place.
If the second update uses the already-changed first vector, it implements a different step from the simultaneous gradient calculation. Add regularisation gradients consistently if the objective includes them.
The coding exercise deliberately saves both old vectors so the dependency is visible.
Find both gradients before changing either factor
One observed rating is four, while the supplied reader and guide factors predict a smaller value. Calculate each gradient from the original pair.
import numpy as np
reader = np.array([1., 0.])
guide = np.array([2., 1.])
rating = 4.0
error = reader @ guide - rating
print(float(error))
print((error * guide).tolist())
print((error * reader).tolist())-2.0
[-4.0, -2.0]
[-2.0, -0.0]Each factor's gradient uses the other factor as it was before the update. Practice turns those gradients into two parameter updates without letting the first change contaminate the second.
4. Check More Than Training Reconstruction
Low training error can reflect memorisation, especially with many factors and few ratings. Tune factor width and regularisation against held-out ratings or ranking tasks. Split by time when predicting future behaviour.
A brand-new item has no fitted factor. A content-based model or fallback is still needed. Learned factors do not solve missing exposure or biased feedback by themselves.
Botie’s workshop computes a score from supplied factors; fitting is shown in Python.
5. One Update Is Not Yet a Fitted Recommender
The factor tables begin with small nonidentical values. Repeatedly visit observed reader-guide pairs, calculate the current prediction, and update the two participating vectors. Save the old vectors while computing both gradients so one update does not unexpectedly use the other's new value.
Repeat over shuffled observed pairs for several passes, tracking held-out performance as well as observed-entry reconstruction. The unobserved cells are not zero targets. They simply do not supply explicit rating errors in this objective.
6. Check Whether the Factors Can Fit a Tiny Rating Table
Before using a sparse real collection, test the training mechanism on a tiny known matrix. Here every entry is observed and the table is exactly rank one. That gives us a deliberately easy diagnostic for the update loop.
We use full-batch gradients from the same old factors. Regularisation is omitted for this mechanism check; it belongs in the later generalisation experiment.
Fit a rank-one table
Rows are two readers and columns are two guides. All four ratings are known.
import numpy as np
rating = np.array([[1.,2.],[2.,4.]])
U = np.array([[.4],[.7]])
V = np.array([[.5],[.8]])
initial = np.mean((U@V.T-rating)**2)
for _ in range(1000):
error = U@V.T-rating
dU = 2*error@V/rating.size
dV = 2*error.T@U/rating.size
U -= .05*dU
V -= .05*dV
print(bool(np.mean((U@V.T-rating)**2) < initial))
print(np.round(U@V.T, 2).tolist())True
[[1.0, 2.0], [2.0, 4.0]]The reconstructed table is identified more directly than the factors: multiplying one factor by a nonzero scale and dividing the other by it can preserve predictions.
7. An Unopened Guide Is Not a Negative Rating
Implicit interactions such as opens tell us which guides were encountered and selected, but usually not why other guides were ignored. An unobserved pair may be unseen, irrelevant, unavailable or simply not yet chosen.
Implicit-feedback methods therefore need a declared confidence or negative-sampling scheme. Sampling a few unobserved candidates creates a training task whose distribution affects the learned scores. Those scores should not automatically be interpreted as probabilities of liking a guide.
Check yourself
Questions on Missing Recommendation Feedback
Question 1 of 1
What does that missing rating establish?
Use this setup.
A guide was never shown to a reader and has no rating from them.8. Small Reconstruction Error Can Still Produce a Poor List
A factor model may fit popular observed ratings while recommending guides already seen or failing new readers entirely. Apply eligibility rules and evaluate ranking on later interactions with a documented candidate set.
Regularisation, factor dimension and training duration should be selected on validation requests. New guides have no learned factor from interactions alone, so a content model or fallback remains necessary. Report that boundary instead of implying the factor table can describe an entity it has never fitted.
Check yourself
Questions on Parts 1 to 8
Question 1 of 4
What is their predicted rating?
Use the information below.
Reader [1,2], item [2,1].Try it with Botie
Build with Botie
Write and run Python