Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

cold start
The problem of recommending for a new user or a new item that has no interactions yet.
collaborative filtering
Recommending items by finding users with similar taste, or items that similar users liked, without reading item content.
explicit feedback
A signal where the user states a preference, such as a rating or a like, rather than one inferred from behaviour.
implicit feedback
A signal inferred from behaviour, such as a click, watch or purchase, that says something happened but not how much it was liked.
matrix factorisation
A method that gives each user and item a short list of learned numbers, so their match is the sum of products.
nDCG
A ranking score that gives more credit to relevant items near the top, then divides by the score of the best possible order.
popularity baseline
A list ordered by how many interactions each item has, used as the simple result every model must beat.
precision at k
The share of the first k items that are relevant, where k is the number of slots shown.
ranking
The second stage that orders a short list of candidates for one user, usually with a slower model.
recall at k
The share of all relevant items that appear in the first k positions.
relevance
How likely one user is to engage with one item; a ranking needs it per user, not as a global score.
retrieval
The first stage that cuts a large catalogue down to a few hundred candidates cheaply.
slot
One position on the screen that can hold an item; the fixed count of them limits how long a list can be.
two-tower retrieval
A design with one network for users and one for items, so the item side is computed before a request.

Recommender systems from the ground up

Two-tower retrieval

Retrieval turns a catalogue of millions into a few hundred candidates. Two towers and a nearest-neighbour index are how that stays cheap, and the split is also the model’s main constraint.

Part 8 of 10

Advanced18 min readBack to Courses

Retrieval is a different job

A production recommender is usually two parts. Retrieval narrows a catalogue of millions to a few hundred candidates. Ranking orders those candidates with an expensive model. The two-tower model is the standard retrieval design, because it turns the narrowing into a nearest-neighbour search.

One tower encodes the user and the context. The other encodes the item. Each ends in a vector, and the score is a dot product or a cosine. The towers never see each other’s features. That limit is what lets the item vectors be computed ahead of time. It is also the model’s main weakness.

The split is the point. Item vectors are built offline and loaded into an index. The user tower runs at request time and produces one vector. A query is then a nearest-neighbour lookup, not a forward pass over every item. Without that split, retrieval at catalogue scale is not possible.

Training with in-batch negatives

The loss is a softmax over the scores of one positive item and many negatives. In-batch negatives use the other items in the same batch as negatives for every user. That is cheap and works well when the batch is large. The scores are scaled by a temperature, which sets how sharply the model separates the positive.

There is a subtle failure. An item that appears twice in a batch is a false negative for the other user who touched it. Remove duplicates within the batch, or mask the diagonal correctly, so the model is not punished for a correct match. With small batches, add negatives sampled from the catalogue to make up.

Hard negatives sharpen the boundary. A random item is easy to reject. An item the user almost clicked teaches more. Hard negatives are usually mined from a previous model’s high-scoring misses. Then they are mixed with random negatives, so the model does not overfit to a narrow set of mistakes.

A two-tower model in TensorFlow Recommenders

import tensorflow as tf
import tensorflow_recommenders as tfrs

class TwoTower(tfrs.models.Model):
    def __init__(self, user_tower, item_tower):
        super().__init__()
        self.user_tower = user_tower
        self.item_tower = item_tower
        self.task = tfrs.tasks.Retrieval()

    def compute_loss(self, features, training=False):
        user = self.user_tower(features["user_id"])
        item = self.item_tower(features["item_id"])
        return self.task(query_embeddings=user, candidate_embeddings=item)

Score a catalogue

Run the dot-product retrieval and read the top two items for each user. Then change u3 to [0.0, 1.0] and confirm the list moves toward the second axis.

Output
Click "Run" to execute your code

The index and its freshness

At serving time the item tower’s output lives in an approximate nearest-neighbour index (a fast index that may miss a few true matches). FAISS, HNSW, ScaNN and managed vector stores are examples. The index trades recall (the share of true matches it returns) for speed, and that recall is a product metric. An index that returns 90 percent of the true top hundred is fine for retrieval. It is not fine if the missing ten percent are the new releases.

Freshness is the harder problem. A new item has no vector until the item tower runs. A changed price or title changes the features the tower reads. Re-embed on a schedule, and keep a path for items that arrive between rebuilds. The usual answer is a small exact index for recent items beside the large approximate one.

Judge retrieval on its own terms. Measure recall at k against the full catalogue, coverage of the item catalogue, and the share of requests with fewer than k candidates. A retrieval model that improves ranking metrics while returning the same head items has not improved retrieval. It has moved the popularity bias one stage earlier.

An item embedding that was computed before a price or title change is stale. Serving it is not a small inconsistency: the ranker is reasoning about an item that no longer exists.

Feature choices and leakage

The user tower can read history, demographics and context. The item tower can read text, image vectors, price and category. Neither can read the other side’s raw features at serving time, because the item vector is computed before the request exists. A cross feature between user and item belongs in the ranking model, not in retrieval.

Leakage in retrieval is usually a feature that is only known after the interaction. A click count updated in real time can do it. So can a rating average that includes the test window, or an item’s total watch time. Each makes the towers look excellent offline and useless online. Build every feature as of the request time. Test the pipeline with a temporal split.

Cold items need a content path. If the item tower only has an id vector, a new item gets a random vector and will never be retrieved. Text and image features give it a sensible starting point. A small exploration budget gives it the impressions that turn the prior into evidence.

In-batch negatives

Run the softmax over the batch and compare the loss with the duplicate-item batch. The duplicate is a false negative, so the model is punished for a correct match.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

Why split retrieval into a user tower and an item tower?