Collaborative filtering tells you what people like. Language models tell you why. Combining the two is how modern recommendation systems solve the cold-start problem and actually understand what they're recommending.
9 min read
Applied ML
Intermediate
Ask most engineers how a recommendation system works and you'll get a clean answer: users who liked A also liked B, so show B to people who liked A. Collaborative filtering has powered recommendations since the Netflix Prize era, and it still works well once you have enough interaction data. The problem is everything it can't see — a brand-new article, a first-time visitor, a product listed an hour ago. None of these have interaction history yet, so a purely behavioral model has nothing to work with.
This is where natural language processing earns its place in the pipeline. Instead of learning only from clicks and purchases, an NLP-aware system reads the actual content — the product description, the article body, the support ticket — and represents its meaning as a vector. That single move changes what a recommender can reason about.
The cold-start problem, solved by reading
A collaborative filter has no opinion about an item until people have interacted with it. A content-understanding model has an opinion immediately, because it can read the item's description the moment it's created. This is why almost every production recommender you've used — a music app, a news feed, a shopping site — is not one model but a blend: a content-based signal that works from day zero, and a behavioral signal that gets sharper as data accumulates.
The behavioral model tells you what's popular among people like this user. The language model tells you what this specific item is actually about.
The two signals fail in different situations, which is exactly why combining them works: behavioral data is sparse for new items but rich in social proof, while text embeddings are available instantly but blind to trends the words don't capture.
From bag-of-words to embeddings
It's worth tracing how we got here, because the history explains the current architecture. Early content-based systems used TF-IDF: count how often a word appears in a document relative to how common it is across all documents, and represent each item as a sparse vector of word weights. Two documents were "similar" if they shared enough distinctively weighted words.
TF-IDF has an obvious limit — it matches vocabulary, not meaning. "Affordable running shoes" and "budget sneakers for jogging" share almost no words but describe the same thing. Transformer-based sentence embedding models solved this by mapping text into a dense vector space where semantic similarity, not lexical overlap, determines distance. Two descriptions that mean the same thing land near each other in that space even if they don't share a single word.
Item text
title + description
Embedding model
text → dense vector
Vector store
nearest-neighbor index
User behavior
clicks, dwell, purchase
Ranking model
learns from interactions
Recommendations
ranked, per user
content embeddings feed retrieval — behavior feeds ranking — both meet at the final list
A minimal working example
The core idea fits in a few lines of code: embed every item's text, embed the query or the user's recent history the same way, then rank by cosine similarity.
from sentence_transformers import SentenceTransformer, util
model = SentenceTransformer("all-MiniLM-L6-v2")
items = [
"Wireless noise-cancelling headphones, 30-hour battery",
"Compact espresso machine with built-in grinder",
"Over-ear studio headphones for long listening sessions",
]
item_vectors = model.encode(items, convert_to_tensor=True)
query = "comfortable headphones for daily commuting"
query_vector = model.encode(query, convert_to_tensor=True)
scores = util.cos_sim(query_vector, item_vectors)[0]
ranked = sorted(zip(items, scores), key=lambda x: -x[1])
for item, score in ranked:
print(f"{score:.3f} {item}")
Run this and the two headphone listings surface above the espresso machine, despite none of the words in the query — "comfortable," "commuting" — appearing verbatim in either headphone description. That's the semantic gap TF-IDF couldn't close.
In production this scales differently: item vectors are precomputed once and stored in an approximate nearest-neighbor index (FAISS, HNSW, or a managed vector database), so retrieval at query time is a lookup, not a re-embedding of the whole catalog.
Why retrieval alone isn't the whole system
Semantic similarity gets you a shortlist of plausible items — the retrieval stage. It does not know that this particular user always returns cheap headphones, or that this item is out of stock, or that engagement on it has been dropping all week. That's the job of a second-stage ranking model, trained on logged interactions, that re-scores the shortlist using features embeddings can't see: price sensitivity, recency, inventory, session context.
1
Retrieve broadly
Use embeddings to pull a few hundred semantically relevant candidates from a catalog of millions — fast, and cold-start safe.
2
Rank precisely
Score those candidates with a model trained on real interactions, weighing business and behavioral signals the text alone doesn't carry.
3
Re-rank for diversity
Adjust the final order so the list isn't ten near-duplicates of the top result — a common failure mode of pure similarity search.
This two-stage pattern — broad semantic retrieval, then a learned ranker — is roughly how most large-scale recommenders you interact with daily are actually built, whether the domain is video, shopping, or news.
Where this still goes wrong
A few failure modes show up often enough to plan for from the start:
Popularity bias compounds. If the ranking model is trained only on past clicks, it learns to keep recommending whatever was already popular, starving new or niche items of exposure even after retrieval surfaces them fairly.
Embeddings drift from intent. A general-purpose sentence embedding model is tuned for broad semantic similarity, not necessarily for your domain's notion of "similar." Fine-tuning on domain pairs (or at least evaluating on held-out domain queries) usually pays off before shipping.
Evaluation offline doesn't match online. Offline metrics like recall@k measure whether the right items were retrievable, not whether users actually engaged with what got shown. Plan for an online A/B test before trusting an offline win.
The takeaway
NLP doesn't replace collaborative filtering in a recommendation system — it fills the gap collaborative filtering can't close on its own. Text embeddings give the system something to say about an item before anyone has interacted with it; behavioral ranking gives it the judgment to know what actually works for a given user once data exists. Most systems worth studying use both, in that order.
If you're building one of these end to end, start with the two-stage pattern above before reaching for anything more elaborate it covers the cold-start case, it's easy to evaluate stage by stage, and it's the foundation most fancier architectures build on top of.
Top comments (0)