Skip to main content

Scalable Image Classification Pipeline Architecture

Comprehensive design document for building production-ready image classification systems with distributed training, real-time inference, and automated model management.

This document outlines the complete architecture for a scalable image classification system that handles large-scale training, real-time inference, and automated model lifecycle management in production environments.

1. Problem Statement & ML Task Definition

Clarifying questions

  1. What is the target image domain (e.g., medical imaging, satellite imagery, consumer photos) and what are the expected class distribution?
  2. What is the required serving QPS and latency budget (e.g., <100ms p99 for real-time inference)?
  3. Is this a batch processing pipeline or real-time online serving use case?
  4. What is the acceptable trade-off between model accuracy and inference speed in production?
  5. What is the expected scale of training data (e.g., millions of images) and how frequently will new data arrive?
  6. What are the key business success metrics (e.g., revenue impact, user engagement)?
  7. How is model performance currently measured and what are the baseline expectations?

Business use case and success criteria

The system will classify images into predefined categories (e.g., product types, object classes) for automated content moderation, e-commerce categorization, or media organization. Success is measured by:

  • Accuracy: Top-1 classification accuracy (target: >90% on validation set)
  • Latency: <100ms p99 inference time for real-time serving
  • Throughput: 10K QPS capacity for batch processing and online serving
  • Business impact: Revenue increase from improved product categorization or reduced manual moderation costs

ML problem formulation

This is a multi-class image classification task. Justification:

  • Input: Images (224x224 RGB) → Output: Discrete class labels (e.g., 1000 ImageNet classes)
  • Predictive task: Assign each image to one of N predefined categories
  • Reasoning: The goal is to distinguish between discrete visual concepts, not rank or generate new content

ML evaluation metrics

Offline (training/validation):

  • AUC-ROC (multi-class) for overall model discrimination
  • Top-K accuracy (Top-1, Top-5) for classification performance
  • F1-score per class for balanced evaluation

Online (production):

  • CTR (click-through rate) for recommendation systems
  • Engagement rate (time spent viewing content)
  • Revenue impact from accurate categorization

Scale and constraints

Back-of-envelope estimates:

  • Training data size: 10M images (50GB raw, 200GB compressed)
  • Number of features: ~1M (image pixel intensities + extracted features)
  • Embedding matrix memory footprint: 20GB (for 1M features at 32-bit precision)
  • Serving QPS math: 10K QPS with 100ms p99 latency = 1GB/s bandwidth requirement

Constraints:

  • QPS: 10K (real-time serving)
  • Latency budget: <100ms p99
  • Data freshness: Daily updates for new training data
  • Model size: <500MB (for fast loading in production)

Non-ML baseline strategy

Rule-based fallback for:

  • Cold start: New user/item classification using basic heuristics (e.g., image size, color histogram)
  • Low confidence: When model confidence < 0.7, fallback to majority class or human review queue
  • Edge cases: Images with no clear visual features → use metadata-based classification (e.g., file type, timestamp)
  • Privacy: For sensitive content, use content filtering as first line of defense before ML classification

2. Data Strategy

Training data sources

  • Internal labeled dataset: High-quality human-annotated images from our product (e.g., user-uploaded content, moderation labels)
  • Public datasets: ImageNet, COCO, OpenImages for pretraining and transfer learning
  • Synthetic data: Generated via GANs or simulation to augment rare classes or create domain-specific variants
  • Third-party APIs: Commercial image labeling services for additional coverage of edge cases or underrepresented categories

Labeling pipeline design

  • Human annotation: For high-value, rare classes or when domain expertise is required (e.g., medical image classification). Managed via platforms like Labelbox or Amazon SageMaker Ground Truth.
  • Weak supervision: Using noisy heuristics, crowdsourced labels, or rule-based classifiers to generate pseudo-labels for large-scale training (e.g., using image metadata, OCR results, or object detection bounding boxes).
  • Programmatic labeling: For large-scale data generation where labels can be derived from existing systems or metadata (e.g., image size, color histogram, or content tags).

Class imbalance handling strategy with justification

  • Oversampling minority classes: Using SMOTE or random oversampling to balance training distribution.
  • Class-aware sampling: During training, weight each class inversely proportional to its frequency (e.g., class_weight = 1 / frequency).
  • Focal Loss: To reduce the impact of easy examples and focus training on hard-to-classify samples.
  • Justification: In real-world image classification, rare classes (e.g., specific disease types in medical imaging) are critical to detect but underrepresented, leading to poor performance if not addressed.

Negative sampling approach

  • Hard negative mining: Select most confidently misclassified examples from the validation set to retrain.
  • Random negative sampling: Sample from the entire dataset with probability inversely proportional to class frequency.
  • Negative class balancing: Ensure that each training batch contains a balanced mix of positive and negative examples for each class.

Data freshness and update frequency requirements

  • Freshness: Daily updates for new user-generated content, weekly for public datasets.
  • Update frequency: Every 24 hours for internal labeled data; every 7 days for third-party sources.
  • Data retention: Keep last 30 days of training data to support drift detection and model retraining.

Privacy compliance (GDPR, CCPA)

  • Data anonymization: Remove any PII (e.g., faces, license plates) using automated tools like OpenCV or commercial services.
  • Retention policy: Delete user data after 6 months of inactivity or upon explicit request.
  • Consent tracking: Log user consent for data usage, with opt-out mechanisms available.

Data quality validation and monitoring

  • Validation checks: Image size, format, resolution, and metadata consistency.
  • Monitoring: Detect corrupted images, duplicate entries, or labeling inconsistencies via hash-based deduplication and visual inspection.
  • Feedback loop: Flag mislabeled images for human review, with a dashboard for quality control.
def validate_image_metadata(image_path):
    try:
        # Basic checks for image validity
        with Image.open(image_path) as img:
            assert img.format in ['JPEG', 'PNG'], "Unsupported image format"
            assert img.size[0] > 0 and img.size[1] > 0, "Invalid image dimensions"
            assert img.mode in ['RGB', 'L'], "Unsupported color mode"
        return True
    except Exception as e:
        print(f"Validation failed for {image_path}: {e}")
        return False

3. Feature Engineering

Feature Taxonomy

