Skip to main content

Music and Podcast Recommendation System Architecture

Comprehensive ML system design for personalized audio content recommendations at scale

Modern audio content platforms require sophisticated recommendation engines to surface relevant music and podcasts to users, balancing exploration and exploitation while handling massive scale data processing and real-time personalization requirements.

1. Problem Statement & ML Task Definition

Clarifying questions

  1. What is the expected QPS and latency budget for serving recommendations? (e.g., 100ms p99, 10K QPS)
  2. Is the system primarily focused on music or podcasts, or both with different weights?
  3. What is the size of training data and feature space? (e.g., 10M users, 50M items, 100K features)
  4. What is the expected accuracy vs. speed trade-off? (e.g., AUC-ROC vs. latency)
  5. How important is real-time personalization vs. batch recomputation?
  6. What is the expected scale of new users/items per day and how are they handled?
  7. What are the key business metrics to optimize for? (e.g., CTR, engagement time, revenue)

Business use case and success criteria

The music/podcast recommendation system aims to increase user engagement by delivering personalized content that aligns with individual listening preferences. The system should drive higher retention, longer listening sessions, and increased revenue through premium content subscriptions or ad engagement.

Success criteria:

  • 20% improvement in user session duration
  • 15% increase in CTR on recommended content
  • 25% reduction in user churn rate within 30 days
  • 10% increase in premium subscription conversion

ML problem formulation

This is a ranking task with the following characteristics:

  • Multi-class classification for content categorization (music/podcast genres, moods)
  • Multi-objective ranking for personalized content presentation
  • Multi-task learning to optimize for engagement, retention, and revenue

Justification: The system must rank items in order of relevance to each user, making ranking the appropriate ML task. The multi-modal nature (music/podcast) and multi-objective optimization requirements necessitate a ranking approach over pure classification or regression.

ML evaluation metrics

Offline evaluation:

  • AUC-ROC for binary classification tasks (e.g., listen vs. skip)
  • NDCG@K for ranked list quality
  • Precision@K (e.g., Precision@5, Precision@10)
  • BLEU score for content similarity in generation tasks

Online evaluation:

  • CTR (Click-Through Rate) for content discovery
  • Engagement time per session
  • Revenue per user (RPU) for monetization
  • User retention rate (30-day, 90-day)

Scale and constraints

Back-of-envelope estimates:

Training data size: 50M user-item interactions (50GB raw, 100GB processed) Number of features: 100K (user features + item features + context features) Embedding matrix memory footprint: 50M items × 128 dimensions × 4 bytes = 25GB Serving QPS math: 10K QPS × 100ms p99 = 1GB memory per second of serving

Constraints:

  • QPS: 10K requests/sec (p99 latency < 100ms)
  • Data freshness: 1-hour update window for user features, 24-hour for item features
  • Model size: < 1GB for efficient serving
  • Latency budget: 50ms for retrieval, 25ms for scoring, 25ms for re-ranking

Non-ML baseline strategy

Rule-based fallback:

  1. Cold start: Recommend popular content from the same genre or mood
  2. User segmentation: Default to trending playlists for new users
  3. Context-based: Use time-of-day, device type, and location to suggest relevant content
  4. Content diversity: Ensure variety in recommendations across genres and artists

Cold start handling:

  • New users: Default to top-k popular content with genre diversity
  • New items: Use content-based filtering based on metadata (genre, artist, mood)
  • Hybrid approach: Combine popularity with user preference signals when available

2. Data Strategy

Training Data Sources

  • Explicit feedback: User ratings, likes/dislikes, playlist additions, skip rates
  • Implicit signals: Play counts, listening duration, repeat plays, queue behavior, session length
  • User demographics: Age, location, device type, listening time preferences
  • Content metadata: Genre, artist, album, release date, tempo, key, mood tags
  • Third-party data: Musicbrainz, Spotify metadata, radio station data, social media sentiment

Labeling Pipeline Design

  • Human annotation: For high-value explicit feedback (user ratings, playlist curation) with 500+ annotators for quality control
  • Weak supervision: Using domain heuristics (e.g., "users who listen to classical music > 30 min are likely to enjoy other classical tracks")
  • Programmatic labeling:
    • Play count > threshold → positive label (1)
    • Play duration < 20% of track length → negative label (0)
    • User skips within first 30 seconds → negative label (0)

Class Imbalance Handling Strategy

  • Oversampling: Minority classes (rare genres, new artists) via SMOTE or synthetic sampling
  • Weighted loss: Apply class weights in training to penalize misclassification of rare classes more heavily
  • Stratified sampling: Ensure balanced representation in training batches

Negative Sampling Approach

  • Time-based negative sampling: Sample items that were not consumed within the last 30 days
  • Popularity-based: Bias sampling towards less popular items to improve diversity
  • Hard negative mining: Select items that were previously consumed but skipped early (high recall potential)

Data Freshness and Update Frequency Requirements

  • Real-time: Play events, skip signals (1–5 second latency)
  • Batch: User profiles, content metadata (daily updates)
  • Weekly: Playlist and user behavior aggregates (for long-term trend analysis)

Privacy Compliance

  • GDPR/CCPA: Anonymize user IDs, remove PII from logs, implement data retention policies (3 years for training data)
  • Differential privacy: Apply to user behavior features before training to prevent re-identification
  • Consent management: Track user preferences for data collection and usage

Data Quality Validation and Monitoring

  • Schema validation: Ensure all fields present with correct types (e.g., play duration > 0)
  • Distribution monitoring: Track feature drift using KL divergence and PSI for key signals
  • Duplicate detection: Identify and remove duplicate user-item interactions (e.g., same play event logged multiple times)
  • Anomaly detection: Flag unusual patterns like sudden spikes in skip rates or play counts
# Sample data validation function
def validate_play_event(event):
    required_fields = ['user_id', 'item_id', 'play_duration', 'timestamp']
    for field in required_fields:
        if field not in event or event[field] is None:
            raise ValueError(f"Missing required field: {field}")
    
    if event['play_duration'] <= 0:
        raise ValueError("Play duration must be positive")
    
    return True

3. Feature Engineering

Feature Taxonomy

