Metode filter menilai setiap fitur sendirian: skor tinggi, lolos; skor rendah, dibuang. Pendekatan itu murah, tetapi melewatkan satu hal penting — nilai sebuah fitur sering baru muncul saat berada berdampingan dengan fitur lain di dalam model. Keluarga wrapper mengambil jalur sebaliknya: model itu sendiri menjadi juri yang menilai kandidat fitur. Chapter ini membedah anggota wrapper yang paling populer, Recursive Feature Elimination (RFE), beserta versi ber-validasi silangnya (RFECV), keduanya diuji pada dataset kampanye pemasaran yang sama dengan chapter sebelumnya.

Keluarga Wrapper: Model Menjadi Juri #

Pencarian subset fitur secara ideal berarti mencoba semua kombinasi — pada 8 fitur itu 255 kemungkinan, dan pada 100 fitur jumlahnya sudah tidak realistis. Wrapper praktis memakai heuristik serakah (greedy): mulai dari semua fitur, buang yang tampak paling lemah, latih ulang, ulangi. Biayanya lebih tinggi dari filter karena model dilatih berulang kali; imbalannya, penilaian memperhitungkan interaksi antar fitur sebagaimana model memakainya. Konsekuensi yang perlu disadari sejak awal: hasil seleksi spesifik terhadap estimator yang menjadi juri — fitur yang dinilai baik oleh regresi logistik belum tentu baik bagi decision tree, dan sebaliknya.

Mekanika RFE Langkah demi Langkah #

RFE (Recursive Feature Elimination) bekerja dalam putaran-putaran pendek:

  1. Latih estimator pada seluruh fitur yang masih tersisa.
  2. Baca bobot kepentingan tiap fitur — koefisien coef_ pada model linier, atau feature_importances_ pada model tree.
  3. Buang step fitur dengan bobot terkecil (bawaan: satu fitur per putaran).
  4. Ulangi sampai tersisa n_features_to_select fitur.

Hasilnya dirangkum dua atribut: support_ menandai fitur yang bertahan, dan ranking_ mencatat urutan waktu pembuangan — nilai 1 berarti bertahan sampai akhir, nilai 2 berarti baru dibuang pada putaran terakhir sebelum selesai, dan seterusnya. Estimator yang boleh menjadi juri adalah semua model yang mengekspos coef_ atau feature_importances_: LogisticRegression, model linier lain, DecisionTreeClassifier, hingga RandomForestClassifier. Parameter step mempercepat proses pada dimensi besar — step=10 atau step=0.2 (buang 20% tiap putaran) lazim ketika fiturnya ratusan.

RFECV: Jumlah Fitur Ditentukan Validasi Silang #

RFE menuntut kita menebak jumlah fitur akhir sejak awal — tebakan yang pada masalah nyata tidak punya jawaban langsung. RFECV menghapus kebutuhan menebak: algoritme ini menjalankan RFE untuk tiap jumlah kandidat dari min_features_to_select sampai seluruh fitur, menilai tiap kandidat lewat cross-validation (mekanikanya dibahas pada Chapter Cross-Validation), lalu memilih jumlah dengan skor tertinggi. Skor tiap kandidat tersimpan di cv_results_["mean_test_score"] sehingga kita bisa membaca kurvanya, bukan cuma pemenangnya. Harganya sepadan dengan kemampuannya: jumlah latihan model berlipat-lipat — kandidat dikali fold dikali putaran eliminasi — dan pada dimensi besar RFECV sungguh mahal untuk dijalankan.

Contoh: Dataset Kampanye yang Sama #

Skrip berikut menjalankan RFE dengan LogisticRegression sebagai juri pada dataset kampanye 120 pelanggan × 8 fitur, lalu RFECV dengan tiga fold, lalu membandingkan hasilnya dengan pilihan metode filter dari chapter sebelumnya:

python
# RFE: seleksi fitur rekursif berpandu model, plus RFECV untuk jumlah optimal.
# Dataset kampanye pemasaran yang sama dengan chapter metode filter.

from functools import partial

import numpy as np
from sklearn.datasets import make_classification
from sklearn.feature_selection import RFECV, RFE, SelectKBest, mutual_info_classif
from sklearn.linear_model import LogisticRegression

