Home▸Articles▸Recommendation and Search

Collaborative Filtering

Building recommendation systems with collaborative filtering - user-item matrices, matrix factorization, implicit feedback, and cold start solutions.

mysimulator teamUpdated 2026-01≈ 6 min read▶ Open the simulation

Collaborative filtering recommends items based on what similar users liked. Instead of analyzing item content, it finds patterns in user behavior - "users who bought X also bought Y". This simple idea powers recommendations at Netflix, Spotify, Amazon, and most modern platforms.

Where It Is Used

Collaborative filtering is the backbone of personalization:

Products: Netflix recommendation engine, Spotify Discover Weekly, Amazon recommendations.

Typical Data You Will See

Aspect Typical Values
Users 100K to 100M
Items 10K to 10M
Interactions 10M to 10B
Sparsity 99% to 99.99% sparse
Embedding dimension 32 to 256

Data types: - Explicit feedback: Ratings (1-5 stars), likes/dislikes - Implicit feedback: Views, clicks, purchases, time spent - User features: Demographics, preferences - Item features: Categories, descriptions, metadata

What You Do in Practice

  1. Define interactions - What user actions indicate interest?
  2. Build interaction matrix - User-item sparse matrix
  3. Handle implicit feedback - Convert actions to signals
  4. Choose algorithm - Matrix factorization, neural, or hybrid
  5. Train model - Optimize for ranking or rating prediction
  6. Generate candidates - Approximate nearest neighbors for items
  7. Add business rules - Diversity, freshness, inventory
  8. Evaluate offline - Recall, NDCG on held-out data
  9. A/B test online - CTR, engagement, conversions
  10. Handle cold start - New users and items

Baseline First

Start with popularity-based recommendations:

# Popularity baseline
def popularity_recommend(n=10):
    """Recommend most popular items."""
    item_popularity = interactions.groupby('item_id').size()
    return item_popularity.nlargest(n).index.tolist()

# User-based co-occurrence
def cooccurrence_recommend(user_id, n=10):
    """Items frequently bought with user's items."""
    user_items = set(interactions[interactions.user_id == user_id].item_id)

    # Find users with similar items
    similar_users = interactions[
        interactions.item_id.isin(user_items)
    ].user_id.unique()

    # Their other items
    candidates = interactions[
        (interactions.user_id.isin(similar_users)) & 
        (~interactions.item_id.isin(user_items))
    ]

    return candidates.item_id.value_counts().head(n).index.tolist()

Why popularity baseline: - Simple and fast - Reasonable for new users - Establishes minimum quality bar - Helps measure personalization lift

Algorithm Approaches

Approach Pros Cons When to Use
Matrix Factorization Fast, interpretable Limited expressiveness Most cases, good starting point
Neural CF Captures complex patterns Needs more data, slower Large scale, complex patterns
Item-based CF Easy to explain Doesn't personalize deeply Item similarity focus
Graph-based Captures relationships Complex to scale Social networks, knowledge graphs

Matrix Factorization with ALS:

from implicit.als import AlternatingLeastSquares
from scipy.sparse import csr_matrix

# Build sparse user-item matrix
user_item = csr_matrix((
    interactions.rating,
    (interactions.user_idx, interactions.item_idx)
))

# Train ALS model
model = AlternatingLeastSquares(
    factors=64,
    regularization=0.01,
    iterations=15
)
model.fit(user_item)

# Get recommendations for user
user_idx = 0
recommendations = model.recommend(
    user_idx, 
    user_item[user_idx],
    N=10,
    filter_already_liked_items=True
)

Neural Collaborative Filtering:

import torch
import torch.nn as nn

class NCF(nn.Module):
    def __init__(self, n_users, n_items, embedding_dim=64):
        super().__init__()
        self.user_embed = nn.Embedding(n_users, embedding_dim)
        self.item_embed = nn.Embedding(n_items, embedding_dim)

        self.mlp = nn.Sequential(
            nn.Linear(embedding_dim * 2, 128),
            nn.ReLU(),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, 1),
            nn.Sigmoid()
        )

    def forward(self, user_ids, item_ids):
        user_emb = self.user_embed(user_ids)
        item_emb = self.item_embed(item_ids)
        concat = torch.cat([user_emb, item_emb], dim=1)
        return self.mlp(concat)

Implicit Feedback Handling

Most production systems use implicit feedback (clicks, views) not explicit ratings:

