Grid search menjanjikan kesistematisan penuh, tetapi membayar mahal untuk itu: setiap dimensi baru melipatgandakan jumlah kombinasi. Random search menawarkan tawar-menawar yang berbeda — alih-alih menguji setiap titik grid, kita mengambil sampel acak dari distribusi dan hanya membayar sebanyak n_iter konfigurasi. Pendekatan ini terdengar kurang rapi, tetapi hasil riset Bergstra dan Bengio (2012) memperlihatkan bahwa pada ruang berdimensi banyak, sampel acak justru menemukan konfigurasi baik lebih efisien daripada grid. Chapter ini menjelaskan cara menyatakan ruang pencarian sebagai distribusi, alasan skala logaritmik begitu penting, dan membandingkan hasilnya dengan grid beranggaran setara.

Dari Daftar Nilai ke Distribusi #

Parameter param_distributions menerima dua jenis nilai: daftar tetap (seperti ["rbf"]) yang akan diambil utuh, dan distribusi scipy yang disampel setiap kali konfigurasi baru dibuat. Tiga distribusi yang paling sering dipakai: randint untuk bilangan bulat kecil seperti jumlah pohon, uniform untuk parameter berskala linier, dan loguniform untuk parameter positif yang rentangnya membentang beberapa orde besaran — C, gamma, learning_rate, dan alpha adalah penghuni tetap kategori terakhir.

Mengapa loguniform penting? Bayangkan menyampel learning_rate pada rentang 0,001 sampai 0,1 dengan uniform. Rentang itu terdiri dari dua dekade: 0,001–0,01 dan 0,01–0,1. Pada skala linier, dekade pertama hanya selebar 0,009 sedangkan dekade kedua 0,09 — akibatnya sekitar 90% sampel jatuh di dekade atas dan wilayah 0,001–0,01 nyaris kosong, padahal nilai kecil justru sering menjadi kandidat kuat. loguniform menyampel merata dalam ruang logaritmik: setiap orde besaran mendapat probabilitas yang sama, sehingga 0,002 dan 0,05 sama berpeluangnya untuk terpilih.

Mengapa Acak Unggul di Dimensi Banyak #

Intuisi Bergstra–Bengio sederhana namun tajam: pada praktiknya, hanya sebagian kecil hyperparameter yang benar-benar mengubah kinerja; sisanya hampir datar. Grid menghabiskan anggaran dengan menguji semua kombinasi termasuk variasi dimensi yang tidak berpengaruh. Sebuah grid 5 × 5 × 5 × 5 = 625 konfigurasi hanya menjelajahi 5 nilai berbeda per hyperparameter. Sebanyak 625 sampel acak pada rentang yang sama menjelajah hingga 625 nilai berbeda per hyperparameter — cakupan yang jauh lebih rapat tepat pada dimensi-dimensi yang penting, karena dimensi yang datar tidak “menghisap” sampel.

Konsekuensinya: dengan anggaran sama, random search melipatkan jumlah nilai berbeda yang dicoba untuk tiap hyperparameter penting. Grid baru bisa menyamai kerapatan itu kalau setiap dimensi diperluas — dan biayanya tumbuh perkalian, bukan bertambah.

API yang Sama dengan GridSearchCV #

RandomizedSearchCV sengaja memelihara antarmuka yang sudah kita kenal: fit(), best_params_, best_score_, best_estimator_, cv_results_, dan refit=True bawaan bekerja persis sama. Dua parameter baru yang mengubah karakter pencarian: n_iter menentukan anggaran (jumlah konfigurasi yang disampel), dan random_state mengendalikan pengambilan sampel sehingga hasil pencarian dapat direproduksi — jalankan ulang dengan seed yang sama dan daftar konfigurasi tersampel akan identik.

Contoh Kode: Sampel Logaritmik vs Grid Setara #

Skrip berikut menyampel 15 konfigurasi (C, gamma) untuk SVM berkernel RBF dari dua distribusi loguniform, mencetak seluruh sampel agar sebaran logaritmiknya terlihat, lalu membandingkannya dengan grid 4 × 4 = 16 titik beranggaran setara.

python
# RandomizedSearchCV mengambil sampel dari DISTRIBUSI kontinu, bukan daftar tetap.
# loguniform menempatkan sampel merata pada skala logaritmik untuk C dan gamma.

import numpy as np
from scipy.stats import loguniform
from sklearn.datasets import make_classification
from sklearn.model_selection import (GridSearchCV, RandomizedSearchCV,
                                     StratifiedKFold, train_test_split)
from sklearn.svm import SVC

X, y = make_classification(n_samples=200, n_features=8, n_informative=5,
                           n_redundant=0, n_clusters_per_class=1, random_state=42)
X_latih, X_uji, y_latih, y_uji = train_test_split(X, y, test_size=0.3,
                                                   stratify=y, random_state=42)
cv = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)

param_distributions = {
    "C": loguniform(0.01, 100),      # rentang ~4 orde besaran
    "gamma": loguniform(0.001, 1),   # rentang ~3 orde besaran
    "kernel": ["rbf"],               # daftar tetap tetap diperbolehkan
}

acak = RandomizedSearchCV(SVC(random_state=42), param_distributions,
                          n_iter=15, cv=cv, random_state=42)
acak.fit(X_latih, y_latih)

print("== 15 sampel (C, gamma) hasil pengambilan acak ==")
for i, p in enumerate(acak.cv_results_["params"], start=1):
    print(f"{i:2d}. C = {p['C']:7.3f}   gamma = {p['gamma']:.4f}")

