Modern AI systems require continuous monitoring to detect performance degradation that can occur due to data drift, concept drift, or changing business conditions. This document outlines a production-ready system architecture for proactively identifying and alerting on model performance issues before they impact end users.
1. Problem Statement & ML Task Definition
Clarifying questions
- What is the expected scale of the system in terms of daily active users, items, and QPS?
- What is the latency budget for model inference in both online and batch serving scenarios?
- Is this a ranking or classification task, and what is the target business metric (e.g., CTR, revenue, engagement)?
- What is the frequency of model retraining and how does the system handle model versioning?
- How critical is real-time performance vs. batch processing for this use case?
- What are the key user segments or slices that require specific monitoring (e.g., new users, mobile vs. desktop)?
- What is the acceptable trade-off between model accuracy and inference latency in production?
Business use case and success criteria
The system aims to maintain high-quality model performance in production by proactively detecting performance degradation and triggering appropriate alerts or recovery actions. This is critical for maintaining user experience, business KPIs (e.g., CTR, revenue, engagement), and ensuring the ML system remains aligned with business objectives.
Success criteria include:
- Early detection of performance degradation within 24–48 hours
- Alert accuracy >90% with <5% false positive rate
- Mean time to detection (MTTD) <2 hours for critical degradation
- System reliability with <5% of alerts being spurious
ML problem formulation
This is a ranking task where the model predicts relevance scores for items to users. The system uses a two-tower neural network architecture for user-item matching, with the goal of maximizing user engagement through accurate item ranking.
ML evaluation metrics
Offline (training/inference):
- AUC-ROC for binary classification tasks
- NDCG@K for ranking quality (e.g., NDCG@10)
- Precision@K for top-K recall
- BLEU score for generation tasks
Online (production):
- Click-through rate (CTR)
- Revenue per user (RPU)
- User engagement duration
- Conversion rate
Scale and constraints
Back-of-envelope estimates:
- Training data size: 50M user-item interactions (20GB raw)
- Number of features: 10,000 (sparse + dense)
- Embedding matrix memory footprint: 50M × 128 dimensions × 4 bytes = ~25GB
- Serving QPS: 10,000 (assuming 10ms p99 latency target)
- Model size: ~500MB (compressed)
Non-ML baseline strategy
Rule-based fallback logic includes:
- Cold start handling for new users/items using popularity-based ranking
- Static feature-based rules (e.g., content category matching)
- Fallback to historical model version if current model performance drops below threshold
def baseline_ranking(user_features, item_features):
# Simple fallback: popularity + content matching
popularity_score = get_item_popularity(item_features)
content_match = compute_content_similarity(user_features, item_features)
return popularity_score * 0.6 + content_match * 0.4
2. Data Strategy
Training data sources
- Explicit feedback: User ratings, clicks, likes, shares, and direct engagement signals (e.g., "save" or "add to cart")
- Implicit signals: Session duration, scroll depth, time spent on item page, view-through rates, and abandon cart behavior
- Third-party data: Demographic information from partner APIs, contextual signals (weather, location), and content metadata (tags, categories)
- Synthetic data: Generated via GANs or rule-based simulations for edge case coverage and handling class imbalance
Labeling pipeline design
- Human annotation: For high-value, rare events (e.g., conversion prediction) with quality control through double labeling and inter-annotator agreement (Cohen's Kappa > 0.85)
- Weak supervision: Using heuristics and rules to generate pseudo-labels for large-scale training (e.g., "user clicked item if they viewed it for >30s")
- Programmatic labeling: For real-time feedback loops (e.g., "if user purchases item within 24h of viewing, label as positive")
Class imbalance handling strategy
- Oversampling: Using SMOTE for rare positive classes in classification tasks to ensure adequate representation
- Class weights: Applying inverse frequency weighting during training (e.g.,
class_weight='balanced'in sklearn) - Focal loss: For classification tasks where rare positives are critical (e.g., fraud detection or conversion prediction)
- Justification: In many recommendation systems, only 1–5% of users engage with items, making class imbalance a critical factor for model performance
Negative sampling approach
- Uniform sampling: For training stability and balanced representation (e.g., 1:1 ratio of positive to negative samples)
- Stratified sampling: Ensuring that negative samples reflect user distribution (e.g., users with no interaction in past 30 days are under-sampled)
- Adversarial sampling: For ranking models, where negative samples are selected to challenge model confidence (e.g., using candidate sampling with top-k items)
- Justification: Proper negative sampling prevents model overfitting to popular items and ensures robustness across user segments
Data freshness and update frequency requirements
- Real-time signals: User behavior updates every 5–10 minutes (e.g., session events, view duration)
- Batch features: Updated daily or weekly for user demographics and item metadata
- Label updates: Hourly for conversion signals, daily for engagement-based labels
- Back-of-envelope estimate:
- Training data size: ~50M user-item interactions
- Feature count: ~10,000 features (user + item + context)
- Embedding matrix size: ~10M users × 128 dimensions = 1.28GB (per model)
- Serving QPS: 5000 requests/sec → 25M events/sec (assuming 5 events per request)
Privacy compliance
- GDPR/CCPA compliance:
- Anonymization of PII (user IDs, IP addresses) using hashing or tokenization
- Data retention policies: 24 months for behavioral data, 6 months for labels
- Subject access request (SAR) pipeline with automated data deletion
- Differential privacy: For training data in sensitive domains (e.g., health or finance)
- Justification: Ensures regulatory compliance and user trust, particularly in global markets
Data quality validation and monitoring
- Data validation checks:
- Schema validation (e.g., timestamp format, numeric ranges)
- Missing value thresholds (e.g., >5% missing features trigger alert)
- Data drift monitoring:
- PSI (Population Stability Index) for feature distributions
- KL divergence between training and serving data
- Anomaly detection:
- Isolation Forest or autoencoder-based anomaly detection for user behavior patterns
- Logging and alerting:
- Data quality alerts via Slack or PagerDuty for drift >5% or missing data >1%
import pandas as pd
from scipy.stats import ks_2samp
def validate_data_quality(df):
# Validate schema and missing values
assert not df.isnull().any().any(), "Missing data detected"
# Validate timestamp format
df['timestamp'] = pd.to_datetime(df['timestamp'], errors='coerce')
assert not df['timestamp'].isna().any(), "Invalid timestamp format"
# Detect drift using Kolmogorov-Smirnov test
training_data = df.sample(frac=0.1) # Simulate training set
serving_data = df.sample(frac=0.1) # Simulate serving set
drift_results = {}
for col in training_data.columns:
if col in ['user_id', 'item_id']:
continue
drift_results[col] = ks_2samp(training_data[col], serving_data[col])
return drift_results
3. Feature Engineering
Feature Taxonomy
Features are categorized into four main types:
- User Features: Demographic data, behavioral signals, preferences (e.g., age, gender, click history, time spent)
- Item Features: Content metadata, category, popularity, price (e.g., title, description, tags, rating)
- Context Features: Temporal signals, device info, location, session state (e.g., hour of day, device type, region)
- Interaction Features: User-item engagement patterns, temporal decay signals (e.g., recency, frequency, dwell time)
Embedding Design
Entity Embeddings:
- User ID embeddings: 128-dim, trained via click-through rate prediction
- Item ID embeddings: 256-dim, learned from item popularity and engagement signals
Text Embeddings:
- Title/Description embeddings: 384-dim using BERT-base, fine-tuned on user-item interaction data
- Category embeddings: 128-dim using Word2Vec trained on item categories
Graph Embeddings:
- User-item interaction graph embeddings: 128-dim using Node2Vec, updated daily
Real-time vs Batch Feature Computation
| Feature Type | Computation Mode | Reason |
|---|---|---|
| User behavior window stats | Real-time (streaming) | Low latency requirements |
| Item popularity scores | Batch (daily) | Computationally expensive, acceptable delay |
| User session context | Real-time (streaming) | Context-sensitive, real-time relevance |
| Historical engagement counts | Batch (hourly) | Can be pre-computed, reduces serving latency |
Feature Store Design
Online Store:
- Redis-based with TTL of 1 hour for real-time features
- Supports low-latency reads (sub-50ms p99)
- Key-value structure with feature name as key
Offline Store:
- Parquet files in S3 for training
- Partitioned by date for efficient querying
Feature Freshness and TTL
| Feature | Source | Update Frequency | Store Type |
|---|---|---|---|
| User last seen timestamp | User activity logs | Real-time | Online |
| Item category | Content catalog | Daily | Offline |
| User recency score | Session logs | Hourly | Online |
| Item popularity | Click logs | Daily | Offline |
| User engagement count | Historical data | Weekly | Offline |
Cross Features and Feature Interaction Strategy
Cross features are engineered using:
- Multiplicative interactions: User age × Item price, User session count × Item category
- Bucketing: Age groups × Category buckets for better generalization
- Temporal decay: User-item interaction count with exponential decay factor
Feature Importance and Selection Methodology
- Permutation importance computed using out-of-bag samples during training
- SHAP values for individual feature contribution analysis
- Variance threshold filtering: Remove features with < 0.01 variance across training set
- Correlation-based filtering: Remove highly correlated features (|Pearson| > 0.95)
import numpy as np
from sklearn.feature_selection import SelectKBest, f_classif
class FeatureSelector:
def __init__(self, k=100):
self.k = k
self.selector = SelectKBest(score_func=f_classif, k=k)
def fit_transform(self, X, y):
# X shape: (n_samples, n_features)
return self.selector.fit_transform(X, y)
def get_support_mask(self):
return self.selector.get_support()
Feature Engineering Pipeline
class FeatureEngineeringPipeline:
def __init__(self):
self.feature_store = {}
def compute_user_features(self, user_data):
# Real-time feature computation
features = {
'user_session_count': len(user_data['sessions']),
'user_avg_time_spent': np.mean(user_data['session_durations']),
'user_last_seen_hours': (datetime.now() - user_data['last_seen']).total_seconds() / 3600
}
return features
def compute_item_features(self, item_data):
# Batch feature computation
features = {
'item_popularity_score': self.calculate_item_popularity(item_data),
'item_category_encoded': self.encode_category(item_data['category']),
'item_avg_rating': np.mean(item_data['ratings'])
}
return features
def calculate_item_popularity(self, item_data):
# Simple popularity score based on recent interactions
recent_interactions = item_data['interactions'][-100:] # Last 100 interactions
return len(recent_interactions) / (1 + np.std(recent_interactions))
Feature Validation and Monitoring
- Feature drift detection: Using KL divergence between training and serving distributions
- Missing value monitoring: Alert when >5% of features have missing values in serving data
- Feature distribution shift: Monthly comparison between training and serving feature distributions
4. Model Architecture
Model selection with justification (logistic regression / GBM / DNN / Transformer — trade-offs)
For a recommendation system, I would select a Two-Tower DNN architecture as the primary model. This choice balances several key factors:
- Scalability: DNNs can scale to large embedding spaces and high-dimensional feature sets
- Expressiveness: Can capture complex user-item interactions through deep layers
- Interpretability: Unlike Transformers, DNNs provide more interpretable feature interactions
- Training efficiency: More efficient than Transformers for this use case with moderate data size
Logistic Regression is too simple to capture complex user preferences and item relationships.
GBM (XGBoost/LightGBM) offers good performance but lacks the embedding capabilities necessary for scalable recommendation systems.
Transformer models provide strong sequence modeling but introduce significant latency and complexity that may not be justified for a recommendation system with moderate data size.
Architecture diagram (Mermaid)
graph TD
A[User Features] --> B[User Tower]
C[Item Features] --> D[Item Tower]
B --> E[Merged Embeddings]
D --> E
E --> F[MLP Layers]
F --> G[Scoring Layer]
G --> H[Final Score]
Two-tower / multi-task / ensemble design if applicable
The system uses a two-tower architecture where:
- User tower processes user features (demographics, history, preferences)
- Item tower processes item features (content, category, metadata)
- Towers are trained jointly to maximize user-item interaction prediction
For multi-task learning, we can extend this to include:
- Click-through rate prediction (main task)
- Purchase probability (secondary task)
- Watch time (tertiary task)
Pre-trained model selection and fine-tuning strategy
For foundational capabilities, we can leverage:
- Pre-trained text embeddings (e.g., BERT) for item content features
- Pre-trained user embeddings from historical interaction data
Fine-tuning strategy:
- Freeze pre-trained embeddings during initial training
- Gradually unfreeze layers for fine-tuning on new data
- Use learning rate scheduling (e.g., cosine decay) for stable convergence
Model complexity vs latency trade-off analysis
| Model | Metric | Latency (p99) | Size |
|---|---|---|---|
| Logistic Regression | AUC | 1ms | 5KB |
| XGBoost | AUC | 5ms | 2MB |
| Two-Tower DNN | AUC | 15ms | 50MB |
| Transformer | AUC | 50ms | 200MB |
Offline evaluation baseline framing
Baseline: Logistic regression with user and item features
Proposed: Two-tower DNN with embeddings + MLP layers
Expected improvement: 15-20% in AUC over baseline
Core model code snippet
import torch
import torch.nn as nn
import torch.nn.functional as F
class TwoTowerDNN(nn.Module):
def __init__(self, user_emb_dim=128, item_emb_dim=128, hidden_dims=[256, 128, 64]):
super(TwoTowerDNN, self).__init__()
# User tower
self.user_tower = nn.Sequential(
nn.Linear(100, 256), # Assuming 100 user features
nn.ReLU(),
nn.Linear(256, user_emb_dim),
nn.ReLU()
)
# Item tower
self.item_tower = nn.Sequential(
nn.Linear(200, 256), # Assuming 200 item features
nn.ReLU(),
nn.Linear(256, item_emb_dim),
nn.ReLU()
)
# MLP for final scoring
self.mlp = nn.Sequential(
nn.Linear(user_emb_dim + item_emb_dim, hidden_dims[0]),
nn.ReLU(),
nn.Linear(hidden_dims[0], hidden_dims[1]),
nn.ReLU(),
nn.Linear(hidden_dims[1], hidden_dims[2]),
nn.ReLU(),
nn.Linear(hidden_dims[2], 1)
)
def forward(self, user_features, item_features):
# Get embeddings from towers
user_emb = self.user_tower(user_features)
item_emb = self.item_tower(item_features)
# Concatenate embeddings
combined = torch.cat([user_emb, item_emb], dim=1)
# Final scoring
score = self.mlp(combined)
return torch.sigmoid(score)
def compute_loss(self, user_features, item_features, labels):
predictions = self.forward(user_features, item_features).squeeze()
loss = F.binary_cross_entropy(predictions, labels.float())
return loss
# Example usage:
# model = TwoTowerDNN()
# user_feat = torch.randn(32, 100) # batch of 32 users
# item_feat = torch.randn(32, 200) # batch of 32 items
# labels = torch.randint(0, 2, (32,)) # batch of 32 binary labels
# loss = model.compute_loss(user_feat, item_feat, labels)
This implementation provides:
- Clear separation between user and item towers
- Modular architecture for easy extension
- Standard loss function for binary classification
- Memory-efficient forward pass with proper tensor operations
- Scalable embedding dimensions that can be tuned based on data size
5. Serving & Inference Architecture
System Diagram
graph TD
A[User Request] --> B[Load Balancer]
B --> C[Model Serving Gateway]
C --> D[Feature Store]
D --> E[Embedding Cache]
E --> F[Model Server]
F --> G[Response Formatter]
G --> H[Return Result]
subgraph Online Serving Stack
C
D
E
F
G
end
subgraph Feature Store
D
E
end
Online vs Batch Serving Decision
Online serving is required for real-time personalization use case with sub-200ms latency SLA. Batch serving is unsuitable due to:
- User-specific personalization needs
- Low-latency requirements (p99 < 200ms)
- Real-time feedback integration
- Interactive user experience demands
Two-Stage Pipeline
1. Candidate Retrieval (p50: 20ms, p99: 50ms)
- Approximate Nearest Neighbor (ANN) search
- Embedding-based similarity matching
- Top-K candidate selection
2. Scoring & Re-ranking (p50: 30ms, p99: 80ms)
- Full model scoring of candidates
- Re-ranking based on contextual features
- Final ranking for user presentation
Serving Infrastructure
Primary choice: TorchServe with custom inference pipeline
Justification:
- Native PyTorch model support
- Built-in model versioning and serving
- Easy integration with feature store
- Production-ready with monitoring hooks
- Support for model compression (quantization, pruning)
Alternative consideration: Triton Inference Server for multi-model serving but higher operational complexity.
Latency Budget Breakdown
| Stage | p50 | p99 | Budget |
|---|---|---|---|
| Feature Retrieval | 15ms | 30ms | 30ms |
| Model Inference | 25ms | 60ms | 60ms |
| Response Formatting | 5ms | 10ms | 10ms |
| Total | 45ms | 100ms | 100ms |
Model Compression Strategy
Quantization (INT8)
- 4x model size reduction
- <5% accuracy degradation for most models
- Enables faster inference on CPU/GPU
Pruning (structured)
- 25% parameter reduction
- Maintains ~95% of original accuracy
- Enables better memory access patterns
Distillation (student model)
- 80% size reduction
- 2-3x faster inference
- Requires retraining but maintains quality
Caching Strategy
Prediction Cache (LRU)
- 50% reduction in redundant requests
- TTL: 30 seconds for user-specific predictions
- Cache hit rate target: >70%
Embedding Cache (LRU)
- 30% reduction in embedding lookups
- TTL: 5 minutes for frequently accessed items
- Memory footprint: ~2GB for top 10K embeddings
KV Cache (for LLMs)
- For transformer models, cache attention keys/values
- Reduces recomputation in generation tasks
- Size: ~500MB for typical 12-layer model
Auto-scaling and Capacity Planning
Horizontal Scaling Strategy:
- Target QPS: 10,000 requests/sec
- Per-node capacity: 500 QPS (with caching)
- Target nodes: 20 nodes (10 primary + 10 backup)
- Auto-scaling based on QPS and latency
Capacity Planning:
Back-of-envelope calculation:
- 10,000 QPS / 500 QPS per node = 20 nodes
- 10% buffer for traffic spikes = 22 nodes
- 50ms p99 target requires 1.5x capacity for peak traffic
- Final capacity: 30 nodes (20 primary + 10 backup)
Health Monitoring:
- CPU utilization <85%
- Memory usage <80%
- Latency p99 < 200ms
- Error rate <0.1%
- Cache hit ratio >70%
Cold Start Handling
New User Fallback:
- Default ranking based on popularity
- Feature store fallback to default values
- Gradual transition to personalized model
New Item Fallback:
- Content-based similarity search
- Collaborative filtering with item popularity
- Hybrid approach combining multiple signals
Multi-region Serving Strategy
Primary Region: US-East (latency optimized) Secondary Region: EU-West (disaster recovery)
Cross-region failover:
- Health checks every 30 seconds
- Automatic traffic shifting on failure
- Synchronous model versioning across regions
Cross-region caching:
- Shared embedding cache between regions
- Regional prediction cache with TTL synchronization
6. Evaluation Framework
Offline evaluation methodology
For offline model evaluation, we employ a temporal cross-validation approach to ensure our model's performance is evaluated on data that reflects realistic future conditions. This methodology splits the dataset chronologically into training, validation, and test sets, with each fold maintaining temporal consistency to prevent data leakage. We use a 3-fold temporal split where:
- Fold 1: Train (2023Q1-Q3), Validate (2023Q4), Test (2024Q1)
- Fold 2: Train (2023Q2-Q4), Validate (2024Q1), Test (2024Q2)
- Fold 3: Train (2023Q3-2024Q2), Validate (2024Q3), Test (2024Q4)
This approach is preferred over random splits to preserve the temporal nature of user behavior and content evolution. We also implement time-based holdout validation where we train on 6 months of data and evaluate on the subsequent 3 months to simulate real-world deployment conditions.
Slice-based evaluation
We implement comprehensive slice-based evaluation to detect performance degradation across different user segments and contexts. Critical slices include:
| Slice Type | Definition | Rationale |
|---|---|---|
| User Demographics | Age groups (18-25, 26-35, 36-50), Gender | Different user behaviors and preferences |
| Geographic Regions | US, EU, APAC, LATAM | Regional content preferences and engagement patterns |
| Device Types | Mobile, Desktop, Tablet | Platform-specific interaction patterns |
| Content Categories | News, Sports, Entertainment, Education | Category-specific recommendation dynamics |
| User Engagement Level | New users (0-30 days), Active users (>30 days) | Cold start vs. established user behavior |
Each slice is evaluated using the same offline metrics to identify performance disparities that may indicate model drift or bias.
A/B test design
We implement a robust A/B testing framework for online model evaluation with the following specifications:
Randomization Unit: User ID (to prevent leakage between users)
Sample Size Calculation:
- Primary metric: CTR (assumed baseline ~2.5%, expected lift ~0.1%)
- Power: 80%
- Significance level: 5%
- Estimated sample size: ~120,000 users per variant (240,000 total)
Test Duration: 30 days to capture seasonal and weekly patterns
Primary Metric: Click-through rate (CTR) with confidence intervals and p-values for statistical significance
Secondary Metrics:
- Revenue per user (RPU)
- Session duration
- Content engagement rate
Guardrail Metrics:
- User satisfaction scores (if available)
- Model confidence distribution shifts
- Feature drift indicators
Guardrail metrics and blocking conditions for launch
To ensure safe deployment, we define the following guardrail thresholds:
| Metric | Threshold | Action |
|---|---|---|
| CTR Drop | >5% from baseline | Block launch |
| Revenue Drop | >3% from baseline | Block launch |
| Model Confidence Variance | >20% increase in low-confidence predictions | Block launch |
| Feature Drift | PSI > 0.1 for >5% of features | Block launch |
| User Satisfaction | <4.0 average rating (if available) | Block launch |
Shadow mode rollout plan before full A/B test
We implement a shadow mode rollout where 10% of traffic is routed to the new model while maintaining the old model's serving. Shadow mode allows us to:
- Validate model predictions in real-time without affecting user experience
- Collect prediction logs for offline analysis
- Compare model outputs with the baseline model to detect discrepancies
- Monitor feature drift in real-time using shadow logs
Long-term metric measurement
We track delayed conversion and engagement decay metrics to understand the model's long-term impact:
- Delayed Conversion: Measure if users who clicked on recommendations later convert to paid content or make purchases within 7-30 days
- Engagement Decay: Track if users who received recommendations show declining engagement over time (e.g., 30-day session count, content consumption)
- User Retention: Monitor if users who received model recommendations show improved retention rates (e.g., 30-day and 90-day retention)
These metrics are collected with a 30-day lookback window to ensure sufficient signal for reliable measurement.
# Example offline evaluation script with slice-based validation
import pandas as pd
from sklearn.metrics import roc_auc_score, precision_score, recall_score
import numpy as np
def evaluate_model_slice(df_slice, y_true_col, y_pred_col):
"""
Evaluate model performance on a specific slice of data
"""
auc = roc_auc_score(df_slice[y_true_col], df_slice[y_pred_col])
precision = precision_score(df_slice[y_true_col], (df_slice[y_pred_col] > 0.5).astype(int))
recall = recall_score(df_slice[y_true_col], (df_slice[y_pred_col] > 0.5).astype(int))
return {
'auc': auc,
'precision': precision,
'recall': recall
}
def temporal_evaluation(df, time_col, target_col, prediction_col, splits=3):
"""
Perform temporal cross-validation evaluation
"""
df_sorted = df.sort_values(time_col)
n = len(df_sorted) // splits
results = []
for i in range(splits):
start_idx = i * n
end_idx = (i + 1) * n
# Validation set is the next period
val_start = end_idx
val_end = min(val_start + n, len(df_sorted))
# Test set is the period after validation
test_start = val_end
test_end = min(test_start + n, len(df_sorted))
if test_end <= test_start:
continue
val_df = df_sorted.iloc[val_start:val_end]
test_df = df_sorted.iloc[test_start:test_end]
# Evaluate on validation set
val_metrics = evaluate_model_slice(val_df, target_col, prediction_col)
# Evaluate on test set
test_metrics = evaluate_model_slice(test_df, target_col, prediction_col)
results.append({
'fold': i + 1,
'val_auc': val_metrics['auc'],
'test_auc': test_metrics['auc'],
'val_precision': val_metrics['precision'],
'test_precision': test_metrics['precision']
})
return pd.DataFrame(results)
7. Training Pipeline
Training Data Pipeline
The training pipeline begins with data ingestion from multiple sources including user interaction logs, item metadata, and external datasets. Data flows through a multi-stage ETL process:
- Raw Data Ingestion: Streaming from Kafka topics for real-time logs, batch loads from S3 for structured datasets
- Preprocessing: Data cleaning, feature extraction, and label generation using PySpark for distributed processing
- Shuffling and Batching: Random shuffling with reservoir sampling to handle data skew, batched processing for GPU/TPU efficiency
- Data Validation: Schema validation and quality checks using Great Expectations
import pyspark.sql.functions as F
from pyspark.sql.types import *
from pyspark.ml.feature import VectorAssembler
def preprocess_training_data(spark_df):
# Remove duplicates and handle missing values
cleaned_df = spark_df.dropDuplicates().na.fill(0)
# Feature engineering
feature_cols = ['user_age', 'item_category', 'engagement_time']
assembler = VectorAssembler(inputCols=feature_cols, outputCol="features")
# Label generation (e.g., click-through rate)
cleaned_df = cleaned_df.withColumn(
"label",
F.when(F.col("clicked") == 1, 1.0).otherwise(0.0)
)
return assembler.transform(cleaned_df)
def create_training_batches(df, batch_size=10000):
"""Create batches for distributed training"""
total_rows = df.count()
num_batches = (total_rows + batch_size - 1) // batch_size
batches = []
for i in range(num_batches):
batch_df = df.limit(batch_size).offset(i * batch_size)
batches.append(batch_df)
return batches
Distributed Training Strategy
For large-scale training, we implement data parallelism with model parallelism for transformer architectures:
- Data Parallelism: Each GPU processes a different batch of data, with gradient averaging across devices
- Model Parallelism: For large transformer models, we split the model across multiple GPUs using PyTorch's
DistributedDataParallel - Pipeline Parallelism: For very large models, we use pipeline parallelism to overlap computation and communication
import torch
import torch.nn as nn
from torch.nn.parallel import DistributedDataParallel as DDP
class DistributedTrainingManager:
def __init__(self, model, device_ids):
self.model = DDP(model, device_ids=device_ids)
self.optimizer = torch.optim.AdamW(self.model.parameters(), lr=1e-4)
def train_step(self, batch_data):
# Forward pass
outputs = self.model(batch_data['features'])
# Loss calculation
loss_fn = nn.BCEWithLogitsLoss()
loss = loss_fn(outputs, batch_data['labels'])
# Backward pass
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
return loss.item()
Training Infrastructure
- Compute Cluster: 16x A100 GPUs (40GB VRAM each) in a multi-node setup
- Training Time Estimate: 24 hours for full dataset with 80M samples and 500 features
- Memory Requirements: ~32GB per GPU for model + features + gradients
Hyperparameter Tuning Approach
We use Bayesian optimization with Optuna for efficient hyperparameter search:
import optuna
from sklearn.model_selection import cross_val_score
def objective(trial):
# Suggest hyperparameters
lr = trial.suggest_float('lr', 1e-5, 1e-2, log=True)
batch_size = trial.suggest_categorical('batch_size', [32, 64, 128, 256])
dropout_rate = trial.suggest_float('dropout_rate', 0.1, 0.5)
# Model instantiation with suggested params
model = MyMLModel(
learning_rate=lr,
batch_size=batch_size,
dropout_rate=dropout_rate
)
# Evaluate using cross-validation
scores = cross_val_score(model, X_train, y_train, cv=5, scoring='roc_auc')
return scores.mean()
# Run optimization
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=50)
Experiment Tracking and Reproducibility
We use MLflow for experiment tracking:
import mlflow
import mlflow.pytorch
def log_experiment_config(model_config, training_params):
with mlflow.start_run():
# Log parameters
mlflow.log_params(model_config)
mlflow.log_params(training_params)
# Log model
mlflow.pytorch.log_model(model, "model")
# Log metrics
mlflow.log_metric("train_auc", train_auc)
mlflow.log_metric("val_auc", val_auc)
Model Versioning and Registry Strategy
- Version Control: Git-based model versioning with semantic versioning (v1.0.0, v1.1.0)
- Model Registry: MLflow model registry with stages (Staging → Production)
- Tagging Strategy: Version tags include release date, feature set, and performance metrics
Continual / Online Learning Design
For real-time adaptation to concept drift:
class OnlineLearningManager:
def __init__(self, model, learning_rate=1e-4):
self.model = model
self.learning_rate = learning_rate
self.update_threshold = 0.05 # 5% performance drop triggers update
def adaptive_retraining(self, new_data_stream):
"""Retrain model when performance degrades"""
recent_performance = self.evaluate_model(new_data_stream)
if recent_performance < self.base_performance * (1 - self.update_threshold):
print("Performance degradation detected, retraining model...")
self.retrain_model(new_data_stream)
def retrain_model(self, new_data):
# Implement incremental training or full retraining based on drift magnitude
pass
Data Pipeline Monitoring and Validation
We implement continuous monitoring of training data quality:
def validate_training_data(data):
"""Validate training data for drift and quality issues"""
# Check for data distribution shifts
current_dist = calculate_distribution(data)
baseline_dist = load_baseline_distribution()
drift_score = calculate_drift_score(current_dist, baseline_dist)
if drift_score > 0.1: # Threshold for significant drift
raise DataDriftException("Training data distribution has drifted significantly")
# Validate feature ranges and missing values
feature_stats = get_feature_statistics(data)
validate_feature_ranges(feature_stats)
return True
Training Pipeline Orchestration
Using Apache Airflow for pipeline orchestration:
from airflow import DAG
from airflow.operators.python_operator import PythonOperator
from datetime import datetime, timedelta
def run_training_pipeline():
# Load and preprocess data
processed_data = preprocess_training_data(raw_data)
# Train model with distributed computing
trained_model = train_distributed_model(processed_data)
# Validate and log results
validate_and_log_results(trained_model)
return trained_model
dag = DAG(
'ml_training_pipeline',
default_args={
'owner': 'ml-engineer',
'retries': 3,
'retry_delay': timedelta(minutes=5)
},
schedule_interval='@daily',
start_date=datetime(2023, 1, 1)
)
train_task = PythonOperator(
task_id='train_model',
python_callable=run_training_pipeline,
dag=dag
)
Training Data Freshness and Retraining Triggers
- Scheduled Retraining: Weekly full retraining with updated data
- Drift-Based Retraining: Triggered when data drift exceeds 10% threshold
- Performance Degradation: Retraining when validation AUC drops below 0.85
Data Quality Assurance
We implement quality checks at every stage:
- Schema Validation: Ensuring consistent column structure across batches
- Distribution Monitoring: Tracking feature distributions over time
- Label Consistency Checks: Verifying label quality and consistency
- Sample Quality Validation: Random sampling validation of training samples
This pipeline ensures robust, scalable, and reproducible model training with proper monitoring and quality control mechanisms.
8. Monitoring & Observability
Data Drift Detection
Data drift detection is critical for maintaining model performance in production. We implement drift detection using statistical tests and distribution-based metrics:
- PSI (Population Stability Index) for categorical features with threshold of 0.1 for alerting
- KL Divergence for continuous features with threshold of 0.05 for alerting
- Chi-Square Test for categorical feature drift with p-value < 0.05 as threshold
import numpy as np
from scipy import stats
from sklearn.metrics import mutual_info_score
class DriftDetector:
def __init__(self, threshold=0.1):
self.threshold = threshold
def psi_drift(self, old_dist, new_dist):
"""Calculate PSI for categorical features"""
psi = 0
for i in range(len(old_dist)):
if old_dist[i] != 0 and new_dist[i] != 0:
psi += (new_dist[i] - old_dist[i]) * np.log(new_dist[i] / old_dist[i])
return psi
def kl_divergence(self, p, q):
"""Calculate KL divergence between two distributions"""
return np.sum(p * np.log((p + 1e-8) / (q + 1e-8)))
Prediction Drift and Label Drift Monitoring
Prediction drift monitors changes in model outputs over time, while label drift monitors actual label distribution shifts:
class PredictionDriftMonitor:
def __init__(self, alert_threshold=0.15):
self.alert_threshold = alert_threshold
self.prediction_history = []
def detect_prediction_drift(self, current_predictions, reference_predictions):
"""Detect drift in model predictions"""
drift_score = np.mean(np.abs(current_predictions - reference_predictions))
return drift_score > self.alert_threshold
def detect_label_drift(self, current_labels, reference_labels):
"""Detect drift in label distribution"""
# Chi-square test for label distribution change
_, p_value, _, _ = stats.chi2_contingency([current_labels, reference_labels])
return p_value < 0.05
Model Performance Degradation Alerting
Performance degradation is monitored through key metrics with configurable thresholds:
class PerformanceMonitor:
def __init__(self, metrics_config):
self.metrics_config = metrics_config
self.alert_history = {}
def evaluate_performance(self, predictions, labels):
"""Evaluate performance and trigger alerts"""
results = {}
for metric_name, thresholds in self.metrics_config.items():
if metric_name == 'auc':
auc = roc_auc_score(labels, predictions)
results['auc'] = auc
if auc < thresholds['alert']:
self.trigger_alert('auc_degradation', auc)
elif metric_name == 'precision':
precision = precision_score(labels, predictions > 0.5)
results['precision'] = precision
if precision < thresholds['alert']:
self.trigger_alert('precision_degradation', precision)
def trigger_alert(self, alert_type, metric_value):
"""Trigger alert for performance degradation"""
timestamp = datetime.now()
self.alert_history[alert_type] = {
'timestamp': timestamp,
'value': metric_value,
'severity': self.calculate_severity(metric_value)
}
# Log to monitoring system
logging.warning(f"Performance degradation alert: {alert_type} = {metric_value}")
Retraining Trigger Strategy
Automated retraining triggers based on drift detection and performance thresholds:
class RetrainingTrigger:
def __init__(self, drift_threshold=0.1, performance_threshold=0.05):
self.drift_threshold = drift_threshold
self.performance_threshold = performance_threshold
def should_retrain(self, drift_metrics, performance_metrics):
"""Determine if model should be retrained"""
drift_detected = any(
drift > self.drift_threshold for drift in drift_metrics.values()
)
performance_degraded = any(
perf < (1 - self.performance_threshold) for perf in performance_metrics.values()
)
return drift_detected or performance_degraded
Logging Design
Comprehensive logging for model inputs, outputs, and metadata:
class ModelLogger:
def __init__(self):
self.prediction_logger = logging.getLogger('prediction_logs')
self.feature_logger = logging.getLogger('feature_logs')
def log_prediction(self, user_id, item_id, features, prediction, timestamp):
"""Log prediction with context"""
log_entry = {
'user_id': user_id,
'item_id': item_id,
'features': features,
'prediction': prediction,
'timestamp': timestamp,
'model_version': self.current_model_version
}
self.prediction_logger.info(json.dumps(log_entry))
def log_feature_update(self, feature_name, value, timestamp):
"""Log feature updates"""
log_entry = {
'feature_name': feature_name,
'value': value,
'timestamp': timestamp
}
self.feature_logger.info(json.dumps(log_entry))
Feedback Loop for Continuous Improvement
Feedback collection and integration pipeline:
class FeedbackCollector:
def __init__(self):
self.feedback_queue = queue.Queue()
def collect_feedback(self, user_id, item_id, feedback_type, timestamp):
"""Collect user feedback for model improvement"""
feedback = {
'user_id': user_id,
'item_id': item_id,
'feedback_type': feedback_type,
'timestamp': timestamp
}
self.feedback_queue.put(feedback)
def process_feedback(self):
"""Process feedback and update training data"""
feedback_batch = []
while not self.feedback_queue.empty() and len(feedback_batch) < 1000:
feedback_batch.append(self.feedback_queue.get())
# Update training dataset with new feedback
self.update_training_data(feedback_batch)
Alerting Configuration
Configurable alert thresholds and escalation policies:
class AlertManager:
def __init__(self):
self.alert_configs = {
'drift_alert': {
'threshold': 0.1,
'escalation_level': 'medium',
'notification_channels': ['email', 'slack']
},
'performance_alert': {
'threshold': 0.05,
'escalation_level': 'high',
'notification_channels': ['email', 'slack', 'pagerduty']
}
}
def send_alert(self, alert_type, severity, details):
"""Send alert based on configuration"""
config = self.alert_configs[alert_type]
if severity >= self.get_severity_threshold(config['escalation_level']):
for channel in config['notification_channels']:
self.send_notification(channel, alert_type, details)
Key Metrics and Thresholds
| Metric | Alert Threshold | Warning Threshold | Monitoring Frequency |
|---|---|---|---|
| AUC-ROC | < 0.85 | < 0.90 | Every hour |
| Precision@K | < 0.75 | < 0.85 | Every hour |
| NDCG | < 0.70 | < 0.80 | Every hour |
| PSI Drift | > 0.10 | > 0.05 | Every day |
| KL Divergence | > 0.05 | > 0.025 | Every day |
| Model Accuracy | < 0.80 | < 0.90 | Every hour |
Back-of-Envelope Calculations
For a production system with:
- 1M daily active users
- 100K items in catalog
- 500M training samples
- 200M features
Expected drift detection overhead:
- Drift monitoring: ~50ms per prediction batch
- Alert generation: ~20ms per alert trigger
- Storage requirements: ~5GB daily for logs
Monitoring Dashboard Components
- Real-time Performance Dashboard - Shows current AUC, Precision, NDCG trends
- Drift Detection Dashboard - Displays PSI and KL divergence metrics per feature
- Alert History Dashboard - Tracks all triggered alerts with severity levels
- Feedback Integration Dashboard - Shows feedback volume and quality metrics
Alert Escalation Matrix
| Alert Type | Severity | Escalation Level | Response Time |
|---|---|---|---|
| Drift Detection | Medium | Medium | 2 hours |
| Performance Drop | High | High | 30 minutes |
| Critical Failure | Critical | Emergency | Immediate |
| Data Quality Issues | Medium | Medium | 4 hours |
Integration with Existing Systems
The monitoring system integrates with:
- Prometheus/Grafana for time-series metric visualization
- Slack/Email for alert notifications
- MLflow for model version tracking
- Data warehouse for historical drift analysis
This comprehensive monitoring framework ensures early detection of performance degradation, enables rapid response to model drift, and supports continuous model improvement through feedback integration.
9. Responsible AI & Safety
Bias Analysis
- Training data audit: Conduct comprehensive audits to identify demographic imbalances in user/item representations, particularly focusing on underrepresented groups (e.g., age, gender, geographic regions). Use statistical tests like chi-square to detect significant disparities in representation.
- Slice-based fairness evaluation: Evaluate model performance across user segments (e.g., by region, device type, age groups) to detect potential bias in predictions. Monitor for disparate impact using metrics like demographic parity and equalized odds.
- Fairness constraints: Implement fairness-aware training techniques (e.g., adversarial debiasing or reweighting) when bias is detected above threshold (e.g., >5% difference in AUC between slices).
Fairness Metrics and Constraints
| Metric | Definition | Acceptable Threshold | Monitoring Frequency |
|---|---|---|---|
| Demographic Parity | P(Ŷ=1 | A=0) = P(Ŷ=1 | A=1) |
| Equalized Odds | P(Ŷ=1 | A=0, Y=1) = P(Ŷ=1 | A=1, Y=1) |
| Calibration | E[Y | Ŷ] = E[Y] | ±10% deviation |
Explainability Requirements and Approach
- SHAP values: Deploy SHAP-based explainability for critical user segments to understand feature importance in model decisions.
- Attention visualization: For transformer-based models, visualize attention weights to ensure relevance of contextual features (e.g., user history, item metadata).
- Stakeholder-driven approach: Provide model explanations to business users and compliance teams in accessible formats (e.g., feature impact heatmaps, decision trees).
For LLM Systems: Safety Guardrails
- Content filtering: Implement real-time content safety filters to block harmful outputs (e.g., hate speech, misinformation) using pre-trained toxicity classifiers.
- Output validation: Validate generated content against factual databases and safety policies (e.g., checking for hallucinations using knowledge grounding).
- Rate limiting: Enforce rate limits per user to prevent abuse and ensure system stability.
- Red teaming: Conduct adversarial testing with human red teams to identify edge cases and safety gaps in model behavior.
Model Card Documentation Plan
- Document structure:
- Model overview (task, architecture, training data)
- Performance metrics and evaluation results
- Fairness and bias assessment findings
- Limitations and known issues
- Usage guidelines and safety recommendations
Incident Response Plan for Model Failures or Harmful Outputs
- Failure detection: Alert thresholds (e.g., >10% drop in precision, >5% increase in toxicity scores) trigger incident response.
- Escalation process:
- P0: Immediate model rollback and investigation for safety-critical issues
- P1: Model retraining with updated data or fine-tuning for performance degradation
- P2: Feature drift investigation and model retraining for minor issues
- Communication protocol: Notify stakeholders (product, compliance, engineering) with impact assessment and mitigation timeline.
# Example: Toxicity score monitoring for LLM outputs
import numpy as np
def detect_toxicity(output_text, toxicity_threshold=0.8):
"""
Simple toxicity classifier for LLM outputs (mock implementation)
In production, use a pre-trained toxicity model like ToxicBert or Perspective API
"""
# Simulated toxicity score (0.0 to 1.0)
toxicity_score = np.random.rand() # Replace with actual model inference
if toxicity_score > toxicity_threshold:
return True, toxicity_score
return False, toxicity_score
# Example usage in serving pipeline
def validate_output_safety(model_output):
is_toxic, score = detect_toxicity(model_output)
if is_toxic:
# Log incident and return safe fallback
print(f"Detected toxic output with score {score}")
return "This content has been flagged for safety reasons."
return model_output
10. Scalability & Reliability
Bottleneck Analysis per Pipeline Stage
| Stage | Bottleneck Type | Back-of-Envelope Estimate | Mitigation Strategy |
|---|---|---|---|
| Data Ingestion | I/O throughput | 10GB/s peak, 200K QPS | Use distributed file systems (e.g., S3 with multiple readers) |
| Feature Computation | CPU/memory | 50GB feature store, 10ms per query | Implement async batch processing with caching |
| Model Serving | Latency | 50ms p99, 100K QPS | Use model compression and multi-stage serving |
| Model Training | GPU memory | 100GB training data, 200M samples | Use distributed training with gradient compression |
Scale-up Strategy
Horizontal Scaling of Serving:
- Deploy model replicas across multiple availability zones
- Use Kubernetes with HPA (Horizontal Pod Autoscaler) to scale based on QPS
- Implement consistent hashing for model version distribution
Distributed Feature Computation:
- Partition feature store by user/item ID ranges
- Use Redis or Memcached for fast real-time feature lookups
- Implement asynchronous feature computation with Kafka streams
Fault Tolerance
Model Serving Fallbacks:
- Implement circuit breaker pattern for model failures
- Maintain warm standby models with same version
- Fallback to rule-based or ensemble model on timeout
Stale Model Serving:
- Implement model versioning with graceful degradation
- Use model version health checks (latency, accuracy)
- Serve last known good model during retraining window
Cold Start Handling Strategy
New Users:
- Use content-based filtering with user demographics
- Implement collaborative filtering with minimal user history
- Fallback to popular items or recent trends
New Items:
- Use item content embeddings for similarity matching
- Implement candidate generation from related items
- Use hybrid approach with rule-based fallback
Multi-region Serving Strategy
Global Distribution:
Region A (Primary) → Region B (Secondary) → Region C (Tertiary)
↓ ↓ ↓
Model A Model B Model C
(Hot) (Warm) (Cold)
Traffic Splitting:
- 80% primary region, 15% secondary, 5% tertiary
- Use weighted round-robin or consistent hashing for distribution
- Implement failover logic with health checks every 5 seconds
Cross-region Model Synchronization:
- Use distributed model registry (e.g., MLflow or custom)
- Implement model version consistency checks
- Use asynchronous model update with conflict resolution
class ModelServingManager:
def __init__(self, model_registry):
self.model_registry = model_registry
self.fallback_model = self.load_fallback_model()
self.health_check_interval = 5 # seconds
def get_model_with_fallback(self, model_version):
try:
model = self.model_registry.get_model(model_version)
if self.is_model_healthy(model):
return model
else:
# Fallback to last known good model or rule-based
return self.fallback_model
except Exception as e:
logger.warning(f"Model {model_version} failed, using fallback: {e}")
return self.fallback_model
def is_model_healthy(self, model):
# Check latency and accuracy thresholds
return (model.avg_latency < self.max_latency and
model.accuracy > self.min_accuracy)
Reliability Patterns
Circuit Breaker Implementation:
class CircuitBreaker:
def __init__(self, failure_threshold=5, timeout=60):
self.failure_threshold = failure_threshold
self.timeout = timeout
self.failure_count = 0
self.last_failure_time = None
self.state = "CLOSED" # CLOSED, OPEN, HALF_OPEN
def call(self, func, *args, **kwargs):
if self.state == "OPEN":
if time.time() - self.last_failure_time > self.timeout:
self.state = "HALF_OPEN"
else:
raise Exception("Circuit breaker is OPEN")
try:
result = func(*args, **kwargs)
self.success()
return result
except Exception as e:
self.failure()
raise e
def success(self):
self.failure_count = 0
self.state = "CLOSED"
def failure(self):
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "OPEN"
Graceful Degradation:
- When model accuracy drops below threshold, switch to ensemble model
- When latency exceeds budget, return cached results or simplified features
- Implement feature fallbacks for unavailable real-time features
Service Level Agreement (SLA) Targets:
- 99.9% availability for model serving
- <50ms p99 latency for inference requests
- <1% degradation in model accuracy over 30-day period
11. Open Questions, Risks & Trade-offs
Decisions requiring product / engineering / policy alignment
- Alerting threshold calibration — how to balance between false positives (unnecessary alerts) and false negatives (missed degradation) requires cross-functional alignment on risk tolerance
- Alert escalation policy — who gets notified when degradation is detected (ML team, product team, SRE) and what actions are triggered
- Grace period for degradation — whether to alert immediately on metric drop or wait for sustained degradation (e.g., 3-day window) to avoid noise from temporary spikes
- Re-training vs alerting — when to trigger model retraining vs just alerting for manual review (e.g., if degradation is within acceptable bounds but still concerning)
Key ML risks
| Risk | Impact | Likelihood | Mitigation |
|---|---|---|---|
| Data drift in training vs serving | High — model performance degrades rapidly | Medium | Implement feature drift detection, use online training triggers |
| Label shift | High — model trained on one distribution but serving another | Medium | Monitor label drift, implement feedback loop for retraining |
| Prediction drift | Medium — model outputs change in distribution | High | Monitor prediction distributions, use statistical tests |
| Model aging | Medium — performance degrades over time without retraining | High | Implement scheduled retraining, drift-based triggers |
| Spike in prediction variance | Medium — model becomes unreliable for some inputs | Low-Medium | Monitor prediction confidence, implement fallback logic |
Trade-offs made with justification
| Trade-off | Justification |
|---|---|
| Accuracy vs latency | Prioritizing low-latency inference (e.g., <10ms) over marginal accuracy gains (e.g., 2-3% AUC improvement) to meet user experience requirements |
| Freshness vs cost | Accepting slightly delayed model updates (e.g., daily instead of hourly) to reduce infrastructure and data processing costs |
| Model size vs quality | Using model compression (quantization, pruning) to reduce serving cost and latency at the expense of some model capacity |
| Alerting sensitivity vs noise | Using statistical thresholds (e.g., 3-sigma) rather than absolute thresholds to reduce false positives while maintaining sensitivity |
| Offline vs online monitoring | Prioritizing real-time alerting over batch-based drift detection for faster degradation response |
Alternatives considered and rejected
| Alternative | Reason |
|---|---|
| Static thresholds | Too brittle — don't adapt to changing data distributions or business conditions |
| Simple mean-based drift detection | Not robust to outliers and doesn't capture complex distribution changes |
| Manual alerting dashboard | Inefficient for large-scale systems, prone to human oversight and delayed response |
| Single metric alerting | Over-simplified — degradation can occur in one metric but not others, missing nuanced issues |
| No alerting at all | High risk of undetected model degradation leading to poor user experience and business impact |
Technical debt acknowledged
- Tight coupling between monitoring logic and model architecture — monitoring code assumes specific model output formats, making refactoring harder
- Inconsistent metric definitions across systems — some metrics are computed in batch, others in real-time, causing discrepancies in alerting logic
- Limited historical context in drift detection — current implementation only looks at recent windows, missing longer-term trends
- No built-in model version rollback capability — alerts can trigger retraining but don't automatically revert to previous working versions
- Hardcoded alerting thresholds — thresholds are not dynamically adjusted based on historical performance or business context
12. Phased Implementation Roadmap
Phase 1: MVP (Baseline System)
Timeline: 4-6 weeks
Deliverables:
- Rule-based baseline alerting for basic metric thresholds (e.g., AUC drop > 5%)
- Basic logging infrastructure for prediction and feature data
- Simple dashboard with basic metric visualization (Grafana/Custom UI)
- Manual retraining trigger mechanism
Dependencies:
- Data pipeline and feature store operational
- Basic model serving stack deployed
- Monitoring infrastructure (Prometheus/Grafana) provisioned
Phase 2: Scale & Automation
Timeline: 6-8 weeks
Deliverables:
- Automated drift detection for key features (PSI, KL divergence)
- Prediction drift monitoring with statistical tests (Kolmogorov-Smirnov)
- Alerting system with escalation paths (Slack/Email notifications)
- Retraining trigger logic based on drift thresholds
- Shadow mode A/B testing infrastructure
Dependencies:
- Feature store with historical data access
- Data quality monitoring pipeline operational
- ML experiment tracking system in place
Phase 3: Advanced Monitoring & Intelligence
Timeline: 8-12 weeks
Deliverables:
- Model performance degradation detection with ML-based drift detection (Isolation Forest, Autoencoders)
- Slice-based alerting for user segments and regions
- Automated retraining pipeline with version control
- Online A/B testing framework with proper randomization and sample size calculation
- Incident response playbook integration with alerting system
Dependencies:
- Full ML pipeline operational (training, serving, monitoring)
- Production-grade model registry and versioning system
- Cross-functional teams aligned on alert response protocols
Critical Path & Dependencies:
- Week 1-2: Setup logging and basic dashboard infrastructure
- Week 3-4: Implement rule-based alerting and drift detection logic
- Week 5-6: Integrate with model serving stack and alerting system
- Week 7-8: Deploy shadow mode A/B testing and retraining triggers
- Week 9-12: Advanced monitoring, slice-based alerts, and incident response integration
Risk Mitigation:
- Data Pipeline Failure: Implement data quality checks at each stage
- False Positive Alerts: Establish confidence thresholds and manual override capability
- Alert Fatigue: Implement alert prioritization and suppression logic for known issues