Features are categorized into four main types:

  1. User Features: Demographics, listening behavior, preferences, and engagement history
  2. Item Features: Audio characteristics, metadata, genre classification, and content attributes
  3. Context Features: Time of day, device type, location, and session information
  4. Interaction Features: User-item interaction signals, temporal dynamics, and sequence patterns

Embedding Design

Entity Embeddings:

  • User ID embeddings (dimension: 128)
  • Item ID embeddings (dimension: 128)
  • Genre embeddings (dimension: 64)
  • Artist embeddings (dimension: 128)

Text Embeddings:

  • Audio content metadata (title, description) → BERT-based embeddings (dimension: 768)
  • Lyrics content (when available) → Sentence-BERT embeddings (dimension: 768)

Graph Embeddings:

  • User-item interaction graph → Node2Vec embeddings (dimension: 128)
  • Artist-to-artist co-listening graph → GraphSAGE embeddings (dimension: 128)

Real-time vs Batch Feature Computation

Feature TypeComputation ModeJustification
User listening historyReal-timeFor dynamic personalization
Item audio featuresBatchComputed once per item update
User demographicsBatchStatic or infrequent updates
Context signalsReal-timeTime-sensitive (e.g., time-of-day)
Interaction countsReal-timeFor recency weighting

Feature Store Design

Online Store:

  • Low-latency Redis-based store for real-time serving
  • TTL: 1 hour for user features, 24 hours for item features

Offline Store:

  • Parquet-based storage in S3 for training
  • Partitioned by date and user ID

Feature Freshness & TTL

FeatureSourceUpdate FrequencyStore Type
User listening historyReal-time logsEvery 5 minutesOnline
Item audio featuresAudio processing pipelineDailyOffline
User demographicsCRMWeeklyOffline
Context signalsSession managerPer requestOnline
Interaction countsAnalytics engineEvery hourOnline

Cross Features & Feature Interactions

Cross features are engineered to capture complex user-item dynamics:

  1. User-Item Interaction: User age × Item genre → 200M possible combinations
  2. Temporal Interactions: User listening frequency × Time of day → Dynamic weightings
  3. Content-Based Interactions: User's favorite genre × Item's genre similarity → Enhanced relevance

Feature Importance & Selection

Methodology:

  • SHAP-based feature importance from trained models
  • Statistical significance testing (Chi-square for categorical features)
  • Online A/B test impact analysis

Selection Strategy:

  • Top 200 features retained for training
  • Feature groups: User (50), Item (80), Context (30), Interactions (40)
# Feature engineering pipeline example
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler

class FeatureExtractor:
    def __init__(self):
        self.scaler = StandardScaler()
        
    def extract_user_features(self, user_df):
        """Extract user behavior features"""
        user_features = {
            'user_listening_frequency': user_df.groupby('user_id')['item_id'].count(),
            'user_avg_session_length': user_df.groupby('user_id')['session_duration'].mean(),
            'user_genre_diversity': user_df.groupby('user_id')['genre'].nunique()
        }
        return pd.DataFrame(user_features)
    
    def extract_item_features(self, item_df):
        """Extract audio and metadata features"""
        item_features = {
            'item_popularity': item_df['play_count'],
            'audio_loudness': item_df['loudness'],
            'audio_tempo': item_df['tempo'],
            'item_duration': item_df['duration_seconds']
        }
        return pd.DataFrame(item_features)
    
    def extract_context_features(self, context_df):
        """Extract time and device features"""
        context_features = {
            'hour_of_day': pd.to_datetime(context_df['timestamp']).dt.hour,
            'day_of_week': pd.to_datetime(context_df['timestamp']).dt.dayofweek,
            'device_type': context_df['device_type'].map({'mobile': 0, 'desktop': 1, 'tablet': 2})
        }
        return pd.DataFrame(context_features)
    
    def compute_interaction_features(self, user_item_df):
        """Compute user-item interaction features"""
        interaction_features = {
            'user_item_recency': user_item_df.groupby(['user_id', 'item_id'])['timestamp'].diff().dt.total_seconds(),
            'user_item_count': user_item_df.groupby(['user_id', 'item_id'])['timestamp'].count(),
            'user_item_avg_rating': user_item_df.groupby(['user_id', 'item_id'])['rating'].mean()
        }
        return pd.DataFrame(interaction_features)

4. Model Architecture

Model Selection with Justification

For a music/podcast recommendation system, I recommend a Two-Tower Neural Collaborative Filtering architecture with the following rationale:

  1. Scalability: Two-tower models scale well to large user/item vocabularies
  2. Efficiency: Fast inference for real-time serving with pre-computed embeddings
  3. Flexibility: Can incorporate diverse feature types (user, item, context)
  4. Interpretability: Clear separation of user and item representation learning

Alternatives considered and rejected:

  • Matrix Factorization: Limited to user/item interactions, cannot incorporate rich feature sets
  • Transformer-based models: Overkill for real-time serving with high latency requirements
  • GBM: Good for structured features but struggles with sparse high-dimensional embeddings

Architecture Diagram

graph TD
    A[User Features] --> B[User Tower]
    C[Item Features] --> D[Item Tower]
    E[Context Features] --> B
    F[Context Features] --> D
    
    B --> G[Merged User Embedding]
    D --> H[Merged Item Embedding]
    
    G --> I[Dot Product Scoring]
    H --> I
    I --> J[Recommendation Score]
    
    K[User Embedding Cache] --> B
    L[Item Embedding Cache] --> D
    
    subgraph "Two-Tower Architecture"
        B
        D
        G
        H
        I
    end

Core Model Implementation

import torch
import torch.nn as nn
import torch.nn.functional as F