X, y = make_classification(
    n_samples=120,
    n_features=8,
    n_informative=3,
    n_redundant=0,
    n_clusters_per_class=1,
    class_sep=1.5,
    shuffle=False,  # agar kolom informatif pasti berada di indeks 0-2
    random_state=42,
)

nama_fitur = [
    "kunjungan_mingguan",  # indeks 0: informatif
    "durasi_kunjungan",    # indeks 1: informatif
    "riwayat_pembelian",   # indeks 2: informatif
    "usia",                # indeks 3: derau
    "kode_area",           # indeks 4: derau
    "skor_kredit",         # indeks 5: derau
    "jumlah_perangkat",    # indeks 6: derau
    "tayangan_iklan",      # indeks 7: derau
]
informatif_sejati = ["kunjungan_mingguan", "durasi_kunjungan", "riwayat_pembelian"]

# --- RFE: pangkas fitur paling lemah satu per satu sampai tersisa 3 ---
rfe = RFE(
    estimator=LogisticRegression(max_iter=2000, random_state=42),
    n_features_to_select=3,
    step=1,
)
rfe.fit(X, y)

print("Ranking RFE (1 = dipertahankan; makin besar makin awal dibuang):")
for nama, ranking in zip(nama_fitur, rfe.ranking_):
    tanda = "  <- dipertahankan" if ranking == 1 else ""
    print(f"  {nama:<20}{ranking:>3}{tanda}")

terpilih_rfe = sorted(n for n, s in zip(nama_fitur, rfe.support_) if s)
print(f"\nFitur terpilih RFE           : {', '.join(terpilih_rfe)}")
print(f"Informatif sejati (generator): {', '.join(sorted(informatif_sejati))}")

# --- RFECV: cross-validation menentukan jumlah fitur optimal ---
rfecv = RFECV(
    estimator=LogisticRegression(max_iter=2000, random_state=42),
    step=1,
    cv=3,
    scoring="accuracy",
    min_features_to_select=1,
)
rfecv.fit(X, y)

print(f"\nJumlah fitur optimal menurut RFECV: {rfecv.n_features_}")
print("Skor akurasi cross-validation rata-rata per jumlah fitur:")
for jumlah, skor in enumerate(rfecv.cv_results_["mean_test_score"], start=1):
    penanda = "  <- optimal" if jumlah == rfecv.n_features_ else ""
    print(f"  {jumlah} fitur: {skor:.3f}{penanda}")
terpilih_rfecv = sorted(n for n, s in zip(nama_fitur, rfecv.support_) if s)
print(f"Fitur terpilih RFECV: {', '.join(terpilih_rfecv)}")

# --- Perbandingan dengan metode filter dari chapter sebelumnya ---
pemilih_filter = SelectKBest(
    score_func=partial(mutual_info_classif, random_state=42), k=3
)
pemilih_filter.fit(X, y)
terpilih_filter = sorted(n for n, s in zip(nama_fitur, pemilih_filter.get_support()) if s)

print(f"\nFilter (mutual information)  : {', '.join(terpilih_filter)}")
print(f"RFE (LogisticRegression)     : {', '.join(terpilih_rfe)}")
if terpilih_filter == terpilih_rfe:
    print("Kesimpulan: kedua keluarga metode sepakat memilih fitur yang sama.")
else:
    print("Kesimpulan: kedua keluarga metode berbeda pilihan pada sebagian fitur.")

Output yang Dihasilkan:

text
Ranking RFE (1 = dipertahankan; makin besar makin awal dibuang):
  kunjungan_mingguan    1  <- dipertahankan
  durasi_kunjungan      1  <- dipertahankan
  riwayat_pembelian     1  <- dipertahankan
  usia                  4
  kode_area             3
  skor_kredit           6
  jumlah_perangkat      2
  tayangan_iklan        5

Fitur terpilih RFE           : durasi_kunjungan, kunjungan_mingguan, riwayat_pembelian
Informatif sejati (generator): durasi_kunjungan, kunjungan_mingguan, riwayat_pembelian

Jumlah fitur optimal menurut RFECV: 3
Skor akurasi cross-validation rata-rata per jumlah fitur:
  1 fitur: 0.900
  2 fitur: 0.975
  3 fitur: 0.983  <- optimal
  4 fitur: 0.975
  5 fitur: 0.975
  6 fitur: 0.975
  7 fitur: 0.975
  8 fitur: 0.975
