Pada model pertama kita, seluruh pekerjaan berat terjadi di balik dua kata kunci: fit dan predict. Keseragaman itu bukan kebetulan, melainkan keputusan rancangan yang disebut kontrak estimator — aturan antarmuka yang diikuti seluruh ratusan algoritme di scikit-learn, dari regresi linier sampai gradient boosting. Kontrak inilah yang membuat dokumentasi terasa seragam, yang membuat Pipeline dan GridSearchCV bisa bekerja untuk model apa pun, dan yang memungkinkan kita mengganti algoritme hanya dengan mengubah satu baris. Chapter ini membedah kontrak tersebut metode demi metode, lalu membuktikannya dengan kode yang menyelidiki isi sebuah estimator secara langsung.

Kontrak Estimator: Satu Bahasa untuk Ratusan Algoritme #

Masalah yang dipecahkan kontrak ini nyata: bayangkan setiap algoritme datang dengan antarmuka panggilan sendiri — satu pakai train(), satu pakai learn(), satu pakai execute(). Setiap pustaka baru akan menuntut pembelajaran ulang dari nol. scikit-learn menegakkan satu bentuk untuk semua: konstruktor hanya menyimpan hyperparameter apa adanya tanpa menghitung apa pun; seluruh pembelajaran terjadi di fit(); seluruh penggunaan terjadi di predict() atau transform(); dan hasil pembelajaran tersimpan di atribut berakhiran garis bawah.

Tabel berikut merangkum metode-metode kontrak beserta perannya:

MetodePeranKapan dipanggil
fit(X, y)Memelajari parameter dari data; mengembalikan dirinya sendiriSekali per pelatihan
predict(X)Menghasilkan label/nilai untuk sampel baruSetelah fit, saat inference
predict_proba(X)Probabilitas tiap kelas (estimator klasifikasi saja)Saat butuh tingkat keyakinan
transform(X)Mengubah data memakai aturan hasil belajarSetelah fit estimator transformer
fit_transform(X)fit + transform dalam satu panggilanSaat pertama menerapkan transformer
inverse_transform(Z)Mengembalikan hasil transform ke bentuk asliSaat perlu membaca balik hasil
get_params()Membaca seluruh hyperparameterInspeksi konfigurasi; dipakai GridSearchCV
set_params(**kwargs)Mengubah hyperparameter tanpa objek baruSaat penyetelan model
score(X, y)Metrik bawaan (akurasi untuk klasifikasi, R² untuk regresi)Evaluasi cepat

Konsekuensi praktisnya luar biasa. Karena setiap estimator menaati kontrak yang sama, mengganti algoritme menjadi operasi satu baris:

python
# Ganti satu baris ini; seluruh sisa kode tetap berjalan tanpa diubah:
model = KNeighborsClassifier(n_neighbors=3)      # algoritme A
model = LogisticRegression()                     # algoritme B, kontrak sama
model = RandomForestClassifier(random_state=42)  # algoritme C, kontrak sama

Rantai Pipeline juga lahir dari kontrak ini: setiap langkah pipa hanya perlu tahu bahwa langkah sebelumnya bisa fit/transform, tanpa peduli apa algoritme di dalamnya. Demikian pula GridSearchCV, yang membaca konfigurasi lewat get_params() dan menyetel ulang lewat set_params() — kita akan memanfaatkannya di section Evaluasi dan Validasi Model.

Membuktikan Kontrak dengan Kode #

Klaim-klaim di atas bisa diverifikasi, bukan sekali dimaklumi. File kontrak-estimator.py berikut melatih LogisticRegression pada data kecil, lalu menyelidiki objeknya: apakah fit benar-benar mengembalikan objek yang sama, apa bentuk keluaran predict, parameter apa saja yang dipelajari, dan bagaimana sisi transformer dari kontrak bekerja lewat StandardScaler.

python
# Kontrak estimator scikit-learn: fit() mengembalikan objek yang sama,
# predict() menghasilkan array, dan parameter hasil belajar berakhiran "_".
# Semua estimator scikit-learn menaati aturan yang sama seperti di bawah ini.

import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler

# Data klasifikasi kecil: 10 titik 2D membentuk dua kelompok terpisah.
X = np.array(
    [
        [1.0, 2.0],
        [1.5, 1.8],
        [2.0, 3.0],
        [1.2, 2.5],
        [2.5, 2.2],
        [6.0, 7.0],
        [6.5, 6.2],
        [7.0, 7.5],
        [6.2, 8.0],
        [7.5, 6.8],
    ]
)
y = np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1])

model = LogisticRegression()

# 1. fit() mengembalikan dirinya sendiri (bukan objek baru).
hasil_fit = model.fit(X, y)
print(f"fit() mengembalikan objek yang sama: {hasil_fit is model}")

# 2. predict() selalu mengembalikan array sepanjang jumlah baris X.
prediksi = model.predict(X)
print(f"predict(X) -> bentuk {prediksi.shape}, isi {prediksi.tolist()}")

# 3. Parameter yang DIPELAJARI tersimpan di atribut berakhiran garis bawah.
print(f"coef_      = [{model.coef_[0][0]:.3f}, {model.coef_[0][1]:.3f}]")
print(f"intercept_ = {model.intercept_[0]:.3f}")

