ML Systems Lab Open interactive version →
Intermediate 24 min read RecSystwo-towerANNembeddingsin-batch negatives

Candidate Generation & Retrieval

Two-tower embeddings, ANN retrieval, dot-product scoring, in-batch negatives

Retrieval has to do something that sounds contradictory: score a user against *every* item in the catalog, cheaply enough to run at request time. The trick that makes it possible is decoupling — and it's the single most important architecture in modern RecSys.


Why joint scoring fails and the two-tower model fixes it. The most accurate way to score a (user, item) pair is to feed both into one model so it can weigh every cross-interaction. But then the item's representation *depends on which user is asking*, so you must recompute all 10M item scores fresh per request — the impossible arithmetic again. The two-tower model breaks the dependency: a user tower encodes the user into a vector, an item tower encodes each item into a vector in the *same* space, and similarity is a plain dot product u·v. Because an item's embedding no longer depends on the user, you compute *all* item embeddings offline, once, and store them.


ANN turns "score everything" into "look up neighbors." With every item pre-embedded, retrieval becomes: encode the one live user (one forward pass), then find the item vectors nearest to u. Exact nearest-neighbor over 10M vectors is still too slow, so we use Approximate Nearest Neighbor (HNSW, IVF, ScaNN) — index structures that trade a little recall for a huge latency win, returning the top few hundred neighbors in ~10ms. Dot-product (or cosine) is chosen precisely because ANN indexes are built for it.


Training: in-batch negatives are the standard recipe, and *why* matters. You have positives (user clicked item) but no explicit negatives. The trick: within a training batch, treat every *other* user's clicked item as a negative for this user — one batch of B pairs yields B positives and B×(B−1) negatives for free, trained with a softmax/contrastive loss. But random in-batch negatives are too *easy* — separating a clicked cooking video from a random car-parts listing gives near-zero gradient and teaches nothing subtle. So you add hard negatives: high-scoring-but-not-clicked items that force the model to learn fine distinctions. Popularity also biases in-batch negatives (popular items appear as negatives more often, getting over-penalized), which is corrected with a logQ / sampled-softmax correction. Concretely, it subtracts each item's log sampling probability from its logit before the softmax, so an item isn't over-penalized just for being sampled as a negative more often.


Retrieval's output is a shortlist, not a final answer. The few hundred candidates ANN returns still aren't ordered — that's the next module's job. Ranking takes exactly this shortlist and runs a more expensive model over it to produce the final top-k.

Key points

Takeaway

Two-tower retrieval decouples user and item encoding so item embeddings can be precomputed offline and ANN-indexed — turning "score 10M items" into "encode one user + a ~10ms neighbor lookup." It's trained with in-batch negatives plus hard-negative mining (random negatives are too easy) and a logQ correction for popularity bias. The output is a shortlist of a few hundred candidates handed to the ranking stage next — not a final recommendation list.

Recap

Check your understanding

Q1. Why can't a cross-attention model that jointly encodes (user, item) be used for retrieval, even though it is more accurate than a two-tower model?

Q2. Your two-tower retriever gets recall@100 of only 0.55. A teammate proposes switching from random in-batch negatives to hard-negative mining. Why does this attack the recall problem specifically?

Q3. After training with in-batch negatives, your retriever systematically *under*-recommends genuinely relevant popular items. Select the *two* statements that correctly diagnose the cause and the fix.

Try it interactively

ML Systems Lab is a free interview-prep platform for ML engineers — work through the full interactive module, quizzes, and drills.

Open ML Systems Lab →