Features are categorized into four main types:

  1. User Features: User demographics, behavior patterns, session data
  2. Item Features: Image metadata (size, format, color histogram), visual content features, category tags
  3. Context Features: Device type, geographic location, time of day, network conditions
  4. Interaction Features: User-item click history, view duration, engagement signals

Embedding Design

Entity Embeddings:

  • User ID embeddings (dimension: 128)
  • Category embeddings (dimension: 64)
  • Device type embeddings (dimension: 32)

Text Embeddings:

  • Image caption embeddings using CLIP or BERT (dimension: 512)
  • OCR text embeddings (dimension: 256)

Graph Embeddings:

  • User-item interaction graph embeddings using Node2Vec (dimension: 128)
  • Category hierarchy embeddings (dimension: 64)

Real-time vs Batch Feature Computation

Feature TypeComputation ModeJustification
Image pixel dataBatchHigh computational cost, computed once per image
User session featuresReal-timeRequired for live scoring
Category embeddingsBatchStatic, computed once per category
User behavior historyReal-timeDynamic, updated on each interaction

Feature Store Design

Online Store:

  • Low-latency key-value store (e.g., Redis or DynamoDB)
  • Optimized for sub-100ms read latency
  • Supports TTL and caching

Offline Store:

  • Data warehouse (e.g., Snowflake or BigQuery)
  • Used for training data preparation and batch feature generation
  • Supports complex joins and aggregations

Feature Freshness and TTL

FeatureSourceUpdate FrequencyStore Type
User sessionReal-time streamEvery 5 minutesOnline
Image metadataImage uploadOn image creationOffline
Category countDBHourlyOnline
User engagementAnalyticsDailyOffline

Cross Features and Feature Interaction Strategy

Cross features are generated for:

  • User-category interactions (user_id × category_id)
  • Device-category combinations
  • Temporal user features (e.g., user × time_of_day)

Cross features are computed during training and stored in feature store for serving.

Feature Importance and Selection Methodology

Methodology:

  • SHAP values from trained model for global feature importance
  • Permutation importance during training validation
  • Correlation analysis to detect redundant features

Selection Strategy:

  • Keep top 80% of features by SHAP importance
  • Remove features with correlation > 0.95
  • Retain domain-specific features (e.g., image size for content filtering)
import torch
import torch.nn as nn
import numpy as np

class FeatureExtractor(nn.Module):
    def __init__(self, image_dim=224, embedding_dim=512):
        super().__init__()
        # Simulated feature extraction from image
        self.conv_layers = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2),
            nn.Conv2d(64, 128, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2)
        )
        self.global_pool = nn.AdaptiveAvgPool2d((1, 1))
        self.embedding = nn.Linear(128, embedding_dim)
        
    def forward(self, x):
        features = self.conv_layers(x)
        features = self.global_pool(features).view(features.size(0), -1)
        return self.embedding(features)

class FeatureAggregator(nn.Module):
    def __init__(self, feature_dims=[512, 128, 64], embedding_dim=256):
        super().__init__()
        self.feature_extractors = nn.ModuleList([
            FeatureExtractor(embedding_dim=dim) 
            for dim in feature_dims
        ])
        self.combiner = nn.Linear(sum(feature_dims), embedding_dim)
        
    def forward(self, image_features, user_features, item_features):
        # Extract features from each modality
        img_emb = self.feature_extractors[0](image_features)
        user_emb = self.feature_extractors[1](user_features)
        item_emb = self.feature_extractors[2](item_features)
        
        # Combine features
        combined = torch.cat([img_emb, user_emb, item_emb], dim=1)
        return self.combiner(combined)

4. Model Architecture

Model Selection with Justification

For an image classification pipeline, I'll select a Vision Transformer (ViT) as the primary model architecture due to its superior performance on large-scale image classification tasks and strong transfer learning capabilities.

Justification:

  • Accuracy: ViT achieves state-of-the-art results on ImageNet and similar benchmarks, outperforming ResNet variants
  • Scalability: Can scale effectively to larger datasets and more complex classification tasks
  • Transfer Learning: Pre-trained ViT models (e.g., ViT-B/16) can be fine-tuned for domain-specific classification
  • Parallelization: Self-attention mechanisms enable efficient parallel processing on modern hardware

Alternatives considered and rejected:

  • ResNet: While robust, inferior accuracy compared to ViT on large-scale datasets
  • EfficientNet: Good efficiency but less scalable than ViT for enterprise use cases
  • CNN-only architectures: Limited by fixed receptive fields and less effective at capturing long-range dependencies

Architecture Diagram

graph TD
    A[Input Image] --> B[Patch Embedding]
    B --> C[Class Token + Positional Encoding]
    C --> D[Transformer Encoder Blocks]
    D --> E[Attention Heads]
    E --> F[MLP Layers]
    F --> G[Classification Head]
    G --> H[Output Classes]

Two-Tower / Multi-Task / Ensemble Design

For enterprise deployment, I'll implement a multi-task learning approach with:

  • Primary classification task (main image class)
  • Auxiliary tasks:
    • Object localization (bounding box regression)
    • Style classification (art period, artistic style)
  • Ensemble of ViT variants with different patch sizes (16x16, 32x32) for robustness

Pre-trained Model Selection and Fine-tuning Strategy

Pre-trained model: ViT-B/16 (ImageNet-21k pre-trained) Fine-tuning approach:

  1. Freeze backbone layers for first 50 epochs
  2. Gradual unfreezing of transformer blocks
  3. Lower learning rate (1e-5) for backbone, 1e-3 for classification head

Model Complexity vs Latency Trade-off Analysis

ModelParametersInference Time (ms)Memory UsageAccuracy (Top-1)
ViT-B/1686M45ms2GB80.3%
ViT-L/16307M85ms4GB82.8%
EfficientNet-B05M25ms1GB77.1%
ResNet-5025M35ms1.5GB76.5%

Offline Evaluation Baseline Framing

Baseline: ResNet-50 with same training data and preprocessing Expected improvement: 3-5% in top-1 accuracy for enterprise-scale dataset

Core Model Code Snippet

import torch
import torch.nn as nn
from torchvision import models

