Python Machine Learning Best Practices¶
Objective: Master senior-level Python machine learning patterns for production systems. When you need to build robust ML pipelines, when you want to implement comprehensive model evaluation, when you need enterprise-grade ML strategiesโthese best practices become your weapon of choice.
Core Principles¶
- Reproducibility: Ensure experiments can be reproduced and validated
- Performance: Optimize for training speed and inference efficiency
- Validation: Implement robust model evaluation and testing
- Monitoring: Track model performance and data drift
- Deployment: Ensure models can be deployed and maintained
ML Pipeline Design¶
Model Training Pipeline¶
# python/01-ml-pipeline-design.py
"""
Machine learning pipeline design patterns and model training workflows
"""
from typing import List, Dict, Any, Optional, Union, Tuple, Callable
from dataclasses import dataclass, asdict
from enum import Enum
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.preprocessing import StandardScaler, LabelEncoder, OneHotEncoder
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression, LinearRegression
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.metrics import mean_squared_error, r2_score, classification_report
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
import joblib
import json
from datetime import datetime, timedelta
import logging
from abc import ABC, abstractmethod
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class ModelType(Enum):
"""Model type enumeration"""
CLASSIFICATION = "classification"
REGRESSION = "regression"
CLUSTERING = "clustering"
DIMENSIONALITY_REDUCTION = "dimensionality_reduction"
class ModelStatus(Enum):
"""Model status enumeration"""
TRAINING = "training"
TRAINED = "trained"
EVALUATED = "evaluated"
DEPLOYED = "deployed"
RETIRED = "retired"
@dataclass
class ModelMetrics:
"""Model metrics definition"""
accuracy: float = 0.0
precision: float = 0.0
recall: float = 0.0
f1_score: float = 0.0
mse: float = 0.0
r2_score: float = 0.0
cross_val_score: float = 0.0
training_time: float = 0.0
inference_time: float = 0.0
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary"""
return asdict(self)
class DataPreprocessor:
"""Data preprocessing utilities"""
def __init__(self):
self.scalers = {}
self.encoders = {}
self.feature_names = []
self.preprocessing_pipeline = None
def prepare_features(self, X: pd.DataFrame, y: pd.Series = None,
fit: bool = True) -> Tuple[np.ndarray, List[str]]:
"""Prepare features for training"""
# Separate numeric and categorical columns
numeric_cols = X.select_dtypes(include=[np.number]).columns.tolist()
categorical_cols = X.select_dtypes(include=['object', 'category']).columns.tolist()
# Create preprocessing pipeline
numeric_transformer = StandardScaler()
categorical_transformer = OneHotEncoder(handle_unknown='ignore')
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_cols),
('cat', categorical_transformer, categorical_cols)
]
)
if fit:
X_processed = preprocessor.fit_transform(X)
self.preprocessing_pipeline = preprocessor
else:
X_processed = self.preprocessing_pipeline.transform(X)
# Get feature names
feature_names = numeric_cols.copy()
if categorical_cols:
cat_feature_names = self.preprocessing_pipeline.named_transformers_['cat'].get_feature_names_out(categorical_cols)
feature_names.extend(cat_feature_names)
self.feature_names = feature_names
return X_processed, feature_names
def save_preprocessor(self, filepath: str) -> None:
"""Save preprocessing pipeline"""
if self.preprocessing_pipeline is not None:
joblib.dump(self.preprocessing_pipeline, filepath)
def load_preprocessor(self, filepath: str) -> None:
"""Load preprocessing pipeline"""
self.preprocessing_pipeline = joblib.load(filepath)
class ModelTrainer(ABC):
"""Abstract model trainer"""
def __init__(self, model_type: ModelType):
self.model_type = model_type
self.model = None
self.metrics = ModelMetrics()
self.training_history = []
@abstractmethod
def train(self, X: np.ndarray, y: np.ndarray, **kwargs) -> None:
"""Train the model"""
pass
@abstractmethod
def predict(self, X: np.ndarray) -> np.ndarray:
"""Make predictions"""
pass
@abstractmethod
def evaluate(self, X: np.ndarray, y: np.ndarray) -> ModelMetrics:
"""Evaluate the model"""
pass
def save_model(self, filepath: str) -> None:
"""Save trained model"""
if self.model is not None:
joblib.dump(self.model, filepath)
def load_model(self, filepath: str) -> None:
"""Load trained model"""
self.model = joblib.load(filepath)
class ClassificationTrainer(ModelTrainer):
"""Classification model trainer"""
def __init__(self, algorithm: str = "random_forest"):
super().__init__(ModelType.CLASSIFICATION)
self.algorithm = algorithm
self._initialize_model()
def _initialize_model(self) -> None:
"""Initialize the model"""
if self.algorithm == "random_forest":
self.model = RandomForestClassifier(random_state=42)
elif self.algorithm == "gradient_boosting":
self.model = GradientBoostingClassifier(random_state=42)
elif self.algorithm == "logistic_regression":
self.model = LogisticRegression(random_state=42)
elif self.algorithm == "svm":
self.model = SVC(random_state=42)
else:
raise ValueError(f"Unsupported algorithm: {self.algorithm}")
def train(self, X: np.ndarray, y: np.ndarray, **kwargs) -> None:
"""Train the classification model"""
start_time = datetime.utcnow()
# Hyperparameter tuning if specified
if 'param_grid' in kwargs:
param_grid = kwargs['param_grid']
cv = kwargs.get('cv', 5)
grid_search = GridSearchCV(
self.model, param_grid, cv=cv, scoring='accuracy'
)
grid_search.fit(X, y)
self.model = grid_search.best_estimator_
logger.info(f"Best parameters: {grid_search.best_params_}")
logger.info(f"Best cross-validation score: {grid_search.best_score_:.4f}")
else:
self.model.fit(X, y)
training_time = (datetime.utcnow() - start_time).total_seconds()
self.metrics.training_time = training_time
logger.info(f"Model trained in {training_time:.2f} seconds")
def predict(self, X: np.ndarray) -> np.ndarray:
"""Make predictions"""
if self.model is None:
raise ValueError("Model not trained yet")
start_time = datetime.utcnow()
predictions = self.model.predict(X)
inference_time = (datetime.utcnow() - start_time).total_seconds()
self.metrics.inference_time = inference_time
return predictions
def predict_proba(self, X: np.ndarray) -> np.ndarray:
"""Get prediction probabilities"""
if self.model is None:
raise ValueError("Model not trained yet")
if hasattr(self.model, 'predict_proba'):
return self.model.predict_proba(X)
else:
raise ValueError(f"Model {self.algorithm} does not support probability predictions")
def evaluate(self, X: np.ndarray, y: np.ndarray) -> ModelMetrics:
"""Evaluate the classification model"""
predictions = self.predict(X)
# Calculate metrics
accuracy = accuracy_score(y, predictions)
precision = precision_score(y, predictions, average='weighted')
recall = recall_score(y, predictions, average='weighted')
f1 = f1_score(y, predictions, average='weighted')
# Cross-validation score
cv_scores = cross_val_score(self.model, X, y, cv=5)
cv_score = cv_scores.mean()
self.metrics.accuracy = accuracy
self.metrics.precision = precision
self.metrics.recall = recall
self.metrics.f1_score = f1
self.metrics.cross_val_score = cv_score
logger.info(f"Model evaluation - Accuracy: {accuracy:.4f}, F1: {f1:.4f}")
return self.metrics
class RegressionTrainer(ModelTrainer):
"""Regression model trainer"""
def __init__(self, algorithm: str = "linear_regression"):
super().__init__(ModelType.REGRESSION)
self.algorithm = algorithm
self._initialize_model()
def _initialize_model(self) -> None:
"""Initialize the model"""
if self.algorithm == "linear_regression":
self.model = LinearRegression()
elif self.algorithm == "random_forest":
self.model = RandomForestClassifier(random_state=42)
elif self.algorithm == "gradient_boosting":
self.model = GradientBoostingClassifier(random_state=42)
else:
raise ValueError(f"Unsupported algorithm: {self.algorithm}")
def train(self, X: np.ndarray, y: np.ndarray, **kwargs) -> None:
"""Train the regression model"""
start_time = datetime.utcnow()
# Hyperparameter tuning if specified
if 'param_grid' in kwargs:
param_grid = kwargs['param_grid']
cv = kwargs.get('cv', 5)
grid_search = GridSearchCV(
self.model, param_grid, cv=cv, scoring='neg_mean_squared_error'
)
grid_search.fit(X, y)
self.model = grid_search.best_estimator_
logger.info(f"Best parameters: {grid_search.best_params_}")
logger.info(f"Best cross-validation score: {grid_search.best_score_:.4f}")
else:
self.model.fit(X, y)
training_time = (datetime.utcnow() - start_time).total_seconds()
self.metrics.training_time = training_time
logger.info(f"Model trained in {training_time:.2f} seconds")
def predict(self, X: np.ndarray) -> np.ndarray:
"""Make predictions"""
if self.model is None:
raise ValueError("Model not trained yet")
start_time = datetime.utcnow()
predictions = self.model.predict(X)
inference_time = (datetime.utcnow() - start_time).total_seconds()
self.metrics.inference_time = inference_time
return predictions
def evaluate(self, X: np.ndarray, y: np.ndarray) -> ModelMetrics:
"""Evaluate the regression model"""
predictions = self.predict(X)
# Calculate metrics
mse = mean_squared_error(y, predictions)
r2 = r2_score(y, predictions)
# Cross-validation score
cv_scores = cross_val_score(self.model, X, y, cv=5, scoring='neg_mean_squared_error')
cv_score = -cv_scores.mean()
self.metrics.mse = mse
self.metrics.r2_score = r2
self.metrics.cross_val_score = cv_score
logger.info(f"Model evaluation - MSE: {mse:.4f}, Rยฒ: {r2:.4f}")
return self.metrics
class ModelEvaluator:
"""Model evaluation utilities"""
def __init__(self):
self.evaluation_results = {}
def cross_validate(self, model, X: np.ndarray, y: np.ndarray,
cv: int = 5, scoring: str = None) -> Dict[str, Any]:
"""Perform cross-validation"""
if scoring is None:
scoring = 'accuracy' if len(np.unique(y)) < 10 else 'neg_mean_squared_error'
cv_scores = cross_val_score(model, X, y, cv=cv, scoring=scoring)
result = {
"cv_scores": cv_scores.tolist(),
"mean_score": cv_scores.mean(),
"std_score": cv_scores.std(),
"cv_folds": cv,
"scoring": scoring
}
return result
def learning_curve(self, model, X: np.ndarray, y: np.ndarray,
train_sizes: List[float] = None) -> Dict[str, Any]:
"""Generate learning curve"""
from sklearn.model_selection import learning_curve
if train_sizes is None:
train_sizes = np.linspace(0.1, 1.0, 10)
train_sizes_abs, train_scores, val_scores = learning_curve(
model, X, y, train_sizes=train_sizes, cv=5
)
result = {
"train_sizes": train_sizes_abs.tolist(),
"train_scores": train_scores.tolist(),
"val_scores": val_scores.tolist(),
"train_scores_mean": train_scores.mean(axis=1).tolist(),
"val_scores_mean": val_scores.mean(axis=1).tolist()
}
return result
def feature_importance(self, model, feature_names: List[str]) -> Dict[str, Any]:
"""Get feature importance"""
if hasattr(model, 'feature_importances_'):
importances = model.feature_importances_
elif hasattr(model, 'coef_'):
importances = np.abs(model.coef_[0])
else:
return {"error": "Model does not support feature importance"}
# Sort features by importance
feature_importance = list(zip(feature_names, importances))
feature_importance.sort(key=lambda x: x[1], reverse=True)
result = {
"feature_importance": feature_importance,
"top_features": feature_importance[:10]
}
return result
class ModelPipeline:
"""Complete ML pipeline"""
def __init__(self, model_type: ModelType, algorithm: str = None):
self.model_type = model_type
self.algorithm = algorithm
self.preprocessor = DataPreprocessor()
self.trainer = None
self.evaluator = ModelEvaluator()
self.status = ModelStatus.TRAINING
self.pipeline_metrics = {}
def setup_trainer(self, algorithm: str) -> None:
"""Setup model trainer"""
if self.model_type == ModelType.CLASSIFICATION:
self.trainer = ClassificationTrainer(algorithm)
elif self.model_type == ModelType.REGRESSION:
self.trainer = RegressionTrainer(algorithm)
else:
raise ValueError(f"Unsupported model type: {self.model_type}")
def train_pipeline(self, X: pd.DataFrame, y: pd.Series,
test_size: float = 0.2, **kwargs) -> Dict[str, Any]:
"""Train complete pipeline"""
# Split data
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=test_size, random_state=42
)
# Preprocess features
X_train_processed, feature_names = self.preprocessor.prepare_features(
X_train, y_train, fit=True
)
X_test_processed, _ = self.preprocessor.prepare_features(
X_test, fit=False
)
# Train model
self.trainer.train(X_train_processed, y_train.values, **kwargs)
self.status = ModelStatus.TRAINED
# Evaluate model
train_metrics = self.trainer.evaluate(X_train_processed, y_train.values)
test_metrics = self.trainer.evaluate(X_test_processed, y_test.values)
# Cross-validation
cv_results = self.evaluator.cross_validate(
self.trainer.model, X_train_processed, y_train.values
)
# Feature importance
feature_importance = self.evaluator.feature_importance(
self.trainer.model, feature_names
)
self.pipeline_metrics = {
"train_metrics": train_metrics.to_dict(),
"test_metrics": test_metrics.to_dict(),
"cv_results": cv_results,
"feature_importance": feature_importance,
"feature_names": feature_names
}
self.status = ModelStatus.EVALUATED
return self.pipeline_metrics
def predict(self, X: pd.DataFrame) -> np.ndarray:
"""Make predictions on new data"""
if self.status not in [ModelStatus.TRAINED, ModelStatus.EVALUATED, ModelStatus.DEPLOYED]:
raise ValueError("Model not trained yet")
X_processed, _ = self.preprocessor.prepare_features(X, fit=False)
return self.trainer.predict(X_processed)
def save_pipeline(self, filepath: str) -> None:
"""Save complete pipeline"""
pipeline_data = {
"model_type": self.model_type.value,
"algorithm": self.algorithm,
"status": self.status.value,
"metrics": self.pipeline_metrics,
"feature_names": self.preprocessor.feature_names
}
# Save model
self.trainer.save_model(f"{filepath}_model.pkl")
# Save preprocessor
self.preprocessor.save_preprocessor(f"{filepath}_preprocessor.pkl")
# Save pipeline metadata
with open(f"{filepath}_metadata.json", 'w') as f:
json.dump(pipeline_data, f, indent=2)
def load_pipeline(self, filepath: str) -> None:
"""Load complete pipeline"""
# Load metadata
with open(f"{filepath}_metadata.json", 'r') as f:
pipeline_data = json.load(f)
self.model_type = ModelType(pipeline_data["model_type"])
self.algorithm = pipeline_data["algorithm"]
self.status = ModelStatus(pipeline_data["status"])
self.pipeline_metrics = pipeline_data["metrics"]
# Load model
self.setup_trainer(self.algorithm)
self.trainer.load_model(f"{filepath}_model.pkl")
# Load preprocessor
self.preprocessor.load_preprocessor(f"{filepath}_preprocessor.pkl")
self.preprocessor.feature_names = pipeline_data["feature_names"]
# Usage examples
def example_ml_pipeline():
"""Example ML pipeline usage"""
# Create sample data
np.random.seed(42)
n_samples = 1000
X = pd.DataFrame({
'feature1': np.random.normal(0, 1, n_samples),
'feature2': np.random.normal(0, 1, n_samples),
'feature3': np.random.normal(0, 1, n_samples),
'category': np.random.choice(['A', 'B', 'C'], n_samples)
})
y = (X['feature1'] + X['feature2'] + np.random.normal(0, 0.1, n_samples) > 0).astype(int)
# Create classification pipeline
pipeline = ModelPipeline(ModelType.CLASSIFICATION)
pipeline.setup_trainer("random_forest")
# Train pipeline
metrics = pipeline.train_pipeline(X, y, test_size=0.2)
print(f"Training metrics: {metrics['train_metrics']}")
print(f"Test metrics: {metrics['test_metrics']}")
# Make predictions
new_data = pd.DataFrame({
'feature1': [1, -1, 0],
'feature2': [1, -1, 0],
'feature3': [1, -1, 0],
'category': ['A', 'B', 'C']
})
predictions = pipeline.predict(new_data)
print(f"Predictions: {predictions}")
# Save pipeline
pipeline.save_pipeline("my_model")
# Load pipeline
new_pipeline = ModelPipeline(ModelType.CLASSIFICATION)
new_pipeline.load_pipeline("my_model")
# Make predictions with loaded pipeline
loaded_predictions = new_pipeline.predict(new_data)
print(f"Loaded pipeline predictions: {loaded_predictions}")
Model Validation¶
# python/02-model-validation.py
"""
Model validation patterns and evaluation strategies
"""
from typing import List, Dict, Any, Optional, Union, Tuple
import pandas as pd
import numpy as np
from sklearn.model_selection import StratifiedKFold, TimeSeriesSplit
from sklearn.metrics import confusion_matrix, roc_auc_score, roc_curve
from sklearn.metrics import precision_recall_curve, average_precision_score
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta
import logging
logger = logging.getLogger(__name__)
class ModelValidator:
"""Comprehensive model validation"""
def __init__(self):
self.validation_results = {}
self.cv_results = {}
def stratified_cv(self, model, X: np.ndarray, y: np.ndarray,
cv: int = 5) -> Dict[str, Any]:
"""Perform stratified cross-validation"""
skf = StratifiedKFold(n_splits=cv, shuffle=True, random_state=42)
cv_scores = []
fold_results = []
for fold, (train_idx, val_idx) in enumerate(skf.split(X, y)):
X_train, X_val = X[train_idx], X[val_idx]
y_train, y_val = y[train_idx], y[val_idx]
# Train model
model.fit(X_train, y_train)
# Make predictions
y_pred = model.predict(X_val)
y_pred_proba = model.predict_proba(X_val)[:, 1] if hasattr(model, 'predict_proba') else None
# Calculate metrics
fold_metrics = {
"fold": fold + 1,
"accuracy": accuracy_score(y_val, y_pred),
"precision": precision_score(y_val, y_pred, average='weighted'),
"recall": recall_score(y_val, y_pred, average='weighted'),
"f1": f1_score(y_val, y_pred, average='weighted')
}
if y_pred_proba is not None:
fold_metrics["auc"] = roc_auc_score(y_val, y_pred_proba)
cv_scores.append(fold_metrics["accuracy"])
fold_results.append(fold_metrics)
result = {
"cv_scores": cv_scores,
"mean_score": np.mean(cv_scores),
"std_score": np.std(cv_scores),
"fold_results": fold_results,
"cv_folds": cv
}
self.cv_results["stratified_cv"] = result
return result
def time_series_cv(self, model, X: np.ndarray, y: np.ndarray,
cv: int = 5) -> Dict[str, Any]:
"""Perform time series cross-validation"""
tscv = TimeSeriesSplit(n_splits=cv)
cv_scores = []
fold_results = []
for fold, (train_idx, val_idx) in enumerate(tscv.split(X)):
X_train, X_val = X[train_idx], X[val_idx]
y_train, y_val = y[train_idx], y[val_idx]
# Train model
model.fit(X_train, y_train)
# Make predictions
y_pred = model.predict(X_val)
# Calculate metrics
fold_metrics = {
"fold": fold + 1,
"train_size": len(train_idx),
"val_size": len(val_idx),
"accuracy": accuracy_score(y_val, y_pred),
"mse": mean_squared_error(y_val, y_pred),
"r2": r2_score(y_val, y_pred)
}
cv_scores.append(fold_metrics["accuracy"])
fold_results.append(fold_metrics)
result = {
"cv_scores": cv_scores,
"mean_score": np.mean(cv_scores),
"std_score": np.std(cv_scores),
"fold_results": fold_results,
"cv_folds": cv
}
self.cv_results["time_series_cv"] = result
return result
def confusion_matrix_analysis(self, y_true: np.ndarray, y_pred: np.ndarray) -> Dict[str, Any]:
"""Analyze confusion matrix"""
cm = confusion_matrix(y_true, y_pred)
# Calculate metrics from confusion matrix
tn, fp, fn, tp = cm.ravel()
accuracy = (tp + tn) / (tp + tn + fp + fn)
precision = tp / (tp + fp) if (tp + fp) > 0 else 0
recall = tp / (tp + fn) if (tp + fn) > 0 else 0
specificity = tn / (tn + fp) if (tn + fp) > 0 else 0
f1 = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0
result = {
"confusion_matrix": cm.tolist(),
"true_negatives": int(tn),
"false_positives": int(fp),
"false_negatives": int(fn),
"true_positives": int(tp),
"accuracy": accuracy,
"precision": precision,
"recall": recall,
"specificity": specificity,
"f1_score": f1
}
self.validation_results["confusion_matrix"] = result
return result
def roc_analysis(self, y_true: np.ndarray, y_pred_proba: np.ndarray) -> Dict[str, Any]:
"""Analyze ROC curve and AUC"""
auc_score = roc_auc_score(y_true, y_pred_proba)
fpr, tpr, thresholds = roc_curve(y_true, y_pred_proba)
# Find optimal threshold
optimal_idx = np.argmax(tpr - fpr)
optimal_threshold = thresholds[optimal_idx]
result = {
"auc_score": auc_score,
"fpr": fpr.tolist(),
"tpr": tpr.tolist(),
"thresholds": thresholds.tolist(),
"optimal_threshold": optimal_threshold,
"optimal_tpr": tpr[optimal_idx],
"optimal_fpr": fpr[optimal_idx]
}
self.validation_results["roc_analysis"] = result
return result
def precision_recall_analysis(self, y_true: np.ndarray, y_pred_proba: np.ndarray) -> Dict[str, Any]:
"""Analyze precision-recall curve"""
precision, recall, thresholds = precision_recall_curve(y_true, y_pred_proba)
ap_score = average_precision_score(y_true, y_pred_proba)
# Find optimal threshold
f1_scores = 2 * (precision * recall) / (precision + recall)
optimal_idx = np.argmax(f1_scores)
optimal_threshold = thresholds[optimal_idx] if optimal_idx < len(thresholds) else thresholds[-1]
result = {
"ap_score": ap_score,
"precision": precision.tolist(),
"recall": recall.tolist(),
"thresholds": thresholds.tolist(),
"f1_scores": f1_scores.tolist(),
"optimal_threshold": optimal_threshold,
"optimal_precision": precision[optimal_idx],
"optimal_recall": recall[optimal_idx],
"optimal_f1": f1_scores[optimal_idx]
}
self.validation_results["precision_recall"] = result
return result
def bias_analysis(self, model, X: np.ndarray, y: np.ndarray,
sensitive_features: List[str]) -> Dict[str, Any]:
"""Analyze model bias across sensitive features"""
bias_results = {}
for feature in sensitive_features:
if feature in X.columns:
feature_values = X[feature].unique()
feature_bias = {}
for value in feature_values:
mask = X[feature] == value
X_subset = X[mask]
y_subset = y[mask]
if len(X_subset) > 0:
y_pred = model.predict(X_subset)
# Calculate metrics for this subgroup
subgroup_metrics = {
"sample_size": len(X_subset),
"accuracy": accuracy_score(y_subset, y_pred),
"precision": precision_score(y_subset, y_pred, average='weighted'),
"recall": recall_score(y_subset, y_pred, average='weighted'),
"f1": f1_score(y_subset, y_pred, average='weighted')
}
feature_bias[value] = subgroup_metrics
bias_results[feature] = feature_bias
self.validation_results["bias_analysis"] = bias_results
return bias_results
class ModelComparator:
"""Model comparison utilities"""
def __init__(self):
self.comparison_results = {}
def compare_models(self, models: Dict[str, Any], X: np.ndarray, y: np.ndarray) -> Dict[str, Any]:
"""Compare multiple models"""
comparison_results = {}
for model_name, model in models.items():
# Train model
model.fit(X, y)
# Make predictions
y_pred = model.predict(X)
y_pred_proba = model.predict_proba(X)[:, 1] if hasattr(model, 'predict_proba') else None
# Calculate metrics
metrics = {
"accuracy": accuracy_score(y, y_pred),
"precision": precision_score(y, y_pred, average='weighted'),
"recall": recall_score(y, y_pred, average='weighted'),
"f1": f1_score(y, y_pred, average='weighted')
}
if y_pred_proba is not None:
metrics["auc"] = roc_auc_score(y, y_pred_proba)
comparison_results[model_name] = metrics
# Find best model
best_model = max(comparison_results.items(), key=lambda x: x[1]["f1"])
result = {
"model_comparison": comparison_results,
"best_model": best_model[0],
"best_metrics": best_model[1]
}
self.comparison_results = result
return result
def statistical_significance_test(self, model1_metrics: List[float],
model2_metrics: List[float]) -> Dict[str, Any]:
"""Test statistical significance between models"""
from scipy import stats
# Paired t-test
t_stat, p_value = stats.ttest_rel(model1_metrics, model2_metrics)
result = {
"t_statistic": t_stat,
"p_value": p_value,
"significant": p_value < 0.05,
"model1_mean": np.mean(model1_metrics),
"model2_mean": np.mean(model2_metrics),
"difference": np.mean(model1_metrics) - np.mean(model2_metrics)
}
return result
# Usage examples
def example_model_validation():
"""Example model validation usage"""
# Create sample data
np.random.seed(42)
n_samples = 1000
X = pd.DataFrame({
'feature1': np.random.normal(0, 1, n_samples),
'feature2': np.random.normal(0, 1, n_samples),
'feature3': np.random.normal(0, 1, n_samples),
'sensitive_feature': np.random.choice(['A', 'B'], n_samples)
})
y = (X['feature1'] + X['feature2'] + np.random.normal(0, 0.1, n_samples) > 0).astype(int)
# Create models
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
models = {
"Random Forest": RandomForestClassifier(random_state=42),
"Logistic Regression": LogisticRegression(random_state=42),
"SVM": SVC(probability=True, random_state=42)
}
# Model validation
validator = ModelValidator()
# Stratified CV
rf_model = RandomForestClassifier(random_state=42)
cv_results = validator.stratified_cv(rf_model, X.values, y.values)
print(f"CV Results: {cv_results['mean_score']:.4f} ยฑ {cv_results['std_score']:.4f}")
# Confusion matrix analysis
rf_model.fit(X.values, y.values)
y_pred = rf_model.predict(X.values)
cm_results = validator.confusion_matrix_analysis(y.values, y_pred)
print(f"Confusion Matrix Analysis: {cm_results['accuracy']:.4f}")
# ROC analysis
y_pred_proba = rf_model.predict_proba(X.values)[:, 1]
roc_results = validator.roc_analysis(y.values, y_pred_proba)
print(f"ROC AUC: {roc_results['auc_score']:.4f}")
# Bias analysis
bias_results = validator.bias_analysis(rf_model, X, y.values, ['sensitive_feature'])
print(f"Bias Analysis: {bias_results}")
# Model comparison
comparator = ModelComparator()
comparison_results = comparator.compare_models(models, X.values, y.values)
print(f"Best Model: {comparison_results['best_model']}")
print(f"Best Metrics: {comparison_results['best_metrics']}")
TL;DR Runbook¶
Quick Start¶
# 1. Data preprocessing
preprocessor = DataPreprocessor()
X_processed, feature_names = preprocessor.prepare_features(X, y, fit=True)
# 2. Model training
trainer = ClassificationTrainer("random_forest")
trainer.train(X_processed, y.values)
# 3. Model evaluation
metrics = trainer.evaluate(X_test, y_test)
print(f"Model accuracy: {metrics.accuracy:.4f}")
# 4. Complete pipeline
pipeline = ModelPipeline(ModelType.CLASSIFICATION)
pipeline.setup_trainer("random_forest")
metrics = pipeline.train_pipeline(X, y)
# 5. Model validation
validator = ModelValidator()
cv_results = validator.stratified_cv(model, X, y)
Essential Patterns¶
# Complete ML setup
def setup_ml_pipeline():
"""Setup complete ML pipeline environment"""
# Data preprocessor
preprocessor = DataPreprocessor()
# Model trainer
trainer = ClassificationTrainer("random_forest")
# Model evaluator
evaluator = ModelEvaluator()
# Model validator
validator = ModelValidator()
# Model comparator
comparator = ModelComparator()
# Complete pipeline
pipeline = ModelPipeline(ModelType.CLASSIFICATION)
print("ML pipeline setup complete!")
This guide provides the complete machinery for Python machine learning. Each pattern includes implementation examples, training strategies, and real-world usage patterns for enterprise ML management.