AI Hints
Home›Deep Learning Tutorial›Beyond Images and Text
Deep Learning · Lesson 81 of 92

Recommenders with Embeddings

You might also like.

⏱ 2 min read▶ Video at 2:30:09🎓 Free
Watch this lesson in the Deep Learning courseWatch on YouTube ↗Full Deep Learning course

Most of what you watch, buy, and listen to was chosen by a recommender. The core idea is amazingly simple. Give each person and each item a short list of numbers. If the numbers line up, then that person will probably like that item.

Recommenders with Embeddings
From the video: Recommenders with Embeddings
  • What you watch and buy
  • Chosen by a recommender
  • Numbers that line up

Matrix factorisation

We've got sixty users and forty items, and we can only see thirty percent of the ratings. The model gives each user four numbers, and each item four numbers. A predicted rating is just their dot product. We train only on ratings that we can see. Run it. Look at the last two lines. On ratings the model has never seen, its error is only point two three, against two for guessing. It has filled in the blanks. It worked out the tastes of users, and the qualities of items, with no descriptions at all. It's only ever seen ratings.

recommender.pypython
import torch

torch.manual_seed(0)
n_users, n_items = 60, 40
true_u, true_i = torch.randn(n_users, 4), torch.randn(n_items, 4)
rating = true_u @ true_i.T + 0.1 * torch.randn(n_users, n_items)
mask = torch.rand(n_users, n_items) < 0.3

class MF(torch.nn.Module):
    def __init__(self):
        super().__init__()
        self.u, self.i = torch.nn.Embedding(n_users, 4), torch.nn.Embedding(n_items, 4)
    def forward(self):
        return self.u.weight @ self.i.weight.T

m = MF()
opt = torch.optim.Adam(m.parameters(), lr=.02, weight_decay=1e-3)
for step in range(800):
    loss = ((m() - rating)[mask] ** 2).mean()
    opt.zero_grad(); loss.backward(); opt.step()

print(f"Seen ratings error: {((m() - rating)[mask] ** 2).mean().sqrt():.2f}")
print(f"Unseen ratings error: {((m() - rating)[~mask] ** 2).mean().sqrt():.2f}")
print(f"Guessing error: {rating.std():.2f}")
Output
Seen ratings error: 0.10
Unseen ratings error: 0.23
Guessing error: 2.01

Learning by comparison

The same idea powers contrastive learning, which trains embeddings without any labels. We take pairs of things that should match, such as two crops of one photo. The goal is for each item to be nearest to its own partner. That turns into simple cross entropy, on a table of similarities. Run it. With true pairs, the loss is near zero. When we mix them up, it jumps to about seven and a half. Train to bring that number down, and you get the embeddings that are behind image search, and models like C L I P.

contrastive.pypython
import torch
from torch.nn.functional import normalize, cross_entropy

torch.manual_seed(0)
a = normalize(torch.randn(8, 16), dim=1)
b = normalize(a + 0.1 * torch.randn(8, 16), dim=1)
sim = a @ b.T / 0.1
print("Loss with matching pairs:", round(cross_entropy(sim, torch.arange(8)).item(), 3))

shuffled = b[torch.randperm(8)]
print("Loss with mixed pairs:   ", round(cross_entropy(a @ shuffled.T / 0.1, torch.arange(8)).item(), 3))
Output
Loss with matching pairs: 0.004
Loss with mixed pairs:    7.504

From demo to production

Real recommenders work in two steps. First, a fast search finds a few hundred candidates, out of millions, using embeddings. Then a heavier model ranks them. Watch out for feedback loops, where you only learn about what you've already shown, and popular items take over. And start simple. Popular items are a tough baseline to beat.

Retrievefast embedding search
Ranka heavier model
Feedback loopsonly seeing what you show
Popular baselinehard to beat

Summary

People and items become vectors, and dot products predict taste. You've now explored far beyond the usual course. Next, let's review the core ideas.

Key takeaways
  • Users and items are vectors
  • Dot products predict ratings
  • Contrastive learning needs no labels
Next up: Review: How Networks Learn — lock in the core