class TwoTowerRecommender(nn.Module):
    def __init__(self, 
                 user_feature_dim=1024,
                 item_feature_dim=1024,
                 embedding_dim=128,
                 hidden_dims=[256, 128]):
        super().__init__()
        
        # User tower
        self.user_tower = nn.Sequential(
            nn.Linear(user_feature_dim, hidden_dims[0]),
            nn.ReLU(),
            nn.Linear(hidden_dims[0], hidden_dims[1]),
            nn.ReLU(),
            nn.Linear(hidden_dims[1], embedding_dim)
        )
        
        # Item tower
        self.item_tower = nn.Sequential(
            nn.Linear(item_feature_dim, hidden_dims[0]),
            nn.ReLU(),
            nn.Linear(hidden_dims[0], hidden_dims[1]),
            nn.ReLU(),
            nn.Linear(hidden_dims[1], embedding_dim)
        )
        
        self.embedding_dim = embedding_dim
        
    def forward(self, user_features, item_features):
        """
        Forward pass for recommendation scoring
        Args:
            user_features: [batch_size, user_feature_dim]
            item_features: [batch_size, item_feature_dim]
        Returns:
            scores: [batch_size] - recommendation scores
        """
        user_embedding = self.user_tower(user_features)
        item_embedding = self.item_tower(item_features)
        
        # Dot product scoring
        scores = torch.sum(user_embedding * item_embedding, dim=1)
        
        return scores
    
    def predict(self, user_features, item_features):
        """Get recommendation probability (sigmoid)"""
        scores = self.forward(user_features, item_features)
        return torch.sigmoid(scores)
    
    def compute_loss(self, user_features, item_features, labels):
        """Binary cross-entropy loss with optional negative sampling"""
        predictions = self.predict(user_features, item_features)
        loss = F.binary_cross_entropy(predictions, labels.float())
        return loss

# Example usage for training
def train_step(model, user_batch, item_batch, labels, optimizer):
    model.train()
    optimizer.zero_grad()
    
    loss = model.compute_loss(user_batch, item_batch, labels)
    loss.backward()
    optimizer.step()
    
    return loss.item()

# Example usage for inference
def get_recommendations(model, user_embedding, item_embeddings, top_k=10):
    """
    Get top-k recommendations for a user
    Args:
        model: trained TwoTowerRecommender
        user_embedding: [1, embedding_dim]
        item_embeddings: [num_items, embedding_dim]
    Returns:
        top_k_indices: indices of top-k items
    """
    model.eval()
    with torch.no_grad():
        user_embeddings = model.user_tower(user_embedding)
        
        # Compute scores for all items
        scores = torch.matmul(user_embeddings, item_embeddings.t())
        
        # Get top-k indices
        top_k_scores, top_k_indices = torch.topk(scores, k=top_k)
        
    return top_k_indices.squeeze()

Model Complexity vs Latency Trade-off

ModelParametersInference Time (ms)Memory UsageNotes
Two-Tower (Small)~2M0.51MBFast, suitable for real-time
Two-Tower (Medium)~8M1.24MBGood balance
Two-Tower (Large)~32M3.016MBHigher accuracy, slower
Transformer (Small)~25M15+64MBHigh latency, requires caching

Offline Evaluation Baseline

Baseline model: Logistic Regression with TF-IDF features

  • Expected AUC-ROC: ~0.65-0.70
  • Expected NDCG@10: ~0.35-0.40

Proposed model (Two-Tower): Expected AUC-ROC: ~0.75-0.80

  • Expected NDCG@10: ~0.50-0.60
  • Expected Precision@K: ~0.45-0.55

Model Training Details

The model uses binary cross-entropy loss with negative sampling for training:

# Sample training loop snippet
def train_model(model, train_loader, optimizer, num_epochs=10):
    for epoch in range(num_epochs):
        total_loss = 0
        for batch in train_loader:
            user_features, item_features, labels = batch
            
            loss = model.compute_loss(user_features, item_features, labels)
            total_loss += loss
            
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            
        print(f"Epoch {epoch+1}, Loss: {total_loss/len(train_loader):.4f}")

Multi-Task Extension (Optional)

For advanced scenarios, we can extend to multi-task learning:

class MultiTaskTwoTower(nn.Module):
    def __init__(self, base_model, task_heads):
        super().__init__()
        self.base_model = base_model
        self.task_heads = nn.ModuleDict(task_heads)
        
    def forward(self, user_features, item_features, task_name):
        # Shared representation
        user_emb = self.base_model.user_tower(user_features)
        item_emb = self.base_model.item_tower(item_features)
        
        # Task-specific outputs
        if task_name in self.task_heads:
            return self.task_heads[task_name](user_emb, item_emb)
        else:
            raise ValueError(f"Unknown task: {task_name}")

This architecture provides a solid foundation for scalable, high-performance music/podcast recommendations with clear separation between user and item representation learning. The model can be easily extended to incorporate additional features like audio embeddings or temporal information.

5. Serving & Inference Architecture

System Diagram

graph TD
    A[User Request] --> B[Load Balancer]
    B --> C[API Gateway]
    C --> D[Ranking Service]
    D --> E[Feature Store]
    E --> F[Embedding Cache]
    F --> G[Model Serving]
    G --> H[Scoring Engine]
    H --> I[Re-ranking Service]
    I --> J[Response Formatter]
    J --> K[User Response]
    
    subgraph "Real-time Serving"
        D
        G
        H
        I
        J
    end
    
    subgraph "Feature & Embedding Layer"
        E
        F
    end

Online vs Batch Serving Decision

For music/podcast recommendations, we require real-time serving with sub-100ms latency for user interactions. Batch serving would not meet the interactive experience requirements where users expect instant recommendations when browsing or playing content.

Two-Stage Pipeline

Candidate Retrieval → Scoring → Re-ranking

  1. Retrieval Stage:

    • Use approximate nearest neighbor (ANN) search to retrieve top-K candidates from item embeddings
    • Leverages Faiss or Annoy for efficient retrieval at scale
  2. Scoring Stage:

    • Apply trained model to score candidates with user and item features
    • Handles cross-features and context-aware scoring
  3. Re-ranking Stage:

    • Apply diversity and freshness constraints
    • Adjust for user preference drift or content variety

Serving Infrastructure

We use TorchServe for model serving due to:

  • Native PyTorch support with efficient inference
  • Built-in model versioning and A/B testing capabilities
  • Easy integration with Kubernetes orchestration
  • Support for dynamic batching to improve throughput

For large-scale LLM-based content generation (if applicable), we would consider vLLM for its optimized attention mechanisms and memory efficiency.

Latency Budget Breakdown

Stagep50 Latencyp99 LatencyBudget
Request Processing5ms15ms20ms
Feature Retrieval10ms25ms30ms
Model Inference15ms40ms50ms
Re-ranking & Output5ms15ms20ms
Total35ms95ms120ms

Model Compression Strategy

  • Quantization: INT8 quantization reduces model size by 4x with <2% accuracy loss
  • Pruning: Remove 30% of weights to reduce inference time by 25%
  • Distillation: Student model trained with teacher model logits for faster serving