# Convert implicit to training signal
def prepare_implicit_data(interactions):
    """Convert raw interactions to training pairs."""
    # Positive samples: actual interactions
    positives = interactions[['user_id', 'item_id']].copy()
    positives['label'] = 1

    # Negative sampling: items user didn't interact with
    all_items = set(interactions.item_id.unique())
    negatives = []

    for user_id in interactions.user_id.unique():
        user_items = set(interactions[
            interactions.user_id == user_id
        ].item_id)

        neg_items = random.sample(
            list(all_items - user_items), 
            k=len(user_items) * 4  # 4:1 negative ratio
        )

        for item_id in neg_items:
            negatives.append({
                'user_id': user_id,
                'item_id': item_id,
                'label': 0
            })

    return pd.concat([positives, pd.DataFrame(negatives)])

Weighting schemes: - Uniform: All interactions equal - Frequency: Weight by interaction count - Recency: Recent interactions weighted higher - Confidence: Weight by interaction strength

Evaluation and Metrics

Metric What It Measures Formula
Recall@k Coverage of relevant items Relevant in top-k / Total relevant
Precision@k Accuracy of top-k Relevant in top-k / k
NDCG@k Ranking quality Normalized discounted cumulative gain
MAP Average precision Mean of precision at each relevant item
Hit Rate@k Any relevant in top-k Users with hit / Total users

Evaluation setup:

def evaluate_model(model, test_data, k=10):
    """Evaluate recommendation model."""
    metrics = {'recall': [], 'ndcg': [], 'hit_rate': []}

    for user_id in test_data.user_id.unique():
        # Ground truth: items user interacted with in test
        true_items = set(test_data[
            test_data.user_id == user_id
        ].item_id)

        # Model predictions
        recs = model.recommend(user_id, k)

        # Recall@k
        hits = len(set(recs) & true_items)
        metrics['recall'].append(hits / len(true_items))

        # Hit rate
        metrics['hit_rate'].append(1 if hits > 0 else 0)

        # NDCG@k
        metrics['ndcg'].append(compute_ndcg(recs, true_items, k))

    return {k: np.mean(v) for k, v in metrics.items()}

Failure Modes and Debugging Checklist

Production Checklist

Before deployment: - [ ] Offline metrics significantly beat popularity baseline - [ ] Cold start strategy defined (content-based fallback) - [ ] Candidate generation pipeline built (ANN index) - [ ] Business rules integrated (diversity, freshness) - [ ] Latency meets SLA (typically < 100ms)

After deployment: - [ ] Online metrics tracked (CTR, engagement, conversions) - [ ] Model refresh schedule established (daily/weekly) - [ ] A/B tests running vs baseline - [ ] Diversity metrics monitored - [ ] Cold start performance tracked separately

Scaling considerations: - [ ] Embedding index (Faiss, ScaNN) for fast retrieval - [ ] Incremental training for new interactions - [ ] Feature caching for user/item features - [ ] Batch vs real-time recommendation trade-offs

Cold Start Solutions

Problem Solution
New user Popular items, demographic-based, content-based
New item Content-based similarity, boost in recommendations
Both new Global popular, category popular

Hybrid approach:

def recommend(user_id, n=10):
    """Hybrid recommendation with cold start handling."""
    user_interactions = get_user_interactions(user_id)

    if len(user_interactions) == 0:
        # Cold start: popularity + content-based
        return popularity_recommend(n)

    elif len(user_interactions) < 5:
        # Warm start: blend CF with content
        cf_recs = collaborative_recommend(user_id, n)
        content_recs = content_based_recommend(user_id, n)
        return blend(cf_recs, content_recs, alpha=0.5)

    else:
        # Full CF
        return collaborative_recommend(user_id, n)

Tools and Stack

Category Tools
Matrix factorization Implicit, Surprise, LightFM
Deep learning PyTorch, TensorFlow Recommenders
ANN search Faiss, ScaNN, Annoy, Milvus
Feature stores Feast, Tecton
Platforms AWS Personalize, Google Recommendations AI

Example Mini-Case

Scenario: E-commerce site with 2M users, 500K products, 50M purchase events wants personalized recommendations.

Approach: 1. Baseline: Popular items - Recall@10: 0.08 2. Data prep: Purchase events with recency weighting 3. Model: ALS matrix factorization (64 factors) 4. Serving: Faiss index for fast candidate retrieval 5. Reranking: Add diversity and freshness rules

Results: - Recall@10: 0.08 → 0.22 (+175%) - A/B test CTR: +18% - Revenue per user: +12% - Latency: 45ms p99

Key learnings: - Recency weighting critical (recent purchases 3x weight) - Cold start users needed category-based fallback - Diversity constraint improved long-term engagement

Related Pages

Further Reading

Try it live

Everything above runs in your browser — open Recommendation Systems: Collaborative Filtering, Content-Based & Two-Tower Retrieval and change the parameters while it is running. Nothing is installed, nothing is uploaded, the whole model lives in one tab.

▶ Open Recommendation Systems: Collaborative Filtering, Content-Based & Two-Tower Retrieval simulation

What did you find?

Add reproduction steps (optional)