Fitur terpilih RFECV: durasi_kunjungan, kunjungan_mingguan, riwayat_pembelian

Filter (mutual information)  : durasi_kunjungan, kunjungan_mingguan, riwayat_pembelian
RFE (LogisticRegression)     : durasi_kunjungan, kunjungan_mingguan, riwayat_pembelian
Kesimpulan: kedua keluarga metode sepakat memilih fitur yang sama.

Mari kita telaah bagian-bagian utama skrip di atas:

  1. Baris 11–20 (X, y = make_classification(...)): dataset yang sama persis dengan chapter filter — 120 baris, 8 fitur, 3 informatif di indeks 0–2 — sehingga hasil kedua keluarga metode bisa dibandingkan apel dengan apel.
  2. Baris 34–40 (rfe = RFE(...)): juri LogisticRegression(max_iter=2000) dengan random_state=42; n_features_to_select=3 dan step=1 berarti satu fitur dibuang tiap putaran sampai tersisa tiga.
  3. Baris 42–49: ranking_ dicetak per fitur dengan penanda bagi nilai 1, lalu fitur terpilih dibandingkan dengan daftar informatif sejati dari generator.
  4. Baris 51–59 (rfecv = RFECV(...)): cv=3 membagi data menjadi tiga fold validasi; min_features_to_select=1 membuat kandidat jumlah fitur mencakup 1 sampai 8.
  5. Baris 61–67: skor rata-rata tiap jumlah fitur dicetak sebagai kurva, dengan penanda pada jumlah optimal n_features_.
  6. Baris 69–81: SelectKBest dari chapter filter dijalankan ulang dan hasilnya disandingkan dengan hasil RFE dalam satu kesimpulan.

Membaca Hasil: Penemuan yang Tervalidasi #

Bagian pertama output adalah pembuktian yang rapi: ketiga fitur dengan ranking_ bernilai 1 — kunjungan_mingguan, durasi_kunjungan, riwayat_pembelian — tepat tiga fitur informatif sejati yang ditanam generator. RFE menemukannya tanpa pernah diberi tahu, dan tanpa menghitung statistik apel per fitur: penilaian lahir dari koefisien model yang melihat kedelapan kolom sekaligus. Satu catatan jujur di baris jumlah_perangkat: fitur derau ini mendapat peringkat 2, artinya bertahan sampai putaran keempat sebelum akhirnya dibuang — pada data berukuran 120 baris, derau bisa tampak berguna cukup lama.

Kurva RFECV layak dibaca perlahan. Dengan satu fitur, akurasi cross-validation 0,900; dengan dua fitur melonjak ke 0,975; dengan tiga fitur mencapai puncak 0,983; setelah itu kurva mendatar di 0,975 untuk empat hingga delapan fitur. Bentuk naik-puncak-mendatar ini adalah pola khas: fitur keempat dan seterusnya tidak menambah informasi, hanya menambah dimensi. RFECV memilih tiga — kebetulan tepat jumlah fitur informatif sebenarnya — dan bagian akhir output menegaskan kesepakatan penuh dengan metode filter pada dataset yang sama. Dua keluarga metode dengan mekanisme berbeda sampai pada jawaban yang sama; pada data nyata tanpa kebenaran generator, konsensus seperti ini adalah bukti terkuat yang bisa kita dapat.

Jebakan Praktis RFE #

Dua jebakan lain bersifat struktural. Pertama, seleksi spesifik model: estimator juri menuliskan seleranya pada hasil — bila model produksi adalah gradient boosting, juri tree lebih relevan daripada juri linier; gunakan estimator yang sejenis dengan model yang akan memakai fitur tersebut. Kedua, juri tree membawa bias sendiri: feature_importances_ berbasis impurity cenderung terlalu menghargai fitur dengan banyak nilai unik — bias yang dibahas tuntas pada chapter berikutnya bersama solusinya yang lebih adil.

Ada satu jebakan terakhir yang paling mahal konsekuensinya: menjalankan seleksi pada seluruh data, lalu mengevaluasi hasilnya dengan cross-validation pada data yang sama. Chapter Feature Selection dengan Metode Embedded menutup section ini sekaligus membahas jebakan tersebut — dan memperkenalkan keluarga metode yang melakukan seleksi di dalam pelatihan itu sendiri.

Referensi Resmi #