Metode filter menilai fitur tanpa model; metode wrapper memakai model sebagai juri dengan biaya latihan berulang. Keluarga ketiga menempuh jalan tengah yang elegan: embedded — seleksi terjadi bersamaan dengan pelatihan, sebagai bagian dari proses belajar model itu sendiri. Dua wujudnya yang paling penting adalah penalti L1 yang menekan koefisien fitur lemah tepat ke nol, dan feature_importances_ yang dikeluarkan model tree secara gratis setelah latihan. Chapter penutup section ini menguji keduanya ditambah alat penyaring SelectFromModel pada dataset kampanye pemasaran yang sama dengan dua chapter sebelumnya, lalu menutup dengan satu peringatan penting tentang urutan seleksi dan validasi.
Seleksi yang Menyatu dengan Pelatihan #
Pada metode embedded, tidak ada prosedur seleksi yang berjalan terpisah: model yang sedang dilatih sekaligus menuliskan penilaiannya atas tiap fitur. Regresi berpenalti L1 melakukannya lewat besar koefisien — fitur lemah berakhir dengan koefisien tepat nol — sedangkan model tree melakukannya lewat seberapa sering dan seberapa besar sebuah fitur dipakai untuk membelah simpul. Kedua mekanisme ini berakar pada teori regularisasi yang dibahas pada Chapter Regularisasi: Ridge, Lasso, dan Elastic Net: seleksi embedded pada dasarnya adalah efek samping yang direkayasa dari bentuk fungsi penalti. Karena tidak ada latihan tambahan, biayanya nyaris nol di atas harga melatih model itu sendiri.
Penalti L1: Koefisien Nol Berarti Fitur Terbuang #
Fungsi biaya model linier biasa hanya meminimalkan galat prediksi. Penalti L1 menambahkan suku jumlah |koefisien| ke fungsi biaya tersebut: setiap koefisien yang tidak sepadan dengan penurunan galat akan didorong tepat ke nol — bukan sekadar diperkecil seperti pada penalti L2. Geometrisnya khas: wilayah penalti L1 berbentuk belah ketupat dengan sudut tajam di sumbu, dan titik optimum sering mendarat persis di sudut itu, tempat sebagian koefisien bernilai nol. Hasilnya adalah model yang renggang (sparse): sekaligus terlatih dan sudah terseleksi.
Untuk klasifikasi, wujudnya adalah LogisticRegression(penalty="l1", solver="liblinear"); untuk regresi, padanannya Lasso. Elastis net menggabungkan kedua penalti lewat l1_ratio — nilai 0 mirip Ridge, nilai 1 identik Lasso — dan dipilih ketika fitur berkorelatan kelompok. Parameter C mengatur kekuatan penalti secara terbalik: C kecil berarti penalti kencang dan lebih banyak koefisien berakhir nol; C besar melonggarkan penalti hingga model mendekati tanpa regularisasi.
feature_importances_: Tree Menilai Fiturnya Sendiri #
Decision tree membangun aturannya dengan memilih, pada tiap simpul, fitur yang paling banyak menurunkan ketidakmurnian (impurity). feature_importances_ mengakumulasi seluruh penurunan impurity tersebut per fitur, tertimbang ukuran simpulnya: fitur yang sering dipakai pada simpul besar nilainya tinggi. Karena penilaian lahir dari model yang melihat kombinasi fitur, importance tree menangkap interaksi yang luput dari uji per-fitur — kekuatan yang sama dengan wrapper, tanpa latihan tambahan.
Biasnya juga harus disebut dengan jujur: importance berbasis impurity cenderung terlalu menghargai fitur dengan banyak nilai unik. Fitur ber-kardinalitas tinggi menawarkan lebih banyak kandidat pembelahan, sehingga punya lebih banyak kesempatan menemukan split yang menguntungkan secara kebetulan — ID transaksi yang unik per baris bisa tampak penting padahal tak bermakna. permutation_importance adalah alternatif yang lebih adil karena menilai dampak fitur terhadap akurasi dengan mengacak nilainya; pembahasan lengkapnya menunggu di Chapter Permutation Importance, Section Interpretabilitas dan Explainable AI.
SelectFromModel: Mengubah Model Menjadi Selektor #
Alat penutup rangkaian ini mengubah model apa pun bertanda coef_ atau feature_importances_ menjadi selektor: SelectFromModel melatih (kloningan) model, menghitung besaran fitur, lalu mempertahankan fitur dengan besaran di atas ambang. Ambangnya fleksibel — "median" menyisakan setengah teratas, "mean" memilih yang di atas rata-rata, atau angka mutlak seperti 0.05 — dan hasilnya dibaca lewat get_support(). Kombinasi lazim: juri tree dengan ambang "median" untuk memangkas dimensi sebelum model utama yang lebih mahal dilatih.
Contoh: Tiga Jalur pada Satu Dataset #
Skrip berikut menjalankan ketiga jalur embedded pada dataset kampanye 120 pelanggan × 8 fitur (3 informatif), lalu merekonsiliasi semuanya terhadap kebenaran generator:
# Seleksi fitur keluarga embedded: penalti L1, feature_importances_, SelectFromModel.
# Dataset kampanye pemasaran yang sama dengan dua chapter sebelumnya.
import numpy as np
from sklearn.datasets import make_classification
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
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"]
# --- (a) Regresi logistik berpenalti L1: koefisien nol = fitur dibuang ---
model_l1 = LogisticRegression(penalty="l1", C=1.0, solver="liblinear", random_state=42)
model_l1.fit(X, y)
print("(a) Koefisien LogisticRegression berpenalti L1 (C=1.0):")
jumlah_nol = 0
for nama, koef in zip(nama_fitur, model_l1.coef_[0]):
if koef == 0.0:
jumlah_nol += 1
status = "NOL (terbuang)" if koef == 0.0 else "nonzero (bertahan)"
print(f" {nama:<20}{koef:>8.3f} {status}")
print(f"Koefisien yang tepat nol: {jumlah_nol} dari {X.shape[1]}")
terpilih_l1 = sorted(n for n, k in zip(nama_fitur, model_l1.coef_[0]) if k != 0.0)
print(f"Fitur bertahan menurut L1 : {', '.join(terpilih_l1)}")
print(f"Informatif sejati (generator): {', '.join(sorted(informatif_sejati))}")
# --- (b) feature_importances_ dari decision tree ---
pohon = DecisionTreeClassifier(random_state=42)
pohon.fit(X, y)
urutan = np.argsort(pohon.feature_importances_)[::-1]
print("\n(b) feature_importances_ DecisionTreeClassifier (diurutkan menurun):")
for indeks in urutan:
print(f" {nama_fitur[indeks]:<20}{pohon.feature_importances_[indeks]:6.3f}")
# --- (c) SelectFromModel: ambang otomatis dari nilai median importance ---
pemilih = SelectFromModel(
estimator=DecisionTreeClassifier(random_state=42),
threshold="median",
)
pemilih.fit(X, y)
terpilih_sfm = sorted(n for n, s in zip(nama_fitur, pemilih.get_support()) if s)
print(f"\n(c) SelectFromModel(threshold='median'), ambang = {pemilih.threshold_:.3f}")
print(f"Fitur terpilih: {', '.join(terpilih_sfm)}")
# --- Rekonsiliasi ketiga metode terhadap informatif sejati ---
tiga_teratas_pohon = sorted(nama_fitur[i] for i in urutan[:3])
print("\nRekonsiliasi terhadap informatif sejati:")
print(f" L1 (koefisien nonzero) : {', '.join(terpilih_l1)}")
print(f" Tree (3 importance teratas) : {', '.join(tiga_teratas_pohon)}")
print(f" SelectFromModel (median) : {', '.join(terpilih_sfm)}")
print(f" Informatif sejati : {', '.join(sorted(informatif_sejati))}")Output yang Dihasilkan:
(a) Koefisien LogisticRegression berpenalti L1 (C=1.0):
kunjungan_mingguan 2.685 nonzero (bertahan)
durasi_kunjungan 2.274 nonzero (bertahan)
riwayat_pembelian 0.000 NOL (terbuang)
usia -0.039 nonzero (bertahan)
kode_area 0.000 NOL (terbuang)
skor_kredit 0.000 NOL (terbuang)
jumlah_perangkat 0.000 NOL (terbuang)
tayangan_iklan 0.000 NOL (terbuang)
Koefisien yang tepat nol: 5 dari 8
Fitur bertahan menurut L1 : durasi_kunjungan, kunjungan_mingguan, usia
Informatif sejati (generator): durasi_kunjungan, kunjungan_mingguan, riwayat_pembelian
(b) feature_importances_ DecisionTreeClassifier (diurutkan menurun):
kunjungan_mingguan 0.726
riwayat_pembelian 0.153
durasi_kunjungan 0.072
usia 0.033
skor_kredit 0.017
kode_area 0.000
tayangan_iklan 0.000
jumlah_perangkat 0.000
(c) SelectFromModel(threshold='median'), ambang = 0.025
Fitur terpilih: durasi_kunjungan, kunjungan_mingguan, riwayat_pembelian, usia
Rekonsiliasi terhadap informatif sejati:
L1 (koefisien nonzero) : durasi_kunjungan, kunjungan_mingguan, usia
Tree (3 importance teratas) : durasi_kunjungan, kunjungan_mingguan, riwayat_pembelian
SelectFromModel (median) : durasi_kunjungan, kunjungan_mingguan, riwayat_pembelian, usia
Informatif sejati : durasi_kunjungan, kunjungan_mingguan, riwayat_pembelianMari kita telaah bagian-bagian utama skrip di atas:
- Baris 10–19 (
X, y = make_classification(...)): dataset identik dengan dua chapter sebelumnya — perbandingan lintas keluarga metode tetap sah. - Baris 33–35 (
model_l1 = LogisticRegression(...)): penalti"l1"denganC=1.0dansolver="liblinear"(satu-satunya solver yang mendukung L1 pada logistic regression di scikit-learn). - Baris 37–47: seluruh koefisien dicetak dengan penanda
NOL (terbuang)ataunonzero (bertahan); fitur bertahan lalu dibandingkan dengan informatif sejati. - Baris 49–56 (
pohon = DecisionTreeClassifier(...)): pohon dilatih,feature_importances_diurutkan menurun lewatargsortterbalik. - Baris 58–66 (
pemilih = SelectFromModel(...)): juri tree dengan ambang"median";threshold_mencetak nilai ambang yang dihitung,get_support()menandai fitur yang lolos. - Baris 68–74: rekonsiliasi akhir — pilihan L1, tiga importance teratas tree, pilihan SelectFromModel, dan kebenaran generator disandingkan dalam satu blok.
Membaca Hasil: Sepakat di Inti, Berselera di Tepi #
Bagian (a) membuktikan janji utama L1: lima dari delapan koefisien berakhir tepat nol tanpa prosedur seleksi apa pun — model yang sekaligus terseleksi. Dua fitur bertahan dengan koefisien besar (kunjungan_mingguan 2,685 dan durasi_kunjungan 2,274), tetapi bacanya tidak sesederhana itu: riwayat_pembelian — informatif sejati dengan skor mutual information kedua tertinggi di chapter filter — justru ditekan ke nol, sementara fitur derau usia lolos dengan koefisien −0,039 yang nyaris tak berarti. Penalti C=1.0 memangkas bukan hanya derau, melainkan juga kontribusi lemah; menaikkan C akan melonggarkan tekanan ini. Perbandingan menarik dengan chapter sebelumnya: RFE dengan LogisticRegression mempertahankan riwayat_pembelian, sementara L1 dengan model yang sama menekannya ke nol — keluarga wrapper dan embedded sungguh membawa kriteria yang berbeda meski juringnya sama.
Bagian (b) menampilkan penilaian tree: tiga importance tertinggi — 0,726, 0,153, 0,072 — jatuh tepat pada tiga fitur informatif sejati, dengan sisa derau terpencar di bawah 0,033. Bagian (c) memperlihatkan mekanisme ambang SelectFromModel: median dari kedelapan importance bernilai 0,025, sehingga seleksi menyisakan empat fitur — tiga informatif plus usia yang lolos tipis. Rekonsiliasi akhir merangkum pola section ini: semua metode sepakat pada dua-tiga fitur terkuat, lalu berbeda selera pada perbatasan — karena penalti, impurity, dan ambang median adalah tiga definisi “penting” yang tidak identik. Pada data nyata tanpa kebenaran generator, konsensus antar metode tetap menjadi ukuran kepercayaan terbaik yang tersedia.
Perbandingan Tiga Keluarga Metode #
| Aspek | Filter | Wrapper (RFE) | Embedded |
|---|---|---|---|
| Biaya komputasi | Paling murah — statistik dihitung sekali | Termahal — model dilatih berulang kali | Sedang — hanya sekali latih model |
| Ketergantungan model | Tidak ada (model-agnostik) | Tinggi — estimator juri menentukan hasil | Tinggi — seleksi lahir dari model pelatih |
| Menangkap interaksi fitur | Sulit — dinilai per fitur terpisah | Ya — model melihat kombinasi | Ya — model melihat kombinasi |
| Risiko utama | Melewatkan fitur yang kuat secara kombinasi | Tidak stabil pada data kecil; mahal | Bias model menular; kekuatan penalti harus disetel |
Urutan pemakaian yang lazim: filter murah untuk pangkas awal dimensi besar, embedded ketika model utama memang berbasis tree atau penalti, dan RFE/RFECV untuk pemangkasan akhir yang telaten pada dimensi yang sudah terkendali.
Seleksi Harus Berada di Dalam Pipeline Validasi #
Solusinya dibungkus rapi oleh scikit-learn: masukkan seleksi dan model ke dalam satu Pipeline, lalu evaluasi pipeline utuh lewat cross_val_score — seleksi dijalankan ulang secara otomatis hanya pada bagian latih tiap fold:
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import Pipeline
pipeline_utuh = Pipeline([
("seleksi", SelectFromModel(
DecisionTreeClassifier(random_state=42), threshold="median")),
("model", LogisticRegression(max_iter=2000)),
])
skor = cross_val_score(pipeline_utuh, X, y, cv=3)Mekanika Pipeline dibahas pada Chapter Pipeline scikit-learn: Dasar, dan pola kebocoran sejenis dibedah pada Chapter Data Leakage dan Cara Mencegahnya.
Section Feature Engineering dan Seleksi sampai di sini lengkap: konsep plafon kinerja, konstruksi fitur, lalu tiga keluarga seleksi yang saling melengkapi. feature_importances_ yang kita pakai sebagai juri sepanjang chapter ini berasal dari satu pohon keputusan — dan langkah alami berikutnya adalah menggabungkan banyak pohon agar penilaian dan prediksinya lebih stabil. Chapter Bagging dan Random Forest di Section Ensemble Learning membuka babak itu.
Referensi Resmi #
- scikit-learn — User Guide: Feature selection
— bagian resmi seleksi berbasis model dan
SelectFromModel. - sklearn.feature_selection.SelectFromModel — API Reference — API selektor berbasis model yang dipakai pada contoh.
- sklearn.linear_model.LogisticRegression — API Reference
— parameter
penalty="l1"danC. - sklearn.tree.DecisionTreeClassifier — API Reference
— atribut
feature_importances_.