nilai_c = [p["C"] for p in acak.cv_results_["params"]]
nilai_gamma = [p["gamma"] for p in acak.cv_results_["params"]]
print(f"\nRentang C     : {min(nilai_c):.4f} sampai {max(nilai_c):7.3f} "
      f"(~{np.log10(max(nilai_c) / min(nilai_c)):.1f} orde besaran)")
print(f"Rentang gamma : {min(nilai_gamma):.4f} sampai {max(nilai_gamma):.4f} "
      f"(~{np.log10(max(nilai_gamma) / min(nilai_gamma)):.1f} orde besaran)")

print(f"\nbest_params_ : C={acak.best_params_['C']:.3f}, "
      f"gamma={acak.best_params_['gamma']:.4f}")
print(f"best_score_  : {acak.best_score_:.4f}")
print(f"Akurasi uji  : {acak.best_estimator_.score(X_uji, y_uji):.3f}")
print(f"Total fit    : {15 * 3} CV + 1 refit = {15 * 3 + 1}")

# Pembanding dengan anggaran setara: grid 4 x 4 = 16 titik tetap.
param_grid = {"C": [0.1, 1, 10, 100], "gamma": [0.001, 0.01, 0.1, 1]}
grid = GridSearchCV(SVC(random_state=42), param_grid, cv=cv)
grid.fit(X_latih, y_latih)

print("\n== Pembanding: GridSearchCV 16 titik (anggaran setara) ==")
print(f"best_params_ : {grid.best_params_}")
print(f"best_score_  : {grid.best_score_:.4f}")
print(f"Akurasi uji  : {grid.best_estimator_.score(X_uji, y_uji):.3f}")
print(f"Total fit    : {16 * 3} CV + 1 refit = {16 * 3 + 1}")

Output yang Dihasilkan:

text
== 15 sampel (C, gamma) hasil pengambilan acak ==
 1. C =   0.315   gamma = 0.7114
 2. C =   8.472   gamma = 0.0625
 3. C =   0.042   gamma = 0.0029
 4. C =   0.017   gamma = 0.3968
 5. C =   2.538   gamma = 0.1331
 6. C =   0.012   gamma = 0.8123
 7. C =  21.368   gamma = 0.0043
 8. C =   0.053   gamma = 0.0035
 9. C =   0.165   gamma = 0.0375
10. C =   0.534   gamma = 0.0075
11. C =   2.802   gamma = 0.0026
12. C =   0.147   gamma = 0.0126
13. C =   0.667   gamma = 0.2267
14. C =   0.063   gamma = 0.0349
15. C =   2.342   gamma = 0.0014

Rentang C     : 0.0121 sampai  21.368 (~3.2 orde besaran)
Rentang gamma : 0.0014 sampai 0.8123 (~2.8 orde besaran)

best_params_ : C=2.538, gamma=0.1331
best_score_  : 0.9715
Akurasi uji  : 0.967
Total fit    : 45 CV + 1 refit = 46

== Pembanding: GridSearchCV 16 titik (anggaran setara) ==
best_params_ : {'C': 1, 'gamma': 0.1}
best_score_  : 0.9715
Akurasi uji  : 0.967
Total fit    : 48 CV + 1 refit = 49

Mari kita bedah bagian-bagiannya:

  1. Baris 17–21 (param_distributions): C disampel dari loguniform(0.01, 100) dan gamma dari loguniform(0.001, 1); kernel tetap berupa daftar satu elemen — campuran distribusi dan daftar dalam satu kamus sepenuhnya sah.
  2. Baris 23–25 (n_iter=15, seed): anggaran dibatasi 15 konfigurasi; random_state=42 pada pencari menjamin sampel yang sama setiap eksekusi, terpisah dari random_state estimator dan lipatan CV.
  3. Baris 27–29 (tabel sampel): lima belas pasangan tercetak lengkap. Perhatikan nilainya menyebar dari C = 0.012 hingga C = 21.368 — dan bukan menumpuk di satu wilayah. Baris 31–36 merangkum rentangnya: sekitar 3,2 orde besaran untuk C dan 2,8 untuk gamma.
  4. Baris 38–42 (hasil juara): pemenangnya C = 2.538, gamma = 0.1331 dengan skor CV 0,9715 dan akurasi uji 0,967. Kedua angka ini bukan titik grid standar — nilai sehalus ini hanya bisa ditemukan kalau pencarian menyampel di antara titik-titik tetap.
  5. Baris 44–53 (pembanding grid): grid 16 titik [0.1, 1, 10, 100] × [0.001, 0.01, 0.1, 1] mencapai skor CV yang sama persis (0,9715) dan akurasi uji yang sama (0,967), tetapi dengan 49 pemanggilan fit() dibanding 46 milik random search. Skor seri dengan anggaran sedikit lebih kecil — dan nilai juara random berada di luar jangkauan grid.

Grid atau Random? #

AspekGrid SearchRandom Search
Ruang yang dijelajahHanya titik-titik yang didaftarkanRentang kontinu lewat distribusi
BiayaJumlah kombinasi × lipatan (tumbuh perkalian)n_iter × lipatan (bebas kita atur)
Resolusi per parameterSejumlah nilai yang didaftarkanHingga n_iter nilai berbeda
Paling tepat saatDimensi sedikit dan kandidat jelasDimensi banyak, atau anggaran terbatas

Aturan praktisnya: kalau ruang pencarian hanya menyentuh dua-tiga hyperparameter dengan kandidat yang sudah jelas, grid tetap layak. Begitu dimensi bertambah atau anggaran ketat, pindah ke random search — atau lebih jauh lagi, biarkan anggaran dialokasikan bertahap hanya pada kandidat yang menjanjikan. Gagasan alokasi bertahap itulah yang dijalankan oleh successive halving, bintang Chapter Halving Search.

Referensi Resmi #