class VisionTransformer(nn.Module):
    def __init__(self, num_classes=1000, patch_size=16, embed_dim=768, num_heads=12, num_layers=12):
        super().__init__()
        self.patch_size = patch_size
        self.embed_dim = embed_dim
        
        # Patch embedding layer
        self.patch_embedding = nn.Conv2d(
            in_channels=3, 
            out_channels=embed_dim,
            kernel_size=patch_size,
            stride=patch_size
        )
        
        # Class token and positional embedding
        self.class_token = nn.Parameter(torch.randn(1, 1, embed_dim))
        self.positional_embedding = nn.Parameter(
            torch.randn(1, 197, embed_dim)  # 197 = (224/16)^2 + 1
        )
        
        # Transformer encoder blocks
        self.transformer_blocks = nn.ModuleList([
            nn.TransformerEncoderLayer(
                d_model=embed_dim,
                nhead=num_heads,
                dim_feedforward=embed_dim * 4,
                dropout=0.1,
                batch_first=True
            ) for _ in range(num_layers)
        ])
        
        # Classification head
        self.classifier = nn.Sequential(
            nn.LayerNorm(embed_dim),
            nn.Linear(embed_dim, num_classes)
        )
        
    def forward(self, x):
        # Patch embedding
        patches = self.patch_embedding(x)  # [B, embed_dim, H/P, W/P]
        patches = patches.flatten(2).transpose(1, 2)  # [B, N, embed_dim]
        
        # Add class token and positional encoding
        batch_size = patches.shape[0]
        class_tokens = self.class_token.expand(batch_size, -1, -1)
        embeddings = torch.cat([class_tokens, patches], dim=1)
        embeddings += self.positional_embedding
        
        # Transformer forward pass
        for block in self.transformer_blocks:
            embeddings = block(embeddings)
        
        # Classification
        class_output = embeddings[:, 0]  # Take class token
        return self.classifier(class_output)
    
    def calculate_loss(self, predictions, targets):
        """Cross entropy loss for multi-class classification"""
        criterion = nn.CrossEntropyLoss()
        return criterion(predictions, targets)

# Example usage
model = VisionTransformer(num_classes=1000)
dummy_input = torch.randn(1, 3, 224, 224)
output = model(dummy_input)
loss = model.calculate_loss(output, torch.tensor([1]))

This implementation provides a production-ready vision transformer with proper attention mechanisms, batch-first processing, and clean loss calculation for training. The model is designed to be easily extensible for multi-task learning scenarios and can be efficiently deployed in enterprise serving environments.

5. Serving & Inference Architecture

System Diagram

graph TD
    A[User Request] --> B[Load Balancer]
    B --> C[Model Router]
    C --> D[Embedding Cache]
    C --> E[Feature Store]
    D --> F[Model Server]
    E --> F
    F --> G[Scoring Engine]
    G --> H[Response Formatter]
    H --> I[User Response]
    
    subgraph Online Serving
        C
        F
        G
        H
    end

Online vs Batch Serving Decision

Online serving is required for real-time image classification with sub-200ms latency SLA. Batch serving would introduce 100-500ms delay due to queueing and processing overhead, which is unacceptable for user-facing applications. The system must support concurrent inference requests with p99 latency under 200ms.

Two-Stage Pipeline

1. Candidate Retrieval → 2. Scoring & Re-ranking
  • Stage 1 (Candidate Retrieval): Fast approximate matching using pre-computed image embeddings or keyword-based filtering
  • Stage 2 (Scoring & Re-ranking): Full model inference with top-K candidates for precise classification

Serving Infrastructure

TorchServe selected as primary serving framework due to:

  • Native PyTorch model support with automatic batching
  • Built-in metrics and monitoring capabilities
  • Easy integration with Kubernetes orchestration
  • Production-grade stability for large-scale deployment

Alternative: Triton Inference Server for multi-model serving with better GPU utilization across heterogeneous model types.

Latency Budget Breakdown

Stagep50 Latencyp99 LatencyBudget
Request Parsing1ms2ms5ms
Feature Retrieval5ms10ms15ms
Model Inference20ms40ms100ms
Response Formatting2ms5ms10ms
Total28ms57ms130ms

Model Compression Strategy

Quantization: INT8 quantization reduces model size by 4x with <2% accuracy drop for ResNet50 variants.

Pruning: Structured pruning removes 30% of parameters with minimal impact on classification accuracy.

Distillation: Knowledge distillation from ResNet50 to MobileNetV2 for 3x speedup with 1% accuracy loss.

Caching Strategy

Prediction Cache:

  • LRU cache for frequent image classes (top 100 classes)
  • TTL: 30 minutes for high-frequency predictions
  • Cache hit rate target: 60%+ for top 10 classes

Embedding Cache:

  • Pre-computed image embeddings stored in Redis
  • TTL: 24 hours for static features
  • Cache size: 10GB for 1M image embeddings

Auto-scaling and Capacity Planning

Horizontal Scaling:

  • Target: 1000 QPS with p99 < 200ms
  • Current estimate: 50 TorchServe instances (each serving ~20 QPS)
  • Auto-scaling: Kubernetes HPA with CPU/memory utilization thresholds

Capacity Planning:

  • Peak QPS: 1500 requests/sec (2x baseline)
  • Model memory footprint: ~2GB per instance
  • GPU utilization target: 70% for optimal throughput

Model Versioning and Rollout Strategy

  • Canary Rollout: 5% of traffic to new model versions
  • Blue-Green Deployment: Zero-downtime updates with rollback capability
  • Feature Flagging: A/B testing of model versions per user segment

Cold Start Handling

For new image classes or models:

  • Fallback to ensemble of pre-trained models (ResNet, EfficientNet)
  • Rule-based classification for common categories (e.g., "cat", "dog")
  • Gradual warm-up of model instances with historical data

Multi-Region Serving Strategy

For global deployment:

  • Regional model replicas in US-West, EU-Central, AP-Southeast
  • Cross-region failover with 500ms failover time budget
  • Geo-distributed feature store with regional caching layers

6. Evaluation Framework

Offline Evaluation Methodology

For offline evaluation, we use temporal cross-validation to ensure our model generalizes well across time. This approach splits data chronologically, with older data used for training and newer data for validation, preventing data leakage. We use 3-fold temporal splits with 60/20/20% train/validation/test ratios respectively. For each fold, we compute AUC-ROC, Precision@K, and F1-score to capture both overall performance and top-K ranking accuracy. We also evaluate NDCG@K for ranked outputs to ensure the model's ability to rank relevant images higher than irrelevant ones.

Slice-Based Evaluation

