Gaussian Mixture Model pada chapter sebelumnya mengajari kita membaca parameter sebagai distribusi; chapter penutup section ini menghadapkan kita ke janji yang paling sering kita klaim tanpa mengujinya: ketika classifier mengeluarkan 0,8, apakah 80% kasus semacam itu benar-benar berlabel positif? Untuk keputusan berbasis peluang — menetapkan premi, memilih ambang peninjauan manual, menghitung nilai harapan — angka yang tidak terkalibrasi menghasilkan keputusan yang salah bahkan ketika akurasinya tinggi. Di chapter ini kita mengukur kesesuaian tersebut lewat kurva kalibrasi dan Brier score, lalu memperbaikinya dengan CalibratedClassifierCV — penutup yang tepat karena alat ini bekerja persis seperti teorema Bayes: memetakan skor mentah model ke peluang yang sudah diperiksa terhadap frekuensi nyata.

Apa itu Kalibrasi #

Model terkalibrasi jika keluaran probabilitasnya cocok dengan frekuensi kejadian di lapangan: dari seluruh kasus yang diprediksi 0,8, sekitar 80% memang positif; dari yang diprediksi 0,1, sekitar 10% positif. Uji mentalnya sederhana — bayangkan mengelompokkan 100 prediksi 0,8 ke satu kantong, lalu menghitung isinya. Kalibrasi berbeda dari diskriminasi (kemampuan memisahkan kelas): model yang selalu memprediksi probabilitas dasar 0,5 pada data berimbang adalah buruk memisahkan namun sempurna terkalibrasi; model yang memisahkan tajam namun selalu terlalu yakin adalah kebalikannya. Idealnya model bagilah keduanya — dan kalibrasi bisa diperbaiki setelah modelnya jadi.

RandomForest adalah kasus klasik yang perlu diperiksa. Probabilitasnya dihitung sebagai fraksi suara pohon, dan fraksi semacam itu condong menjauh dari 0 dan 1 — pohon yang beragam jarang bulat sepakat. Akibatnya 0,3 dari model bisa berarti 0,1 yang sesungguhnya, dan 0,6 bisa berarti 0,75.

Kurva Kalibrasi dan Brier Score #

calibration_curve dari sklearn.calibration membagi prediksi ke dalam beberapa bin, lalu mengembalikan dua deret: rata-rata probabilitas prediksi per bin dan frekuensi empiris label positif per bin. Model terkalibrasi sempurna membuat kedua deret berimpit di garis diagonal. Argumen strategy="quantile" mengisi tiap bin dengan jumlah sampel yang sama (stabil secara statistik); strategy="uniform" membagi rentang [0, 1] sama lebar (bin tepi bisa nyaris kosong). Satu kebiasaan yang harus diingat: fungsi ini mengembalikan (prob_true, prob_pred) — frekuensi empiris lebih dulu — sehingga urutan penamaan variabel mudah terbalik.

Sebagai angka tunggal, Brier score adalah rata-rata kuadrat selisih probabilitas prediksi terhadap label nyata:

text
Brier = (1/n) Σ (p_i - y_i)²

Skor ini menghukum sekaligus kalibrasi dan ketajaman pemisahan, bernilai 0 untuk prediksi sempurna dan 0,25 untuk tebakan konstan 0,5 pada data berimbang. Karena label 0/1 dan prediksi 0/1 juga dihukum kuadrat, prediksi percaya diri yang keliru dikenai denda terbesar — perilaku yang persis kita inginkan saat menilai probabilitas.

Praktik: RandomForest Sebelum dan Sesudah Kalibrasi #

Kita bekerja pada 1.200 sampel make_classification dengan label yang cukup berisik (flip_y=0.08) supaya probabilitas menengah benar-benar muncul. RandomForest dasar dilatih pada 720 sampel latih; dua versi terkalibrasi — sigmoid (Platt scaling) dan isotonic — dilatih lewat CalibratedClassifierCV dengan cv=3; seluruh penilaian dilakukan pada 480 sampel uji yang tidak pernah disentuh proses kalibrasi.

python
# Kalibrasi probabilitas: kurva kalibrasi, Brier score, CalibratedClassifierCV.
# Model dasar: RandomForestClassifier -- fraksi suara pohon cenderung bias.

import numpy as np
from sklearn.calibration import CalibratedClassifierCV, calibration_curve
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, brier_score_loss
from sklearn.model_selection import train_test_split

