2. Structured ML Pipeline
Duration2h30 x 2 AI Banned
Introduction
This practical session focuses on transforming exploratory notebook code into structured, production-ready machine learning pipelines. You’ll learn to convert the air quality analysis notebook you previously developed into a modular, testable, and maintainable Python package that follows software engineering best practices.
Through this hands-on workshop, you will master essential skills for transitioning from data science experimentation to production deployment:
- Code Architecture: Transform monolithic notebook cells into modular, reusable classes with clear responsibilities
- Software Engineering Practices: Apply separation of concerns and proper error handling (advanced concepts like dependency injection are introduced but not fully implemented)
- Test-Driven Development: Understand the critical role of automated testing in ML pipelines through comprehensive unit tests
- Production Readiness: Create command-line interfaces and structured workflows suitable for deployment
- Professional Standards: Learn industry best practices used by Machine Learning Engineers in production environments
The challenge is not just to make the code work, but to understand the underlying architecture and complete missing implementations. This deep understanding of code structure will be invaluable for your future internships, projects, and professional work where you’ll need to build upon and extend existing ML systems.
Prerequisites
Before starting this activity, you should already have completed:
- Environment Setup - Python environment with required packages installed
- Git & GitLab How-to - Git basics
- Setup a Data Science project - Git workflow and project structure
- Air Quality Data Analysis - The notebook that serves as the foundation for this structured implementation
Do not forget to also read the introductory lesson:
This activity assumes you have a working air quality analysis notebook and are familiar with the complete data science workflow from data loading to model evaluation.
Understanding the Target Architecture
The air quality project follows a modular pipeline architecture that separates concerns while maintaining clear data flow:
scripts/
├── crypto_utils.py
├── run_pipeline.py
├── run_tests.py
src/
├── pipeline/
│ ├── data_processor.py
│ ├── feature_engineer.py
│ ├── model_trainer.py
│ └── evaluator.py
└── utils/
├── config.py
└── utils.pyCore Components
DataProcessor: Handles data loading, cleaning, and preprocessing operationsFeatureEngineer: Manages feature extraction, encoding, and selection processesModelTrainer: Handles model training, persistence, and prediction workflowsEvaluator: Provides model evaluation metrics and cross-validation functionality
Pipeline Orchestration
run_pipeline.py: Main execution script that orchestrates the complete ML pipeline by integrating all components in the correct sequence
Supporting Infrastructure
config.py: Centralizes configuration parameters and constantsutils.py: Provides general utility functions for plotting and data operationsevaluation_utils.py: Offers advanced evaluation and visualization capabilities
Getting Started
Before implementing the structured architecture, you need to obtain the source code and set up your development environment.
The air quality project source code is available as a downloadable archive containing all necessary files and structure.
Download Air Quality ProjectEnvironment Setup with uv
After downloading and extracting the project, set up your environment using uv with project.toml file.
|
|
Running Commands
Throughout this tutorial, use uv run to execute Python scripts:
|
|
Initial Validation
Before starting your implementation, ensure the project structure is correct:
|
|
You should see test failures for unimplemented TODO sections - this is expected and confirms the testing framework is working correctly.
Implementation Guide
You will transform your notebook code into the structured architecture by completing TODO sections in four main classes. Each method corresponds to specific sections of your original notebook, making the transition straightforward.
Implementation Overview
Complete the following components in order:
- DataProcessor Implementation - Data loading, cleaning, and preprocessing operations
- FeatureEngineer Implementation - Feature extraction, encoding, and selection processes
- ModelTrainer Implementation - Model training, persistence, and prediction workflows
- Evaluator Implementation - Model evaluation metrics and cross-validation functionality
- Pipeline Integration - Integrate all steps into a Machine Learning pipeline
DataProcessor Implementation
The DataProcessor class handles all data-related operations, ensuring temporal and geographic consistency throughout the preprocessing pipeline.
Core Responsibilities:
- Loading training and test datasets
- Managing missing values with temporal awareness
- Creating geographic cross-validation folds
- Maintaining data integrity throughout preprocessing
Info
Before starting implementation: Carefully observe the structure of the DataProcessor class in src/pipeline/data_processor.py. Examine the imports used (pandas, numpy, sklearn, as well as configuration and logging modules) and note how the __init__ constructor initializes the train_data and test_data attributes.
Tip
Pre-implemented functions: Some functions are already provided to help you. The preprocess_data() function orchestrates the complete preprocessing pipeline by calling different steps in the correct order, while load_and_preprocess() is a convenience method that combines loading and preprocessing in a single step.
Method: load_data()
Purpose: Load training and test datasets from CSV files.
Notebook Connection: This implements the data loading section where you read the CSV files into DataFrames.
|
|
Details
Action Required: Implement this method in src/pipeline/data_processor.py. Test your implementation with uv run python scripts/run_tests.py --module data_processor after completion.
Method: forward_back_fill()
Purpose: Handle missing values in time-series data while respecting geographic boundaries.
Notebook Connection: This implements the missing value handling section where you applied forward and backward fill within each city.
|
|
Details
Action Required: Implement this method in src/pipeline/data_processor.py. Test your implementation with uv run python scripts/run_tests.py --module data_processor after completion.
Method: handle_missing_values()
Purpose: Process both training and test datasets city by city to maintain consistency.
|
|
Details
Action Required: Implement this method to process each city independently. Test after implementation.
Method: drop_high_missing_columns()
Purpose: Remove columns with excessive missing data to improve data quality and model performance.
Notebook Connection: This implements the data cleaning section where you identified and removed columns with too many missing values.
|
|
Details
Action Required: Implement this method in src/pipeline/data_processor.py. The method should calculate missing data percentages, identify columns exceeding the threshold, and drop them from both datasets. Test your implementation with uv run python scripts/run_tests.py --module data_processor after completion.
Method: create_geographic_folds()
Purpose: Create cross-validation folds that respect geographic boundaries.
|
|
Details
Action Required: Implement geographic fold creation using GroupKFold. Test with uv run python scripts/run_tests.py --module data_processor.
FeatureEngineer Implementation
The FeatureEngineer class transforms raw data into meaningful features for machine learning models.
Core Responsibilities:
- Extracting temporal patterns (seasonality, trends)
- Creating geographic identifiers and location features
- Encoding categorical variables consistently
- Selecting optimal feature subsets using statistical and model-based methods
Info
Before starting implementation: Carefully observe the structure of the FeatureEngineer class in src/pipeline/feature_engineer.py. Examine the imports used (pandas, numpy, sklearn preprocessing and feature selection modules, as well as configuration modules) and note how the __init__ constructor initializes attributes for storing feature columns, label encoders, and feature selectors.
Tip
Pre-implemented functions: Some functions are already provided to help you. The get_feature_columns() function automatically identifies which columns can be used as features by excluding target and metadata columns, extract_all_features() applies all feature extraction steps in sequence, and select_best_features() provides a unified interface for feature selection methods.
Method: extract_temporal_features()
Purpose: Extract time-based features that capture seasonal and cyclical patterns in air quality data.
Notebook Connection: This corresponds to the temporal feature engineering section where you extracted year, month, day, etc.
|
|
Details
Action Required: Implement temporal feature extraction. Test with uv run python scripts/run_tests.py --module feature_engineer after completion.
Method: extract_geographic_features()
Purpose: Create location-based identifiers for different monitoring stations.
|
|
Details
Action Required: Implement geographic feature creation using coordinate combination.
Method: encode_categorical_features()
Purpose: Convert categorical variables to numerical format while maintaining consistency between training and test sets.
|
|
Details
Action Required: Implement categorical encoding using LabelEncoder for consistent train/test processing.
Method: select_features_selectkbest()
Purpose: Select features using statistical significance testing.
|
|
Details
Action Required: Implement statistical feature selection using SelectKBest.
Method: select_features_rfe()
Purpose: Select features using Recursive Feature Elimination with a linear model.
|
|
Details
Action Required: Implement RFE-based feature selection. Test with uv run python scripts/run_tests.py --module feature_engineer after completion.
ModelTrainer Implementation
The ModelTrainer class handles model creation and training.
Core Responsibilities:
- Training models with proper configuration
- Managing model hyperparameters
- Creating model instances for different algorithms
Info
Before starting implementation: Carefully observe the structure of the ModelTrainer class in src/pipeline/model_trainer.py. Examine the imports used (pandas, numpy, sklearn models, as well as the Evaluator class) and note how the __init__ constructor initializes dictionaries for storing trained models and evaluator instances.
Tip
Pre-implemented functions: Some functions are already provided to help you. The create_model() function is a factory method that creates model instances based on type specifications (a factory is a design pattern that creates objects without specifying their exact class), and predict() provides a standardized interface for making predictions.
Method: train_single_model()
Purpose: Train a single model instance on provided data.
Notebook Connection: This corresponds to the model training sections where you fit LinearRegression models.
|
|
Details
Action Required: Implement model training method. Test with uv run python scripts/run_tests.py --module model_trainer after completion.
Evaluator Implementation
The Evaluator class provides comprehensive model assessment and cross-validation functionality.
Core Responsibilities:
- Calculating standard regression metrics (RMSE, R², MSE)
- Performing cross-validation across geographic folds
- Comparing multiple models systematically
- Generating performance reports and summaries
Info
Before starting implementation: Carefully observe the structure of the Evaluator class in src/pipeline/evaluator.py. Examine the imports used (pandas, numpy, sklearn model selection and metrics modules, as well as configuration and logging modules) and note how the __init__ constructor is simple with no specific attributes to initialize.
Tip
Pre-implemented functions: This class is more focused on core functionality, so most methods require implementation. The constructor __init__() is already provided as it simply initializes an empty evaluator instance without specific attributes.
Method: calculate_metrics()
Purpose: Calculate standard regression metrics for model evaluation.
|
|
Details
Action Required: Implement metrics calculation. Test with uv run python scripts/run_tests.py --module evaluator after completion.
Method: cross_validate_model()
Purpose: Perform cross-validation using GroupKFold to ensure geographic integrity and prevent data leakage.
Notebook Connection: This implements the model evaluation section where you performed cross-validation to assess model performance across different data splits.
|
|
Details
Action Required: Implement cross-validation method with GroupKFold for geographic data splitting. When groups are provided, use GroupKFold to prevent data leakage across cities. When no groups are provided, use standard KFold. Test with uv run python scripts/run_tests.py --module evaluator after completion.
Pipeline Integration
After completing all individual component implementations, you need to integrate them into the main pipeline execution script. The scripts/run_pipeline.py file contains a Step 4 section that requires completion to properly integrate your Evaluator implementation with the overall workflow.
Step 4: Cross-Validation Integration
Purpose: Integrate the cross-validation functionality into the main pipeline execution to evaluate model performance using geographic fold validation.
Notebook Connection: This step integrates all your previous implementations into a complete pipeline that mirrors the evaluation workflow from your original notebook.
Details
Action Required: Complete the TODO sections in Step 4 of scripts/run_pipeline.py to integrate cross-validation into the pipeline execution. Test your implementation with uv run python scripts/run_pipeline.py --log-level verbose.
Testing Strategy
The project includes 45 comprehensive tests that validate technical correctness across all components:
- Data Processing Tests (9 tests): Validate data loading, preprocessing, and missing value handling
- Feature Engineering Tests (11 tests): Check feature extraction, encoding, and selection methods
- Model Training Tests (14 tests): Verify model creation, training, and persistence
- Evaluation Tests (11 tests): Confirm metrics calculation and cross-validation logic
Final Validation
After completing all implementations, run the complete test suite to validate your work:
|
|
Details
Success Criteria: All 45 tests should pass, confirming that your implementation meets the technical requirements for production-ready ML code.
What Tests Validate vs. What They Don’t
✅ Tests Validate:
- Technical Implementation: Methods execute without errors and return expected data structures
- Data Integrity: Original data is preserved through transformations
- API Contracts: Functions accept correct inputs and return expected outputs
- Error Handling: Proper exceptions for invalid inputs
- Integration: Components work together seamlessly
⚠️ Tests Don’t Validate:
- Model Performance: Tests don’t check if models achieve good RMSE/R² scores
- Data Quality: Tests use synthetic data, not real-world validation
- Statistical Significance: Tests verify functionality, not predictive accuracy
- Hyperparameter Optimization: Tests use default parameters for speed
Conclusion
This practical session has guided you through the essential process of transforming exploratory notebook code into production-ready machine learning pipelines. By completing this workshop, you have gained hands-on experience with:
- Modular Architecture: Converting monolithic notebook cells into reusable, testable components
- Software Engineering Practices: Applying separation of concerns, proper error handling, and comprehensive testing
- Production Readiness: Creating structured workflows suitable for deployment and team collaboration
- Professional Standards: Following industry best practices used by Machine Learning Engineers
The structured approach you’ve learned represents a fundamental skill for modern data scientists and ML engineers. This foundation will serve you well in professional environments where code quality, maintainability, and collaboration are essential.
Next Steps
In the next practical session, you will build upon this structured foundation to explore advanced model evaluation and comparison techniques. You’ll learn to:
- Model Selection: Systematically evaluate multiple algorithms and configurations
- Hyperparameter Optimization: Fine-tune model parameters for optimal performance
- Advanced Evaluation: Apply sophisticated validation strategies and performance metrics
- Model Comparison: Compare different approaches using statistical significance testing
This progression from structured code to advanced modeling techniques mirrors the real-world ML development process, preparing you for the challenges of professional machine learning projects.