Caching Strategy

  1. Prediction Cache:

    • Cache top-K recommendations for 5-minute TTL per user
    • Reduces redundant computation for returning users
  2. Embedding Cache:

    • Store user/item embeddings in Redis with 1-hour TTL
    • Reduces feature computation overhead for frequent queries
  3. KV Cache (for LLMs):

    • For content generation use cases, cache attention keys/values
    • Reduces recomputation in streaming scenarios

Auto-scaling and Capacity Planning

  • Horizontal Scaling: Scale model instances based on QPS with Kubernetes HPA
  • Warm-up Strategy: Pre-load embeddings and model weights during deployment
  • Capacity Planning:
    • Estimate 10K QPS peak with 50ms p99 target
    • Requires ~200 model instances running on GPU nodes (assuming 50ms latency per instance)
    • Maintain 20% buffer for traffic spikes and model retraining
# Example scoring logic for real-time inference
class RecommendationScorer:
    def __init__(self, model_path):
        self.model = torch.load(model_path)
        self.model.eval()
    
    def score_candidates(self, user_features, item_embeddings):
        """
        Score candidates in real-time
        Returns: (scores, ranked_indices)
        """
        # Batch process to reduce overhead
        user_tensor = torch.tensor(user_features, dtype=torch.float32)
        
        # Apply model forward pass
        with torch.no_grad():
            scores = self.model(user_tensor, item_embeddings)
        
        # Convert to probabilities and rank
        probs = torch.sigmoid(scores)
        ranked_indices = torch.argsort(probs, descending=True)
        
        return probs, ranked_indices
    
    def re_rank(self, scores, indices, diversity_factor=0.3):
        """
        Apply diversity and freshness re-ranking
        """
        # Simple diversity logic - penalize similar content types
        # In production, this would use more sophisticated algorithms
        
        # Example: reduce score for items with same genre
        adjusted_scores = scores.clone()
        
        return adjusted_scores, indices

6. Evaluation Framework

Offline Evaluation Methodology

For offline evaluation, we use a temporal split approach to ensure that our model is evaluated on data that reflects realistic user behavior and avoids data leakage. The training set spans from 2022-01-01 to 2023-06-30, validation from 2023-07-01 to 2023-08-31, and test from 2023-09-01 to 2023-10-31. This ensures that temporal trends and seasonal patterns are captured in the evaluation process.

We also implement time-aware cross-validation with 3-fold splits to validate model robustness across different time windows. For each fold, we hold out the most recent 30 days of data for testing.

Slice-Based Evaluation

We define critical slices to ensure model fairness and performance consistency across user segments:

SliceDefinitionRationale
User DemographicsAge groups (18-25, 26-35, 36+)Different listening habits
Geographic RegionUS, EU, APACRegional content availability
Device TypeMobile, Desktop, WebPlatform-specific preferences
Listening TimeMorning (6-12), Evening (18-23)Temporal preference patterns
Subscription StatusFree, PremiumContent access differences

A/B Test Design

We design a randomized controlled experiment with the following specifications:

  • Randomization Unit: User ID

  • Sample Size Calculation:

    • Target: 5% uplift in engagement (e.g., average listening time)
    • Power: 80%
    • Significance Level: 5%
    • Estimated baseline: 120 seconds/listen
    • Required sample size: ~35,000 users per variant
  • Test Duration: 4 weeks (to capture weekly cycles)

  • Primary Metric: Click-through rate (CTR) on recommendations

  • Secondary Metrics:

    • Average listening duration
    • User retention (30-day)
    • Revenue per user (for premium features)

Guardrail Metrics and Blocking Conditions

We define guardrail metrics to ensure system safety:

MetricThresholdAction
CTR Drop Below Baseline10%Block rollout
User Disengagement Rate>5%Immediate investigation
Model Confidence Variance>30%Retrain required
Content Diversity<50% unique categoriesRebalance model

Shadow Mode Rollout Plan

Before full A/B testing, we implement a shadow mode where the new model generates recommendations but does not serve them to users. Instead, we log both the shadow model's predictions and the baseline model's predictions for comparison:

# Shadow mode logging example
def shadow_mode_log(user_id, model_output, baseline_output):
    log_entry = {
        "user_id": user_id,
        "shadow_model_recommendations": model_output,
        "baseline_model_recommendations": baseline_output,
        "timestamp": datetime.now()
    }
    # Write to logging service for offline analysis
    return log_entry

# Example usage in production pipeline
def process_user_request(user_id, context):
    shadow_output = shadow_model.predict(context)
    baseline_output = baseline_model.predict(context)
    
    # Log for offline evaluation
    shadow_mode_log(user_id, shadow_output, baseline_output)
    
    # Serve baseline for now
    return baseline_output

Long-Term Metric Measurement

We track delayed conversion and engagement decay:

  • Delayed Conversion:

    • 7-day retention rate (users who return after 7 days)
    • 30-day revenue per user (for premium content)
  • Engagement Decay:

    • Weekly listening time decay (user engagement drops over time)
    • Content re-listening rate (how often users revisit recommended content)

We implement delayed metric tracking where we log user behavior for up to 30 days post-recommendation, allowing us to understand long-term impact and model drift over time.

# Delayed metric tracking example
def track_delayed_metrics(user_id, content_id, timestamp):
    # Store user engagement over time
    delayed_metrics = {
        "user_id": user_id,
        "content_id": content_id,
        "timestamp": timestamp,
        "engagement_1d": get_engagement(user_id, content_id, days=1),
        "engagement_7d": get_engagement(user_id, content_id, days=7),
        "engagement_30d": get_engagement(user_id, content_id, days=30),
        "relistens_7d": get_relistens(user_id, content_id, days=7),
        "relistens_30d": get_relistens(user_id, content_id, days=30)
    }
    
    # Store in time-series database for long-term analysis
    return delayed_metrics

7. Training Pipeline

Training Data Pipeline