# 1) Data klasifikasi sintetis dengan label berisik (flip_y).
X, y = make_classification(
    n_samples=1200, n_features=10, n_informative=4, n_redundant=2,
    n_clusters_per_class=2, flip_y=0.08, random_state=42,
)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.4, random_state=42, stratify=y
)
print(f"latih {len(y_train)} sampel, uji {len(y_test)} sampel")

# 2) Model dasar + dua versi terkalibrasi (dilatih ulang lewat cv=3).
dasar = RandomForestClassifier(n_estimators=300, random_state=42)
dasar.fit(X_train, y_train)
sigmoid = CalibratedClassifierCV(
    estimator=dasar, method="sigmoid", cv=3
).fit(X_train, y_train)
isotonic = CalibratedClassifierCV(
    estimator=dasar, method="isotonic", cv=3
).fit(X_train, y_train)
proba_dasar = dasar.predict_proba(X_test)[:, 1]
proba_sig = sigmoid.predict_proba(X_test)[:, 1]
proba_iso = isotonic.predict_proba(X_test)[:, 1]

# 3) Fraksi suara pohon: sempit di tengah, jarang benar-benar 0 atau 1.
ekstrem_dasar = float(np.mean((proba_dasar < 0.10) | (proba_dasar > 0.90)))
ekstrem_sig = float(np.mean((proba_sig < 0.10) | (proba_sig > 0.90)))
print(f"\nProporsi prediksi ekstrem (<0.10 atau >0.90):")
print(f"  tanpa kalibrasi : {ekstrem_dasar:.3f}")
print(f"  sigmoid         : {ekstrem_sig:.3f}")

# 4) Brier score: galat kuadrat rata-rata probabilitas positif.
print("\nBrier score (makin kecil makin baik):")
print(f"  tanpa kalibrasi : {brier_score_loss(y_test, proba_dasar):.4f}")
print(f"  sigmoid         : {brier_score_loss(y_test, proba_sig):.4f}")
print(f"  isotonic        : {brier_score_loss(y_test, proba_iso):.4f}")

print("\nAkurasi label (ambang 0.5):")
print(f"  tanpa kalibrasi : {accuracy_score(y_test, proba_dasar >= 0.5):.4f}")
print(f"  sigmoid         : {accuracy_score(y_test, proba_sig >= 0.5):.4f}")


# 5) Kurva kalibrasi 8 bin kuantil: prob_prediksi vs frekuensi empiris.
def tabel_kalibrasi(nama, proba):
    # calibration_curve mengembalikan (prob_true, prob_pred):
    # frekuensi empiris lebih dulu, rata-rata prediksi belakangan.
    frekuensi, prediksi = calibration_curve(
        y_test, proba, n_bins=8, strategy="quantile"
    )
    gap = float(np.max(np.abs(prediksi - frekuensi)))
    print(f"\nKurva kalibrasi ({nama}), 8 bin kuantil:")
    print("  prob_prediksi  frekuensi_empiris")
    for p, e in zip(prediksi, frekuensi):
        print(f"  {p:>12.3f}  {e:>19.3f}")
    print(f"  gap maksimum |prediksi - frekuensi| = {gap:.3f}")


tabel_kalibrasi("tanpa kalibrasi", proba_dasar)
tabel_kalibrasi("sigmoid", proba_sig)

Output yang Dihasilkan:

text
latih 720 sampel, uji 480 sampel

Proporsi prediksi ekstrem (<0.10 atau >0.90):
  tanpa kalibrasi : 0.315
  sigmoid         : 0.577

Brier score (makin kecil makin baik):
  tanpa kalibrasi : 0.0968
  sigmoid         : 0.0913
  isotonic        : 0.0919

Akurasi label (ambang 0.5):
  tanpa kalibrasi : 0.8792
  sigmoid         : 0.8833

Kurva kalibrasi (tanpa kalibrasi), 8 bin kuantil:
  prob_prediksi  frekuensi_empiris
         0.044                0.033
         0.124                0.065
         0.214                0.102
         0.336                0.300
         0.585                0.750
         0.801                0.857
         0.891                0.965
         0.960                0.983
  gap maksimum |prediksi - frekuensi| = 0.165

Kurva kalibrasi (sigmoid), 8 bin kuantil:
  prob_prediksi  frekuensi_empiris
         0.033                0.050
         0.063                0.033
         0.124                0.133
         0.267                0.283
         0.637                0.733
         0.881                0.850
         0.944                0.967
         0.969                0.983
  gap maksimum |prediksi - frekuensi| = 0.097