We define critical slices for evaluation:

  • User Demographics: Age groups (18-25, 26-35, 36-50), gender distribution
  • Geographic Regions: US, EU, APAC, LATAM (with sample size thresholds)
  • Device Types: Mobile, Desktop, Tablet
  • Image Categories: By domain (e.g., fashion, food, nature) with minimum sample size thresholds

We monitor performance degradation across these slices to detect potential bias or drift in specific user segments.

A/B Test Design

We implement a randomized controlled experiment with the following specifications:

  • Randomization Unit: User ID (to prevent leakage between users)
  • Sample Size Calculation: Using power analysis with 80% power and 5% significance level, we estimate ~25,000 users per variant for a 5% effect size detection
  • Duration: 2 weeks for stable signal, with 1-week warm-up period
  • Primary Metric: Image classification accuracy (top-1) with secondary metrics: AUC-ROC, F1-score, and user engagement rate (e.g., time spent on image results)

We implement shadow mode rollout where 10% of traffic is routed to the new model for initial validation before full A/B testing.

Guardrail Metrics and Blocking Conditions

To ensure safe deployment, we define guardrail thresholds:

  • Accuracy Drop: >2% drop in top-1 accuracy across any slice
  • Precision Drop: >3% drop in Precision@K for any category
  • Latency Spike: >50ms increase in p99 inference time
  • Error Rate: >1% increase in prediction errors

If any guardrail is breached, the model is automatically flagged for rollback or retraining.

Shadow Mode Rollout Plan

We implement shadow mode where 10% of real-time traffic is routed to the new model without affecting user experience. We log all predictions and compare them with the baseline model's outputs to validate performance consistency. If shadow results show >5% degradation in key metrics, we trigger a rollback or retraining cycle.

Long-Term Metric Measurement

We track delayed conversion and engagement decay metrics:

  • Delayed Conversion: Track whether users who interacted with model outputs later convert (e.g., make purchases or engage more deeply)
  • Engagement Decay: Monitor if user engagement with model outputs decreases over time (e.g., 30-day retention rate)
  • User Feedback Loop: Collect explicit feedback from users to identify model misclassifications or edge case failures

These metrics help ensure our model's long-term impact and prevent degradation in user experience over time.

# Sample offline evaluation code snippet
import numpy as np
from sklearn.metrics import accuracy_score, classification_report, roc_auc_score

def evaluate_model_offline(y_true, y_pred_proba, y_pred):
    """
    Offline evaluation of image classification model.
    
    Args:
        y_true: Ground truth labels (one-hot encoded or integer)
        y_pred_proba: Predicted probabilities for each class
        y_pred: Predicted class labels
    
    Returns:
        Dictionary of evaluation metrics
    """
    # Accuracy and classification report
    accuracy = accuracy_score(y_true, y_pred)
    
    # AUC-ROC (for binary classification or multi-class with averaging)
    if len(np.unique(y_true)) == 2:
        auc_roc = roc_auc_score(y_true, y_pred_proba[:, 1])
    else:
        auc_roc = roc_auc_score(y_true, y_pred_proba, multi_class='ovr', average='macro')
    
    # Precision@K for top-K predictions
    k = 3
    top_k_preds = np.argsort(y_pred_proba, axis=1)[:, -k:]
    precision_at_k = np.mean([
        1.0 if y_true[i] in top_k_preds[i] else 0.0
        for i in range(len(y_true))
    ])
    
    return {
        "accuracy": accuracy,
        "auc_roc": auc_roc,
        "precision_at_k": precision_at_k
    }

# Example usage:
# y_true = [0, 1, 2, 1, 0]
# y_pred_proba = np.array([
#     [0.8, 0.1, 0.1],
#     [0.2, 0.7, 0.1],
#     [0.1, 0.2, 0.7],
#     [0.3, 0.6, 0.1],
#     [0.9, 0.05, 0.05]
# ])
# y_pred = np.argmax(y_pred_proba, axis=1)
# metrics = evaluate_model_offline(y_true, y_pred_proba, y_pred)

7. Training Pipeline

Training Data Pipeline

The training pipeline begins with data ingestion from multiple sources including internal labeled datasets, public datasets (ImageNet, COCO), and synthetic data generation. Data preprocessing includes image resizing to 224x224 pixels, normalization using ImageNet statistics (mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), and data augmentation including random horizontal flip (p=0.5), random crop, and color jittering.

import torch
from torchvision import transforms
from torch.utils.data import DataLoader, Dataset

class ImageClassificationDataset(Dataset):
    def __init__(self, image_paths, labels, transform=None):
        self.image_paths = image_paths
        self.labels = labels
        self.transform = transform
    
    def __len__(self):
        return len(self.image_paths)
    
    def __getitem__(self, idx):
        image = Image.open(self.image_paths[idx]).convert('RGB')
        label = self.labels[idx]
        
        if self.transform:
            image = self.transform(image)
            
        return image, label

