Dataset nyata sering memuat puluhan sampai ratusan kolom, padahal tidak semuanya membantu prediksi. Topik ini membahas dua keluarga seleksi fitur, yaitu seleksi univariat dengan SelectKBest dan seleksi berbasis model dengan SelectFromModel, beserta aturan anti-kebocoran yang wajib diikuti. Perbandingan akurasi sebelum dan sesudah seleksi pada data sintetis memperlihatkan bahwa membuang fitur tak berguna dapat menaikkan skor sekaligus menyisakan model yang lebih ringkas.
Seleksi univariat dengan SelectKBest#
Seleksi univariat menilai setiap fitur secara terpisah (univariate) tanpa melihat fitur lain, kemudian mempertahankan fitur dengan skor tertinggi. Fungsi skor f_classif menghitung statistik uji F ANOVA (analysis of variance), yaitu perbandingan antara variasi rata-rata antar kelas dan variasi di dalam kelas. Fitur yang distribusinya berbeda jauh antar kelas memperoleh skor besar, sedangkan fitur yang sebarannya hampir sama memperoleh skor kecil. Alternatifnya adalah informasi mutual (mutual information) yang mampu menangkap hubungan tak linear dengan biaya perhitungan yang lebih mahal.
SelectKBest(score_func=f_classif, k=4) menghitung skor lalu menyimpan k fitur teratas. Aturan anti-kebocoran berlaku penuh di sini, yaitu skor hanya boleh dihitung dari data latih, kemudian transform diterapkan pada data uji dengan skor yang sama. Program berikut menggunakan data sintetis berisi dua puluh fitur dengan tiga fitur informatif dan satu fitur redundan, lalu membandingkan KNeighborsClassifier sebelum dan sesudah seleksi.
import numpy as np
import pandas as pd
from sklearn.datasets import make_classification
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
# 20 fitur, hanya 3 yang informatif dan 1 yang redundan
X, y = make_classification(
n_samples=500,
n_features=20,
n_informative=3,
n_redundant=1,
flip_y=0.05,
class_sep=0.9,
random_state=42,
)
nama = [f"fitur_{i:02d}" for i in range(20)]
df = pd.DataFrame(X, columns=nama)
X_train, X_test, y_train, y_test = train_test_split(
df, y, test_size=0.25, stratify=y, random_state=42
)
# skor univariat dihitung HANYA dari data latih
seleksi = SelectKBest(score_func=f_classif, k=4).fit(X_train, y_train)
skor = pd.Series(seleksi.scores_, index=nama).sort_values(ascending=False)
print("Delapan skor f_classif terbesar:")
for nama_fitur, nilai in skor.head(8).items():
print(f" {nama_fitur} {nilai:.1f}")
print("Indeks terpilih (get_support):", np.where(seleksi.get_support())[0].tolist())
print("Nama terpilih (get_feature_names_out):", list(seleksi.get_feature_names_out()))
# KNN tidak punya penahan fitur tak berguna, sehingga efek seleksinya terasa
model = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=9))
akurasi_sebelum = model.fit(X_train, y_train).score(X_test, y_test)
akurasi_sesudah = model.fit(seleksi.transform(X_train), y_train).score(
seleksi.transform(X_test), y_test
)
print(f"Akurasi data uji sebelum seleksi: {akurasi_sebelum:.3f}")
print(f"Akurasi data uji sesudah seleksi: {akurasi_sesudah:.3f}")Delapan skor f_classif terbesar:
fitur_18 224.8
fitur_16 71.4
fitur_13 23.1
fitur_11 1.6
fitur_03 1.1
fitur_00 0.9
fitur_05 0.8
fitur_04 0.8
Indeks terpilih (get_support): [11, 13, 16, 18]
Nama terpilih (get_feature_names_out): ['fitur_11', 'fitur_13', 'fitur_16', 'fitur_18']
Akurasi data uji sebelum seleksi: 0.768
Akurasi data uji sesudah seleksi: 0.928Keluaran menunjukkan jurang skor yang tajam, yaitu tiga fitur di atas 23.0 dan sisanya di bawah 2.0, sehingga pilihan k=4 jatuh pada keempat fitur yang menonjol dari sisanya. Akurasi KNN naik dari 0.768 menjadi 0.928 setelah enam belas fitur dibuang. Kenaikan sebesar ini terjadi karena KNN mengukur jarak pada seluruh fitur sekaligus, sehingga enam belas fitur derau mengaburkan jarak antar sampel, sedangkan SelectKBest menyaringnya lebih dahulu. Model dengan regularisasi bawaan, seperti regresi logistik, biasanya hanya mendapat perubahan kecil dari seleksi semacam ini.
Seleksi berbasis model dengan SelectFromModel#
Seleksi berbasis model menilai fitur melalui model yang sudah terlatih, sehingga ketergantungan antar fitur ikut terbaca. RandomForestClassifier menyediakan feature_importances_, yaitu porsi penurunan ketidakmurnian yang dibawa setiap fitur pada seluruh pemisahan pohon. SelectFromModel menggunakan nilai tersebut sebagai dasar seleksi, dan argumen threshold="median" mempertahankan fitur yang kepentingannya di atas median, artinya separuh dari jumlah fitur awal.
import pandas as pd
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import SelectFromModel
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
# data yang sama dengan berkas seleksi-univariat.py
X, y = make_classification(
n_samples=500,
n_features=20,
n_informative=3,
n_redundant=1,
flip_y=0.05,
class_sep=0.9,
random_state=42,
)
nama = [f"fitur_{i:02d}" for i in range(20)]
df = pd.DataFrame(X, columns=nama)
X_train, X_test, y_train, y_test = train_test_split(
df, y, test_size=0.25, stratify=y, random_state=42
)
# kepentingan pohon dihitung dari data latih
hutan = RandomForestClassifier(n_estimators=200, random_state=42).fit(X_train, y_train)
penting = pd.Series(hutan.feature_importances_, index=nama).sort_values(ascending=False)
print("Lima feature_importances_ terbesar:")
for nama_fitur, nilai in penting.head(5).items():
print(f" {nama_fitur} {nilai:.3f}")
# ambang median menyisakan separuh fitur dengan kepentingan tertinggi
pemilih = SelectFromModel(
RandomForestClassifier(n_estimators=200, random_state=42), threshold="median"
).fit(X_train, y_train)
print("Jumlah fitur tersisa:", len(list(pemilih.get_feature_names_out())))
print("Fitur tersisa:", list(pemilih.get_feature_names_out()))
model = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=9))
akurasi_sebelum = model.fit(X_train, y_train).score(X_test, y_test)
akurasi_sesudah = model.fit(pemilih.transform(X_train), y_train).score(
pemilih.transform(X_test), y_test
)
print(f"Akurasi data uji sebelum seleksi: {akurasi_sebelum:.3f}")
print(f"Akurasi data uji sesudah seleksi: {akurasi_sesudah:.3f}")Lima feature_importances_ terbesar:
fitur_18 0.308
fitur_13 0.120
fitur_16 0.112
fitur_17 0.055
fitur_11 0.034
Jumlah fitur tersisa: 10
Fitur tersisa: ['fitur_04', 'fitur_05', 'fitur_06', 'fitur_09', 'fitur_11', 'fitur_13', 'fitur_15', 'fitur_16', 'fitur_17', 'fitur_18']
Akurasi data uji sebelum seleksi: 0.768
Akurasi data uji sesudah seleksi: 0.880Daftar kepentingan pohon memunculkan fitur yang sama dengan skor ANOVA, yaitu fitur_18, fitur_13, dan fitur_16, tetapi urutannya tidak identik karena keduanya mengukur hal yang berbeda. Ambang median menyisakan sepuluh fitur, termasuk beberapa yang kepentingannya rendah, sehingga akurasi 0.880 berada di antara hasil tanpa seleksi dan hasil univariat. Kedua pendekatan mengungguli model tanpa seleksi, dan pilihan di antara keduanya bergantung pada kebutuhan, yaitu seleksi univariat lebih agresif dan cepat, sedangkan seleksi berbasis model lebih toleran terhadap fitur yang bekerja secara gabungan.
Memilih antara dua keluarga seleksi#
Kedua keluarga seleksi melayani kebutuhan yang berbeda, sehingga pilihannya mengikuti bentuk data dan tujuan pemodelan. Tabel berikut merangkum perbedaan yang paling menentukan.
| Aspek | SelectKBest | SelectFromModel |
|---|---|---|
| Dasar penilaian | skor statistik per fitur secara terpisah | kepentingan dari model terlatih |
| Hubungan antar fitur | diabaikan sepenuhnya | ikut terbaca oleh model |
| Biaya perhitungan | sangat kecil, satu kali uji statistik | mengikuti biaya melatih model yang menilainya |
| Perilaku pada fitur gabungan | mudah tersingkir | tetap terdeteksi bila modelnya memanfaatkannya |
| Titik rawan kebocoran | skor dihitung dari data uji | kepentingan dihitung dari data uji |
Seleksi univariat cocok menjadi saringan pertama ketika jumlah fitur sangat besar dan waktu komputasi terbatas, misalnya pada data teks dengan ribuan kolom. Seleksi berbasis model lebih tepat ketika fitur diduga bekerja secara gabungan atau ketika model penilainya sudah menjadi bagian dari rencana pemodelan. Keduanya juga dapat digunakan bertingkat, yaitu SelectKBest menyaring seribu kandidat menjadi seratus, kemudian SelectFromModel menyeleksi lagi di atas sisa tersebut. Apa pun kombinasinya, aturan yang sama mengikat, yaitu setiap selektor hanya melihat data latih dan jumlah fitur akhirnya ditentukan lewat cross-validation, bukan lewat skor data uji.
Kesalahan umum#
- Memilih
kdengan melihat skor data uji. Data uji yang digunakan untuk memilih jumlah fitur kehilangan fungsinya sebagai pengukur yang jujur, sama seperti pada penalaan model. Perbaikannya dengan menentukankdari cross-validation di dalam data latih. - Mengandalkan skor univariat untuk fitur yang hanya berguna secara gabungan. Dua fitur yang masing-masing lemah tetapi kuat bila dikombinasikan akan tersingkir oleh
f_classif. Perbaikannya dengan menyeimbangkanSelectKBestdan seleksi berbasis model yang mampu membaca ketergantungan antar fitur. - Membaca nama fitur setelah transformasi tanpa
get_feature_names_out. Hasiltransformberupa array atau DataFrame dengan kolom yang tersisa, sehingga indeks lama tidak lagi cocok. Perbaikannya dengan mengambil nama dari selektor itu sendiri sebelum kolom dibuang.
Ringkasan#
SelectKBestdenganf_classifmenghitung skor ANOVA per fitur dari data latih saja dan mempertahankankfitur teratasnya.- Pada dua puluh fitur sintetis dengan empat yang berguna, akurasi KNN naik dari 0.768 menjadi 0.928 setelah enam belas fitur derau dibuang.
SelectFromModelmenggunakan kepentingan model terlatih, danthreshold="median"menyisakan sepuluh fitur dengan akurasi 0.880 pada data uji.- Seleksi masuk ke dalam
Pipelinedan nilainya ditala lewat cross-validation agar tidak mengintip data uji.
Referensi#
- sklearn.feature_selection.SelectKBest — scikit-learn documentation
— Dokumentasi selektor univariat beserta atribut
scores_danget_support. - sklearn.feature_selection.f_classif — scikit-learn documentation — Rujukan fungsi skor ANOVA F yang digunakan pada contoh utama.
- 1.13. Feature selection — scikit-learn documentation — Panduan resmi seleksi univariat, berbasis model, dan peringatan kebocorannya.
- sklearn.ensemble.RandomForestClassifier — scikit-learn documentation
— Sumber atribut
feature_importances_yang digunakanSelectFromModel. - sklearn.datasets.make_classification — scikit-learn documentation
— Generator data sintetis dengan parameter
n_informativedann_redundant.