The training data pipeline follows a multi-stage ETL process:

  1. Data Collection: Raw data from user interactions (play, skip, like, share), item metadata (genre, artist, duration), and user profiles (age, location, listening habits)
  2. Preprocessing:
    • Normalize timestamps and convert to UTC
    • Handle missing values with appropriate imputation strategies
    • Filter out low-quality or spam interactions (e.g., < 5s plays, multiple skips)
  3. Shuffling and Batching:
    • Use reservoir sampling for large datasets (100M+ records)
    • Batch size: 256 for DNN models, 1024 for transformer-based models
    • Temporal shuffling to prevent data leakage
import tensorflow as tf
from sklearn.model_selection import train_test_split

def preprocess_data(raw_data):
    # Normalize timestamps and filter interactions
    filtered_data = raw_data[
        (raw_data['play_duration'] > 5) & 
        (raw_data['interaction_type'].isin(['play', 'like', 'skip']))
    ]
    
    # Convert to proper datetime format
    filtered_data['timestamp'] = pd.to_datetime(filtered_data['timestamp'])
    
    # Create user and item IDs
    filtered_data['user_id'] = filtered_data['user_id'].astype('category')
    filtered_data['item_id'] = filtered_data['item_id'].astype('category')
    
    return filtered_data

def create_tf_dataset(data, batch_size=256):
    dataset = tf.data.Dataset.from_tensor_slices({
        'user_id': data['user_id'].values,
        'item_id': data['item_id'].values,
        'play_count': data['play_count'].values,
        'genre': data['genre'].values,
        'duration': data['duration'].values
    })
    
    # Shuffle and batch
    dataset = dataset.shuffle(buffer_size=10000).batch(batch_size)
    
    return dataset

Distributed Training Strategy

For large-scale training, we use data parallelism with model parallelism for transformer-based models:

  1. Data Parallelism:

    • Split training data across multiple workers (e.g., 8 GPUs per node)
    • Each worker processes a subset of the dataset
    • Gradient synchronization via AllReduce
  2. Model Parallelism:

    • For transformer models, split attention layers across GPUs
    • Use PyTorch's DistributedDataParallel for model synchronization
  3. Training Infrastructure:

    • Cluster: 4 nodes with 8x A100 GPUs each
    • Estimated training time: 2-4 hours for full dataset (50M samples)
    • Memory usage: ~16GB per GPU with mixed precision training

Hyperparameter Tuning Approach

We implement Bayesian optimization for efficient hyperparameter search:

  1. Key Hyperparameters:

    • Learning rate (0.001, 0.01)
    • Batch size (64, 128, 256)
    • Embedding dimension (32, 64, 128)
    • Number of layers (2, 3, 4)
    • Dropout rate (0.1, 0.2, 0.3)
  2. Search Strategy:

    • Use Optuna or Ray Tune for Bayesian optimization
    • Objective: AUC-ROC on holdout set (5% of data)
    • Search space: 20-50 trials for convergence

Experiment Tracking and Reproducibility

We use MLflow for comprehensive experiment tracking:

  1. Tracking Components:

    • Model parameters and metrics (AUC, NDCG)
    • Training logs and checkpoints
    • Feature engineering steps and preprocessing parameters
  2. Versioning Strategy:

    • Model versioning using Git tags for reproducibility
    • Registry system to track model performance over time

Model Versioning and Registry Strategy

  • Version Control: Semantic versioning (v1.0.0, v1.1.0) based on performance improvements
  • Registry: Centralized model registry with metadata (training date, dataset version, performance metrics)
  • Rollback Capability: Automatic rollback to previous versions if performance degrades by >5%

Continual / Online Learning Design

For real-time adaptation to user behavior:

  1. Online Learning Window:

    • 30-day rolling window for model updates
    • Incremental learning using Adam optimizer with small learning rate (1e-5)
  2. Replay Mechanism:

    • Store recent interactions (last 30 days) for periodic retraining
    • Use replay buffer to prevent catastrophic forgetting
  3. Adaptive Sampling:

    • Prioritize recent user interactions (weight = 1.0) vs older ones (weight = 0.5)
    • Implement importance sampling to balance recent and historical data
class OnlineLearningTrainer:
    def __init__(self, model, learning_rate=1e-5):
        self.model = model
        self.optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
        
    def update_model(self, new_data_batch):
        # Forward pass
        predictions = self.model(new_data_batch)
        
        # Compute loss with recent data weight
        weighted_loss = self.compute_weighted_loss(predictions, new_data_batch)
        
        # Backward pass
        self.optimizer.zero_grad()
        weighted_loss.backward()
        self.optimizer.step()
        
    def compute_weighted_loss(self, predictions, batch):
        # Recent interactions weighted more heavily
        recent_weight = torch.where(
            batch['timestamp'] > self.recent_threshold,
            torch.tensor(1.0),
            torch.tensor(0.5)
        )
        
        return F.binary_cross_entropy_with_logits(
            predictions, 
            batch['labels'],
            weight=recent_weight
        )

8. Monitoring & Observability

Data Drift Detection

Data drift monitoring is critical for maintaining model performance in a music recommendation system where user behavior and content characteristics evolve over time.

Feature Drift Detection:

  • PSI (Population Stability Index) for categorical features like user demographics, device types, and listening habits
  • KL Divergence for continuous features such as user engagement scores, audio feature statistics
  • Statistical Tests: Kolmogorov-Smirnov test for continuous features, Chi-square test for categorical features
import numpy as np
from scipy import stats
from sklearn.metrics import mutual_info_score

def calculate_psi(expected, actual, bins=10):
    """Calculate Population Stability Index"""
    # Bin the data
    _, bin_edges = np.histogram(expected, bins=bins)
    
    # Count occurrences in each bin
    expected_counts = np.histogram(expected, bins=bin_edges)[0]
    actual_counts = np.histogram(actual, bins=bin_edges)[0]
    
    # Avoid zero division
    expected_counts = np.where(expected_counts == 0, 1e-6, expected_counts)
    actual_counts = np.where(actual_counts == 0, 1e-6, actual_counts)
    
    psi = np.sum((actual_counts - expected_counts) * 
                 np.log(actual_counts / expected_counts))
    return psi

def detect_feature_drift(expected_data, actual_data, feature_name):
    """Detect drift for a single feature"""
    psi = calculate_psi(expected_data, actual_data)
    
    # Thresholds for drift detection
    if psi > 0.2:
        return "HIGH_DRIFT", f"PSI={psi:.3f} - High drift detected"
    elif psi > 0.1:
        return "MODERATE_DRIFT", f"PSI={psi:.3f} - Moderate drift detected"
    else:
        return "NO_DRIFT", f"PSI={psi:.3f} - No significant drift"