# Data augmentation pipeline
train_transform = transforms.Compose([
    transforms.RandomResizedCrop(224),
    transforms.RandomHorizontalFlip(p=0.5),
    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

val_transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

Distributed Training Strategy

For distributed training, we use data parallelism with PyTorch's DistributedDataParallel (DDP) to scale across multiple GPUs. The training job is configured with 8 GPUs per node, and we use a global batch size of 256 (32 samples per GPU). This configuration allows us to train efficiently while maintaining stable gradient updates.

Training Infrastructure

The training infrastructure is provisioned on a GPU cluster with 8x A100 80GB GPUs per node. With 4 nodes, we have 32 GPUs total. Based on our model size and batch configuration, training is estimated to take approximately 12 hours for 100 epochs. Each epoch processes ~500,000 images from our training dataset.

Hyperparameter Tuning Approach

We implement Bayesian optimization for hyperparameter tuning using Optuna. Key hyperparameters include:

  • Learning rate (0.001 to 0.01)
  • Batch size (16 to 64)
  • Weight decay (1e-5 to 1e-3)
  • Learning rate scheduler warmup steps (0 to 1000)

Experiment Tracking and Reproducibility

All experiments are tracked using MLflow with automatic logging of:

  • Model checkpoints (every 5 epochs)
  • Training metrics (loss, accuracy)
  • Hyperparameters
  • Model artifacts

Model Versioning and Registry Strategy

Model versions are managed through MLflow model registry with the following lifecycle:

  • Models are tagged as "staging" after successful validation
  • Only models passing A/B test criteria move to "production"
  • Versioned using semantic versioning (v1.0.0, v1.0.1, etc.)

Continual / Online Learning Design

For continual learning, we implement a sliding window approach where:

  • Every 30 days, we retrain on the latest 1M images from our data stream
  • We maintain a small online learning component that adapts to new image distributions
  • The model is updated with a 5% learning rate decay for the first 10 epochs of new data
# Example of continual learning update logic
def update_model_with_new_data(model, new_data_loader, num_epochs=10):
    optimizer = torch.optim.AdamW(model.parameters(), lr=0.0001, weight_decay=1e-4)
    scheduler = torch.optim.lr_scheduler.LinearLR(optimizer, start_factor=1.0, end_factor=0.95, total_iters=10)
    
    model.train()
    for epoch in range(num_epochs):
        for batch_idx, (data, target) in enumerate(new_data_loader):
            optimizer.zero_grad()
            output = model(data)
            loss = torch.nn.functional.cross_entropy(output, target)
            loss.backward()
            optimizer.step()
            scheduler.step()

The training pipeline is designed to be resilient to data drift and supports both batch and incremental learning approaches for maintaining model performance over time.

8. Monitoring & Observability

Data Drift Detection

Data drift monitoring is critical for maintaining model performance in production. We implement drift detection using Population Stability Index (PSI) and Kullback-Leibler divergence for continuous features, and Chi-square tests for categorical features.

import numpy as np
from scipy import stats
from sklearn.metrics import log_loss

class DriftDetector:
    def __init__(self, feature_names):
        self.feature_names = feature_names
        self.baseline_stats = {}
        
    def calculate_psi(self, current_data, baseline_data):
        """Calculate Population Stability Index"""
        # Binning data into 10 equal-width bins
        bins = np.linspace(
            min(np.min(current_data), np.min(baseline_data)),
            max(np.max(current_data), np.max(baseline_data)),
            11
        )
        
        current_counts, _ = np.histogram(current_data, bins=bins)
        baseline_counts, _ = np.histogram(baseline_data, bins=bins)
        
        # Avoid zero division
        current_counts = np.clip(current_counts, 1e-6, None)
        baseline_counts = np.clip(baseline_counts, 1e-6, None)
        
        psi = np.sum((current_counts - baseline_counts) * 
                    np.log(current_counts / baseline_counts))
        return psi
    
    def detect_drift(self, current_features, baseline_features):
        drift_report = {}
        for i, feature in enumerate(self.feature_names):
            if current_features[i].dtype in ['int64', 'float64']:
                psi = self.calculate_psi(current_features[i], baseline_features[i])
                drift_report[feature] = {
                    'psi': psi,
                    'drift_detected': psi > 0.2  # Threshold for drift
                }
            else:
                chi2, p_value = stats.chi2_contingency(
                    [current_features[i], baseline_features[i]]
                )
                drift_report[feature] = {
                    'chi2': chi2,
                    'p_value': p_value,
                    'drift_detected': p_value < 0.05
                }
        return drift_report

Prediction Drift and Label Drift Monitoring

Prediction drift monitors changes in model outputs, while label drift tracks shifts in true labels. We use statistical tests to detect both:

def detect_prediction_drift(current_predictions, baseline_predictions):
    """Detect drift in model predictions"""
    # Use Kolmogorov-Smirnov test for continuous prediction distributions
    ks_stat, p_value = stats.ks_2samp(current_predictions, baseline_predictions)
    
    return {
        'ks_statistic': ks_stat,
        'p_value': p_value,
        'drift_detected': p_value < 0.05
    }

def detect_label_drift(current_labels, baseline_labels):
    """Detect drift in label distributions"""
    # Chi-square test for categorical label distribution shift
    chi2, p_value, dof, expected = stats.chi2_contingency(
        [current_labels, baseline_labels]
    )
    
    return {
        'chi2_statistic': chi2,
        'p_value': p_value,
        'drift_detected': p_value < 0.05
    }

Model Performance Degradation Alerting

Performance degradation is monitored using threshold-based alerting with exponential moving averages:

class PerformanceMonitor:
    def __init__(self, window_size=1000):
        self.window_size = window_size
        self.performance_history = []
        self.alert_thresholds = {
            'auc': 0.95,
            'accuracy': 0.90,
            'log_loss': 0.3
        }
        
    def update_performance(self, metrics):
        self.performance_history.append(metrics)
        if len(self.performance_history) > self.window_size:
            self.performance_history.pop(0)
            
    def check_degradation(self):
        if len(self.performance_history) < self.window_size:
            return False
            
        # Calculate moving averages
        recent_metrics = self.performance_history[-100:]
        avg_auc = np.mean([m['auc'] for m in recent_metrics])
        avg_accuracy = np.mean([m['accuracy'] for m in recent_metrics])
        
        # Alert if performance drops below thresholds
        alerts = []
        if avg_auc < self.alert_thresholds['auc']:
            alerts.append('AUC degradation')
        if avg_accuracy < self.alert_thresholds['accuracy']:
            alerts.append('Accuracy degradation')
            
        return alerts if alerts else None

Retraining Trigger Strategy

Retraining is triggered based on drift detection, performance degradation, and data volume thresholds:

class RetrainingTrigger:
    def __init__(self, drift_threshold=0.2, degradation_threshold=0.05):
        self.drift_threshold = drift_threshold
        self.degradation_threshold = degradation_threshold
        self.data_volume_threshold = 10000  # samples
        
    def should_retrain(self, drift_report, performance_degradation, 
                       data_volume, time_since_last_retrain):
        """Determine if model should be retrained"""
        
        # Check drift in key features
        drift_detected = any(
            report['drift_detected'] 
            for report in drift_report.values()
        )
        
        # Check performance degradation
        degradation_detected = (
            performance_degradation and 
            performance_degradation != []
        )
        
        # Check data volume threshold
        volume_exceeded = data_volume > self.data_volume_threshold
        
        # Retrain if any condition met
        return (
            drift_detected or 
            degradation_detected or 
            volume_exceeded or
            time_since_last_retrain > 30  # days since last retrain
        )

Logging Design

Comprehensive logging includes prediction logs, feature logs, and label collection:

import json
from datetime import datetime

class PredictionLogger:
    def __init__(self, log_file_path):
        self.log_file = log_file_path
        
    def log_prediction(self, prediction_data):
        """Log model predictions with metadata"""
        log_entry = {
            'timestamp': datetime.now().isoformat(),
            'prediction_id': prediction_data['id'],
            'input_features': prediction_data['features'],
            'predicted_class': prediction_data['predicted_class'],
            'confidence_scores': prediction_data['confidence_scores'],
            'model_version': prediction_data.get('model_version', 'v1'),
            'user_id': prediction_data.get('user_id'),
            'session_id': prediction_data.get('session_id')
        }
        
        with open(self.log_file, 'a') as f:
            f.write(json.dumps(log_entry) + '\n')
            
    def log_label_feedback(self, feedback_data):
        """Log human-verified labels for model improvement"""
        feedback_entry = {
            'timestamp': datetime.now().isoformat(),
            'prediction_id': feedback_data['id'],
            'correct_label': feedback_data['correct_label'],
            'user_feedback': feedback_data.get('feedback_text', ''),
            'verification_source': feedback_data.get('source', 'manual')
        }
        
        with open(self.log_file, 'a') as f:
            f.write(json.dumps(feedback_entry) + '\n')

Feedback Loop for Continuous Improvement

The feedback loop enables continuous model improvement through human-in-the-loop:

class FeedbackProcessor:
    def __init__(self, feedback_queue):
        self.feedback_queue = feedback_queue
        
    def process_feedback(self):
        """Process human feedback for model retraining"""
        feedback_batch = []
        
        # Collect batch of feedback
        while len(feedback_batch) < 100 and self.feedback_queue:
            feedback_batch.append(self.feedback_queue.pop(0))
            
        # Update training dataset with feedback
        updated_dataset = self._merge_feedback_with_training_data(
            feedback_batch
        )
        
        # Trigger retraining if sufficient new data
        if len(updated_dataset) > 5000:
            self._trigger_retraining(updated_dataset)
            
    def _merge_feedback_with_training_data(self, feedback_batch):
        """Merge human feedback with existing training data"""
        # Implementation would integrate feedback into training pipeline
        return feedback_batch  # Simplified for example
        
    def _trigger_retraining(self, updated_dataset):
        """Trigger model retraining with new data"""
        # Implementation would call training pipeline
        pass

Data Quality Monitoring

Data quality checks ensure that inputs to the model are valid and consistent:

class DataQualityMonitor:
    def __init__(self):
        self.quality_metrics = {
            'missing_values': 0.05,  # 5% threshold
            'outliers': 0.02,        # 2% threshold
            'data_type_consistency': 0.95  # 95% consistency threshold
        }
        
    def validate_input_data(self, input_features):
        """Validate input data quality"""
        validation_results = {}
        
        # Check missing values
        missing_ratio = np.sum(np.isnan(input_features)) / input_features.size
        validation_results['missing_values'] = {
            'ratio': missing_ratio,
            'valid': missing_ratio < self.quality_metrics['missing_values']
        }
        
        # Check for outliers using IQR method
        Q1 = np.percentile(input_features, 25)
        Q3 = np.percentile(input_features, 75)
        IQR = Q3 - Q1
        outlier_count = np.sum(
            (input_features < Q1 - 1.5 * IQR) | 
            (input_features > Q3 + 1.5 * IQR)
        )
        outlier_ratio = outlier_count / input_features.size
        validation_results['outliers'] = {
            'ratio': outlier_ratio,
            'valid': outlier_ratio < self.quality_metrics['outliers']
        }
        
        return validation_results

Alerting and Escalation

Comprehensive alerting system with escalation paths:

class AlertSystem:
    def __init__(self):
        self.alert_channels = {
            'slack': 'https://hooks.slack.com/services/...',
            'email': 'admin@company.com',
            'pagerduty': 'https://events.pagerduty.com/...'
        }
        
    def send_alert(self, alert_type, message, severity='medium'):
        """Send alerts based on severity"""
        if severity == 'high':
            # Send to all channels
            for channel, endpoint in self.alert_channels.items():
                self._send_notification(channel, message)
        elif severity == 'medium':
            # Send to critical channels only
            self._send_notification('slack', message)
        else:
            # Log to monitoring system only
            print(f"ALERT ({severity}): {message}")
            
    def _send_notification(self, channel, message):
        """Send notification to specific channel"""
        # Implementation would use appropriate client libraries
        print(f"Sending alert via {channel}: {message}")

Observability Dashboard

Dashboard components for real-time monitoring:

class MonitoringDashboard:
    def __init__(self):
        self.metrics = {
            'prediction_volume': 0,
            'model_accuracy': 0.0,
            'drift_score': 0.0,
            'latency_p95': 0.0
        }
        
    def update_metrics(self, new_metrics):
        """Update dashboard metrics"""
        for key, value in new_metrics.items():
            if key in self.metrics:
                self.metrics[key] = value
                
    def generate_report(self):
        """Generate monitoring report"""
        return {
            'timestamp': datetime.now().isoformat(),
            'metrics': self.metrics,
            'status': self._get_system_status(),
            'recommendations': self._get_recommendations()
        }
        
    def _get_system_status(self):
        """Determine overall system health"""
        if self.metrics['drift_score'] > 0.2:
            return 'degraded'
        elif self.metrics['model_accuracy'] < 0.85:
            return 'warning'
        else:
            return 'healthy'
            
    def _get_recommendations(self):
        """Generate system recommendations"""
        recommendations = []
        
        if self.metrics['drift_score'] > 0.2:
            recommendations.append('Consider retraining model due to data drift')
            
        if self.metrics['latency_p95'] > 1000:  # 1 second
            recommendations.append('Investigate inference latency issues')
            
        return recommendations

Key Monitoring Metrics

MetricDescriptionThresholdAlert Level
AUC-ROCModel discrimination power< 0.95High
AccuracyOverall prediction correctness< 0.90Medium
Log LossModel confidence calibration> 0.3Medium
PSI DriftFeature distribution shift> 0.2High
Latency P95Request response time> 1000msMedium
Data QualityMissing/outlier ratios> thresholdsMedium

Retraining Triggers

  • Drift Detection: PSI > 0.2 or Chi-square p-value < 0.05
  • Performance Degradation: Accuracy drop > 5% over 1000 predictions
  • Data Volume: 10,000 new samples collected
  • Time-Based: Every 30 days since last retraining

Alert Escalation Path

  1. Medium Severity: Slack notification
  2. High Severity: Slack + Email + PagerDuty
  3. Critical: All channels + manual intervention required

This monitoring and observability framework ensures continuous model health, early detection of performance degradation, and automated retraining triggers to maintain system reliability.

9. Responsible AI & Safety

Bias Analysis

  • Training data audit: Conduct comprehensive analysis of training dataset for demographic imbalance across age, gender, ethnicity, and geographic regions. Use tools like fairlearn to detect bias in label distributions.
  • Slice-based fairness evaluation: Evaluate model performance across key user segments (e.g., gender, age groups) using demographic parity and equalized odds constraints. For example, ensure that precision for male vs female predictions does not differ by more than 5%.

Fairness Metrics and Constraints

  • Metrics: Implement demographic parity (equal positive prediction rates across groups) and equalized odds (equal true positive rates across groups).
  • Decision: Prioritize equalized odds over demographic parity to ensure that the model's ability to correctly identify positive cases is consistent across protected attributes, which is more aligned with business goals of minimizing false negatives in critical applications.

Explainability Requirements and Approach

  • Approach: Implement attention visualization for CNNs using Grad-CAM and SHAP for model-agnostic explanations. For production systems, provide a dashboard that allows stakeholders to inspect which image regions influenced classification decisions.
  • Stakeholder needs: Business users require high-level explanations for decision confidence, while technical teams need detailed model insights for debugging.

For LLM Systems: Safety Guardrails

  • Content filtering: Implement content moderation using pre-trained classifiers for inappropriate or harmful content (e.g., NSFW, hate speech).
  • Output validation: Validate model outputs against predefined constraints (e.g., image content must match label taxonomy).
  • Rate limiting: Enforce rate limits to prevent abuse and ensure system stability under high load.
  • Red teaming: Conduct adversarial testing with human red teams to identify edge cases and potential biases in model behavior.

Model Card Documentation Plan

  • Content: Document model architecture, training data sources, performance metrics, fairness analysis, and known limitations.
  • Format: Use standardized model card templates (e.g., Hugging Face's model cards) to ensure consistency and transparency in model documentation.

Incident Response Plan for Model Failures or Harmful Outputs

  • Failure detection: Alert on prediction drift, label drift, and performance degradation via monitoring dashboards.
  • Escalation: If model accuracy drops below 85% or bias thresholds are exceeded, trigger an incident response process including rollback to previous model version and human review of flagged outputs.
  • Communication: Establish communication channels with stakeholders for immediate updates on model failures and mitigation steps.
# Example SHAP-based explainability snippet for image classification
import shap
import torch
from torchvision import models

def explain_prediction(model, image_tensor, target_class):
    """Explain a model prediction using SHAP"""
    # Wrap model in SHAP explainer
    explainer = shap.Explainer(model, image_tensor)
    
    # Get SHAP values for the target class
    shap_values = explainer(image_tensor)
    
    # Visualize SHAP values for the target class
    shap.image_plot(shap_values[target_class], image_tensor)
    
    return shap_values

10. Scalability & Reliability

Bottleneck Analysis per Pipeline Stage

Training Pipeline:

  • Data loading: 20GB/s peak, bottlenecked by disk I/O for large image datasets
  • Model training: 500GB GPU memory requirement, limited by single-GPU capacity (8GB)
  • Distributed training: 2x bandwidth bottleneck for gradient synchronization across nodes

Serving Pipeline:

  • Model inference: 50ms p99 latency target, bottlenecked by embedding computation and attention layers
  • Feature lookup: 10ms p99 target, bottlenecked by feature store read latency (5ms) + embedding cache miss rate (20%)

Back-of-envelope estimates:

  • Training data size: 5M images × 2MB = 10TB
  • Feature dimensionality: 10K features per image (resnet-50 features)
  • Model size: ~2GB (resnet-50) + 10GB embedding tables = 12GB total
  • Serving QPS: 5000 QPS with 50ms p99 latency target

Scale-up Strategy

Horizontal Scaling:

  • Feature Store: Use distributed Redis clusters with sharding across 10 nodes for 50ms p99 target
  • Model Serving: Deploy across 20 GPU nodes with model parallelism (split ResNet into 4 parts)
  • Training: Scale to 10 GPU nodes with data parallelism (batch size = 64 per node)

Batch vs. Real-time Processing:

  • Batch feature computation: Use Ray for distributed feature extraction (10x faster than single-threaded)
  • Real-time feature computation: Cache embeddings in Redis with TTL = 1 hour

Fault Tolerance

Model Serving Fallbacks:

  • Primary model: ResNet-50 with 95% accuracy
  • Fallback model: MobileNetV2 (85% accuracy) for model failure scenarios
  • Graceful degradation: Serve pre-computed class probabilities from cache during outages

Stale Model Serving:

  • Implement model versioning with 30-day TTL on model artifacts
  • Fallback to last known good model version if current model fails validation (AUC < 0.85)

Cold Start Handling Strategy

New User Handling:

  • Fallback to global average class probabilities (no user-specific features)
  • Use content-based filtering from item metadata for 100% of new users

New Item Handling:

  • Use pre-trained ResNet-50 features with 1000-class output
  • Apply transfer learning on 1000-item batches with new labels

Multi-region Serving Strategy

Deployment Architecture:

graph TD
    A[User Request] --> B[Load Balancer]
    B --> C[Regional Edge Cache]
    C --> D[Regional Model Server]
    D --> E[Feature Store]
    E --> F[Global Embedding Cache]

Regional Deployment:

  • Primary region (US-East): 50% of traffic, 20 GPU servers
  • Secondary region (EU-West): 30% of traffic, 10 GPU servers
  • Backup region (Asia-Southeast): 20% of traffic, 5 GPU servers

Traffic Distribution:

  • Auto-failover on region failure (500ms RTT threshold)
  • Load balancing with weighted round-robin based on region performance
  • Cache warming strategy: pre-populate 80% of most frequent classes in each region

Cross-region Consistency:

  • Feature store replication with 1-hour sync window
  • Model version synchronization via GitOps (Kubernetes manifests)
  • Global embedding cache with 15-minute TTL for consistency

Reliability Metrics

SLO Targets:

  • Availability: 99.9% (10m downtime/month max)
  • Latency: 50ms p99 for inference, 10ms p99 for feature lookup
  • Throughput: 5000 QPS with 95% success rate

Monitoring:

  • Health checks every 30 seconds across all services
  • Auto-restart policy for failed model servers (max 3 restarts per hour)
  • Circuit breaker pattern for feature store failures (timeout = 500ms)

11. Open Questions, Risks & Trade-offs

Decisions requiring product / engineering / policy alignment

  • Labeling strategy alignment: Whether to use human annotation or weak supervision for fine-grained categories, especially for edge cases and ambiguous classes
  • Privacy vs performance trade-off: Whether to collect and store user images for model improvement or rely purely on public datasets
  • Deployment strategy: Whether to use managed ML services (e.g., AWS SageMaker, GCP Vertex AI) or build custom infrastructure for full control and optimization
  • Model versioning policy: How to handle model rollbacks when new versions introduce regressions or bias in specific user segments

Key ML risks

RiskImpactLikelihoodMitigation
Data drift in image distributionHigh (model performance degradation)MediumImplement feature drift detection, retrain on fresh data
Bias in training data (e.g., under-representation of minority classes)High (unfair predictions, regulatory risk)MediumImplement fairness-aware training, slice-based evaluation
Model hallucination in edge casesMedium (user experience degradation)Low-MediumImplement robustness testing, adversarial validation
Cold start for new image categoriesMedium (limited utility)HighDesign progressive model updates, fallback to rule-based logic
Privacy and compliance violationsHigh (regulatory penalties)Low-MediumImplement strict data governance, anonymization protocols

Trade-offs made with justification

  • Accuracy vs latency: Prioritized model compression and efficient inference (e.g., quantization, pruning) over absolute accuracy to meet real-time serving requirements (~100ms p99 latency). This trade-off is acceptable for most use cases where near-real-time classification is more valuable than marginal accuracy gains.
  • Freshness vs cost: Chose batch training with daily updates over continuous online learning to reduce infrastructure complexity and cost, while maintaining model relevance through periodic retraining on fresh data.
  • Model size vs quality: Selected a medium-sized CNN architecture (e.g., ResNet-50) over larger models like Vision Transformer to balance performance with deployment constraints (memory, latency, cost).
  • Online vs batch serving: Chose online serving for low-latency response to user queries, despite higher infrastructure cost, because the user experience demands real-time classification.

Alternatives considered and rejected

  • Transformer-based architectures: Rejected Vision Transformers for production use due to high computational cost and latency, despite their superior accuracy in research settings.
  • Fully custom inference engine: Rejected building a custom inference engine from scratch due to high engineering cost and risk of suboptimal performance compared to mature solutions like TorchServe or Triton.
  • Rule-based fallback: Rejected pure rule-based classification (e.g., color histogram matching) as a primary solution due to poor generalization and inability to scale with new categories.

Technical debt acknowledged

  • Feature store dependency: Current design assumes a mature feature store, but the system may need to support real-time feature computation in future phases where feature store is not yet available.
  • Model versioning limitations: Current model registry approach does not support A/B testing of multiple model variants in production, which could be a limitation for future experimentation.
  • Offline evaluation gap: The model is evaluated in offline settings but may not fully capture real-world performance due to differences in data distribution and user behavior.
  • Limited explainability: The model is not currently designed to provide explanations for predictions, which may be required for certain enterprise or regulatory use cases.

12. Phased Implementation Roadmap

Phase 1: MVP (Weeks 1-4)

Objective: Establish baseline image classification system with rule-based fallback and basic ML model.

Deliverables:

  • Rule-based image classifier using OpenCV/Haar cascades as fallback
  • Basic CNN model (e.g., ResNet18) trained on 10k images
  • Online serving infrastructure with TorchServe or Flask
  • Simple feature extraction pipeline (e.g., color histogram, texture features)
  • Basic A/B testing infrastructure with 10% shadow traffic

Dependencies:

  • Access to labeled dataset (5k images minimum)
  • GPU cluster access (1x V100 or equivalent)
  • Feature store with 24h TTL for batch features

Phase 2: Scale (Weeks 5-8)

Objective: Expand to production-scale image classification with feature store and distributed training.

Deliverables:

  • Feature store implementation (Redis + PostgreSQL)
  • Distributed training pipeline using PyTorch DDP
  • Real-time feature computation (e.g., image embeddings via ResNet)
  • Multi-model ensemble (CNN + Vision Transformer)
  • 50% A/B test with 20% traffic split
  • Model versioning and experiment tracking (MLflow)

Dependencies:

  • 50k+ training images with balanced classes
  • Distributed GPU cluster (4x V100 or 2x A100)
  • Feature store with sub-second read latency
  • CI/CD pipeline for model deployment

Phase 3: Excellence (Weeks 9-12)

Objective: Advanced capabilities including real-time features, continuous learning, and model monitoring.

Deliverables:

  • Real-time feature pipeline with 100ms latency budget
  • Online learning capability for concept drift handling
  • Model monitoring dashboard with drift detection (PSI, KL divergence)
  • Multi-task model supporting classification + attribute prediction
  • Production-grade A/B testing with statistical significance (p<0.05)
  • Automated retraining triggers based on performance degradation

Dependencies:

  • 200k+ training images with regular updates
  • 1000+ QPS serving infrastructure (2x redundancy)
  • Real-time feature computation pipeline (100ms p99)
  • Model registry with rollback capability

Critical Path Dependencies:

  1. Dataset availability - Must have 5k+ labeled images by Week 2
  2. Infrastructure setup - GPU cluster and feature store ready by Week 3
  3. Model training pipeline - Distributed training working by Week 5
  4. A/B testing framework - Production A/B test ready by Week 6

Risk Mitigation:

  • Data quality issues: Implement data validation pipeline in Phase 1
  • Latency constraints: Use model compression (quantization) in Phase 2
  • Cold start handling: Maintain rule-based fallback throughout all phases
  • Model drift: Implement continuous monitoring and retraining triggers

Success Metrics:

  • Phase 1: <50% accuracy improvement over rule-based baseline
  • Phase 2: <100ms inference latency, 95% model availability
  • Phase 3: <2% performance degradation over 30-day period, 99.9% uptime

Was this article helpful?

Topics in this article

More from AI/ML System Design

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.