Python for Data Science
Week 3 Project Report
Hyperparameter Tuning · Pipelines · Advanced Models · Deployment
1. Week 3 Overview
Week 3 builds on the baseline models trained in Week 2. The focus shifts from building models
to improving, systematising, and deploying them. You will tune hyperparameters, create end-to-
end pipelines, explore advanced gradient boosting algorithms, handle class imbalance, and
deploy your first model as a live web API.
Pillar Focus Key Outcome
Hyperparameter Tuning Grid & Randomised search Optimised model performance
Pipelines Combine pre-processing + model Reproducible ML workflow
Advanced Models XGBoost & Gradient Boosting Higher accuracy predictions
Class Imbalance SMOTE & class weighting Fairer model for rare events
Model Deployment Flask REST API Live prediction endpoint
2. Five-Day Project Plan
Day 1 — Hyperparameter Tuning
Default model settings are rarely optimal. Hyperparameter tuning systematically searches for
the configuration that maximises validation performance without overfitting.
Concepts
• GridSearchCV: exhaustive search over a defined parameter grid
• RandomizedSearchCV: random sampling — faster for large search spaces
• Cross-validation: evaluate each candidate on k folds to avoid lucky splits
Python for Data Science — Week 3 Report | Page 1
• Scoring metrics: accuracy, f1, roc_auc — choose based on your problem
Code — GridSearchCV
from [Link] import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
param_grid = {
'n_estimators': [100, 200, 300],
'max_depth': [None, 5, 10, 15],
'min_samples_split': [2, 5, 10],
'max_features': ['sqrt', 'log2']
}
rf = RandomForestClassifier(random_state=42)
grid_search = GridSearchCV(
rf, param_grid, cv=5,
scoring='roc_auc', n_jobs=-1, verbose=1
)
grid_search.fit(X_train, y_train)
print('Best params:', grid_search.best_params_)
print('Best AUC: ', round(grid_search.best_score_, 4))
Code — RandomizedSearchCV
from sklearn.model_selection import RandomizedSearchCV
from [Link] import randint
param_dist = {
'n_estimators': randint(100, 500),
'max_depth': randint(3, 20),
'min_samples_split': randint(2, 15),
'max_features': ['sqrt', 'log2']
}
rand_search = RandomizedSearchCV(
rf, param_dist, n_iter=50, cv=5,
scoring='roc_auc', n_jobs=-1, random_state=42
)
rand_search.fit(X_train, y_train)
print('Best params:', rand_search.best_params_)
Day 2 — Scikit-learn Pipelines
A Pipeline chains pre-processing steps and a model into a single, reusable object. This
eliminates data leakage, simplifies cross-validation, and makes your workflow production-ready.
Why Pipelines?
• Prevent data leakage: scaler is fit only on training data, never the test set
• Cleaner code: one fit() and predict() call covers all steps
• Serialisable: save the entire workflow to a single .pkl file
• CV-compatible: GridSearchCV works directly on the pipeline
Python for Data Science — Week 3 Report | Page 2
Code — Building a Pipeline
from [Link] import Pipeline
from [Link] import StandardScaler
from [Link] import SimpleImputer
from [Link] import RandomForestClassifier
from [Link] import ColumnTransformer
from [Link] import OneHotEncoder
numeric_features = ['Age', 'Fare', 'FamilySize']
categoric_features = ['Pclass', 'Sex', 'Embarked']
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
categoric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(handle_unknown='ignore'))
])
preprocessor = ColumnTransformer(transformers=[
('num', numeric_transformer, numeric_features),
('cat', categoric_transformer, categoric_features)
])
pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('model', RandomForestClassifier(n_estimators=200, random_state=42))
])
[Link](X_train, y_train)
print('Pipeline Accuracy:', [Link](X_test, y_test))
Code — Tuning a Pipeline with GridSearchCV
# Note: use __ to reference steps inside the pipeline
param_grid = {
'model__n_estimators': [100, 200],
'model__max_depth': [5, 10, None]
}
grid = GridSearchCV(pipeline, param_grid, cv=5,
scoring='roc_auc', n_jobs=-1)
[Link](X_train, y_train)
print('Best pipeline AUC:', round(grid.best_score_, 4))
Day 3 — Advanced Models: XGBoost & Gradient Boosting
Gradient boosting algorithms consistently top Kaggle leaderboards and real-world benchmarks.
They build an ensemble of weak learners sequentially, each correcting the errors of the last.
Model Comparison
Python for Data Science — Week 3 Report | Page 3
Model Speed Accuracy Interpretabilit Best For
y
Logistic Regression Very fast Moderate High Baselines, linear data
Random Forest Fast Good Medium General classification
Gradient Boosting Medium Very good Low Structured/tabular data
XGBoost Fast Excellent Low Competitions & production
LightGBM Very fast Excellent Low Large datasets
Code — XGBoost
# pip install xgboost
from xgboost import XGBClassifier
from [Link] import roc_auc_score
xgb = XGBClassifier(
n_estimators=300,
max_depth=5,
learning_rate=0.05,
subsample=0.8,
colsample_bytree=0.8,
use_label_encoder=False,
eval_metric='logloss',
random_state=42
)
[Link](X_train, y_train,
eval_set=[(X_test, y_test)],
verbose=50)
y_proba = xgb.predict_proba(X_test)[:, 1]
print('XGB AUC:', round(roc_auc_score(y_test, y_proba), 4))
Code — LightGBM
# pip install lightgbm
import lightgbm as lgb
lgbm = [Link](
n_estimators=300,
max_depth=6,
learning_rate=0.05,
num_leaves=31,
random_state=42
)
[Link](X_train, y_train)
y_proba_lgb = lgbm.predict_proba(X_test)[:, 1]
print('LGB AUC:', round(roc_auc_score(y_test, y_proba_lgb), 4))
Python for Data Science — Week 3 Report | Page 4
Day 4 — Handling Class Imbalance
In many real-world datasets (fraud detection, medical diagnosis, churn prediction), one class is
far rarer than the other. Standard models trained on imbalanced data tend to ignore the minority
class.
Strategies
• Class weighting: penalise misclassifications of the minority class more heavily
• SMOTE: Synthetic Minority Over-sampling Technique — generate synthetic minority
samples
• Under-sampling: randomly remove majority-class samples to balance the dataset
• Threshold tuning: lower the classification threshold to favour minority recall
Code — Class Weighting
from [Link] import RandomForestClassifier
# class_weight='balanced' auto-adjusts weights inversely
# proportional to class frequencies
rf_balanced = RandomForestClassifier(
n_estimators=200,
class_weight='balanced',
random_state=42
)
rf_balanced.fit(X_train, y_train)
from [Link] import classification_report
print(classification_report(y_test, rf_balanced.predict(X_test)))
Code — SMOTE Oversampling
# pip install imbalanced-learn
from imblearn.over_sampling import SMOTE
from collections import Counter
print('Before:', Counter(y_train))
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X_train, y_train)
print('After:', Counter(y_resampled))
# Train on balanced data
rf_smote = RandomForestClassifier(n_estimators=200, random_state=42)
rf_smote.fit(X_resampled, y_resampled)
print(classification_report(y_test, rf_smote.predict(X_test)))
Code — Threshold Tuning
import numpy as np
from [Link] import f1_score
proba = rf_balanced.predict_proba(X_test)[:, 1]
Python for Data Science — Week 3 Report | Page 5
# Find threshold that maximises F1 for minority class
thresholds = [Link](0.1, 0.9, 0.01)
f1_scores = [f1_score(y_test, proba >= t) for t in thresholds]
best_t = thresholds[[Link](f1_scores)]
print(f'Best threshold: {best_t:.2f} F1: {max(f1_scores):.4f}')
Day 5 — Model Deployment with Flask
A trained model only creates value when others can use it. Day 5 serialises your best model
and exposes it as a REST API using Flask, accepting JSON input and returning predictions.
Step 1 — Save the Model
import joblib
# Save trained pipeline to disk
[Link](pipeline, 'model_pipeline.pkl')
print('Model saved.')
# Later — load it back
loaded_model = [Link]('model_pipeline.pkl')
Step 2 — Build the Flask API ([Link])
# pip install flask
from flask import Flask, request, jsonify
import joblib, pandas as pd
app = Flask(__name__)
model = [Link]('model_pipeline.pkl')
@[Link]('/predict', methods=['POST'])
def predict():
data = request.get_json()
df = [Link]([data])
pred = [Link](df)[0]
prob = model.predict_proba(df)[0][1]
return jsonify({
'prediction': int(pred),
'probability': round(float(prob), 4)
})
if __name__ == '__main__':
[Link](debug=True, port=5000)
Step 3 — Test the API
# Run in terminal: python [Link]
# Test with curl
curl -X POST [Link] \
-H 'Content-Type: application/json' \
-d '{"Pclass":1,"Sex":"female","Age":29,
"Fare":100,"FamilySize":1,
Python for Data Science — Week 3 Report | Page 6
"Embarked":"S"}'
# Expected response:
# { "prediction": 1, "probability": 0.8732 }
3. Key Libraries Reference
Library Purpose Install Command
scikit-learn Pipelines, tuning, baseline models pip install scikit-learn
xgboost Extreme gradient boosting pip install xgboost
lightgbm Fast gradient boosting (large data) pip install lightgbm
imbalanced-learn SMOTE & resampling techniques pip install imbalanced-learn
joblib Model serialisation (save/load) pip install joblib
flask Lightweight REST API server pip install flask
scipy Statistical distributions for search pip install scipy
4. Week 3 Model Performance Tracker
Use this table to record your results as you work through each day. Fill in your actual AUC,
accuracy, and F1 scores.
Model Tuned AUC Accurac F1 Notes
? Score y (Minority)
Random Forest No — — — Week 2 baseline
(default)
Random Forest Yes — — — GridSearchCV Day 1
(tuned)
Pipeline + RF Yes — — — Day 2 Pipeline
XGBoost Yes — — — Day 3
LightGBM Yes — — — Day 3
RF + SMOTE No — — — Day 4 Imbalance
Best Model (Flask Yes — — — Day 5 Deployed
API)
Python for Data Science — Week 3 Report | Page 7
5. Week 3 Deliverables Checklist
# Deliverable Status
1 GridSearchCV tuning completed on Random Forest ☐ Pending
2 RandomizedSearchCV run and compared to GridSearch ☐ Pending
3 End-to-end scikit-learn Pipeline built ☐ Pending
4 Pipeline tuned with GridSearchCV ☐ Pending
5 XGBoost model trained and evaluated ☐ Pending
6 LightGBM model trained and evaluated ☐ Pending
7 Class imbalance handled (SMOTE or class weighting) ☐ Pending
8 Optimal classification threshold identified ☐ Pending
9 Best model saved with joblib ☐ Pending
10 Flask API built, tested, and returning predictions ☐ Pending
11 Model performance tracker table filled in ☐ Pending
12 5 insights and conclusions documented in notebook ☐ Pending
6. Sample Week 3 Insights
Document findings like these in Markdown cells inside your Jupyter Notebook:
• Insight 1: Hyperparameter tuning lifted the Random Forest AUC from 0.87 to 0.91 — a
meaningful gain from roughly 30 minutes of grid search.
• Insight 2: The scikit-learn Pipeline eliminated data leakage; accuracy improved by ~2%
compared to the manually scaled Week 2 approach.
• Insight 3: XGBoost (AUC ~0.93) outperformed the tuned Random Forest, consistent
with its reputation on structured/tabular data.
• Insight 4: SMOTE increased recall for the minority class from 61% to 79%, at the cost of
a 3% drop in overall accuracy — a worthwhile trade-off.
• Insight 5: The Flask API returned predictions in under 5 ms per request, confirming that
the pipeline is production-viable for low-latency use cases.
7. Week 4 Preview
Having deployed your first model, Week 4 moves into unsupervised learning, deep learning
fundamentals, and professional MLOps practices:
Python for Data Science — Week 3 Report | Page 8
• Unsupervised learning: K-Means clustering and Principal Component Analysis (PCA)
• Neural networks: introduction to TensorFlow / Keras for tabular and image data
• Model monitoring: detecting data drift and model degradation in production
• Docker basics: containerise your Flask API for consistent deployment
• MLflow: experiment tracking, model registry, and reproducibility
End of Week 3 Report
Python for Data Science — Week 3 Report | Page 9