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
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")
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.
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
| Stage | Drawn directly from |
|---|---|
| Collection | dsproj1-1 (scraping), dsproj1-3 (single API), dsproj1-6 (combining APIs) |
| Cleaning | dsproj1-2's own reusable, report-generating pipeline pattern |
| EDA | dsproj1-5's own flowing, narrative exploration approach |
| Feature engineering | dsproj2-2's own derived features and one-hot encoding |
| Model comparison & evaluation | dsproj2-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 persistence | New in this capstone — joblib, saving trained state between runs |
| Interface | New in this capstone — Streamlit, deliberately minimal |
- No production deployment or hosting — running
streamlit runlocally 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.
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-1sentiment 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