Confusion matrix memberi kita empat angka; sekarang kita timbang empat angka itu menjadi keputusan. Dua pertanyaan paling penting dalam klasifikasi dunia nyata jarang dijawab akurasi: ketika model membunyikan alarm, seberapa bisa dipercaya alarm itu? Dan dari seluruh kasus yang benar-benar perlu perhatian, berapa yang berhasil ditemukan? Precision menjawab pertanyaan pertama, recall menjawab yang kedua, dan F1-score mencoba berpegang pada keduanya sekaligus. Pada chapter ini kita menghitung ketiganya dari empat sel confusion matrix, membaca laporan otomatis classification_report, lalu menggeser ambang keputusan untuk melihat pertukaran antara keduanya secara langsung.
Precision dan Recall dari Empat Sel #
Kedua metrik ini sama-sama bertumpu pada sel TP, tetapi pembaginya berbeda. Precision membagi TP dengan jumlah prediksi positif (TP + FP — kolom positif confusion matrix); recall membagi TP dengan jumlah kasus positif yang sebenarnya (TP + FN — baris positif confusion matrix). Kita hitung keduanya pada kasus deteksi fraud sebuah bank: dari 400 transaksi yang diperiksa, 40 di antaranya fraud, dan model menandai 90 transaksi sebagai mencurigakan:
tp, fp, fn, tn = 36, 54, 4, 306
precision = tp / (tp + fp)
recall = tp / (tp + fn)
f1 = 2 * precision * recall / (precision + recall)
rata_aritmetik = (precision + recall) / 2
print("Precision = TP/(TP+FP) = %d/%d = %.3f" % (tp, tp + fp, precision))
print("Recall = TP/(TP+FN) = %d/%d = %.3f" % (tp, tp + fn, recall))
print("F1 = 2*P*R/(P+R) = %.3f" % f1)
print("Rata-rata aritmetik P dan R = %.3f" % rata_aritmetik)Output yang Dihasilkan:
Precision = TP/(TP+FP) = 36/90 = 0.400
Recall = TP/(TP+FN) = 36/40 = 0.900
F1 = 2*P*R/(P+R) = 0.554
Rata-rata aritmetik P dan R = 0.650Mari kita telaah angka-angka tersebut:
- Recall 0.900: dari 40 transaksi fraud, 36 tertangkap dan 4 lolos. Tim investigasi tidak akan menganggur — hampir semua kasus terdeteksi.
- Precision 0.400: dari 90 alarm yang dibunyikan, hanya 36 yang benar. Dua dari tiga pemberitahuan ke nasabah berakhir dengan rasa tidak nyaman tanpa dasar.
- Skor tinggi di satu sisi bukan berita buruk: angka ini justru profil yang disengaja untuk fraud — membiarkan kasus lolos jauh lebih mahal daripada memeriksa transaksi sah secara manual. Menghubungi nasabah sebelum transaksi besar dieksekusi dapat mencegah kerugian besar; permintaan maaf atas pemeriksaan yang keliru hanyalah biaya operasional.
- F1 0.554: rata-rata harmonik kedua nilai — pembahasannya menyusul di bagian berikutnya.
Kapan masing-masing menjadi prioritas? Aturan ringkasnya mengikuti biaya kesalahan:
| Prioritas | Pertanyaan kunci | Contoh kasus | Kesalahan yang paling dihindari |
|---|---|---|---|
| Precision tinggi | Apa setiap alarm layak ditindak? | Filter spam, rekomendasi konten | FP: email penting tercecer, pengguna pergi |
| Recall tinggi | Apa tidak ada kasus yang lolos? | Deteksi fraud, skrining penyakit, prediksi churn pelanggan bernilai besar | FN: kerugian finansial atau kasus terlambat ditangani |
| Keduanya seimbang | Berapa skor tunggal yang jujur? | Perbandingan model awal saat biaya belum jelas | F1-score sebagai ringkasan netral |
F1-Score: Rata-rata Harmonik yang Tidak Bisa Dibohongi #
Sekilas F1 tampak seperti rata-rata biasa, tetapi perhitungan kita memperlihatkan selisihnya: rata-rata aritmetik precision dan recall adalah 0.650, sedangkan F1 hanya 0.554. Rata-rata harmonik selalu condong ke nilai yang lebih kecil, sehingga satu sisi yang rendah menyeret keseluruhan turun — model dengan precision 1.0 dan recall 0.02 mendapat F1 sekitar 0.04 meski rata-rata aritmetiknya 0.51. Sifat ini yang membuat F1 sulit dibohongi: skornya hanya tinggi jika kedua komponennya sama-sama tinggi. Saat kita belum tahu biaya kesalahan mana yang lebih mahal, atau saat membandingkan banyak model secara cepat, F1 adalah ringkasan yang lebih jujur daripada akurasi maupun rata-rata aritmetik.
Membaca classification_report #
Menghitung tiap metrik satu per satu menjadi cepat melelahkan untuk banyak kelas. Fungsi classification_report merangkum semuanya dalam satu tabel; kita jalankan pada data 60 transaksi (8 fraud) dari chapter sebelumnya dengan prediksi TP=6, FP=4, FN=2, TN=48:
from sklearn.metrics import classification_report
# 60 transaksi: 8 fraud (1), 52 sah (0)
aktual = [1] * 8 + [0] * 52
prediksi = [1] * 6 + [0] * 2 + [1] * 4 + [0] * 48
laporan = classification_report(
aktual, prediksi, target_names=["sah", "fraud"], digits=2
)
print(laporan)Output yang Dihasilkan:
precision recall f1-score support
sah 0.96 0.92 0.94 52
fraud 0.60 0.75 0.67 8
accuracy 0.90 60
macro avg 0.78 0.84 0.80 60
weighted avg 0.91 0.90 0.90 60Cara membaca laporan ini menjadi keterampilan harian:
- Satu baris per kelas: baris
fraudmemuat precision 0.60 (6 dari 10 alarm benar), recall 0.75 (6 dari 8 fraud tertangkap), dan F1 0.67. Kolomsupportmencatat jumlah baris aktual per kelas — 52 sah dan 8 fraud — angka yang wajib dicek lebih dulu untuk mengetahui seberapa timpang datanya. accuracysatu baris penuh: 0.90 di kolom tengah, mengingatkan kita pada angka yang kemarin tampak meyakinkan itu.macro avg: rata-rata sederhana kedua kelas tanpa memandang jumlah — precision (0.96+0.60)/2 = 0.78. Rata-rata ini memperlakukan kelas minoritas se penting kelas mayoritas, sehingga cocok diamati pada data timpang.weighted avg: rata-rata tertimbangsupport— angkanya tertarik ke kelas besar. Selisih macro dan weighted (0.78 vs 0.91 untuk precision) adalah alat ukur cepat seberapa timpang performa antar kelas.
Trade-off Precision dan Recall lewat Ambang Keputusan #
Selama ini kita diam-diam memakai ambang 0.5: prediksi positif jika probabilitas melebihi setengah. Padahal predict_proba memberi kita probabilitas mentah, dan ambang adalah kenop yang bisa kita putar. Kita pakai model LogisticRegression yang memprediksi churn 60 member toko daring dari skor risiko; 45 member menjadi data latih, 15 sisanya diuji pada dua ambang berbeda:
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import precision_score, recall_score
from sklearn.model_selection import train_test_split
rng = np.random.default_rng(42)
skor_risiko = rng.uniform(0, 10, 60)
churn = (skor_risiko + rng.normal(0, 2.5, 60) > 5).astype(int)
X = skor_risiko.reshape(-1, 1)
y = churn
X_latih, X_uji, y_latih, y_uji = train_test_split(
X, y, test_size=0.25, random_state=42, stratify=y
)
model = LogisticRegression()
model.fit(X_latih, y_latih)
probabilitas = model.predict_proba(X_uji)[:, 1]
for ambang in [0.5, 0.3]:
keputusan = (probabilitas >= ambang).astype(int)
print("Ambang %.1f -> precision %.2f, recall %.2f"
% (ambang,
precision_score(y_uji, keputusan, zero_division=0),
recall_score(y_uji, keputusan)))Output yang Dihasilkan:
Ambang 0.5 -> precision 0.83, recall 0.71
Ambang 0.3 -> precision 0.67, recall 0.86Mari kita telaah mekanisme di balik pergeserannya:
predict_proba(X_uji)[:, 1]: mengembalikan probabilitas kelas positif untuk tiap baris — angka kontinu antara 0 dan 1, bukan label final. Kolom[:, 1]dipilih karena kolom 0 menyimpan probabilitas kelas negatif.keputusan = (probabilitas >= ambang): perbandingan array mengubah probabilitas menjadi label 0/1 sesuai ambang — persis keputusan yang biasanya disembunyikanpredict()di balik ambang 0.5.- Ambang 0.5: precision 0.83 (5 dari 6 alarm benar), recall 0.71 (5 dari 7 member berisiko tertangkap). Dua member berisiko berprobabilitas di bawah 0.5 dan lolos.
- Ambang 0.3: recall naik ke 0.86 karena satu member berisiko berprobabilitas 0.47 kini ikut tertangkap. Harganya: dua member bertahan berprobabilitas antara 0.3-0.5 ikut terbawa sebagai alarm palsu, sehingga precision turun ke 0.67. Satu member berisiko berprobabilitas 0.27 tetap lolos di kedua ambang.
Perhatikan polanya: menurunkan ambang selalu bergerak ke arah recall naik dan precision turun; menaikkan ambang bergerak sebaliknya. Tidak ada satu ambang yang menaikkan keduanya sekaligus — model dan datanya yang menentukan kurvanya, ambang hanya memilih titik kerja kita di kurva itu.
Rangkuman #
- Precision = TP/(TP+FP) mengukur keandalan alarm: dari yang diprediksi positif, berapa yang benar; recall = TP/(TP+FN) mengukur jangkauan: dari seluruh kasus positif, berapa yang tertangkap.
- Demo fraud 400 transaksi menghasilkan precision 0.400 dan recall 0.900 — profil yang disengaja karena kasus lolos lebih mahal daripada pemeriksaan manual.
- F1 adalah rata-rata harmonik kedua metrik: nilainya condong ke sisi terlemah (0.554 vs rata-rata aritmetik 0.650), sehingga hanya tinggi bila keduanya tinggi.
classification_reportmerangkum precision, recall, F1, dan support per kelas; bandingkanmacro avgdenganweighted avguntuk mengintip ketimpangan performa antar kelas.- Ambang keputusan menggeser titik kerja model di kurva precision-recall: ambang turun menaikkan recall dan menurunkan precision — pilihan angkanya mengikuti biaya kesalahan.
Menggeser ambang satu per satu membuat kita penasaran: bagaimana peta lengkap semua kemungkinan ambang sekaligus? Peta itu ada — kurva ROC — dan luas daerah di bawahnya, AUC, menjadi salah satu angka paling sering dirujuk saat membandingkan model. Kita gambar keduanya pada chapter ROC dan AUC .