Content-based recommendation uses item attributes to recommend similar items to what a user has liked before. Unlike collaborative filtering which uses user-item interactions, content-based methods analyze item features - text descriptions, categories, images, metadata. This approach excels at handling the cold-start problem for new items.
Where It Is Used
Content-based recommendations power personalization across industries:
- E-commerce: "Similar products" based on attributes, descriptions
- Media: Article recommendations based on content, topics
- Music: Song recommendations based on audio features, genre
- Jobs: Job matching based on skills, requirements
- Real estate: Property recommendations based on features
- Education: Course recommendations based on content, prerequisites
Products: Google News (topic similarity), Spotify (audio features + content), Indeed (job matching).
How Content-Based Differs from Collaborative
| Aspect | Content-Based | Collaborative Filtering |
|---|---|---|
| Data needed | Item attributes | User-item interactions |
| Cold-start items | Handles well | Struggles |
| Cold-start users | Needs some history | Struggles |
| Serendipity | Low (similar to past) | Higher |
| Interpretability | High (feature-based) | Lower |
| Scalability | Scales with items | Scales with users × items |
When to use content-based: - New items frequently added - Rich item metadata available - User history limited - Explainability important - Domain where similarity is well-defined
Typical Data You Will See
| Aspect | Typical Values |
|---|---|
| Items | 1K to 10M+ |
| Item features | Text, categories, images, numeric |
| User history | 5 to 1000+ interactions |
| Feature dimensions | 50 to 1000+ |
| Update frequency | Real-time to daily |
Item feature types: - Text: Titles, descriptions, reviews - Categorical: Genre, brand, category - Numeric: Price, rating, duration - Images: Product photos, thumbnails - Structured: Tags, specifications
What You Do in Practice
- Define item representation - What features describe items?
- Extract features - Text embeddings, category encoding, image features
- Build item profiles - Combined feature vectors
- Create user profiles - Aggregation of liked item profiles
- Compute similarity - Cosine similarity, nearest neighbors
- Generate recommendations - Top-K most similar items
- Handle cold-start - New item/user strategies
- Evaluate offline - Precision, recall, diversity
- A/B test online - CTR, engagement, conversions
- Iterate and improve - Feature engineering, hybrid approaches
Baseline First
Start with TF-IDF on text features:
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
# Create TF-IDF vectors from item descriptions
tfidf = TfidfVectorizer(max_features=5000, stop_words='english')
item_vectors = tfidf.fit_transform(items['description'])
# Compute similarity matrix
similarity_matrix = cosine_similarity(item_vectors)
def get_recommendations(item_id, n=10):
"""Get top-N similar items"""
idx = item_id_to_idx[item_id]
sim_scores = list(enumerate(similarity_matrix[idx]))
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
sim_scores = sim_scores[1:n+1] # Exclude self
return [idx_to_item_id[i] for i, score in sim_scores]
Why TF-IDF baseline: - Simple, fast, interpretable - Works well for text-heavy items - No training required - Good starting point
Move to embeddings when TF-IDF limitations are clear.
Feature Extraction Methods
Text features:
| Method | Description | Best For |
|---|---|---|
| TF-IDF | Term frequency weighting | Short text, keywords |
| Word2Vec avg | Average word vectors | Medium text |
| Doc2Vec | Document embeddings | Longer documents |
| BERT/Sentence-BERT | Contextual embeddings | Semantic similarity |
from sentence_transformers import SentenceTransformer
# Modern approach: Sentence embeddings
model = SentenceTransformer('all-MiniLM-L6-v2')
embeddings = model.encode(items['description'].tolist())
Categorical features: - One-hot encoding (small cardinality) - Embedding layers (large cardinality) - Target encoding (with caution)
Image features: - Pre-trained CNN (ResNet, EfficientNet) - CLIP embeddings (text-image alignment)
Combined features:
# Concatenate normalized feature vectors
from sklearn.preprocessing import normalize
text_emb = normalize(text_embeddings)
cat_emb = normalize(category_embeddings)
img_emb = normalize(image_embeddings)
# Weighted combination
item_vectors = np.hstack([
0.5 * text_emb,
0.3 * cat_emb,
0.2 * img_emb
])
User Profile Construction
Build user profiles from their interaction history:
def build_user_profile(user_id, item_vectors, interactions):
"""Aggregate item vectors user has interacted with"""
user_items = interactions[interactions['user_id'] == user_id]
# Simple: Average of liked item vectors
liked_vectors = item_vectors[user_items['item_idx']]
user_profile = liked_vectors.mean(axis=0)
# Better: Weight by recency or engagement
weights = np.exp(-0.1 * user_items['days_ago'])
weights = weights / weights.sum()
user_profile = np.average(liked_vectors, weights=weights, axis=0)
return user_profile
def recommend_for_user(user_id, item_vectors, n=10):
user_profile = build_user_profile(user_id, item_vectors, interactions)
similarities = cosine_similarity([user_profile], item_vectors)[0]
# Exclude already seen items
seen_items = get_user_history(user_id)
similarities[seen_items] = -1
top_indices = similarities.argsort()[-n:][::-1]
return [idx_to_item_id[i] for i in top_indices]
Evaluation and Metrics
| Metric | Description | Use Case |
|---|---|---|
| Precision@K | Relevant items in top K | Accuracy of recommendations |
| Recall@K | Fraction of relevant items retrieved | Coverage |
| NDCG@K | Ranking quality | Position matters |
| Coverage | Fraction of items recommended | Catalog exploration |
| Diversity | Dissimilarity within recommendations | Avoid repetition |
| Novelty | Recommendation of less popular items | Discovery |
Offline evaluation:
def evaluate_recommendations(test_interactions, recommend_func, k=10):
precisions, recalls = [], []
for user_id in test_interactions['user_id'].unique():
actual = set(test_interactions[test_interactions['user_id'] == user_id]['item_id'])
predicted = set(recommend_func(user_id, n=k))
if len(predicted) > 0:
precision = len(actual & predicted) / len(predicted)
recall = len(actual & predicted) / len(actual) if len(actual) > 0 else 0
precisions.append(precision)
recalls.append(recall)
return np.mean(precisions), np.mean(recalls)
Failure Modes and Debugging Checklist
- [ ] Over-specialization: Recommendations too similar to history
- [ ] Feature quality: Poor item descriptions/metadata
- [ ] Cold-start users: No history to build profile
- [ ] Stale profiles: User preferences changed
- [ ] Popularity bias: Popular items dominate features
- [ ] Missing features: Items with incomplete metadata
- [ ] Scaling issues: Similarity computation slow
- [ ] Low diversity: Same types of items recommended
Production Checklist
Before deployment: - [ ] Item features extracted and indexed - [ ] Similarity computation optimized (ANN) - [ ] User profile computation tested - [ ] Cold-start handling defined - [ ] Latency requirements met - [ ] A/B test designed
After deployment: - [ ] Recommendation CTR tracked - [ ] User engagement monitored - [ ] Feature freshness maintained - [ ] Index updated with new items - [ ] Feedback loop for improvements - [ ] Diversity metrics tracked
Scaling with Approximate Nearest Neighbors
For large catalogs, exact similarity is too slow:
import faiss
# Build FAISS index
dimension = item_vectors.shape[1]
index = faiss.IndexFlatIP(dimension) # Inner product (cosine with normalized vectors)
index.add(item_vectors.astype('float32'))
# Fast similarity search
def get_similar_items(item_id, n=10):
query = item_vectors[item_id_to_idx[item_id]].reshape(1, -1)
distances, indices = index.search(query.astype('float32'), n + 1)
return [idx_to_item_id[i] for i in indices[0][1:]] # Exclude self
ANN options: FAISS, Annoy, ScaNN, Milvus.
Tools and Stack
| Category | Tools |
|---|---|
| Text embeddings | Sentence-Transformers, OpenAI, Cohere |
| Image features | torchvision, CLIP, timm |
| Vector similarity | FAISS, Annoy, ScaNN |
| Vector databases | Pinecone, Weaviate, Milvus |
| Frameworks | LensKit, Surprise, RecBole |
Example Mini-Case
Scenario: News platform needs article recommendations based on content similarity.
Data: 500K articles, text content, categories, publication date.
Approach: 1. Baseline: TF-IDF on article text - Precision@10: 0.12 2. Better embeddings: Sentence-BERT - Precision@10: 0.18 3. Add metadata: Category + recency weighting - Precision@10: 0.22 4. Hybrid: + collaborative signals - Precision@10: 0.28 5. Optimization: FAISS index for 10ms latency
Architecture:
Articles → Embedding Service → Vector Index (FAISS)
↓
User Request → User Profile → ANN Search → Recommendations
Results: - Recommendation latency: 15ms p99 - CTR improvement: +35% vs popularity baseline - User engagement: +20% time on site - New article coverage: 95% recommended within 24h
Related Pages
- Index: ML Field Guide
- Collaborative Filtering
- Semantic Search
- RAG Systems
Further Reading
- Sentence-Transformers Documentation
- FAISS Wiki
- Content-Based Filtering - Wikipedia
- Practical Recommendations Tutorial
Try it live
Everything above runs in your browser — open Music Recommender — Matrix Factorization (ALS) Live and change the parameters while it is running. Nothing is installed, nothing is uploaded, the whole model lives in one tab.
▶ Open Music Recommender — Matrix Factorization (ALS) Live simulation