Menyusun proses training yang jujur dan terukur: train/val/test split yang benar, cross-validation yang menghormati waktu, hyperparameter tuning dengan search strategy, serta pipeline training terstruktur yang bisa dijalankan ulang dan dibandingkan antar eksperimen

Setelah di episode 4 kita membangun feature pipeline yang konsisten, sekarang tiba saatnya bagian yang paling sering disalahpahami pemula: cara training dan validasi yang benar. Kebanyakan orang langsung memanggil model.fit() dan melihat akurasi — padahal tanpa disiplin validasi, angka akurasi itu sering kali bohong.
Masalah terbesar di industri bukan model yang kurang pintar, melainkan model yang terlihat bagus di eksperimen tapi melorot di produksi. Sering kali penyebabnya bukan modelnya, tapi cara kita memisahkan data, cara kita tuning hyperparameter, dan cara kita mengevaluasi. Episode ini membangun fondasi yang akan menyelamatkan kalian dari mimpi buruk itu.
Tiga bagian data dengan tiga peran yang tidak boleh dicampur:
| Split | Peran | Jangan dipakai untuk |
|---|---|---|
| Training | Melatih parameter model | Evaluasi final, tuning |
| Validation | Memilih model & tuning hyperparameter | Evaluasi final |
| Test | Evaluasi final satu kali saja | Tuning atau pemilihan model |
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
X_train, X_val, y_train, y_val = train_test_split(
X_train, y_train, test_size=0.2, random_state=42, stratify=y_train
)stratify=y menjaga proporsi kelas agar sama di semua split — penting untuk data tidak seimbang. random_state membuat split reproducible.
Jika data memiliki dimensi waktu (transaksi, log, clickstream), random split adalah kesalahan serius: model akan "mencontek" data masa depan. Gunakan temporal split — training hanya pada periode lalu, validasi/test pada periode berikutnya.
train = df[df["date"] < "2026-05-01"]
val = df[(df["date"] >= "2026-05-01") & (df["date"] < "2026-06-01")]
test = df[df["date"] >= "2026-06-01"]Aturan emasnya: test set harus meniru kondisi produksi. Jika di produksi kalian memprediksi "hari ini" berdasarkan data "kemarin", maka test set harus disusun persis seperti itu.
K-fold cross-validation memakai seluruh data secara bergantian sebagai validation, sehingga estimasi performa lebih stabil dari satu split:
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import GradientBoostingClassifier
scores = cross_val_score(GradientBoostingClassifier(), X_train, y_train, cv=5)
print(f"mean: {scores.mean():.4f} ± {scores.std():.4f}")Dua varian penting:
Untuk data temporal, jangan pernah memakai KFold biasa; gunakan TimeSeriesSplit.
Setelah baseline terlatih, kalian mengeksplorasi ruang hyperparameter. Dua pendekatan utama:
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import uniform, randint
param_dist = {
"n_estimators": randint(100, 400),
"max_depth": randint(3, 12),
"learning_rate": uniform(0.01, 0.3),
}
search = RandomizedSearchCV(
GradientBoostingClassifier(),
param_distributions=param_dist,
n_iter=50, cv=5, scoring="roc_auc", random_state=42,
)
search.fit(X_train, y_train)
print(search.best_params_)Ada aturan penting yang sering dilanggar: tuning hanya menyentuh validation set. Jika kalian memilih hyperparameter berdasarkan performa di test set, test set sudah ter-bocor dan angka finalnya tidak bisa dipercaya lagi.
Training yang baik adalah proses yang bisa dijalankan ulang dan dibandingkan. Struktur yang umum:
from src.data import load_and_clean
from src.features import build_feature_pipeline
from src.config import TrainConfig
def run_training(config: TrainConfig):
df = load_and_clean(config.raw_path)
X_train, X_val, X_test, y_train, y_val, y_test = config.split(df)
preprocessor = build_feature_pipeline(X_train)
model = config.make_model()
model.fit(preprocessor.transform(X_train), y_train)
val_score = model.score(preprocessor.transform(X_val), y_val)
return model, preprocessor, {"val_score": val_score}
if __name__ == "__main__":
model, preprocessor, metrics = run_training(TrainConfig())
print(metrics)Praktik yang membuat pipeline ini production-grade:
random_state) agar hasil reproducible.Tip
Aturan yang banyak dipelajari dengan pahit: evaluasi test set hanya satu kali. Jika kalian memakai test set berulang-ulang untuk mengambil keputusan, secara tidak sadar kalian sedang tuning terhadap test set — dan angka final kalian tidak mencerminkan performa di data baru.
| Pitfall | Dampak | Pencegahan |
|---|---|---|
| Random split pada data temporal | Model mencontek masa depan | TimeSeriesSplit / temporal split |
| Tanpa stratify pada data imbalance | Split tidak mewakili populasi | stratify=y |
| Tuning sampai menyentuh test set | Test set ter-bocor, angka tidak jujur | Test set hanya untuk evaluasi final |
| GridSearch pada ruang besar | Boros waktu & compute | RandomizedSearch |
| Tanpa baseline | Tidak tahu apakah model berguna | Selalu sediakan baseline naif |
Pada episode 5 ini, kalian telah membangun fondasi training yang sehat:
Di episode 6 selanjutnya kita akan membahas Experiment Tracking & Registry — mencatat setiap eksperimen dengan MLflow/W&B, membandingkan run secara sistematis, dan mengelola model registry untuk menjawab pertanyaan "model mana yang sedang live dan kenapa". Sampai jumpa di episode 6!