Prediction Drift and Label Drift Monitoring

Prediction drift occurs when the distribution of model predictions changes over time, indicating potential model degradation or data shift.

Prediction Drift Detection:

  • Monitor distribution of predicted scores using statistical tests
  • Track change in prediction confidence distributions
  • Implement early warning systems for prediction drift

Label Drift Detection:

  • Monitor change in user engagement patterns (e.g., click-through rates)
  • Track shift in implicit feedback signals (play counts, skips, likes)

Model Performance Degradation Alerting

Implement comprehensive alerting system for model performance degradation:

class ModelPerformanceMonitor:
    def __init__(self, thresholds):
        self.thresholds = thresholds  # dict of {metric: threshold}
        
    def check_performance_degradation(self, current_metrics, baseline_metrics):
        """Check if performance has degraded beyond acceptable thresholds"""
        degradation_alerts = []
        
        for metric, threshold in self.thresholds.items():
            current_val = current_metrics.get(metric)
            baseline_val = baseline_metrics.get(metric)
            
            if baseline_val is not None and current_val is not None:
                # Calculate percentage change
                change_pct = (current_val - baseline_val) / baseline_val
                
                if change_pct < threshold:
                    degradation_alerts.append({
                        'metric': metric,
                        'current': current_val,
                        'baseline': baseline_val,
                        'change_pct': change_pct,
                        'alert': f'Degradation detected: {change_pct:.2%}'
                    })
                    
        return degradation_alerts

Retraining Trigger Strategy

Implement multiple retraining triggers to ensure model freshness:

  1. Scheduled Retraining: Weekly or bi-weekly retraining based on business cycles
  2. Drift-Triggered: When data drift exceeds thresholds (PSI > 0.15 for key features)
  3. Performance-Triggered: When AUC drops below threshold (e.g., 5% from baseline)
  4. Volume-Triggered: When new data volume exceeds threshold (e.g., 10M new samples)

Logging Design

Comprehensive logging for debugging, monitoring, and continuous improvement:

import json
from datetime import datetime

class RecommendationLogger:
    def __init__(self):
        self.prediction_logs = []
        self.feature_logs = []
        self.feedback_logs = []
    
    def log_prediction(self, user_id, item_id, score, features, timestamp):
        """Log prediction for monitoring and debugging"""
        log_entry = {
            'timestamp': timestamp.isoformat(),
            'user_id': user_id,
            'item_id': item_id,
            'score': score,
            'features': features,
            'request_id': self.generate_request_id()
        }
        self.prediction_logs.append(log_entry)
        
    def log_feedback(self, user_id, item_id, feedback_type, timestamp):
        """Log user feedback for model improvement"""
        log_entry = {
            'timestamp': timestamp.isoformat(),
            'user_id': user_id,
            'item_id': item_id,
            'feedback_type': feedback_type,
            'request_id': self.generate_request_id()
        }
        self.feedback_logs.append(log_entry)
        
    def generate_request_id(self):
        """Generate unique request ID for tracking"""
        return f"req_{datetime.now().timestamp()}"

Feedback Loop for Continuous Improvement

Implement feedback collection and model retraining pipeline:

class FeedbackProcessor:
    def __init__(self, feedback_queue):
        self.feedback_queue = feedback_queue
        self.feedback_stats = {}
        
    def process_feedback(self, feedback_data):
        """Process user feedback for model improvement"""
        processed_feedback = []
        
        for feedback in feedback_data:
            # Normalize feedback (e.g., convert to binary labels)
            normalized_feedback = self.normalize_feedback(feedback)
            
            # Update feedback statistics
            self.update_feedback_stats(feedback)
            
            processed_feedback.append(normalized_feedback)
            
        return processed_feedback
    
    def normalize_feedback(self, feedback):
        """Convert various feedback formats to standardized labels"""
        # Example: Convert skip rate to binary label
        if feedback['action'] == 'skip':
            return {'label': 0, 'confidence': 0.9}
        elif feedback['action'] == 'play':
            return {'label': 1, 'confidence': 0.8}
        else:
            return {'label': 1, 'confidence': 0.5}

Key Monitoring Metrics and Dashboards

  • Real-time Performance: AUC, NDCG, Precision@K, Recall@K
  • Data Quality: Feature coverage, missing values, data distribution shifts
  • System Health: Latency, error rates, throughput, cache hit ratios
  • Business Impact: CTR, engagement rate, revenue per user

Alerting Configuration

  • Critical Alerts: Performance drops >10%, data drift PSI > 0.25
  • Warning Alerts: Performance drops >5%, data drift PSI > 0.15
  • Info Alerts: Minor performance changes, new feature addition

Retraining Pipeline Integration

Monitor retraining pipeline health:

  • Training job success rate
  • Data freshness in training sets
  • Model version consistency across environments
  • Feature store synchronization status

This monitoring framework ensures that the music recommendation system maintains high quality and performance over time, with early detection of issues that could impact user experience or business metrics.

9. Responsible AI & Safety

Bias Analysis

  • Training data audit: Conduct systematic analysis of training data to identify representation gaps across demographic groups (age, gender, geographic regions) and musical genres. Use statistical tests to detect disproportionate representation in minority categories.
  • Slice-based fairness evaluation: Evaluate model performance across user segments (e.g., age groups 18-25, 26-35, etc.) and geographic regions to detect potential bias in recommendations. Monitor precision/recall disparities between slices.

Fairness Metrics and Constraints

  • Primary fairness metric: Demographic parity (equal recommendation rates across user groups) for key demographic slices
  • Secondary metric: Equalized odds (same true positive rates across slices) to ensure consistent performance regardless of user characteristics
  • Decision rationale: Demographic parity is prioritized over equalized odds to ensure equitable exposure across user segments, particularly important for underrepresented musical genres or artist groups

Explainability Requirements and Approach

  • Stakeholder needs:
    • Business: High-level explanation of why certain recommendations are made (e.g., "similar to your previous favorites")
    • Content creators: Insight into how their content is being recommended (genre-based matching)
    • Users: Personalized explanations for individual recommendations
  • Explainability methods:
    • Attention visualization in transformer-based models to show which user/item features contributed most to recommendation scores
    • SHAP feature importance for linear models and gradient-boosted trees
    • Rule-based explanation engine for simple, human-readable reasons

