MLOps Prototyping
Goal
To create standardized, reproducible, and production-ready prototypes in Jupyter notebooks. This skill enforces a structured layout (Imports -> Configs -> Load -> EDA -> Modeling -> Eval) and robust engineering practices (Pipelines, Split-Verification) to prevent technical debt and data leakage.
Prerequisites
- Language: Python 3.14
- Environment:
uv managed project (.venv), with ipykernel in a notebook dependency group
- Context: Executed within a
.ipynb file or converting to one.
Instructions
1. Notebook Structure
Enforce the following linear sections in every notebook to ensure readability and maintainability.
- Title & Purpose: H1 Title and a brief description of the experiment goals.
- Imports: Group standard libraries, third-party, and usage-specific imports.
- Configs: Define Global Constants (paths, random seeds, hyperparameters) here. No magic numbers deeper in the code.
- Datasets: Load, validate, and split data.
- Analysis (EDA): Inspect target distributions and correlations.
- Modeling: Define and train
sklearn.pipeline.Pipeline objects.
- Evaluations: Compute metrics and visualize performance on held-out data.
2. Configuration Standards
Expose all "knobs" at the top of the notebook for easy experimentation.
Randomness: Define RANDOM_STATE = 42 and use it in splits and model initialization.
Paths: Use pathlib for robust path handling.
from pathlib import Path
ROOT = Path("..")
DATA_PATH = ROOT / "data" / "input.parquet"
Hyperparameters: Group model params (e.g., N_ESTIMATORS, MAX_DEPTH).
Toggles: Use booleans for expensive operations (e.g., USE_GPU = True, RUN_GRID_SEARCH = False).
3. Data Management
Ensure data integrity and prevent leakage.
- Loading: Prefer
pd.read_parquet for speed/types, or pd.read_csv.
- Splitting:
- Always split into
X_train, X_test, y_train, y_test before any data-dependent transformations (imputation, scaling).
- Random Split: Use
sklearn.model_selection.train_test_split with stratify for balanced classification.
- Time Series: Use
sklearn.model_selection.TimeSeriesSplit if data has a temporal dimension (do NOT shuffle).
- Use
random_state=RANDOM_STATE.
4. Pipeline Construction
Prohibit raw data transformations on the full dataset.
Mandate: Use sklearn.pipeline.Pipeline or ColumnTransformer.
Why: Automation of fit on train and transform on test prevents data leakage.
Example:
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
CACHE = "./.cache" # Define a cache directory
numeric_transformer = Pipeline(
steps=[
("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler()),
]
)
preprocessor = ColumnTransformer(
transformers=[("num", numeric_transformer, numeric_features)]
)
# Use 'memory' to cache transformer outputs, speeding up GridSearch
model = Pipeline(
steps=[
("preprocessor", preprocessor),
("classifier", RandomForestClassifier()),
],
memory=CACHE,
)
5. Experiment Tracking from the Notebook
Prototypes are experiments; record them from the first run rather than retrofitting tracking later.
- Backend: Point MLflow at a SQL store, not the deprecated file store:
mlflow.set_tracking_uri("sqlite:///mlflow.db"). SQLite is a real SQLAlchemy backend, it supports the model registry, and it is the same store shape as a production Postgres — so moving up later is a URI change, not a rewrite.
- Autologging:
mlflow.autolog() before fit captures parameters, metrics, and the model for scikit-learn without extra code.
- Scope: Keep one MLflow experiment per notebook question, and name runs after the hypothesis being tested.
6. Evaluation & Visualization
Go beyond accuracy/MSE.
- Metrics: Use
sklearn.metrics appropriate for the task (F1, ROC-AUC, RMSE, MAE).
- Baselines: Compare against a "Dummy" model (mean/mode) to verify learning.
- Visualization:
- Regression: Residual plots, Actual vs Predicted.
- Classification: Confusion Matrix, ROC Curve, Precision-Recall.
- Feature Importance: Visualize
feature_importances_ or SHAP values.
7. Transition to Production
Facilitate the move from notebook to python package (src/).
- Function Refactoring: Once a block of code is stable (e.g., a complex data cleaning step), refactor it into a function within the notebook. This makes moving it to a
.py file trivial later.
- Cell Tagging: Use tags like
parameters (for Papermill) or export to mark cells that should be part of the final documentation or automated pipeline.
- Clean State: Ensure the notebook runs top-to-bottom (
Restart Kernel and Run All) without errors before committing.
- Formatting: Ruff 0.16 formats Python inside Markdown too, so
mise run format normalizes the snippets you paste into notes and docs. Run mise run all before committing a notebook alongside package code.
- Next step: mlops-industrialization covers the package layout the refactored functions move into.
Self-Correction Checklist
1---2name: mlops-prototyping3description: Structure reproducible Jupyter notebooks with a fixed section layout, hoisted configuration, and leakage-free scikit-learn pipelines. Use when exploring a dataset, training a first model, or preparing a notebook for promotion.4license: MIT5---67# MLOps Prototyping89## Goal1011To create standardized, reproducible, and production-ready prototypes in Jupyter notebooks. This skill enforces a structured layout (Imports -> Configs -> Load -> EDA -> Modeling -> Eval) and robust engineering practices (Pipelines, Split-Verification) to prevent technical debt and data leakage.1213## Prerequisites1415- **Language**: Python 3.1416- **Environment**: `uv` managed project (`.venv`), with `ipykernel` in a `notebook` dependency group17- **Context**: Executed within a `.ipynb` file or converting to one.1819## Instructions2021### 1. Notebook Structure2223Enforce the following linear sections in every notebook to ensure readability and maintainability.24251. **Title & Purpose**: H1 Title and a brief description of the experiment goals.261. **Imports**: Group standard libraries, third-party, and usage-specific imports.271. **Configs**: Define **Global Constants** (paths, random seeds, hyperparameters) here. No magic numbers deeper in the code.281. **Datasets**: Load, validate, and split data.291. **Analysis (EDA)**: Inspect target distributions and correlations.301. **Modeling**: Define and train `sklearn.pipeline.Pipeline` objects.311. **Evaluations**: Compute metrics and visualize performance on held-out data.3233### 2. Configuration Standards3435Expose all "knobs" at the top of the notebook for easy experimentation.3637- **Randomness**: Define `RANDOM_STATE = 42` and use it in splits and model initialization.38- **Paths**: Use `pathlib` for robust path handling.3940 ```python41 from pathlib import Path4243 ROOT = Path("..")44 DATA_PATH = ROOT / "data" / "input.parquet"45 ```4647- **Hyperparameters**: Group model params (e.g., `N_ESTIMATORS`, `MAX_DEPTH`).48- **Toggles**: Use booleans for expensive operations (e.g., `USE_GPU = True`, `RUN_GRID_SEARCH = False`).4950### 3. Data Management5152Ensure data integrity and prevent leakage.5354- **Loading**: Prefer `pd.read_parquet` for speed/types, or `pd.read_csv`.55- **Splitting**:56 - **Always** split into `X_train`, `X_test`, `y_train`, `y_test` **before** any data-dependent transformations (imputation, scaling).57 - **Random Split**: Use `sklearn.model_selection.train_test_split` with `stratify` for balanced classification.58 - **Time Series**: Use `sklearn.model_selection.TimeSeriesSplit` if data has a temporal dimension (do NOT shuffle).59 - Use `random_state=RANDOM_STATE`.6061### 4. Pipeline Construction6263Prohibit raw data transformations on the full dataset.6465- **Mandate**: Use `sklearn.pipeline.Pipeline` or `ColumnTransformer`.66- **Why**: Automation of `fit` on train and `transform` on test prevents data leakage.67- **Example**:6869 ```python70 from sklearn.compose import ColumnTransformer71 from sklearn.impute import SimpleImputer72 from sklearn.pipeline import Pipeline73 from sklearn.preprocessing import StandardScaler7475 CACHE = "./.cache" # Define a cache directory7677 numeric_transformer = Pipeline(78 steps=[79 ("imputer", SimpleImputer(strategy="median")),80 ("scaler", StandardScaler()),81 ]82 )8384 preprocessor = ColumnTransformer(85 transformers=[("num", numeric_transformer, numeric_features)]86 )8788 # Use 'memory' to cache transformer outputs, speeding up GridSearch89 model = Pipeline(90 steps=[91 ("preprocessor", preprocessor),92 ("classifier", RandomForestClassifier()),93 ],94 memory=CACHE,95 )96 ```9798### 5. Experiment Tracking from the Notebook99100Prototypes are experiments; record them from the first run rather than retrofitting tracking later.1011021. **Backend**: Point MLflow at a SQL store, not the deprecated file store: `mlflow.set_tracking_uri("sqlite:///mlflow.db")`. SQLite is a real SQLAlchemy backend, it supports the model registry, and it is the same store shape as a production Postgres — so moving up later is a URI change, not a rewrite.1031. **Autologging**: `mlflow.autolog()` before `fit` captures parameters, metrics, and the model for scikit-learn without extra code.1041. **Scope**: Keep one MLflow experiment per notebook question, and name runs after the hypothesis being tested.105106### 6. Evaluation & Visualization107108Go beyond accuracy/MSE.109110- **Metrics**: Use `sklearn.metrics` appropriate for the task (F1, ROC-AUC, RMSE, MAE).111- **Baselines**: Compare against a "Dummy" model (mean/mode) to verify learning.112- **Visualization**:113 - **Regression**: Residual plots, Actual vs Predicted.114 - **Classification**: Confusion Matrix, ROC Curve, Precision-Recall.115 - **Feature Importance**: Visualize `feature_importances_` or SHAP values.116117### 7. Transition to Production118119Facilitate the move from notebook to python package (`src/`).120121- **Function Refactoring**: Once a block of code is stable (e.g., a complex data cleaning step), refactor it into a function _within_ the notebook. This makes moving it to a `.py` file trivial later.122- **Cell Tagging**: Use tags like `parameters` (for Papermill) or `export` to mark cells that should be part of the final documentation or automated pipeline.123- **Clean State**: Ensure the notebook runs top-to-bottom (`Restart Kernel and Run All`) without errors before committing.124- **Formatting**: Ruff 0.16 formats Python inside Markdown too, so `mise run format` normalizes the snippets you paste into notes and docs. Run `mise run all` before committing a notebook alongside package code.125- **Next step**: [mlops-industrialization](../mlops-industrialization/SKILL.md) covers the package layout the refactored functions move into.126127## Self-Correction Checklist128129- [ ] **No Magic Numbers**: Are all parameters in the `Configs` section?130- [ ] **No Data Leakage**: Is `fit` called ONLY on `X_train`?131- [ ] **Reproducibility**: Is `random_state` set for all stochastic operations?132- [ ] **Resilience**: Are paths defined relative to the project root?133- [ ] **Tracking**: Do runs land in a SQL-backed MLflow store rather than the deprecated file store?134- [ ] **Clarity**: Does the notebook read like a report (Markdown cells explaining the _Why_)?