Belajar Machine Learning - Model Evaluation & Hyperparameter Tuning
Episode 10 of 25

Belajar Machine Learning - Model Evaluation & Hyperparameter Tuning

Menyempurnakan model secara disiplin: cross-validation k-fold dan stratified, validation curves untuk membaca bias-variance, lalu tuning hyperparameter dengan GridSearchCV, RandomizedSearchCV, Optuna, dan early stopping agar evaluasi jujur dan reproduksibel.

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

Setelah di episode 9 kita membangun dan memilih fitur, pada episode ini kita menjawab pertanyaan yang selalu mengikuti: model mana yang terbaik, dan dengan parameter apa? Jawabannya tidak bisa "kira-kira" — karena keputusan yang salah di sini membuat model overfit dan menipu evaluasi.

Mengapa topik ini krusial? Karena tuning adalah tempat kalian bisa membuang waktu berhari-hari jika tidak disiplin. Dengan cross-validation yang benar, strategi pencarian yang tepat, dan pemahaman bias-variance, kalian bisa mencapai performa optimal dengan cara yang terukur dan jujur.

Cross-Validation

Ingat pelajaran episode 2: test set hanya disentuh sekali di akhir. Lalu bagaimana cara memilih hyperparameter tanpa menyentuh test set? Jawabannya: cross-validation — membagi train set menjadi beberapa lipatan (fold) dan menguji model bergantian.

k-Fold CV

Dengan k=5, train set dipecah menjadi 5 bagian. Model dilatih pada 4 bagian, dievaluasi pada 1 bagian tersisa, dan ini berulang 5 kali sehingga setiap bagian pernah menjadi evaluasi. Hasil akhir: rata-rata dan standar deviasi dari 5 skor.

Cross-validation
from sklearn.model_selection import cross_val_score
 
scores = cross_val_score(
    pipeline, X_train, y_train,
    cv=5, scoring="roc_auc",
)
 
print(f"CV mean = {scores.mean():.4f} ± {scores.std():.4f}")

Perhatikan ± std: jika varian antar fold besar, performa model tidak stabil — menandakan model terlalu sensitif terhadap data tertentu.

Stratified k-Fold

Untuk classification dengan data tidak seimbang, gunakan StratifiedKFold: setiap fold menjaga proporsi kelas yang sama dengan data penuh. Ini menghindari fold yang tidak punya kelas minoritas sama sekali — yang akan merusak evaluasi.

Stratified k-fold
from sklearn.model_selection import StratifiedKFold
 
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(pipeline, X_train, y_train, cv=cv, scoring="roc_auc")

shuffle=True dengan random_state penting agar pembagian fold deterministik — bagian dari reproducibility (episode 2).

Validation Curves dan Bias-Variance

Validation curve menunjukkan bagaimana performa berubah saat satu hyperparameter naik, pada train dan validation secara bersamaan:

Validation curve
import numpy as np
from sklearn.model_selection import validation_curve
from sklearn.ensemble import RandomForestClassifier
 
param_range = np.arange(1, 21)
 
train_scores, val_scores = validation_curve(
    RandomForestClassifier(random_state=42),
    X_train, y_train,
    param_name="max_depth",
    param_range=param_range,
    cv=5, scoring="roc_auc",
)
 
val_mean = val_scores.mean(axis=1)
best_depth = param_range[np.argmax(val_mean)]
print(f"max_depth terbaik = {best_depth}")

Interpretasi kurva inilah inti bias-variance trade-off:

Pola kurvaDiagnosisTindakan
Train rendah, val rendahUnderfit (bias tinggi)Model terlalu sederhana — tambah kapasitas
Train tinggi, val rendahOverfit (variance tinggi)Model hafal data latih — kurangi kapasitas / regularisasi
Train & val tinggi & berdekatanSeimbangZona ideal

Kurva kalian idealnya menunjukkan titik di mana performa validation memuncak — di sanalah max_depth terbaik berada, bukan di titik train tertinggi.

GridSearchCV

Grid search mencoba semua kombinasi parameter secara sistematis:

GridSearchCV
from sklearn.model_selection import GridSearchCV
 
param_grid = {
    "model__max_depth": [4, 8, 12],
    "model__n_estimators": [100, 300],
    "model__min_samples_leaf": [1, 5],
}
 
search = GridSearchCV(
    pipeline, param_grid,
    cv=5, scoring="roc_auc", n_jobs=-1,
)
search.fit(X_train, y_train)
 
print(search.best_params_)
print(f"CV terbaik = {search.best_score_:.4f}")
print(f"Test       = {search.score(X_test, y_test):.4f}")