LLM Safety Guardrails (if applicable)

  • Content filtering: Implement content moderation to prevent recommendation of inappropriate or harmful content (explicit language, hate speech, misinformation)
  • Output validation: Validate that recommended content aligns with platform policies and user preferences
  • Rate limiting: Implement rate limits to prevent abuse or spam in recommendation generation
  • Red teaming: Regular security testing with diverse content samples to identify edge cases and potential safety issues

Model Card Documentation Plan

  • Required elements:
    • Model architecture and training methodology
    • Performance metrics across different user segments
    • Known limitations and edge cases
    • Data sources and preprocessing steps
    • Fairness metrics and bias analysis results
    • Safety measures and content moderation policies

Incident Response Plan for Model Failures or Harmful Outputs

  • Failure detection: Monitor for sudden drops in recommendation quality, user engagement, or negative feedback signals
  • Escalation protocol:
    • Immediate flagging of recommendations that violate content policies
    • Automatic fallback to rule-based recommendations when model confidence drops below threshold (e.g., <70%)
    • Human review queue for flagged recommendations
  • Recovery actions:
    • Quick model retraining with updated data samples
    • Temporary disabling of problematic recommendation logic
    • User feedback loop to quickly identify and address harmful recommendations

10. Scalability & Reliability

Bottleneck Analysis per Pipeline Stage

Training Pipeline:

  • Data ingestion: 100GB/day → 10TB/month, 500GB/s peak ingestion rate
  • Feature computation: 200M users × 100 features = 20B feature values, 50GB memory footprint per training job
  • Model training: 100M samples, 500M features → 200GB model size (sparse), 10 hours training on 8x A100
  • Model serialization: 200GB → 50GB compressed, 10s to load

Serving Pipeline:

  • QPS requirement: 50K requests/sec (1M concurrent users, 50ms average latency)
  • Model inference: 20ms p99 (10ms p50), 500M features → 2GB embedding lookup, 10ms inference
  • Feature store: 20B features × 8 bytes = 160GB, 5ms lookup latency
  • Cache hit ratio: 95% (1M concurrent users, 200ms TTL)

Back-of-Envelope Calculations:

  • Training data size: 100M samples × 500M features = 50TB (compressed)
  • Feature store size: 20B features × 8 bytes = 160GB (online store)
  • Model size: 200GB compressed, 50GB in memory (quantized)
  • Serving QPS: 50K/sec → 1.7M requests/hour, 43M requests/day

Scale-up Strategy

Horizontal Scaling:

  • Feature store: 100-node cluster with sharding (2GB per node)
  • Model serving: 500-node inference fleet, 100 nodes per model version
  • Training: 200-node GPU cluster (8x A100s), batch jobs with 500 workers

Distributed Feature Computation:

  • User features: 1000 shards, 200M users → 200K users per shard
  • Item features: 100 shards, 5M items → 50K items per shard
  • Real-time features: 1000 streaming partitions, 500K events/sec

Fault Tolerance

Model Serving Fallbacks:

  • Primary model (v2.1) → fallback to v2.0 if >3% error rate
  • Cache warming: 50% of cache pre-warmed, 20% warm-up on failure
  • Graceful degradation: 50% of top-K recommendations served from cold start model

Stale Model Serving:

  • Model version rollback: 30s TTL for model updates, 5 min grace period
  • Feature drift detection: 10% threshold for feature drift → fallback to previous model version

Cold Start Handling Strategy

New User Cold Start:

  • Default playlist (50 songs) from popular trends
  • Content-based filtering using user profile (age, location, listening history)
  • Hybrid approach: 70% default + 30% user preference matching

New Item Cold Start:

  • Content-based ranking using item metadata (genre, artist, duration)
  • Collaborative filtering with 10+ interactions threshold
  • Hybrid: 80% content + 20% popularity boosting

Multi-region Serving Strategy

Primary Region (US-West):

  • 50% of traffic, 1000 serving nodes, 200M concurrent users
  • Model version: v2.1, 50% cache hit ratio

Secondary Region (EU-Central):

  • 25% of traffic, 250 serving nodes, 50M concurrent users
  • Model version: v2.0 (fallback), 40% cache hit ratio
  • Failover: 5s latency threshold, automatic region switch

Global Load Balancing:

  • Traffic distribution: 50% US-West, 25% EU-Central, 25% Asia-Pacific
  • Health checks: 10s interval, 3x fail threshold for region switch
  • Geo-aware routing: 50ms latency target per region

Cross-region Model Synchronization:

  • Model version sync: 15-minute intervals, 5% delta threshold
  • Feature drift sync: 1-hour intervals, 10% drift trigger
  • A/B test consistency: 5% sample size across regions

11. Open Questions, Risks & Trade-offs

Decisions requiring product / engineering / policy alignment

  • User consent and data collection policies - What level of user behavior tracking is acceptable for recommendation quality? How do we balance personalization with privacy?
  • Content moderation and safety thresholds - What constitutes "harmful" content that should be filtered from recommendations? How do we handle edge cases in content classification?
  • Cross-platform recommendation consistency - Should the same recommendations be served across web, mobile, and smart speaker platforms? What's the impact on user experience?
  • Content creator monetization alignment - How do we balance organic content discovery with revenue-generating recommendations (e.g., paid content, sponsorships)?
  • Cross-domain recommendation strategy - Should music recommendations influence podcast suggestions and vice versa? What's the optimal cross-domain signal sharing?

Key ML risks

RiskImpactLikelihoodMitigation
Data drift in user listening patternsHigh - Model performance degradationMedium - User behavior evolves over timeImplement online drift detection, scheduled retraining, and shadow mode A/B testing
Cold start for new users/itemsMedium - Reduced recommendation qualityHigh - New user/item influxImplement hybrid approaches with demographic features, content-based fallbacks, and popularity-based baselines
Bias amplification in recommendationHigh - Fairness violations, user dissatisfactionMedium - Historical bias in training dataImplement fairness-aware training, slice-based evaluation, and regular bias audits
Privacy leakage through recommendation patternsMedium - User profiling risksLow - Moderate if proper anonymizationApply differential privacy techniques, limit feature exposure, and implement strict data governance
Model overfitting to popularityMedium - Reduced diversity, user engagement dropHigh - Popularity bias in training dataImplement diversity-aware loss functions and explicit popularity regularization

