Regresi logistik merangkum data latih menjadi beberapa koefisien lalu datanya boleh dibuang. Algoritme ini melakukan hal sebaliknya: tidak merangkum apa pun. k-Nearest Neighbors (KNN) menyimpan seluruh data latih, lalu saat sebuah email baru datang, KNN mencari k data lama yang paling mirip dan mengadakan voting. Falsafahnya seperti ungkapan “beritahu siapa teman bergaulmu, maka kutahap siapa dirimu” — tanpa rumus eksplisit, tetapi efektif. Kita akan membedah cara kerjanya, mengapa pemilihan angka k menentukan nasib model, dan apa yang sebenarnya dikembalikan predict_proba.
Pembelajaran Malas vs Pembelajaran Rajin #
KNN adalah contoh utama pembelajaran malas (lazy learning): pemanggilan fit() hanya menyimpan data — tidak ada estimasi parameter, tidak ada iterasi optimasi. Seluruh pekerjaan berat ditunda hingga momen prediksi. Regresi logistik adalah pembelajaran rajin (eager learning): pelatihan memeras data menjadi parameter w dan b, sehingga prediksi tinggal menghitung satu perkalian titik.
Konsekuensi praktisnya simetris. Pelatihan KNN instan, tetapi setiap prediksi harus membandingkan email baru dengan seluruh data latih — biaya prediksi tumbuh seiring jumlah data. Prediksi regresi logistik sangat murah, tetapi melatih ulang saat data bertambah memerlukan optimasi penuh. Untuk kotak masuk yang data emailnya bertambah terus, pertukaran ini layak dihitung: model rajin biasanya menang ketika volume prediksi tinggi.
Jarak adalah Fitur Paling Penting #
Kedekatan diukur dengan metrik jarak. Dua yang paling umum:
euclidean : d(a, b) = akar( Σᵢ (aᵢ - bᵢ)² )
manhattan : d(a, b) = Σᵢ |aᵢ - bᵢ|Jarak euclidean adalah jarak lurus sebagaimana penggaris; manhattan menjumlahkan selisih per dimensi seperti berjalan di kota berpetak. Parameter p pada KNeighborsClassifier mengatur pilihan ini: p=2 (bawaan) untuk euclidean dan p=1 untuk manhattan.
Karena keputusan sepenuhnya dibangun di atas jarak, skala fitur menjadi penentu. Jika fitur “jumlah kata” bernilai ribuan sementara “rasio tautan” bernilai 0–1, jarak euclidean didominasi jumlah kata dan rasio tautan nyaris tak berpengaruh. Standardisasi dengan StandardScaler — materi Chapter Skala Fitur dengan Scaling dan Normalisasi — adalah langkah wajib sebelum KNN pada data nyata; data sintetis contoh kita sengaja sudah berada pada skala seragam sehingga demonstrasi tetap ringkas.
Memilih k #
Angka k — jumlah tetangga yang diajak voting — adalah hyperparameter paling berpengaruh pada KNN. Logikanya trade-off klasik bias-varians: k kecil membuat batas keputusan berlekuk mengikuti tiap titik data (varians tinggi), k besar membuat batas keputusan mulus dan kaku (bias tinggi). Dua ekstremanya mudah dibayangkan: k=1 menimbulkan “pulau” keputusan di sekitar tiap data latih, sementara k sebesar seluruh data membuat model selalu menebak kelas mayoritas.
| Nilai k | Bentuk batas keputusan | Bias | Varians | Risiko utama |
|---|---|---|---|---|
| k = 1 | Mengikuti tiap titik | Rendah | Tinggi | Menghafal derau (overfitting) |
| k kecil (3–9) | Berlekuk halus | Rendah | Sedang | Umumnya pilihan seimbang |
| k besar (25+) | Nyaris lurus dan mulus | Tinggi | Rendah | Meratakan pola (underfitting) |
| k = semua data | Satu wilayah saja | Maksimal | Nol | Selalu menebak kelas mayoritas |
Untuk klasifikasi binier, pilih k ganjil agar voting tidak seri. Mari kita amati pola ini pada 60 email sintetis bertiga kategori:
# k-Nearest Neighbors: pemilihan k dan mekanisme voting tetangga.
# Kisah: mengategorikan email ke tiga folder (utama, sosial, promosi).
import numpy as np
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
kategori = ["utama", "sosial", "promosi"]
X, y = make_blobs(
n_samples=60,
centers=[(-2.5, 0.0), (0.0, 2.5), (2.5, 0.0)],
cluster_std=2.0,
random_state=42,
)
X_latih, X_uji, y_latih, y_uji = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
print("Pengaruh jumlah tetangga k (42 email latih, 18 email uji):")
print(" k akurasi latih akurasi uji")
for k in [1, 5, 25]:
model = KNeighborsClassifier(n_neighbors=k)
model.fit(X_latih, y_latih)
print(f" {k:<3} {model.score(X_latih, y_latih):>12.3f} "
f"{model.score(X_uji, y_uji):>11.3f}")
# Bedah mekanisme voting: satu email baru dan lima tetangga terdekatnya.
model5 = KNeighborsClassifier(n_neighbors=5)
model5.fit(X_latih, y_latih)
email_baru = np.array([[0.0, 1.0]])
jarak, indeks = model5.kneighbors(email_baru)
print("\nLima tetangga terdekat untuk email baru di (0.0, 1.0):")
print(" urutan jarak label tetangga")
for urut, (d, i) in enumerate(zip(jarak[0], indeks[0]), start=1):
print(f" {urut:>6} {d:>5.2f} {kategori[y_latih[i]]}")
proba = model5.predict_proba(email_baru)[0]
print("\nHasil voting:")
for nama, p in zip(kategori, proba):
print(f" {nama:<8} {p:.3f}")
print(f" prediksi akhir: {kategori[model5.predict(email_baru)[0]]}")
# Pembobotan suara: uniform (satu tetangga satu suara) vs distance (dekat lebih kuat).
print("\nPembobotan suara tetangga (k=5):")
for bobot in ["uniform", "distance"]:
model = KNeighborsClassifier(n_neighbors=5, weights=bobot)
model.fit(X_latih, y_latih)
print(f" {bobot:<9} akurasi uji {model.score(X_uji, y_uji):.3f}")Output yang Dihasilkan:
Pengaruh jumlah tetangga k (42 email latih, 18 email uji):
k akurasi latih akurasi uji
1 1.000 0.722
5 0.857 0.833
25 0.786 0.833
Lima tetangga terdekat untuk email baru di (0.0, 1.0):
urutan jarak label tetangga
1 0.75 sosial
2 0.85 utama
3 0.92 sosial
4 0.93 sosial
5 0.98 sosial
Hasil voting:
utama 0.200
sosial 0.800
promosi 0.000
prediksi akhir: sosial
Pembobotan suara tetangga (k=5):
uniform akurasi uji 0.833
distance akurasi uji 0.778Mari kita telaah baris-baris kode di atas:
- Baris 11–16 (
make_blobs): 60 email dibentuk menjadi gumpalan tiga kategori di sekitar tiga pusat yang sengaja didekatkan (cluster_std=2.0) sehingga wilayah kategori saling menumpuk — meniru email nyata yang tidak selalu bersih terpisah.random_state=42menjaga data identik antar eksekusi. - Baris 18–20 (
train_test_split): 42 email latih dan 18 email uji, proporsi kategori dijaga denganstratify=y. - Baris 24–28 (sweep k): tiga model dilatih pada data yang sama persis; satu-satunya perbedaan adalah nilai
n_neighbors. Pemisahan latih/uji inilah yang membuat perbandingannya jujur. - Baris 31–35 (
kneighbors): metodekneighborsmengembalikan jarak dan indeks tetangga terdekat di data latih; indeks dipakai untuk membaca label aslinya dariy_latih. - Baris 39–46 (voting manual): label lima tetangga dicetak satu per satu, lalu
predict_probadibandingkan dengan hitungan suara — inilah pembuktian bahwa probabilitas KNN hanyalah fraksi suara. - Baris 49–53 (pembobotan): dua model k=5 dibedakan hanya oleh parameter
weights; sisanya identik agar efek pembobotan terlihat murni.
Pola pada tabel k persis seperti teori. Model k=1 mencetak akurasi latih sempurna 1,000 — setiap email latih adalah tetangga terdekat dirinya sendiri, sehingga model ini secara definisi menghafal data latih. Bayarannya tampak pada akurasi uji 0,722, terendah dari ketiganya. Model k=5 menurunkan akurasi latih ke 0,857 tetapi akurasi ujinya naik ke 0,833. Model k=25 semakin halus: akurasi latih turun lagi ke 0,786 — tanda awal underfitting — meski akurasi uji masih bertahan di 0,833. Pelajarannya: akurasi latih yang sempurna bukan berita baik; model terbaik justru yang meninggalkan sedikit galat latih demi generalisasi.
Mekanisme votingnya juga kini transparan. Email baru di titik (0,0; 1,0) memiliki lima tetangga: empat berlabel kategori “sosial” dan satu “utama”, dengan jarak 0,75 hingga 0,98. Voting menghasilkan probabilitas 0,800 untuk sosial, 0,200 untuk utama, dan 0,000 untuk promosi — predict_proba KNN memang sekadar fraksi suara, sehingga nilainya melompat diskrit (0,2; 0,4; 0,6) dan tidak akan pernah sehalus probabilitas sigmoid pada regresi logistik. Prediksi akhirnya “sosial” karena mayoritas suara.
KNN mengandalkan geometri: jarak, tetangga, voting. Algoritme berikutnya mengandalkan peluang: satu teorema klasik probabilitas yang membalik arah penalaran dari “fitur ke kelas”. Mari kita jumpa Naive Bayes.