Perhatikan model__max_depth (dua underscore) — ini cara menyebut parameter estimator di dalam Pipeline. Kelemahan grid search: kombinasi tumbuh eksponensial. Contoh di atas saja sudah 3×2×2×5 = 60 kali training.

RandomizedSearchCV

Random search mengambil sampel acak dari ruang parameter dalam jumlah iterasi tetap. Hasilnya sering lebih baik daripada grid search untuk ruang berdimensi banyak, dengan biaya yang sama.

RandomizedSearchCV
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint, uniform
 
param_dist = {
    "model__max_depth": randint(3, 20),
    "model__n_estimators": randint(100, 800),
    "model__learning_rate": uniform(0.01, 0.3),
}
 
search = RandomizedSearchCV(
    pipeline, param_dist,
    n_iter=50, cv=5, scoring="roc_auc",
    random_state=42, n_jobs=-1,
)
search.fit(X_train, y_train)

Dengan scipy.stats kita memberi distribusi, bukan daftar nilai — sehingga ruang pencarian jauh lebih halus.

Warning

Grid/random search mengoptimalkan skor CV di data train. Risiko tersembunyi: jika data sangat tidak seimbang, gunakan metrik CV yang sesuai (roc_auc, f1, precision) — jangan accuracy yang bisa menyesatkan.

Optuna dan Early Stopping

Optuna adalah framework tuning modern berbasis Bayesian optimization: ia belajar dari percobaan sebelumnya dan menebak area parameter yang paling menjanjikan, bukan sekadar mencoba acak.

Optuna
import optuna
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
 
def objective(trial):
    params = {
        "max_depth": trial.suggest_int("max_depth", 3, 20),
        "n_estimators": trial.suggest_int("n_estimators", 100, 800),
        "min_samples_leaf": trial.suggest_int("min_samples_leaf", 1, 20),
        "max_features": trial.suggest_float("max_features", 0.1, 1.0),
    }
    model = RandomForestClassifier(**params, random_state=42, n_jobs=-1)
    return cross_val_score(model, X_train, y_train, cv=5,
                           scoring="roc_auc").mean()
 
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=50)
 
print(study.best_params)
print(f"CV terbaik = {study.best_value:.4f}")

Optuna biasanya menemukan parameter yang sama bagusnya dengan grid search dalam jauh lebih sedikit percobaan — terutama saat ruang parameter besar.

Early stopping adalah cara berbeda untuk menangani n_estimators pada boosting: berhenti saat validation error tidak membaik dalam N iterasi, alih-alih menebak angka estimasi:

Early stopping di LightGBM
import lightgbm as lgb
 
model = lgb.LGBMClassifier(
    n_estimators=5000,
    learning_rate=0.05,
    random_state=42,
)
 
model.fit(
    X_train, y_train,
    eval_set=[(X_val, y_val)],
    eval_metric="auc",
    callbacks=[lgb.early_stopping(50)],
)

Model berhenti pada iterasi optimal secara otomatis — tidak perlu tuning n_estimators manual.

Alur Kerja Disiplin

100%

Poin pentingnya: test set tetap suci. Semua keputusan tuning memakai CV di train set; test set hanya untuk konfirmasi terakhir.

Common Pitfalls

  • Tuning setelah melihat test set — test set bocor, evaluasi jadi tidak jujur.
  • CV tanpa stratified pada data tidak seimbang.
  • Grid search terlalu sempit — kemungkinan besar melewatkan optimum.
  • Mengabaikan variance CV — mean tinggi tapi std besar tetap berisiko.
  • Tidak set random_state pada search dan CV — hasil tidak reproduksibel.
  • Menggunakan akurasi sebagai metrik tuning pada data tidak seimbang.

Penutup

Pada episode 10 ini, kalian telah menguasai evaluasi dan tuning yang disiplin.

Inti yang harus dibawa pulang:

  • Cross-validation (k-fold/stratified) untuk memilih model tanpa menyentuh test set.
  • Validation curves untuk membaca bias-variance dan overfitting.
  • GridSearchCV ekshaustif, RandomizedSearchCV lebih efisien, Optuna paling cerdas.
  • Early stopping untuk boosting tanpa menebak jumlah pohon.
  • Test set hanya disentuh sekali, di akhir.

Di episode 11 selanjutnya kita memasuki fase baru: experiment tracking & reproducibility — logging metric dan artifact dengan MLflow, perbandingan dengan Weights & Biases, seed/hashing untuk reproducibility, dan eksperimen berbasis konfigurasi. Sampai jumpa di episode 11!

Belajar Machine Learning - Model Evaluation & Hyperparameter Tuning | Belajar Machine Learning