Trade-offs made with justification

Trade-offJustification
Accuracy vs Latency - Prioritize sub-100ms serving latency over marginal accuracy gainsFor real-time music streaming, user experience degradation from latency outweighs small accuracy improvements. Users expect immediate response when they tap play.
Freshness vs Cost - Batch feature updates every 24h instead of real-timeReal-time feature computation is expensive and often not necessary for music recommendations. 24h freshness provides good balance between relevance and cost, with minimal impact on user experience.
Model Size vs Quality - Use model compression over larger modelsProduction systems require efficient serving. Quantization and pruning enable deploying high-quality models at scale while maintaining acceptable performance.
Personalization vs Diversity - Balance between user-specific and diverse recommendationsPure personalization leads to echo chambers. A hybrid approach with diversity constraints improves long-term user satisfaction and discovery of new content.
Online vs Batch Training - Use batch training with online feature updatesOnline learning introduces complexity and instability. Batch training with periodic model updates provides more stable, reproducible results with better control over training data quality.

Alternatives considered and rejected

  • Pure collaborative filtering - Rejected due to cold start problems and scalability limitations for large-scale content catalogs
  • Content-based filtering alone - Rejected because it lacks the rich user interaction signals that drive better recommendations
  • Real-time reinforcement learning - Rejected due to high complexity, latency requirements, and difficulty in defining proper reward signals for music recommendations
  • Multi-armed bandit systems - Rejected because they're better suited for advertising or content selection rather than music discovery where exploration is valuable
  • Explicit user feedback collection - Rejected due to high operational cost and low user engagement in providing explicit ratings

Technical debt acknowledged

  • Feature store synchronization lag - The system assumes feature stores are updated in near real-time, but actual sync delays may cause stale recommendations
  • Model versioning complexity - Current implementation uses simple version tags but lacks proper rollback and rollback testing infrastructure
  • Cross-domain feature alignment - No shared embedding space between music and podcast domains, leading to potential inconsistency in user preference modeling
  • Shadow mode operational overhead - Shadow mode testing requires significant infrastructure investment and monitoring complexity that may not be justified for all use cases
  • Offline evaluation gap - The offline metrics may not fully capture real-world user engagement patterns, creating a gap between training and production performance

12. Phased Implementation Roadmap

Phase 1: MVP (Weeks 1-8)

Objective: Establish baseline recommendation capability with minimal viable system.

Key Deliverables:

  • Rule-based fallback system (popularity-based recommendations)
  • Basic ML model using collaborative filtering with user-item interaction data
  • Simple online serving infrastructure (single model endpoint)
  • Basic A/B testing infrastructure

Dependencies:

  • Data pipeline ready with user-item interaction logs
  • Basic feature store (read-only) established

Critical Path:

  1. Implement rule-based baseline with top-k popular items
  2. Train initial collaborative filtering model (SVD or matrix factorization)
  3. Deploy model to online serving endpoint
  4. Set up basic A/B testing infrastructure with 10% traffic split

Phase 2: Scale (Weeks 9-16)

Objective: Expand system to handle scale and improve accuracy with feature engineering.

Key Deliverables:

  • Feature store implementation (both online and offline)
  • Real-time feature computation pipeline
  • Multi-model ensemble approach (collaborative filtering + content-based)
  • Distributed training infrastructure
  • Production A/B testing framework

Dependencies:

  • Feature store fully operational with real-time capabilities
  • Distributed training cluster provisioned
  • Monitoring and alerting system established

Critical Path:

  1. Build feature store with TTL management for user/item features
  2. Implement real-time feature computation pipeline (streaming)
  3. Deploy ensemble model with weighted combination of multiple algorithms
  4. Set up distributed training pipeline (data parallelism)
  5. Launch full A/B testing with 50% traffic split

Phase 3: Excellence (Weeks 17-24)

Objective: Achieve production-grade recommendation system with advanced features.

Key Deliverables:

  • Advanced model architectures (Transformer-based or DNN)
  • Real-time personalization with continuous learning
  • Multi-task learning for related objectives (engagement, retention)
  • Advanced monitoring and drift detection system
  • Multi-region serving capability

Dependencies:

  • Production-grade ML platform with experiment tracking
  • Advanced monitoring system with drift detection
  • Multi-region cloud infrastructure provisioned

Critical Path:

  1. Implement transformer-based recommendation model with attention mechanism
  2. Deploy continuous learning pipeline for model updates
  3. Add multi-task learning for engagement and retention objectives
  4. Implement comprehensive monitoring with automated retraining triggers
  5. Enable multi-region serving for global availability

Cross-Phase Dependencies:

  • Data pipeline must be robust and scalable across all phases
  • Feature store must support real-time access for Phase 2+
  • Model versioning and registry must be established early
  • A/B testing infrastructure must be production-ready from Phase 1

Risk Mitigation:

  • Maintain rule-based fallback for all phases to ensure system stability
  • Implement gradual rollout strategy for new models and features
  • Establish clear rollback procedures for each phase's deliverables

Success Metrics:

  • QPS capacity: 10,000+ requests per second by Phase 3
  • Model accuracy improvement: 25%+ over baseline by Phase 3
  • A/B test statistical significance: p < 0.05 for all major metrics
  • System availability: 99.9% uptime across all phases

Was this article helpful?

Topics in this article

More from AI/ML System Design

  • AI/ML System Design

    Matrix Factorization and Collaborative Filtering System Design

    Comprehensive AI/ML system design for recommendation engines using matrix factorization and collaborative filtering techniques with production-ready architecture considerations

  • AI/ML System Design

    Amazon Product Recommendation System Architecture

    Comprehensive design document for scalable recommendation systems with real-time serving, batch processing, and machine learning pipelines.

  • AI/ML System Design

    AI-Powered People Recommendation System Design

    Comprehensive system design for scalable friend and follow recommendations using machine learning algorithms and real-time processing.

Stay in the Loop

Get curated DSA patterns, coding problems, frontend and backend LLD/HLD, and AI/ML System Design insights delivered straight to your inbox. No spam, just focused interview and engineering content.