Final Lab, Evaluasi Proyek & Integrasi Sistem
Panduan menyelesaikan proyek akhir — pipeline end-to-end yang reproducible, validasi silang, interpretasi model, struktur repositori, template laporan, dan rubrik penilaian.
Notebook lab (pratinjau)
Unduh lalu jalankan di Jupyter, VS Code, atau Google Colab.
Lab Minggu 17 — Final Lab: Template Pipeline End-to-End
Notebook ini adalah template proyek akhir. Contoh kasusnya prediksi churn, tetapi strukturnya bisa kamu salin untuk data proyekmu sendiri.
| Tahap KDD | Bagian notebook |
|---|---|
| Selection | 1. Masalah & data |
| Preprocessing + Transformation | 2–3. EDA & Pipeline |
| Data Mining | 4. Perbandingan model |
| Interpretation | 5–7. Evaluasi, interpretasi, laporan |
import sys
sys.path.insert(0, "..")
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.compose import ColumnTransformer
from sklearn.dummy import DummyClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.impute import SimpleImputer
from sklearn.inspection import permutation_importance
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import (ConfusionMatrixDisplay, RocCurveDisplay, classification_report,
f1_score, precision_recall_curve)
from sklearn.model_selection import StratifiedKFold, cross_validate, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.tree import DecisionTreeClassifier
from common.datasets import churn_pelanggan
SEED = 42 # satu seed untuk seluruh proyek → reproducible
1. Masalah bisnis & data
Masalah: perusahaan layanan langganan kehilangan ±25% pelanggan. Tim retensi hanya sanggup menghubungi sebagian pelanggan per bulan → butuh model untuk memprioritaskan.
Metrik: F1 (keseimbangan precision & recall) + recall sebagai perhatian utama (pelanggan churn yang terlewat = pendapatan hilang).
df = churn_pelanggan()
TARGET = "churn"
print(df.shape)
df.head()
2. EDA singkat
print("Proporsi churn:", df[TARGET].mean().round(3))
df.describe().T.round(2)
fig, axes = plt.subplots(1, 3, figsize=(15, 3.5))
df.groupby("kontrak")[TARGET].mean().plot.bar(ax=axes[0], title="Churn rate per kontrak", rot=0)
df.boxplot(column="lama_langganan", by=TARGET, ax=axes[1]); axes[1].set_title("Lama langganan vs churn")
df.groupby("jumlah_keluhan")[TARGET].mean().plot(ax=axes[2], marker="o", title="Churn rate vs jumlah keluhan")
plt.suptitle(""); plt.tight_layout(); plt.show()
3. Split data & pipeline preprocessing
Split sebelum apa pun di-fit. Semua transformasi masuk ke Pipeline.
X, y = df.drop(columns=TARGET), df[TARGET]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, stratify=y, random_state=SEED)
num_cols = [c for c in X.columns if pd.api.types.is_numeric_dtype(X[c])]
cat_cols = [c for c in X.columns if c not in num_cols]
print("Numerik :", num_cols)
print("Kategorikal:", cat_cols)
def bangun_pipeline(model):
prep = ColumnTransformer([
("num", Pipeline([("imputer", SimpleImputer(strategy="median")),
("scaler", StandardScaler())]), num_cols),
("cat", Pipeline([("imputer", SimpleImputer(strategy="most_frequent")),
("onehot", OneHotEncoder(handle_unknown="ignore"))]), cat_cols),
])
return Pipeline([("prep", prep), ("model", model)])
bangun_pipeline(LogisticRegression())
4. Perbandingan model dengan CV berstrata (fold sama untuk semua)
models = {
"baseline (mayoritas)": DummyClassifier(strategy="most_frequent"),
"decision tree d=5": DecisionTreeClassifier(max_depth=5, random_state=SEED),
"logreg balanced": LogisticRegression(max_iter=1000, class_weight="balanced"),
"random forest": RandomForestClassifier(n_estimators=200, class_weight="balanced", random_state=SEED),
}
cv = StratifiedKFold(5, shuffle=True, random_state=SEED)
baris = []
for nama, mdl in models.items():
r = cross_validate(bangun_pipeline(mdl), X_train, y_train, cv=cv,
scoring=["f1", "recall", "precision", "roc_auc"])
baris.append({"model": nama,
**{f"{k[5:]}_mean": v.mean() for k, v in r.items() if k.startswith("test_")},
"f1_std": r["test_f1"].std()})
hasil_cv = pd.DataFrame(baris).set_index("model").sort_values("f1_mean", ascending=False)
hasil_cv.round(3)
hasil_cv["f1_mean"].plot.barh(xerr=hasil_cv["f1_std"], figsize=(7, 3), title="F1 CV 5-fold (mean ± std)")
plt.show()
5. Evaluasi final pada data uji (sekali saja!)
terbaik = hasil_cv.index[0]
pipe = bangun_pipeline(models[terbaik]).fit(X_train, y_train)
pred = pipe.predict(X_test)
print("Model terpilih:", terbaik)
print(classification_report(y_test, pred, digits=3))
fig, axes = plt.subplots(1, 2, figsize=(11, 4))
ConfusionMatrixDisplay.from_predictions(y_test, pred, ax=axes[0], colorbar=False)
RocCurveDisplay.from_estimator(pipe, X_test, y_test, ax=axes[1])
plt.tight_layout(); plt.show()
Threshold sesuai kapasitas tim retensi
Misal tim hanya sanggup menghubungi 20% pelanggan → ambil 20% probabilitas tertinggi.
proba = pipe.predict_proba(X_test)[:, 1]
ambang = np.quantile(proba, 0.80)
pred_kapasitas = (proba >= ambang).astype(int)
print(f"ambang={ambang:.3f}, dihubungi={pred_kapasitas.sum()}, "
f"churn tertangkap={((pred_kapasitas == 1) & (y_test == 1)).sum()} dari {y_test.sum()}")
6. Interpretasi: permutation importance
imp = permutation_importance(pipe, X_test, y_test, scoring="f1", n_repeats=10, random_state=SEED)
penting = pd.Series(imp.importances_mean, index=X_test.columns).sort_values()
penting.plot.barh(title="Permutation importance (penurunan F1)"); plt.show()
7. Laporan otomatis (Markdown)
from sklearn.metrics import accuracy_score, precision_score, recall_score, roc_auc_score
metrik = {"accuracy": accuracy_score(y_test, pred), "precision": precision_score(y_test, pred),
"recall": recall_score(y_test, pred), "f1": f1_score(y_test, pred),
"roc_auc": roc_auc_score(y_test, proba)}
top = penting.sort_values(ascending=False)
laporan = "\n".join(
[f"# Laporan Model Churn — {terbaik}", "", "## Metrik Evaluasi", "", "| Metrik | Nilai |", "|---|---|"]
+ [f"| {k} | {v:.3f} |" for k, v in metrik.items()]
+ ["", "## Fitur Terpenting", ""]
+ [f"{i}. {n} ({v:.3f})" for i, (n, v) in enumerate(top.head(3).items(), 1)]
)
print(laporan)
✍️ Coba sendiri (adaptasi ke proyekmu)
- Ganti dataset dengan data proyek kelompokmu. Bagian mana saja yang perlu diubah?
- Tambahkan satu model lagi (mis.
KNeighborsClassifieratauGaussianNB) ke perbandingan. - Lakukan tuning hyperparameter
Cpada logistic regression denganGridSearchCV(tetap di dalam pipeline!). - Tulis 3 rekomendasi bisnis berdasarkan permutation importance.
- Simpan pipeline terbaik dengan
joblib.dumpdan muat ulang untuk memprediksi 5 pelanggan baru.
# 2.
# 3.
# 5.
Rekomendasi bisnis (no. 4):
- ...
- ...
- ...
Selanjutnya
Kerjakan kuis dan exercise.py, lalu jalankan python check.py 17 dari folder utama.