Advanced Introduction to ML Topics
Advanced ML workflow patterns, system design notes, and production considerations.
Table of Contents
- Advanced ML Workflow Patterns
- ML System Design Principles
- Data-Centric vs Model-Centric Approaches
- ML Project Management
- Advanced Problem Framing
- ML in Production Considerations
Advanced ML Workflow Patterns
Iterative ML Development
# Traditional workflow
def traditional_ml_workflow():
"""
1. Collect data
2. Train model
3. Deploy
4. Monitor
"""
pass
# Iterative workflow
def iterative_ml_workflow():
"""
1. Define problem
2. Collect initial data
3. Train baseline model
4. Evaluate and identify issues
5. Improve (data or model)
6. Repeat steps 3-5
7. Deploy
8. Monitor and iterate
"""
pass
Experiment Tracking
# Track experiments systematically
experiments = []
def track_experiment(name, params, metrics, model):
experiment = {
'name': name,
'parameters': params,
'metrics': metrics,
'model': model,
'timestamp': datetime.now()
}
experiments.append(experiment)
return experiment
# Example
track_experiment(
name='baseline_model',
params={'algorithm': 'logistic_regression', 'C': 1.0},
metrics={'accuracy': 0.85, 'f1': 0.82},
model=model
)
Version Control for ML
# Track data versions
import hashlib
def get_data_hash(data):
"""Generate hash for data versioning"""
return hashlib.md5(str(data).encode()).hexdigest()
# Track model versions
def save_model_version(model, version, metrics):
"""Save model with version info"""
model_info = {
'version': version,
'metrics': metrics,
'timestamp': datetime.now(),
'model': model
}
# Save to model registry
return model_info
ML System Design Principles
Scalability
# Design for scale from the start
class ScalableMLSystem:
def __init__(self):
self.model = None
self.preprocessor = None
self.cache = {}
def preprocess(self, data):
"""Preprocessing that scales"""
# Use vectorized operations
return vectorized_preprocess(data)
def predict(self, data):
"""Batch prediction for efficiency"""
if len(data) == 1:
# Single prediction
return self._predict_single(data[0])
else:
# Batch prediction
return self._predict_batch(data)
def _predict_batch(self, data):
"""Efficient batch processing"""
preprocessed = self.preprocess(data)
return self.model.predict(preprocessed)
Modularity
# Modular ML system design
class MLPipeline:
def __init__(self):
self.data_loader = DataLoader()
self.preprocessor = Preprocessor()
self.feature_engineer = FeatureEngineer()
self.model = Model()
self.evaluator = Evaluator()
def run(self, data_source):
# Load
data = self.data_loader.load(data_source)
# Preprocess
data = self.preprocessor.transform(data)
# Feature engineering
features = self.feature_engineer.transform(data)
# Predict
predicti>self.model.predict(features)
# Evaluate
metrics = self.evaluator.evaluate(predictions)
return predictions, metrics
Reproducibility
import random
import numpy as np
# Set seeds for reproducibility
def set_seeds(seed=42):
random.seed(seed)
np.random.seed(seed)
# For TensorFlow
# tf.random.set_seed(seed)
# For PyTorch
# torch.manual_seed(seed)
# Save configuration
def save_config(config, path):
"""Save experiment configuration"""
import json
with open(path, 'w') as f:
json.dump(config, f, indent=2)
# Load configuration
def load_config(path):
"""Load experiment configuration"""
import json
with open(path, 'r') as f:
return json.load(f)
Data-Centric vs Model-Centric Approaches
Model-Centric Approach
# Focus on improving the model
def model_centric_approach():
"""
Strategy:
1. Fix dataset
2. Improve model architecture
3. Tune hyperparameters
4. Try different algorithms
Example:
- Try more complex models (XGBoost, Neural Networks)
- Ensemble methods
- Hyperparameter tuning
"""
models = [
LogisticRegression(),
RandomForestClassifier(),
XGBClassifier(),
NeuralNetwork()
]
for model in models:
train_and_evaluate(model)
Data-Centric Approach
# Focus on improving the data
def data_centric_approach():
"""
Strategy:
1. Collect more data
2. Improve data quality
3. Better labeling
4. Data augmentation
5. Feature engineering
Example:
- Clean noisy labels
- Collect edge cases
- Balance dataset
- Add relevant features
"""
# Improve data quality
clean_labels()
collect_edge_cases()
balance_dataset()
engineer_features()
# Then train simple model
model = LogisticRegression()
train_and_evaluate(model)
Hybrid Approach
# Best of both worlds
def hybrid_approach():
"""
1. Start with data-centric improvements
2. Train baseline model
3. Identify model limitations
4. Improve model if needed
5. Iterate on data based on model errors
"""
# Step 1: Improve data
improved_data = improve_data_quality(data)
# Step 2: Baseline model
baseline = train_baseline(improved_data)
# Step 3: Analyze errors
errors = analyze_errors(baseline, improved_data)
# Step 4: Improve data based on errors
better_data = fix_error_cases(improved_data, errors)
# Step 5: Train better model
final_model = train_final_model(better_data)
return final_model
ML Project Management
Project Structure
ml_project/
├── data/
│ ├── raw/
│ ├── processed/
│ └── external/
├── notebooks/
│ ├── 01_exploration.ipynb
│ ├── 02_preprocessing.ipynb
│ └── 03_modeling.ipynb
├── src/
│ ├── data/
│ ├── features/
│ ├── models/
│ └── visualization/
├── models/
├── reports/
├── configs/
└── requirements.txt
Agile ML Development
# Sprint planning for ML projects
class MLSprint:
def __init__(self, duration=2): # 2 weeks
self.duration = duration
self.tasks = []
def add_task(self, task, priority):
self.tasks.append({
'task': task,
'priority': priority,
'status': 'todo'
})
def prioritize(self):
"""Prioritize tasks by impact and effort"""
self.tasks.sort(key=lambda x: (
x['priority'], # Higher priority first
-estimate_effort(x['task']) # Lower effort first
))
# Example sprint
sprint = MLSprint()
sprint.add_task('Collect more training data', priority=1)
sprint.add_task('Improve feature engineering', priority=2)
sprint.add_task('Try new algorithm', priority=3)
sprint.prioritize()
Documentation
# Document ML projects
class MLDocumentation:
def __init__(self, project_name):
self.project_name = project_name
self.secti>
def add_section(self, name, content):
self.sections[name] = content
def generate_report(self):
"""Generate project documentation"""
report = f"""
# {self.project_name} - ML Project Documentation
## Problem Statement
{self.sections.get('problem', '')}
## Data
{self.sections.get('data', '')}
## Methodology
{self.sections.get('methodology', '')}
## Results
{self.sections.get('results', '')}
## Conclusions
{self.sections.get('conclusions', '')}
"""
return report
Advanced Problem Framing
Problem Decomposition
# Break down complex problems
def decompose_problem(problem):
"""
Example: "Predict customer churn"
Sub-problems:
1. Identify churn indicators
2. Predict churn probability
3. Recommend retention actions
"""
sub_problems = [
'Identify churn indicators',
'Predict churn probability',
'Recommend retention actions'
]
return sub_problems
# Solve each sub-problem
def solve_sub_problems(sub_problems):
soluti>
for problem in sub_problems:
solution = solve(problem)
solutions[problem] = solution
return solutions
Success Metrics Definition
# Define clear success metrics
class SuccessMetrics:
def __init__(self):
self.metrics = {}
def add_metric(self, name, target, current=None):
self.metrics[name] = {
'target': target,
'current': current,
'achieved': current is not None and current >= target
}
def check_success(self):
"""Check if all metrics are achieved"""
return all(m['achieved'] for m in self.metrics.values())
# Example
metrics = SuccessMetrics()
metrics.add_metric('accuracy', target=0.90, current=0.92)
metrics.add_metric('latency', target=100, current=80) # ms
metrics.add_metric('cost', target=0.01, current=0.008) # per prediction
if metrics.check_success():
print("Project is successful!")
Risk Assessment
# Assess ML project risks
class MLRiskAssessment:
def __init__(self):
self.risks = []
def add_risk(self, risk, probability, impact, mitigation):
self.risks.append({
'risk': risk,
'probability': probability, # High/Medium/Low
'impact': impact, # High/Medium/Low
'mitigation': mitigation
})
def get_critical_risks(self):
"""Get high probability, high impact risks"""
return [r for r in self.risks
if r['probability'] == 'High' and r['impact'] == 'High']
# Example
assessment = MLRiskAssessment()
assessment.add_risk(
risk='Data quality issues',
probability='High',
impact='High',
mitigation='Implement data validation pipeline'
)
assessment.add_risk(
risk='Model performance degradation',
probability='Medium',
impact='High',
mitigation='Set up monitoring and retraining pipeline'
)
ML in Production Considerations
Model Monitoring
# Monitor model in production
class ModelMonitor:
def __init__(self, model, baseline_metrics):
self.model = model
self.baseline_metrics = baseline_metrics
self.predicti>
self.performance_history = []
def log_prediction(self, input_data, prediction, actual=None):
"""Log prediction for monitoring"""
log_entry = {
'timestamp': datetime.now(),
'input': input_data,
'prediction': prediction,
'actual': actual
}
self.predictions.append(log_entry)
def check_drift(self):
"""Check for data drift"""
recent_data = self.get_recent_data()
baseline_dist = self.get_baseline_distribution()
# Statistical test for drift
drift_detected = statistical_test(recent_data, baseline_dist)
return drift_detected
def check_performance(self):
"""Check model performance"""
recent_metrics = self.calculate_recent_metrics()
# Compare with baseline
performance_degraded = (
recent_metrics['accuracy'] < self.baseline_metrics['accuracy'] * 0.95
)
return performance_degraded
A/B Testing Framework
# A/B testing for ML models
class ABTest:
def __init__(self, model_a, model_b, traffic_split=0.5):
self.model_a = model_a
self.model_b = model_b
self.traffic_split = traffic_split
self.results_a = []
self.results_b = []
def route_request(self, request):
"""Route request to A or B"""
if random.random() < self.traffic_split:
prediction = self.model_a.predict(request)
self.results_a.append(prediction)
return prediction, 'A'
else:
prediction = self.model_b.predict(request)
self.results_b.append(prediction)
return prediction, 'B'
def analyze_results(self):
"""Analyze A/B test results"""
from scipy import stats
# Statistical test
statistic, p_value = stats.ttest_ind(
self.results_a, self.results_b
)
return {
'statistic': statistic,
'p_value': p_value,
'significant': p_value < 0.05,
'mean_a': np.mean(self.results_a),
'mean_b': np.mean(self.results_b)
}
Model Versioning
# Version control for models
class ModelVersioning:
def __init__(self):
self.versi>
self.current_version = None
def register_version(self, version, model, metrics, metadata):
"""Register new model version"""
self.versions[version] = {
'model': model,
'metrics': metrics,
'metadata': metadata,
'timestamp': datetime.now()
}
def promote_version(self, version, environment='production'):
"""Promote version to environment"""
if version in self.versions:
self.current_version = version
# Deploy to environment
self.deploy(version, environment)
def rollback(self, previous_version):
"""Rollback to previous version"""
if previous_version in self.versions:
self.promote_version(previous_version)
Key Takeaways
- Workflow: Use iterative development with experiment tracking
- Design: Build scalable, modular, and reproducible systems
- Approach: Balance data-centric and model-centric improvements
- Management: Use proper project structure and documentation
- Production: Plan for monitoring, A/B testing, and versioning
Try next: Add a config file and a seed to your last training script. Re-run and confirm the metric matches.