Capstone: An End-to-End ML-Powered Mini App

Data Science & ML Projects (Intermediate)

Chapter 8 · Capstone: An End-to-End ML-Powered Mini App

Sixteen chapters across both Projects courses, and every one of them has been a script — producing files, printed output, saved charts. This capstone adds the one piece that's been missing the whole time: a way for someone who isn't running Python to actually use what was built.

What We're Building

A "Churn Risk Checker" — reusing dsproj2-2's own churn-prediction pipeline in full (collect, clean, engineer features, train, evaluate), then wrapping the trained model in a minimal Streamlit interface: a small web form where a user enters a customer's own details and gets back a churn risk prediction, with no Python knowledge required to use it.

Stage 1 — Collect, Clean, Engineer (dsproj2-2, Reused)

import pandas as pd

df = pd.read_csv("telco_churn.csv")
df["avg_monthly_spend"] = df["TotalCharges"] / df["tenure"].replace(0, 1)
df = pd.get_dummies(df, columns=["Contract", "InternetService", "TechSupport"], drop_first=True)

y = (df["Churn"] == "Yes").astype(int)
X = df.drop(columns=["customerID", "Churn", "TotalCharges"])

Directly dsproj2-2's own Step 1, unchanged — nothing new to teach here, only to reuse.

Stage 2 — Train & Evaluate (dsproj2-2, Reused)

from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
print(classification_report(y_test, model.predict(X_test)))

Stage 3 — Persisting the Model (New)

import joblib

joblib.dump(model, "churn_model.joblib")
joblib.dump(list(X.columns), "model_columns.joblib")
A genuinely new step — no prior chapter needed this
Every earlier chapter's own model lived and died inside a single script run. An interface needs the trained model to persist between runs — someone opening the app tomorrow shouldn't have to retrain a random forest first. joblib.dump() saves the trained model to disk; saving the exact column order alongside it matters too, since the interface will need to build input rows in that identical order.

Stage 4 — The Minimal Interface

# churn_app.py — run with: streamlit run churn_app.py
import streamlit as st
import pandas as pd
import joblib

model = joblib.load("churn_model.joblib")
columns = joblib.load("model_columns.joblib")

st.title("Churn Risk Checker")

tenure = st.slider("Tenure (months)", 0, 72, 12)
monthly_charges = st.slider("Monthly Charges ($)", 0, 150, 70)
contract = st.selectbox("Contract Type", ["Month-to-month", "One year", "Two year"])

if st.button("Check Risk"):
    row = pd.DataFrame([[0] * len(columns)], columns=columns)
    row["tenure"] = tenure
    row["MonthlyCharges"] = monthly_charges
    row["avg_monthly_spend"] = monthly_charges
    col_name = f"Contract_{contract}"
    if col_name in row.columns:
        row[col_name] = 1

    risk = model.predict_proba(row)[0][1]
    st.metric("Churn Risk", f"{risk:.0%}")

Streamlit turns each line into a real, interactive widget — st.slider(), st.selectbox(), st.button() — with no HTML, CSS, or JavaScript required. Building the input row with the exact saved columns order (Stage 3) is what lets a few manually-set values slot correctly into the same feature layout the model was trained on.

Deliberately minimal, not a substitute for fastapi1
Streamlit is the real, standard tool data scientists reach for to wrap a model in an interface without becoming a web developer — genuinely appropriate for this chapter's own scope. It is not a substitute for fastapi1's own full web-framework depth: no proper routing, no authentication, no production-grade request handling. For a real, public-facing application, that's the right course to reach for instead.

Chapter Attribution Table — Spanning Both Courses

StageDrawn directly from
Collectiondsproj1-1 (scraping), dsproj1-3 (single API), dsproj1-6 (combining APIs)
Cleaningdsproj1-2's own reusable, report-generating pipeline pattern
EDAdsproj1-5's own flowing, narrative exploration approach
Feature engineeringdsproj2-2's own derived features and one-hot encoding
Model comparison & evaluationdsproj2-2's own logistic regression/random forest comparison, cross-validation, and full metric picture
Deep learning (where relevant)dsproj2-1 (LSTM), dsproj2-4 (CNN), dsproj2-6 (BiLSTM tagger)
Model persistenceNew in this capstone — joblib, saving trained state between runs
InterfaceNew in this capstone — Streamlit, deliberately minimal
Scope Note — What This Capstone Deliberately Doesn't Do
  • No production deployment or hosting — running streamlit run locally is not the same as serving a real public application.
  • No authentication, input validation hardening, or security review — this interface trusts its own inputs completely.
  • No automated testing or CI — every project in both Projects courses has been run and checked by hand.
  • No monitoring of the deployed model's own real-world accuracy over time — a genuinely separate discipline (MLOps) this course never claimed to cover.

Model persistence

joblib.dump()/load() let a trained model outlive the script that trained it.

Streamlit widgets

Real interactivity with no web development knowledge required.

Column-order consistency

Saved feature columns keep new input rows aligned with training data.

A real, honest scope boundary

Minimal by design — named explicitly, not glossed over.

Extend This Project

Try these on your own:

  • Add the remaining feature inputs (internet service, tech support) as additional Streamlit widgets, matching every column the model was actually trained on.
  • Show feature_importances_ (from dsproj2-2) directly in the app, so a user sees which factors drove their own specific prediction.
  • Swap the churn model for the dsproj2-1 sentiment model, and build a second small Streamlit app around it instead.
  • Read Streamlit's own deployment documentation and try hosting this app for real, on Streamlit Community Cloud or similar.

Data Science & ML Subject Complete

That's all 16 chapters of the Data Science & ML Projects track — dsproj1's own 8 beginner projects and dsproj2's own 8 intermediate ones — and with it, the complete Data Science & ML subject: Data Science Fundamentals → Machine Learning Fundamentals → Neural Networks & Deep Learning → NLP → LLMs → Projects, six courses built one on top of the last, ending here with a real, working, interactive application built from every piece of it.

Chapter 8 Quick Reference — Course & Subject Summary

  • Reuses dsproj2-2's own full churn pipeline unchanged, adding the one genuinely new piece: persistence + a minimal interface
  • joblib.dump()/load() let a trained model survive between script runs — necessary for any real interface
  • Streamlit widgets provide real interactivity with zero web development knowledge — deliberately minimal, not a fastapi1 substitute
  • Chapter-attribution table spans both dsproj1 and dsproj2 — the full track, combined in one final project
  • This completes Data Science & ML Projects, Intermediate (8 chapters) and the entire six-course Data Science & ML subject