Referensi cepat metrik evaluasi model ML. Classification, regression, ranking, confusion matrix, ROC AUC, cross-validation, dan kapan pakai metrik apa. Buat AI engineer yang mengukur kualitas prediksi.
Accuracy 95 persen bisa menipu. Jika 95 persen transaksi memang bukan fraud, model yang selalu menebak bukan fraud juga mendapat accuracy 95 persen tanpa menemukan satu fraud pun. Memilih metrik berarti memutuskan jenis kesalahan mana yang paling mahal, lalu mengukur model dengan aturan itu.
Tiga hal yang harus kamu bedakan sebelum menghitung metrik. Pertama, label asli y_true. Kedua, prediksi keras y_pred yang sudah berupa kelas atau angka. Ketiga, skor probabilitas y_score yang berupa angka kontinu sebelum ambang. Beberapa metrik hanya menerima salah satu bentuk.
from sklearn.metrics import (
accuracy_score,
precision_score,
recall_score,
f1_score,
roc_auc_score,
mean_absolute_error,
mean_squared_error,
r2_score,
)Confusion matrix menghitung tiap sel hasil prediksi untuk klasifikasi. Empat istilah dasarnya dihitung untuk kelas yang kamu anggap positif.
| Istilah | Nama | Arti |
|---|---|---|
| TP | true positive | diprediksi positif dan benar |
| TN | true negative | diprediksi negatif dan benar |
| FP | false positive | diprediksi positif, ternyata negatif |
| FN | false negative | diprediksi negatif, ternyata positif |
Baca orientasi matriks dengan hati-hati. Baris biasanya label asli, kolom prediksi, tapi susunan ini bisa berbeda di tiap library. Selalu cek label di sumbu, bukan menghafal posisinya.
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
y_true = [0, 1, 0, 1, 0, 1, 1, 0, 1, 0]
y_pred = [0, 1, 0, 0, 0, 1, 1, 0, 1, 1]
cm = confusion_matrix(y_true, y_pred)
print(cm)
# [[4 1]
# [1 4]]Pada contoh di atas ada 1 false positive dan 1 false negative. Untuk kelas tidak seimbang, empat angka ini jauh lebih informatif daripada satu angka accuracy.
accuracy = (TP + TN) / (TP + TN + FP + FN). Proporsi prediksi yang benar. Paling mudah dipahami, paling lemah saat kelas tidak seimbang.
precision = TP / (TP + FP). Dari semua yang kamu prediksi positif, berapa yang benar. Salah di sini berarti false positive mahal, misalnya menandai pelanggan normal sebagai fraud dan memblokir transaksinya.
recall = TP / (TP + FN). Dari semua kejadian positif sebenarnya, berapa yang berhasil kamu tangkap. Salah di sini berarti false negative mahal, misalnya gagal mendeteksi pasien atau kegagalan mesin.
precision = precision_score(y_true, y_pred)
recall = recall_score(y_true, y_pred)
print(f"precision {precision:.3f}")
print(f"recall {recall:.3f}")Precision dan recall saling tarik menarik. Menurunkan ambang prediksi membuat lebih banyak hal diprediksi positif. Recall naik, precision turun. Tentukan mana yang lebih mahal berdasarkan biaya bisnis, bukan preferensi.
F1 menggabungkan precision dan recall sebagai harmonic mean. Kalau salah satu sangat rendah, F1 ikut rendah. Ini berguna saat kamu mau satu angka yang menyeimbangkan keduanya.
from sklearn.metrics import f1_score
f1 = f1_score(y_true, y_pred)
print(f1)Jika keseimbangan bukan tujuan, F-beta memberi bobot lebih ke recall atau precision. beta > 1 lebih menghargai recall. beta < 1 lebih menghargai precision.
from sklearn.metrics import fbeta_score
f2 = fbeta_score(y_true, y_pred, beta=2) # recall dua kali lebih penting
f05 = fbeta_score(y_true, y_pred, beta=0.5) # precision lebih pentingHarmonic mean dipakai, bukan arithmetic mean, supaya kasus ekstrem seperti recall 0 atau precision 0 tidak menghasilkan skor menengah yang menyesatkan.
Jika kamu punya predict_proba, jangan buang informasinya dengan mengubah ke kelas dulu. Ambang default 0.5 itu sewenang-wenang. Kalibrasi ambang memakai kurva adalah cara menemukan titik yang sesuai biaya bisnismu.
y_score = model.predict_proba(X_test)[:, 1]Dengan skor kontinu kamu bisa membuat precision-recall curve dan ROC curve. Kurva itu memindai semua ambang, bukan satu titik.
Kurva precision-recall menggambarkan precision di tiap tingkat recall. Kurva ini lebih jujur untuk data tidak seimbang, karena ROC AUC bisa terlihat bagus ketika precision sebenarnya buruk akibat banyak false positive pada proporsi positif yang kecil.
from sklearn.metrics import precision_recall_curve, average_precision_score
precision, recall, thresholds = precision_recall_curve(y_true, y_score)
ap = average_precision_score(y_true, y_score)
print(ap)Average precision adalah area di bawah precision-recall curve. Nilai baseline setara proporsi kelas positif. Kalau kelas positif cuma 5 persen, model acak punya AP sekitar 0.05.
import matplotlib.pyplot as plt
plt.plot(recall, precision)
plt.xlabel("Recall")
plt.ylabel("Precision")
plt.title("Precision-Recall Curve")
plt.show()ROC menggambarkan true positive rate terhadap false positive rate pada berbagai ambang. True positive rate sama dengan recall. False positive rate adalah proporsi kejadian negatif yang salah diprediksi positif.
from sklearn.metrics import roc_curve, roc_auc_score
fpr, tpr, thresholds = roc_curve(y_true, y_score)
auc = roc_auc_score(y_true, y_score)
print(auc)AUC 0.5 berarti model sebaik tebakan acak. AUC 1.0 berarti pemisahan sempurna. AUC adalah peluang model memberi skor lebih tinggi ke sampel positif acak daripada sampel negatif acak.
plt.plot(fpr, tpr)
plt.plot([0, 1], [0, 1], linestyle="--", color="gray")
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.show()Kalau tujuannya memilih ambang, cari titik pada ROC yang menyeimbangkan biaya false positive dan false negative. Jangan hanya memilih ambang 0.5. Untuk klasifikasi multi-kelas, ROC AUC bisa dihitung per kelas memakai one-versus-rest.
auc_ovr = roc_auc_score(y_true_multiclass, y_score_multiclass, multi_class="ovr")Cara cepat membaca metrik per kelas sekaligus.
from sklearn.metrics import classification_report
print(classification_report(y_true, y_pred, target_names=["bukan fraud", "fraud"]))Kolom support menunjukkan jumlah sampel sebenarnya tiap kelas. Macro average menghitung metrik per kelas lalu rata-rata tanpa bobot. Weighted average membobot tiap kelas menurut support. Jika kelas tidak seimbang, macro lebih peka terhadap performa kelas minoritas.
Untuk prediksi nilai kontinu, metrik mengukur seberapa jauh prediksi dari nilai asli.
mean_absolute_error atau MAE menghitung rata-rata nilai absolut selisih. Satuannya sama dengan target, mudah dijelaskan ke pemangku kepentingan.
mean_squared_error atau MSE menghitung rata-rata kuadrat selisih. Karena mengkuadratkan, kesalahan besar dihukum jauh lebih berat daripada kesalahan kecil.
RMSE adalah akar dari MSE. Ia kembali ke satuan target tetapi tetap menghukum outlier besar.
from sklearn.metrics import mean_absolute_error, mean_squared_error
mae = mean_absolute_error(y_true, y_pred_reg)
mse = mean_squared_error(y_true, y_pred_reg)
rmse = mean_squared_error(y_true, y_pred_reg, squared=False)
print(mae, mse, rmse)Pilih MAE saat semua kesalahan sama mahalnya dan kamu ingin angka mudah dijelaskan. Pilih RMSE saat kesalahan besar sangat mahal dan layak dihukum. Keduanya bisa dilaporkan bersama.
r2_score atau R-squared menyatakan proporsi variasi target yang dijelaskan model. Nilai 1 sempurna, 0 berarti sebaik prediksi rata-rata, dan bisa negatif saat model lebih buruk daripada menebak rata-rata.
from sklearn.metrics import r2_score
r2 = r2_score(y_true, y_pred_reg)
print(r2)mean_absolute_percentage_error atau MAPE memberi kesalahan dalam persen. Berguna saat target tidak pernah nol dan kamu mau angka relatif yang mudah dibandingkan antarproduk.
from sklearn.metrics import mean_absolute_percentage_error
mape = mean_absolute_percentage_error(y_true, y_pred_reg)
print(mape)Hindari MAPE saat target bisa nol atau sangat dekat nol, karena pembagian dengan nilai kecil membuat metrik meledak.
log_loss mengukur seberapa yakin model dengan prediksi probabilitasnya. Skor lebih rendah lebih baik. Metrik ini menghukum model yang sangat yakin tapi salah.
from sklearn.metrics import log_loss
loss = log_loss(y_true, y_score)
print(loss)Kalibrasi adalah kecocokan antara probabilitas yang dilaporkan model dengan frekuensi kejadian sebenarnya. Model yang memprediksi 80 persen seharusnya benar sekitar 80 dari 100 kasus. Gunakan kurva kalibrasi untuk memeriksanya.
from sklearn.calibration import CalibrationDisplay
CalibrationDisplay.from_predictions(y_true, y_score, n_bins=10)
plt.show()Model modern seperti gradient boosting sering menghasilkan probabilitas yang kurang terkalibrasi. Kalau kamu memakai probabilitas sebagai input keputusan, kalibrasi ulang dengan isotonic atau sigmoid bisa memperbaiki.
Mengubah ambang mengubah keseimbangan kesalahan. Hitung biaya total pada berbagai ambang memakai kurva precision-recall.
cost_fn = 50 # biaya false negative jauh lebih besar
cost_fp = 1 # biaya false positive
best_threshold = None
best_cost = float("inf")
for precision_val, recall_val, threshold in zip(precision, recall, thresholds):
# Dari confusion matrix yang bisa diturunkan untuk threshold ini.
fp_rate = 1 - precision_val
fn_rate = 1 - recall_val
cost = (fn_rate * cost_fn) + (fp_rate * cost_fp)
if cost < best_cost:
best_cost = cost
best_threshold = threshold
print(best_threshold, best_cost)Perhatikan bahwa perhitungan di atas memakai rate, bukan jumlah absolut. Untuk biaya nyata, kamu butuh jumlah sampel. Prinsipnya tetap: ambang terbaik adalah yang meminimalkan biaya yang kamu definisikan, bukan yang memaksimalkan metrik generik.
Satu train-test split bisa memberi hasil beruntung. Cross-validation membagi data menjadi beberapa lipatan, melatih dan menguji berulang, lalu melaporkan rata-rata dan sebaran skor. Ini memberi gambaran kestabilan model.
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
model = Pipeline([
("scale", StandardScaler()),
("clf", LogisticRegression()),
])
scores = cross_val_score(model, X, y, cv=5, scoring="roc_auc")
print(scores)
print(f"{scores.mean():.3f} +/- {scores.std():.3f}")cv=5 membuat lima lipatan. Untuk data time series, lipatan acak bocor karena masa depan ikut melatih masa lalu. Gunakan TimeSeriesSplit agar urutan waktu dihormati.
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)
scores = cross_val_score(model, X, y, cv=tscv, scoring="roc_auc")Jangan menyetel hyperparameter lalu melaporkan skor cross-validation dari penyetelan itu sebagai skor final. Itu kebocoran. Pisahkan data menjadi train, validation, dan test. Hyperparameter disetel di validation. Skor final dilaporkan dari test yang tidak pernah disentuh.
Metrik hanya sebaik data yang diuji. Kebocoran terjadi saat informasi dari masa depan atau dari target ikut masuk fitur. Skor tinggi dari data bocor tidak bisa dipercaya di produksi.
Contoh kebocoran umum: menyertakan kolom yang dihitung setelah kejadian target terjadi, menormalisasi memakai statistik seluruh data termasuk test set, atau membagi data secara acak padahal observasi saling bergantung seperti beberapa transaksi milik pelanggan yang sama.
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
)stratify=y menjaga proporsi kelas pada tiap split. Ini penting untuk data tidak seimbang. Untuk data pelanggan dengan banyak baris per pelanggan, gunakan group split agar satu pelanggan tidak tersebar di train dan test sekaligus.
Untuk klasifikasi dengan tiga kelas atau lebih, precision, recall, dan F1 dihitung per kelas lalu diagregasi. macro memberi rata-rata tanpa bobot, weighted membobot menurut jumlah sampel, micro menghitung metrik global dari seluruh TP, FP, dan FN.
from sklearn.metrics import precision_score, recall_score, f1_score
f1_macro = f1_score(y_true_mc, y_pred_mc, average="macro")
f1_weighted = f1_score(y_true_mc, y_pred_mc, average="weighted")
f1_micro = f1_score(y_true_mc, y_pred_mc, average="micro")Untuk multi-label, tiap sampel bisa punya banyak label. average="samples" menghitung metrik per sampel lalu rata-rata, berguna saat tiap sampel punya jumlah label yang berbeda.
| Situasi | Metrik yang disarankan |
|---|---|
| Kelas seimbang, semua salah sama mahal | accuracy |
| False positive mahal, misal spam yang salah blokir | precision |
| False negative mahal, misal fraud yang lolos | recall |
| Ingin satu angka seimbang | F1 |
| Recall lebih penting daripada precision | F-beta dengan beta lebih dari 1 |
| Kelas sangat tidak seimbang | PR AUC atau average precision |
| Perlu memindai semua ambang | ROC curve atau PR curve |
| Prediksi nilai kontinu, kesalahan kecil sama mahal | MAE |
| Prediksi kontinu, kesalahan besar sangat mahal | RMSE |
| Target kontinu dan mau angka relatif | MAPE, kalau target tidak pernah nol |
| Menilai kualitas probabilitas | log loss, Brier score, kurva kalibrasi |
| Membandingkan model dengan baseline rata-rata | R-squared |
Pilih dulu metrik utama berdasarkan biaya kesalahan, baru optimalkan model. Jangan mengumpulkan sepuluh metrik lalu memilih yang terlihat terbaik.
| Istilah | Arti praktis |
|---|---|
| Accuracy | Proporsi prediksi yang benar dari seluruh prediksi. |
| AUC | Area di bawah kurva ROC, mengukur kemampuan memisahkan kelas. |
| Calibration | Kecocokan antara probabilitas model dan frekuensi kejadian nyata. |
| Cross-validation | Teknik membagi data berulang untuk menaksir performa yang stabil. |
| False negative | Kejadian positif yang tidak tertangkap. |
| False positive | Kejadian negatif yang salah diprediksi positif. |
| F1 score | Rata-rata harmonis precision dan recall. |
| Leakage | Informasi dari luar data pelatihan yang bocor ke fitur sehingga skor menipu. |
| Log loss | Hukuman atas keyakinan prediksi probabilitas yang salah. |
| Precision | Proporsi prediksi positif yang benar. |
| Recall | Proporsi kejadian positif yang tertangkap. |
| RMSE | Akar dari MSE, menghukum kesalahan besar dengan bobot kuadrat. |
Login atau daftar akun gratis untuk membaca cheat sheet ini.