# 4. get_params() mengekspos semua hyperparameter (diatur KITA, bukan dipelajari).
params = model.get_params()
print(f"jumlah hyperparameter: {len(params)}")
print(f"daftar kunci          : {', '.join(sorted(params))}")

# 5. set_params() mengubah hyperparameter tanpa membangun objek baru.
model.set_params(C=0.5)
print(f"set_params(C=0.5) -> C sekarang: {model.get_params()['C']}")
print()

# 6. Sisi lain kontrak: transformer memakai transform, bukan predict.
skala = StandardScaler()
Z = skala.fit_transform(X)  # fit + transform digabung dalam satu panggilan
print(f"transformer StandardScaler:")
print(f"  mean tiap kolom setelah transform ~ 0 : {np.allclose(Z.mean(axis=0), 0)}")
print(f"  std tiap kolom setelah transform ~ 1  : {np.allclose(Z.std(axis=0), 1)}")
print(f"  inverse_transform mengembalikan data  : {np.allclose(skala.inverse_transform(Z), X)}")
print(f"  atribut hasil belajar: mean_ = {skala.mean_[0]:.3f}, {skala.mean_[1]:.3f}")

Output yang Dihasilkan:

text
fit() mengembalikan objek yang sama: True
predict(X) -> bentuk (10,), isi [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
coef_      = [0.739, 0.709]
intercept_ = -6.397
jumlah hyperparameter: 15
daftar kunci          : C, class_weight, dual, fit_intercept, intercept_scaling, l1_ratio, max_iter, multi_class, n_jobs, penalty, random_state, solver, tol, verbose, warm_start
set_params(C=0.5) -> C sekarang: 0.5

transformer StandardScaler:
  mean tiap kolom setelah transform ~ 0 : True
  std tiap kolom setelah transform ~ 1  : True
  inverse_transform mengembalikan data  : True
  atribut hasil belajar: mean_ = 4.140, 4.700

Bedah kode baris demi baris:

  1. Baris 9–24 (X dan y): sepuluh titik dua dimensi membentuk dua kelompok terpisah — lima titik di sekitar (1–2, 2–3) berkelas 0 dan lima titik di sekitar (6–7, 6–8) berkelas 1. Data selengkap ini cukup untuk regresi logistik belajar tanpa peringatan konvergensi.
  2. Baris 26, 28–30 (hasil_fit is model): pemanggilan fit() TIDAK menghasilkan objek baru; ia memodifikasi model di tempat lalu mengembalikan referensi yang sama, dibuktikan operator is yang mencetak True. Nilai balik self inilah yang membuat pemanggilan berantai seperti LogisticRegression().fit(X, y).predict(X) sah secara sintaksis.
  3. Baris 32–34 (predict): keluaran predict selalu berupa array sepanjang jumlah baris X — di sini bentuk (10,) dengan isi [0, 0, 0, 0, 0, 1, 1, 1, 1, 1], tepat mengikuti pola dua kelompok. Model tidak pernah mengembalikan dirinya sendiri dari predict; data masuk, prediksi keluar.
  4. Baris 36–38 (coef_, intercept_): dua atribut inilah parameter hasil belajar — bobot tiap fitur dan titik potongnya. Regresi logistik memakainya untuk menghitung probabilitas kelas. Nilainya (0,739; 0,709; −6,397) muncul dari fit(), bukan dari kita.
  5. Baris 40–43 (get_params): lima belas kunci hyperparameter terbaca — C, penalty, solver, max_iter, dan sebagainya. Inilah daftar tuas yang bisa kita putar; GridSearchCV membaca daftar persis ini saat mencari kombinasi terbaik.
  6. Baris 45–47 (set_params): C diubah menjadi 0,5 tanpa membangun objek baru — objek yang sama kini membawa konfigurasi berbeda dan siap dilatih ulang.
  7. Baris 50–57 (StandardScaler): sisi transformer dari kontrak. fit_transform mempelajari rata-rata dan simpangan tiap kolom lalu langsung menerapkan transformasinya; hasil Z memiliki rata-rata ≈ 0 dan simpangan ≈ 1 (dibuktikan np.allclose yang mencetak True). inverse_transform mengembalikan data asli secara utuh, dan rata-rata hasil belajar tersimpan di atribut mean_ — sekali lagi berakhiran garis bawah.

Aturan Garis Bawah: Pemisah Parameter dan Konfigurasi #

Konvensi penamaan scikit-learn tegas: atribut berakhiran _ hanya ada setelah fit() dijalankan. coef_ dan intercept_ milik regresi logistik, cluster_centers_ milik KMeans, classes_ milik classifier — semuanya adalah parameter yang dipelajari dari data. Sebaliknya, n_neighbors, C, atau n_clusters tanpa garis bawah adalah hyperparameter yang tetap tersimpan sejak konstruktor dipanggil, bahkan sebelum model melihat satu baris data pun.

Kontrak fit/predict kini sudah menjadi refleks, dan kita tahu persis apa yang tersimpan di dalam objek model. Namun satu jebakan besar masih menunggu di depan: model yang tampil sempurna pada data latih belum tentu baik pada data baru. Fenomena menghafal-dan-gagal itulah bahasan Chapter Overfitting dan Underfitting.

Referensi Resmi #