The Big Picture: Problem Definition & Trade-offs
The cold-start problem is fundamental to recommendation systems: how do you recommend when you have no user-item interaction history? This manifests in three distinct scenarios:
User Cold-Start
New user has no interaction history
You know nothing about their preferences. Must rely on demographics, context, or exploration.
Item Cold-Start
New item has no interaction signals
No one has clicked, rated, or engaged. Must use content features and business rules.
System Cold-Start
Brand new platform, minimal data
Both users and items are new. Must bootstrap from content and external signals.
Hybrid Cold-Start
Mixed warm and cold entities
Mature platform with occasional new users/items. Blending strategies essential.
Why Cold-Start Matters
Every new user is a potential long-term customer—first impressions determine whether they stay or churn. Every new item needs exposure to gather interaction data; without visibility, even high-quality items languish unseen. Cold-start directly impacts:
- User retention: Poor cold-start recommendations cause churn
- Item discoverability: New items need traffic to get feedback signals
- Platform diversity: Without exposure, niche items never surface
- Revenue: New sellers/creators need initial visibility to succeed
The Exploration-Exploitation Trade-off
At the core of cold-start is a fundamental decision: should you recommend what you think the user will like (exploitation), or should you show diverse items to learn their preferences (exploration)?
Diagnosing Cold-Start: Measurement & Thresholds
To address cold-start effectively, you must first diagnose its severity. Not all new users or items are equally "cold"—some have minimal signals, others have just begun accumulating data.
Defining Cold-Start Severity Thresholds
| Severity Level | Interaction Count | Characteristics | Recommended Approach |
|---|---|---|---|
| Strictly Cold | 0 interactions | Zero signals; brand new entity | Content-based, demographics, exploration |
| Cold | <5 interactions | Minimal history; high uncertainty | Bandit algorithms, hybrid blending |
| Warm-Up Phase | 5–20 interactions | Emerging pattern; moderate confidence | Increase CF weight; blend approaches |
| Warm | >20 interactions | Clear pattern; high confidence | Full collaborative filtering |
Measuring Cold-Start Impact
Track three key metrics to understand cold-start severity in your system:
- New User Retention: % of users with first interaction who return within 7 days. Compare cold-start cohorts to warm users.
- New Item Click-Through Rate (CTR): % of cold items that receive clicks within first N impressions. Indicates content appeal.
- Recommendation Quality by Age: NDCG, precision, or engagement metrics stratified by user/item age.
Table Example: Percentage of users in each bucket changes over platform lifetime.
| Platform Stage | Strictly Cold % | Cold % | Warm-Up % | Warm % |
|---|---|---|---|---|
| Day 1 (Launch) | 100% | 0% | 0% | 0% |
| Week 1 | 60% | 25% | 12% | 3% |
| Month 1 | 20% | 18% | 22% | 40% |
| Quarter 1 | 5% | 8% | 15% | 72% |
User Cold-Start: Content-Based & Demographic Approaches
When a new user arrives with no interaction history, the most direct approach is to ask them about their preferences or use demographic signals to place them into known segments.
Onboarding Questionnaire
The simplest strategy: collect user preferences upfront. A short onboarding quiz gathers initial signals:
Example Onboarding Questions: 1. "Select your top 3 genres" (multi-select) 2. "Pick 5 items from this curated list" (image gallery) 3. "What's your primary use case?" (radio buttons) Advantage: Direct, explicit signal Drawback: Friction (users may skip, answers may be inaccurate)
Demographic-Based Fallback
When users skip onboarding, use demographics to place them into known groups:
- Age/Gender: Gender-specific item preferences (statistically valid)
- Location: Regional content preferences (language, local creators)
- Device: Mobile vs desktop users consume differently
- Referral Source: Where they came from indicates intent (ad campaign, social, organic search)
Group users by demographic attributes, then use the group's average preferences (aggregate of warm users in that segment).
Content-Based Features for New Users
Beyond demographics, leverage content metadata:
| Content Feature | Representation | How to Use |
|---|---|---|
| Title/Description | Text embedding (TF-IDF or BERT) | Match new user's stated interests to item text |
| Category | One-hot or hierarchical embedding | Filter to categories user selected |
| Tags/Keywords | Bag-of-words or semantic embedding | Rank items by tag overlap with user profile |
| Popularity | Interaction count, rating | Bias toward popular items (safe choice) |
Cross-Reference: Content-Based Filtering
For deeper understanding of feature engineering and content-based similarity, see collaborative_filtering_guide.html (content-based section). For semantic embeddings using BERT, see bert_guide.html.
User Cold-Start: Exploration Strategies
Even with content-based recommendations, you need a way to actively learn user preferences. Multi-armed bandit algorithms balance showing items you think are good (exploitation) with showing diverse items to learn (exploration).
Upper Confidence Bound (UCB)
UCB treats each item as an "arm" in a bandit. For each arm, track successes and trials, then score:
UCB Score = μ (mean reward) + c * sqrt(log(t) / n)
Where:
μ = observed mean reward (engagement rate)
c = exploration constant (typically 1-2)
t = total rounds played
n = times this arm played
Intuition: Known good arms (high μ) are favored.
Unplayed arms (low n) get bonus.
Over time, as t grows, bonus decreases.Thompson Sampling
Thompson Sampling uses Bayesian inference. For each arm, maintain a Beta distribution of success probability:
For each item (arm):
α = successes + 1 (prior: pseudocount)
β = failures + 1
At each step:
1. Sample θ ~ Beta(α, β) for each arm
2. Select arm with highest sampled θ
3. Get reward (click = 1, no click = 0)
4. Update: α += reward, β += (1 - reward)
Intuition: Uncertainty is modeled explicitly.
Arms with high sampled values get more plays.
Natural exploration as uncertainty shrinks.Epsilon-Greedy
Simpler alternative: with probability ε, pick a random arm; otherwise pick the best known arm.
if rand() < epsilon:
recommendation = random_item()
else:
recommendation = best_performing_item()
Challenge: ε is fixed; doesn't adapt as confidence grows.
Advantage: Simple to implement and understand.Contextual Bandits: LinUCB
For personalized exploration, use contextual bandits like LinUCB. Each user-context pair has its own exploration strategy:
LinUCB Score = (w_a^T * x_c) + α * sqrt(x_c^T * (A_a^-1) * x_c)
Where:
w_a = learned weights for arm a
x_c = context vector (user + item features)
A_a = design matrix for arm a
α = exploration parameter
Benefit: Personalized exploration based on user context
Learns faster than non-contextual banditsPractical Implementation Tips
- Warm-up period: Allocate 5-10% of traffic to exploration for cold users to build confidence faster
- Decay parameter: Reduce exploration rate over time as interaction count grows
- Segment by device: Mobile and desktop users may need different exploration strategies
- Combine with content: Use content-based retrieval to pre-filter arms (e.g., only arms matching user's stated interests)
Item Cold-Start: Content Features & Similarity
New items have no interaction signals, but they do have content metadata. Even before anyone clicks, you can use features to find similar warm items and inherit their audience.
Content Feature Extraction
| Feature Type | Representation | Data Source |
|---|---|---|
| Title/Description | Text embedding (TF-IDF, BERT, Word2Vec) | Item metadata; crawled/OCR'd from content |
| Category | One-hot or hierarchical embedding | Item taxonomy (provided by creator) |
| Tags/Keywords | Bag-of-words, learned embeddings | Creator-provided; auto-generated via NLP |
| Creator/Seller Info | Creator ID embedding, reputation score | Creator history; warm items by same creator |
| Image/Visual | CNN embeddings (ResNet, ViT) | Product photos, thumbnails |
| Price/Attributes | Normalized numerical features | Item listing data |
Content-Based Item Similarity for Cold Items
Once you have features for a new item, find similar warm items using cosine similarity or other distance metrics:
new_item_embedding = encode(title, category, tags, image)
# Find K nearest neighbors (warm items)
similarity = cosine_similarity(new_item_embedding,
warm_item_embeddings)
neighbors = top_k(similarity, k=10)
# Inherit audience: who interacted with neighbors?
inherited_users = union(neighbors_audiences)
# Score: boosted preference for inherited users
recommendation_score = base_score + 0.5 * inheritance_scoreCross-Reference: Embeddings & Semantic Retrieval
For detailed BERT and dense retrieval methods, see bert_guide.html and dense_retrieval_guide.html. These techniques apply directly to item feature extraction.
Creator Reputation as a Signal
A new item from a proven creator should inherit some trust:
- Creator history: If creator has successful warm items, boost new items from them
- Creator embedding: Learn a creator ID embedding; new items from the same creator are similar
- Quality gate: Only boost new items if creator's warm items meet quality threshold (e.g., average rating > 4.0)
Item Cold-Start: Exposure Boosting & Traffic Allocation
Even with great content features, new items need visibility to gather interaction data. Exposure boosting is a business rule: allocate a fixed percentage of traffic to cold items during a warm-up period.
Traffic Allocation Strategy
Classic approach: Reserve 5-10% of impressions for new items.
if rand() < 0.05: # 5% traffic to cold items
candidates = items(age < 7_days, interactions < 100)
recommendation = sample_weighted(candidates, weight=quality)
else:
recommendation = normal_ranking_algorithm()
Stratified Sampling: Ensure Diverse Exposure
Don't just allocate traffic randomly. Ensure new items reach different user segments:
- By user age: Allocate to both new and warm users (new users more exploratory)
- By geography: Spread new items across regions proportionally
- By device: Test on mobile and desktop equally
- By time of day: Avoid peak hours for risky items
Duration-Based Warm-Up
Boost allocation decays with item age:
item_age_days = (now - item_created_at).days
if item_age_days < 1:
boost_multiplier = 10x # First 24 hours: aggressive
elif item_age_days < 3:
boost_multiplier = 5x # Days 2-3: moderate
elif item_age_days < 7:
boost_multiplier = 2x # Week 1: gentle
elif item_age_days < 14:
boost_multiplier = 1.2x # Week 2: minimal
else:
boost_multiplier = 1.0 # Normal ranking
traffic_allocation = 0.05 * boost_multiplier
Quality Gating
Only boost items above a quality threshold. A low-quality item shouldn't get boosted just because it's new:
- Metadata quality: Complete title, description, image, and category required
- Creator reputation: Only boost items from creators with average rating > 3.5
- Policy compliance: Manual review for sensitive items before boosting
- Engagement threshold: Once item reaches 10 interactions, evaluate CTR. If CTR < expected baseline for category, reduce boost.
AB Testing New Items
Before full rollout, test new items in controlled manner:
Experiment Design: Control: Normal ranking (no cold-start boost) Treatment: Cold-start boost (5-10% allocation) Metrics: - New item CTR (primary) - User engagement (session time, multi-click) - Creator satisfaction (retention, repeat uploads) - System diversity (% budget to top 10 items) Duration: Run for 1 week per cohort Rollout: If CTR_treatment > CTR_control * 1.2, deploy
Position Bias Correction for Cold Items
New items may appear in lower-ranked positions even with boost. Account for position bias:
observed_ctr = clicks / impressions # Position i has inherent CTR discount (e.g., 0.7x for rank 5) position_discount = position_bias[i] # Predicted CTR if item were at rank 1 predicted_true_ctr = observed_ctr / position_discount # Use true CTR to evaluate item quality (not position-biased)
Hybrid Approaches: Blending Content & Collaborative
As a user or item transitions from cold to warm, you want to smoothly shift from content-based recommendations (which work without history) to collaborative filtering (which leverages history for better personalization).
Dynamic Blending by Interaction Count
Interpolate between content-based and collaborative filtering scores based on how much data you have:
interaction_count = user.num_interactions
# Warm-up curve: as interactions grow, CF weight increases
cf_weight = tanh(interaction_count / 10)
content_weight = 1.0 - cf_weight
# Blend scores
final_score = (content_weight * content_score +
cf_weight * cf_score)
Example:
0 interactions: content_weight=1.0, cf_weight=0.0 (100% content)
5 interactions: content_weight=0.38, cf_weight=0.62 (38% content, 62% CF)
20 interactions: content_weight=0.10, cf_weight=0.90 (10% content, 90% CF)
50+ interactions: content_weight≈0.0, cf_weight≈1.0 (100% CF)Two-Tower Architecture with Cold-Start Extension
In a two-tower model (see two_tower_models_guide.html), user embeddings and item embeddings are learned independently. For cold items:
- Item tower uses content features: When item ID embedding is untrained (new item), rely on content-feature embeddings (title, category, image)
- User tower learns from interactions: With each interaction, user embedding improves
- Gradual ID embedding training: As cold item accumulates interactions, its ID embedding begins training, eventually replacing content-based representation
For cold items: item_embedding = α * content_embedding + (1-α) * id_embedding Where: content_embedding = learned from title, category, image id_embedding = trainable embedding (initialized near zero) α = 1.0 when item is brand new α = 0.0 when item is warm (many interactions) Smooth transition as item_interactions grow
Cascade Ranking: Content → CF → Reranking
A practical hybrid approach: use content for retrieval, CF for ranking, then rerank for diversity and exploration:
Stage 1 - Retrieval (Content-based):
candidates = top_1000_by_content_similarity(user_profile)
Stage 2 - Ranking (Collaborative Filtering):
candidates = sort_by_cf_score(candidates)
candidates = take_top_100(candidates)
Stage 3 - Reranking (Business rules + exploration):
final_ranking = apply_business_rules(candidates)
- Boost new items (cold-start rule)
- Suppress already-seen items
- Diversify by category
return final_ranking[:10]Meta-Embedding for Cold Items
When an item is cold but has collaborative neighbors, use their embeddings as a proxy:
if item.interactions < 10:
# Find K warm items similar by content
neighbors = find_content_neighbors(item, k=5)
# Average their ID embeddings as proxy
item.cf_embedding = mean([n.id_embedding for n in neighbors])
# Use proxy for user-item scoring
score = dot_product(user.embedding, item.cf_embedding)
else:
# Once warm, use true ID embedding
score = dot_product(user.embedding, item.id_embedding)two_tower_models_guide.html for detailed two-tower architecture. The key insight for cold-start: use content features in the item tower when ID embeddings are untrained.
Meta-Learning for Fast Adaptation
Meta-learning ("learning to learn") trains models to adapt quickly to new users from just a few examples. Instead of starting from scratch with each new user, the model learns how to learn user preferences fast.
Model-Agnostic Meta-Learning (MAML)
MAML trains a model that can be quickly fine-tuned to new users:
MAML Training Loop:
Outer loop (meta-update):
For each task (user with K examples + evaluation set):
Inner loop (adapt to user):
1. Sample K interactions from user (support set)
2. Take 1 gradient step: θ' = θ - α * ∇L(θ, K)
3. Evaluate on evaluation set using θ'
4. Compute meta-loss
Update meta-parameters: θ = θ - β * ∇(meta-loss)
Inference on new user:
1. Observe their first K interactions
2. Compute adapted parameters: θ_user = θ - α * ∇L(θ, K)
3. Use θ_user for recommendation
Benefit: After 1-5 gradient steps, model adapts to new user
Better generalization than training from scratchPrototypical Networks
A simpler meta-learning approach: learn item embeddings such that similar items cluster together. For a new user, their representation is the mean of items they clicked:
User Representation = mean(item_embeddings of clicked items) For new user: 1. They click on K items 2. Represent user as avg of those K item embeddings 3. Score candidates by distance to user representation score = -||candidate_embedding - user_representation||² Strength: No gradient updates needed; very fast Weakness: Limited to items with pre-trained embeddings
User Clustering for Cold-Start
Cluster warm users by behavior; when a new user arrives, assign them to nearest cluster, then apply that cluster's model:
Offline (one-time): 1. Cluster warm users by interaction patterns (K-means, EM) 2. For each cluster, train a mini-model Online (new user): 1. Observe their first K interactions 2. Compute distance to each cluster centroid 3. Assign to nearest cluster 4. Use that cluster's model for ranking As user accumulates more data: 5. Optionally re-cluster or transition to personalized model
When to Use Meta-Learning
- High user velocity: Thousands of new users daily; content-based approaches become tedious
- Limited onboarding data: You can only ask users 3-5 questions; need fast adaptation from few examples
- Diverse item ecosystem: Prototypical networks work when items are diverse; clustering works when patterns are clear
Practical Engineering Solutions
Beyond algorithms, cold-start requires practical engineering patterns for speed and reliability.
Feature Stores for Fast Attribute Lookup
New items need quick access to content features (embedding, category, tags). Use a feature store:
- Redis/Memcached: In-memory cache of item features (embedding vectors, metadata)
- Dedicated feature store (Feast, Tecton): Handles feature versioning, joining, and serving
- Batch + online serving: Pre-compute features offline, serve online with sub-100ms latency
Hashing Trick for Unknown Entities
If a user/item is so cold you have no embedding, use hashing to place them in an embedding table:
If user.embedding is None:
# Hash user ID to bucket
bucket_idx = hash(user_id) % num_buckets
user.embedding = global_bucket_embedding[bucket_idx]
# Alternative: use demographic-based bucket
bucket_idx = hash(user_age, user_location) % num_buckets
user.embedding = demographic_bucket_embedding[bucket_idx]
Benefit: Every user/item gets an embedding
Drawback: Many users/items map to same bucket (collisions)Side Information in Embedding Tables
Initialize cold item embeddings as a mix of content and ID embeddings:
item_embedding[i] = (
content_embedding[i] * 0.8 + # Majority: content features
id_embedding[i] * 0.2 # Small component: ID embedding (near zero for cold items)
)
As item warms up (gains interactions):
- ID embedding component learns from interactions
- Content component weight decays
- Eventually: item_embedding ≈ learned id_embeddingLazy Evaluation & Caching
Don't compute personalized scores for every user-item pair. Use strategic caching:
- Per-user cache: Cache top-K recommendations for each user; refresh every 1-24 hours
- Global cache for cold users: Cache generic recommendations (not personalized) for strictly cold users
- Lazy personalization: If user has <3 interactions, serve pre-computed recommendations; don't run expensive CF model
Cold-Warm Transition Pattern
Implement a smooth switchover as user accumulates data:
if user.interactions < 3:
recommendations = generic_recommendations()
elif user.interactions < 20:
recommendations = hybrid_blend(content=0.6, cf=0.4)
else:
recommendations = cf_recommendations()
# Monitor user experience (engagement, churn) per bucket
# Adjust thresholds based on cohort performanceMonitoring & Instrumentation
Key metrics to track for cold-start performance:
| Metric | Definition | Target |
|---|---|---|
| Cold-Start CTR | Click rate for new items (age < 7d) | ≥ warm item CTR * 0.8 |
| New User Retention | % returning after first session | ≥ 30% (varies by industry) |
| Diversity (Gini Index) | Inequality of item exposure | Lower Gini = more diverse (target 0.4-0.6) |
| Median NDCG (by user age) | Recommendation quality per cohort | Improve from day 1 → day 30 |
Decision Framework: When to Use Which Solution
No single approach works everywhere. Your choice depends on problem type, available data, latency constraints, and platform maturity.
Approach Selection by Problem Type
| Problem | Recommended Approach | Data Requirements | Latency | Complexity |
|---|---|---|---|---|
| User Cold-Start | Onboarding questionnaire → Content-based → Bandits | User preferences; item features | Fast (<100ms) | Medium |
| Item Cold-Start | Content similarity → Traffic boosting → Quality gating | Item metadata; popularity baseline | Fast (<200ms) | Low |
| Both Cold | Hybrid (content + bandits) → CF as data grows | Item features; user context signals | Medium (100-500ms) | High |
| Warm + Cold Mix | Segment users; apply approach per segment | Interaction history; user age | Medium | Medium |
Industry Patterns
Streaming Platform
Music, video, podcasts
- Start with genre-based onboarding
- Use bandit for exploration
- Boost trending content for new items
E-Commerce Marketplace
Products, sellers, categories
- Use search queries as user signal
- Boost new sellers gradually
- Product reviews as quality gate
Social Network
Follows, interactions, connections
- Onboard with suggested accounts
- Use demographic + friend network
- Viral content boost for new creators
News Aggregator
Articles, topics, publishers
- Topic preferences in onboarding
- Editor picks for new articles
- Time-decay (freshness) important
Platform Maturity Considerations
Early-stage platform (MVP): Most users and items are cold. Focus on content-based + simple rules. Avoid ML overhead.
Growth stage (6-18 months): Mix of cold and warm. Introduce bandits for user exploration. Traffic boosting for items. Monitor cold-start cohorts.
Mature platform (2+ years): Majority warm. Cold-start affects <5% of recommendations. Use advanced techniques (MAML, contextual bandits) for marginal gains. Prioritize other areas (ranking, diversity, serendipity).
Quick Decision Tree
1. Do you have user preferences? YES → Onboarding questionnaire, content-based filtering NO → Proceed to #2 2. Do you have item metadata (title, category, image)? YES → Content-based similarity for items NO → Proceed to #3 3. Can you afford to boost cold items? YES → Traffic allocation + quality gating NO → Proceed to #4 4. Do you have user context (age, location, device)? YES → Demographic clustering NO → Random exploration (last resort) 5. Are you accumulating enough interaction data? YES (10+ interactions/user) → Transition to hybrid blending NO → Keep content-based approach
Python Implementation: Core Algorithms
Hands-on implementations of key cold-start algorithms. All code uses NumPy and Pandas for reproducibility.
import numpy as np
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
from scipy.sparse import csr_matrix
# ============ 1. UCB BANDIT ALGORITHM ============
class UCBBandit:
"""Upper Confidence Bound multi-armed bandit for exploration."""
def __init__(self, n_arms, c=1.0):
self.n_arms = n_arms
self.c = c # exploration constant
self.counts = np.zeros(n_arms) # times each arm played
self.successes = np.zeros(n_arms) # clicks per arm
self.total_plays = 0
def select_arm(self):
"""Select arm with highest UCB score."""
ucb_scores = []
for arm in range(self.n_arms):
if self.counts[arm] == 0:
# Unplayed arm: infinite UCB
ucb_scores.append(float('inf'))
else:
mean_reward = self.successes[arm] / self.counts[arm]
confidence = self.c * np.sqrt(np.log(self.total_plays) / self.counts[arm])
ucb_scores.append(mean_reward + confidence)
return np.argmax(ucb_scores)
def update(self, arm, reward):
"""Update counts after arm selection and reward observation."""
self.counts[arm] += 1
self.successes[arm] += reward
self.total_plays += 1
# ============ 2. THOMPSON SAMPLING ============
class ThompsonSampling:
"""Thompson Sampling using Beta-Binomial model."""
def __init__(self, n_arms):
self.n_arms = n_arms
self.alphas = np.ones(n_arms) # successes + prior
self.betas = np.ones(n_arms) # failures + prior
def select_arm(self):
"""Sample from Beta distribution; select arm with highest sample."""
samples = np.random.beta(self.alphas, self.betas)
return np.argmax(samples)
def update(self, arm, reward):
"""Update Beta parameters."""
self.alphas[arm] += reward
self.betas[arm] += (1 - reward)
# ============ 3. CONTENT-BASED COLD-START ============
class ContentBasedCold:
"""Content-based recommendations for cold items."""
def __init__(self, item_features):
"""
item_features: (n_items, d) array of item embeddings
"""
self.item_features = item_features
self.n_items = item_features.shape[0]
def recommend_for_cold_item(self, cold_item_idx, k=10):
"""
Find k similar warm items using cosine similarity.
"""
cold_embedding = self.item_features[cold_item_idx:cold_item_idx+1]
similarities = cosine_similarity(cold_embedding, self.item_features)[0]
# Exclude self
similarities[cold_item_idx] = -1
# Top-k similar items
neighbors = np.argsort(similarities)[::-1][:k]
return neighbors, similarities[neighbors]
# ============ 4. HYBRID COLD-WARM BLENDING ============
class HybridColdWarm:
"""Blend content-based and CF scores based on user age."""
def __init__(self, content_scores, cf_scores):
"""
content_scores: (n_users, n_items) content-based scores
cf_scores: (n_users, n_items) collaborative filtering scores
"""
self.content_scores = content_scores
self.cf_scores = cf_scores
self.n_users = content_scores.shape[0]
def get_blended_scores(self, user_interactions):
"""
user_interactions: array of interaction counts per user
Returns blended scores based on interaction count.
"""
blended = np.zeros_like(self.content_scores)
for user_idx in range(self.n_users):
interactions = user_interactions[user_idx]
# Warm-up curve: tanh maps 0→1 range
cf_weight = np.tanh(interactions / 10.0)
content_weight = 1.0 - cf_weight
blended[user_idx] = (
content_weight * self.content_scores[user_idx] +
cf_weight * self.cf_scores[user_idx]
)
return blended
# ============ EXAMPLE: SYNTHETIC DATASET & EVALUATION ============
def create_synthetic_dataset(n_users=1000, n_items=500, n_interactions=5000):
"""
Create synthetic user-item interaction matrix with mix of cold/warm users.
"""
# Random user-item interactions
rows = np.random.randint(0, n_users, n_interactions)
cols = np.random.randint(0, n_items, n_interactions)
data = np.ones(n_interactions)
interaction_matrix = csr_matrix((data, (rows, cols)), shape=(n_users, n_items))
# Random item features (embeddings)
item_features = np.random.randn(n_items, 64)
item_features = item_features / np.linalg.norm(item_features, axis=1, keepdims=True)
# User interaction counts
user_interactions = np.array(interaction_matrix.sum(axis=1)).flatten()
return {
'interaction_matrix': interaction_matrix,
'item_features': item_features,
'user_interactions': user_interactions,
'n_users': n_users,
'n_items': n_items
}
# ============ UCB EXPLORATION SIMULATION ============
def simulate_ucb_exploration(n_items=20, n_rounds=200, true_ctr=None):
"""Simulate UCB bandit over rounds; plot exploration curve."""
if true_ctr is None:
true_ctr = np.random.uniform(0.1, 0.5, n_items)
bandit = UCBBandit(n_items, c=1.0)
arm_selections = np.zeros(n_rounds)
for round_idx in range(n_rounds):
arm = bandit.select_arm()
arm_selections[round_idx] = arm
# Simulate reward
reward = 1 if np.random.rand() < true_ctr[arm] else 0
bandit.update(arm, reward)
return arm_selections, bandit.counts, bandit.successes
# Test UCB
arm_selections, counts, successes = simulate_ucb_exploration(n_items=10, n_rounds=500)
exploration_rate = (len(np.unique(arm_selections)) / 10.0) * 100
print(f"UCB Exploration: {exploration_rate:.1f}% of arms explored")
print(f"Top arm selected {counts[np.argmax(successes)]:.0f} times")
# ============ CONTENT-BASED COLD ITEM RECOMMENDATION ============
data = create_synthetic_dataset()
content_model = ContentBasedCold(data['item_features'])
# Recommend for a cold item (item 0)
neighbors, similarities = content_model.recommend_for_cold_item(0, k=10)
print(f"\nContent-based neighbors for item 0:")
for idx, neighbor in enumerate(neighbors):
print(f" Rank {idx+1}: Item {neighbor} (similarity: {similarities[idx]:.3f})")
# ============ HYBRID BLENDING DEMO ============
n_users, n_items = 100, 50
content_scores = np.random.randn(n_users, n_items)
cf_scores = np.random.randn(n_users, n_items)
user_interactions = np.array([0, 2, 5, 10, 20, 50]) # Varying interaction counts
hybrid = HybridColdWarm(content_scores[:len(user_interactions)],
cf_scores[:len(user_interactions)])
blended = hybrid.get_blended_scores(user_interactions)
print(f"\nHybrid blending weights by interaction count:")
for interactions in [0, 2, 5, 10, 20, 50]:
cf_w = np.tanh(interactions / 10.0)
content_w = 1.0 - cf_w
print(f" {interactions} interactions: content={content_w:.2f}, CF={cf_w:.2f}")Key Takeaways from Implementation
- UCB Bandit: Balance exploration (unplayed arms get bonus) and exploitation (high-reward arms favored)
- Thompson Sampling: Natural uncertainty modeling via Beta distribution; often outperforms UCB
- Content-Based: Simple cosine similarity finds neighbors for cold items fast
- Hybrid Blending: Smooth transition from content → CF as interaction count grows
Extensions & Future Work
- Contextual Bandits (LinUCB): Add user/context features for personalized exploration
- MAML: Implement inner/outer loop for fast user adaptation
- Online Learning: Stream user interactions; update models in real-time
- A/B Testing: Compare approaches on real traffic; measure retention, diversity, engagement
- Monitor cold-start CTR, retention, diversity metrics
- Implement gradual rollout for new algorithms (shadow traffic, canary deployment)
- Use feature store for fast item feature lookup
- Cache recommendations for cold users (reduce latency)
- AB test on real traffic (metrics often surprise)