Mari kita bedah hasil eksekusinya:

  1. Baris 22–29 (tiga model): CalibratedClassifierCV(estimator=dasar, method=..., cv=3) menerima estimator melalui argumen estimator (sejak scikit-learn 1.2 menggantikan base_estimator) dan melatih ulang model dasar pada tiap fold sebelum memasang lapisan kalibrasi — peta kalibrasi tidak pernah melihat data yang dipakai menilainya.
  2. Baris 35–39 (prediksi ekstrem): tanpa kalibrasi hanya 31,5% prediksi berada di bawah 0,10 atau di atas 0,90; setelah kalibrasi sigmoid proporsinya 57,7%. Ini bukan model jadi lebih berani tanpa alasan — peta sigmoid meregangkan kembali probabilitas yang sebelumnya terjepit ke tengah oleh fraksi suara pohon.
  3. Baris 43–45 (Brier score): 0,0968 turun ke 0,0913 (sigmoid) dan 0,0919 (isotonic). Perbaikan terlihat sederhana sebagai angka, tetapi pada aplikasi yang memakai peluang secara langsung — misalnya penetapan premi asuransi — pemangkasan galat kuadrat sebesar ini berarti seluruh tabel keputusan bergeser ke arah yang benar.
  4. Baris 47–49 (akurasi label): 0,8792 menjadi 0,8833. Kalibrasi tidak menuntut model mengklasifikasi lebih baik — peta kalibrasi bersifat monoton sehingga urutan skor nyaris tak berubah; yang berubah adalah makna angkanya.
  5. Baris 53–64 (kurva kalibrasi): pada model dasar, bin 5 memperlihatkan prediksi rata-rata 0,585 sementara frekuensi nyatanya 0,750 — gap terbesar 0,165. Pola fraksi suara terlihat konsisten: bin 1 memprediksi 0,044 saat kenyataannya 0,033, bin 8 memprediksi 0,960 saat kenyataannya 0,983 — angka menjauh dari 0 dan 1. Setelah kalibrasi sigmoid, gap maksimum menyusut ke 0,097 dan deret prediksi–frekuensi berjalan berdampingan jauh lebih rapat.

Sigmoid vs Isotonic #

Dua metode di CalibratedClassifierCV berbeda pada bentuk peta kalibrasinya:

Aspekmethod="sigmoid"method="isotonic"
Bentuk petaRegresi logistik (Platt scaling)Fungsi bertahap monoton naik (isotonic regression)
Parameter bebas2 (kemiringan, intersep)Banyak — satu segmen per kelompok data
FleksibilitasRendah, halusTinggi, mengikuti data
Kebutuhan dataAman pada ratusan sampelIdeal di atas ~1.000 sampel per kelas
RisikoTerlalu sederhana saat kurva melengkung tajamTerlalu rapat ke data (overfitting) saat kecil

Pada contoh kita keduanya berakhir nyaris seri (0,0913 vs 0,0919) — dengan 720 sampel latih, fleksibilitas isotonic belum dibayar lunas. Semakin kecil data, semakin kuat alasan memilih sigmoid.

Kapan Kalibrasi Diperlukan #

Kalibrasi wajib dipertimbangkan ketika probabilitasnya dikonsumsi langsung: penetapan premi atau limit kredit, ambang keputusan berbasis nilai harapan, peringkat berpeluang pada sistem rekomendasi, sampai meta-features untuk model stacking. Sebaliknya, kalau downstream hanya membaca urutan — AUC, pemeringkatan pencarian, pemilihan top-k — peta monoton tidak mengubah urutan dan kalibrasi jadi tidak mendesak. Aturan praktisnya satu kalimat: jika angkanya dijumlah, kalibrasi penting; jika hanya urutannya yang dipakai, kalibrasi opsional.

Section Probabilistic Machine Learning sampai di sini lengkap: kita memandang model sebagai distribusi, memperbarui keyakinan lewat Bayes, menaksir parameter lewat likelihood, memodelkan data sebagai campuran, dan memastikan probabilitas keluaran bisa dipercaya. Benang merah berikutnya tetap soal kejadian langka: bagaimana menandai titik yang menurut model hampir mustahil terjadi? Itu pembukaan Section Anomaly Detection, dimulai dari Chapter Konsep Anomaly Detection.

Referensi Resmi #