Supervised learning adalah keluarga machine learning yang paling banyak dipakai industri: model diberi data berlabel, belajar pola dari contoh, lalu memprediksi label untuk data baru. Setelah menyiapkan dataset pada chapter sebelumnya, sekarang kita melatih model sungguhan. Empat algoritma klasik akan kita cicipi dengan dataset iris yang sama, ditutup rasa regression dengan LinearRegression, lalu kita buktikan overfitting dengan angka train dan test yang nyata.
k-Nearest Neighbors: Tetangga Menentukan Nasib #
Algoritma k-Nearest Neighbors (k-NN) memprediksi data baru dengan melihat k tetangga terdekatnya di data latih lalu mengambil suara mayoritas. Pada dataset iris, model k-NN mencapai akurasi berikut pada data uji:
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)
print(f"Akurasi k-NN: {knn.score(X_test, y_test):.2%}")Output yang Dihasilkan:
Akurasi k-NN: 97.78%Method fit() pada k-NN tidak membangun apa pun selain menyimpan data latih; seluruh komputasi terjadi saat predict() menghitung jarak tiap data baru ke seluruh baris latih. Karena bergantung penuh pada jarak Euclidean, k-NN adalah konsumen setia scaling yang kita bahas pada chapter Feature Engineering. Parameter n_neighbors sendiri adalah hyperparameter: nilai kecil membuat model sensitif terhadap noise, nilai besar membuat keputusan terlalu general.
Decision Tree: Model yang Bisa Dibaca #
Decision tree mempelajari rangkaian pertanyaan ya/tidak yang membelah data berdasarkan ambang fitur. Keunggulan utamanya adalah interpretasi; scikit-learn bahkan bisa mengekspor struktur pohonnya sebagai teks:
from sklearn.tree import DecisionTreeClassifier, export_text
pohon = DecisionTreeClassifier(max_depth=2, random_state=42)
pohon.fit(X_train, y_train)
print(export_text(pohon, feature_names=iris.feature_names))
print(f"Akurasi decision tree: {pohon.score(X_test, y_test):.2%}")Output yang Dihasilkan:
|--- petal length (cm) <= 2.45
| |--- class: 0
|--- petal length (cm) > 2.45
| |--- petal width (cm) <= 1.55
| | |--- class: 1
| |--- petal width (cm) > 1.55
| | |--- class: 2
Akurasi decision tree: 88.89%Pohon di atas bisa dibaca langsung: bunga dengan panjang kelopak di bawah 2.45 cm dipastikan spesies pertama (setosa); selebihnya keputusan lanjut ke lebar kelopak. Hanya dua level kedalaman (max_depth=2) sudah menangkap akurasi 88.89 persen, bukti bahwa dua fitur kelopak memisahkan ketiga spesies hampir seluruhnya. Keterbacaan inilah yang membuat decision tree populer untuk skenario yang menuntut penjelasan, meski akurasinya biasanya di bawah ensemble.
Logistic Regression dan Random Forest #
Logistic regression, meski namanya “regression”, adalah model classification yang menghitung peluang tiap kelas lewat kombinasi linear fitur. Random forest adalah ensemble: ratusan decision tree dilatih pada sampel data dan fitur yang berbeda-beda, lalu hasilnya divoting. Keduanya kita adu dengan dua model sebelumnya pada split data yang sama:
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
kandidat = {
"k-NN": KNeighborsClassifier(n_neighbors=5),
"Decision Tree": DecisionTreeClassifier(max_depth=3, random_state=42),
"Logistic Regression": LogisticRegression(max_iter=200),
"Random Forest": RandomForestClassifier(n_estimators=100, random_state=42),
}
for nama, model in kandidat.items():
model.fit(X_train, y_train)
print(f"{nama:<20} akurasi test = {model.score(X_test, y_test):.2%}")Output yang Dihasilkan:
k-NN akurasi test = 97.78%
Decision Tree akurasi test = 97.78%
Logistic Regression akurasi test = 93.33%
Random Forest akurasi test = 88.89%Empat model dengan cara kerja sama sekali berbeda menghasilkan akurasi yang berdekatan pada dataset kecil yang rapi seperti iris. Pelajaran pentingnya: kualitas data dan fitur sering lebih menentukan daripada pilihan algoritma. Pemilihan model akhir biasanya mempertimbangkan kecepatan prediksi, kemudahan interpretasi, dan ketahanan terhadap data kotor, bukan sekadar selisih akurasi satu-dua persen.
Rasa Regression: LinearRegression #
Bergeser ke ranah regression, target kita bukan lagi kategori melainkan angka kontinu. Contoh klasik: memprediksi harga rumah dari luasnya. Kita buat data sintetis deterministik dengan hubungan linear 5.000 rupiah per meter persegi plus noise:
import numpy as np
from sklearn.linear_model import LinearRegression
rng = np.random.RandomState(42)
luas = rng.uniform(30, 120, 50)
harga = 5000 * luas + rng.normal(0, 15000, 50)
X_reg = luas.reshape(-1, 1)
regresi = LinearRegression().fit(X_reg, harga)
print(f"koefisien (harga per m2): {regresi.coef_[0]:.0f}")
print(f"intercept : {regresi.intercept_:,.0f}")
print(f"R^2 : {regresi.score(X_reg, harga):.3f}")Output yang Dihasilkan:
koefisien (harga per m2): 4963
intercept : 2,568
R^2 : 0.989Model menaksir koefisien 4.963 rupiah per meter persegi, cukup dekat dengan nilai sebenarnya 5.000 yang kita tanam. Metrik R^2 (koefisien determinasi) dibaca sebagai proporsi variasi target yang berhasil dijelaskan model: 0.989 berarti 98.9 persen variasi harga tertangkap oleh luas, sisanya noise. Nilai R^2 maksimal 1.0; makin jauh dari nilai itu, makin banyak informasi yang luput dari model.
Underfitting dan Overfitting dalam Angka #
Teori pada chapter Dasar Machine Learning menyebut model terlalu sederhana akan underfit dan model yang terlalu bebas akan overfit. Sekarang kita buktikan. Data berikut hanya punya pola sederhana plus 30 persen label yang sengaja kita acak; decision tree tanpa batas kedalaman akan “menghafal” label acak tersebut:
from sklearn.model_selection import train_test_split
rng = np.random.RandomState(7)
X_noisy = rng.randn(150, 2)
y_noisy = (X_noisy[:, 0] + X_noisy[:, 1] > 0).astype(int)
acak = rng.choice(150, 45, replace=False)
y_noisy[acak] = 1 - y_noisy[acak]
X_latih, X_uji, y_latih, y_uji = train_test_split(
X_noisy, y_noisy, test_size=0.3, random_state=42
)
for kedalaman in [2, None]:
pohon2 = DecisionTreeClassifier(max_depth=kedalaman, random_state=42)
pohon2.fit(X_latih, y_latih)
skor_train = pohon2.score(X_latih, y_latih)
skor_test = pohon2.score(X_uji, y_uji)
kedalaman_teks = "tanpa batas" if kedalaman is None else kedalaman
print(f"max_depth={kedalaman_teks}: akurasi train = {skor_train:.2%}, akurasi test = {skor_test:.2%}")Output yang Dihasilkan:
max_depth=2: akurasi train = 66.67%, akurasi test = 64.44%
max_depth=tanpa batas: akurasi train = 100.00%, akurasi test = 40.00%Kedua ujung spektrum terlihat jelas:
| Kondisi | Skor train | Skor test | Diagnosis |
|---|---|---|---|
max_depth=2 | 66.67% | 64.44% | Underfitting: sama-sama rendah, gap kecil |
max_depth tanpa batas | 100.00% | 40.00% | Overfitting: train sempurna, test jatuh |
Pohon dangkal gagal karena terlalu sederhana untuk menangkap pola; pohon bebas justru hancur karena menghafal 45 label yang kita acak, keputusan yang tidak mungkin bergeneralisasi. Gap 60 poin persentase antara train dan test adalah tanda overfitting paling klasik. Menemukan titik tengahnya, misalnya dengan tuning max_depth, adalah pekerjaan chapter Training dan Evaluation.