Candidate Generation: Two-Tower Models and Collaborative Filtering
~45 min read
How to narrow millions of items to hundreds of candidates in milliseconds: two-tower retrieval, matrix factorization, and content-based approaches.
Candidate generation is Stage 1 of the recommendation funnel. Its job: retrieve a manageable set of plausible candidates from a catalog that's too large to score exhaustively. Optimize for recall — missing a great item here means it can never be recommended.
Two-Tower Model
Architecture: two independent neural networks — one for users, one for items — each producing an embedding vector of the same dimension.
User Tower inputs: Item Tower inputs:
user_id embedding item_id embedding
age, country category, tags
watch_history_emb avg_rating, views
↓ ↓
Dense layers Dense layers
↓ ↓
user_emb (128d) item_emb (128d)
↓ ↓
└──── dot_product ────────────┘
↓
relevance score
Training uses in-batch negatives: for each (user, item) positive pair in the batch, use all other items in the batch as negatives. Efficient — no separate negative mining pass.
Serving: pre-compute all item embeddings → build FAISS HNSW index. At query time, compute user embedding → approximate nearest neighbor (ANN) search → top-K items. 10ms latency for top-200 from 10M items with FAISS.
Why are the towers served independently? Item embeddings are pre-computed and cached in the FAISS index — never recomputed at query time. Only the user embedding is computed online. This is the source of the speed advantage.
Collaborative Filtering (Matrix Factorization)
Decompose the user-item interaction matrix R (shape: users × items) into: R ≈ U × Vᵀ, where U is users × k and V is items × k (k = latent factor dimension)
Training: minimize ||R - UVᵀ||² + λ(||U||² + ||V||²) using ALS or SGD. Inference: candidate score for user i and item j = U_i · V_j
Strengths: captures 'users like you also liked' patterns without explicit item features. Weakness: requires interaction history for both users AND items. Cold start = no embedding.
Content-Based Filtering
Represent items by their content features (category vector, description embedding, tags). Represent user preference as a weighted average of the features of items they engaged with. Score items by cosine similarity between item features and user preference vector.
Strengths: works immediately for new items with features (no interaction history needed). Weakness: over-specialization — a user who clicked 3 mystery novels only gets mystery novels.
Hybrid approach: most production systems blend all three:
- Two-tower for personalized retrieval
- Content-based to cover cold-start items
- Popularity-based as a fallback for cold-start users
- Merge candidates from all sources, deduplicate, pass to ranker
💬 Deep Dive with AI
Key points
- •Two-tower models pre-compute item embeddings offline and serve only user embeddings online — this is what makes sub-10ms retrieval over millions of items possible
- •The candidate generation stage must optimize for recall, not precision — any great item not retrieved here can never be recommended regardless of ranker quality
- •Collaborative filtering captures community taste (users like you liked X) but fails cold start; content-based works for new items but causes over-specialization — production systems blend both