Lewati ke konten utama

Seleksi Fitur di R dengan Paket R Boruta

Tangani seleksi fitur di R: jelajahi algoritma Boruta, sebuah wrapper di atas algoritma klasifikasi Random Forest, beserta implementasinya!
· 10 mnt Baca

Jelajahi bersama AI

Data berdimensi tinggi, dalam hal jumlah fitur, semakin umum ditemui dalam masalah machine learning saat ini. Untuk mengekstrak informasi berguna dari volume data yang besar ini, Anda perlu menggunakan teknik statistik untuk mengurangi noise atau data yang berlebihan. Alasannya, Anda sering kali tidak perlu menggunakan setiap fitur yang tersedia untuk melatih model. Anda dapat meningkatkan model dengan hanya memasukkan fitur-fitur yang tidak berkorelasi dan tidak redundan. Di sinilah seleksi fitur berperan penting. Selain membantu mempercepat pelatihan model, seleksi fitur juga mengurangi kompleksitas model, memudahkan interpretasi, dan meningkatkan akurasi, presisi, atau recall—apa pun metrik kinerjanya.

Dalam tutorial ini, Anda akan mempelajari konsep berikut:

  • Pertama, Anda akan mempelajari lebih lanjut tentang seleksi fitur: kapan dapat menggunakannya, dan jenis metode apa saja yang tersedia untuk memilih fitur paling penting bagi model Anda!

  • Kemudian, Anda akan diperkenalkan pada algoritma Boruta. Anda akan melihat cara menggunakannya untuk melakukan pencarian top-down terhadap fitur relevan dengan membandingkan kepentingan atribut asli dengan kepentingan yang mungkin dicapai secara acak, yang diestimasi menggunakan salinan teracaknya, dan secara bertahap mengeliminasi fitur yang tidak relevan.

  • Anda juga akan sekilas melihat dataset yang akan digunakan untuk melakukan seleksi fitur. Anda akan melihat cara mudah untuk mengimputasi nilai hilang dengan bantuan paket Amelia.

  • Terakhir, Anda akan mempelajari lebih lanjut tentang paket Boruta, yang dapat Anda gunakan untuk menjalankan algoritma.

Seleksi Fitur

Secara umum, ketika Anda ingin mengurangi dimensi data, Anda akan menemukan metode seperti Principal Component Analysis, Singular Value Decomposition, dan lain-lain. Maka wajar jika bertanya mengapa Anda tetap memerlukan metode seleksi fitur lainnya. Masalahnya, teknik-teknik ini adalah cara unsupervised untuk seleksi fitur: misalnya, PCA menggunakan varians dalam data untuk menemukan komponen. Teknik-teknik ini tidak mempertimbangkan informasi antara nilai fitur dan kelas atau nilai target. Selain itu, ada asumsi tertentu, seperti kenormalan, yang terkait dengan metode tersebut dan memerlukan beberapa transformasi sebelum menerapkannya. Kendala-kendala ini tidak berlaku untuk semua jenis data.

Secara umum ada tiga jenis metode seleksi fitur:

  • Metode Filter: metode filter umumnya digunakan sebagai langkah prapemrosesan. Pemilihan fitur tidak bergantung pada algoritma machine learning apa pun. Sebaliknya, fitur dipilih berdasarkan skor mereka dalam berbagai uji statistik untuk korelasinya dengan variabel keluaran. Beberapa metode filter umum adalah metrik Korelasi (Pearson, Spearman, Distance), uji Chi-Squared, Anova, Fisher's Score, dan sebagainya.

  • Metode Wrapper: dalam metode wrapper, Anda mencoba menggunakan subset fitur dan melatih model dengan fitur-fitur tersebut. Berdasarkan inferensi dari model sebelumnya, Anda memutuskan menambah atau menghapus fitur dari subset. Forward Selection, Backward Elimination adalah beberapa contohnya.

  • Metode Embedded: ini adalah algoritma yang memiliki metode seleksi fiturnya sendiri secara bawaan. Regresi LASSO adalah salah satu contohnya.

Dalam tutorial ini Anda akan menggunakan salah satu metode wrapper yang tersedia di R melalui paket bernama Boruta.

Algoritma Boruta

Algoritma Boruta adalah wrapper yang dibangun di atas algoritma klasifikasi random forest. Algoritma ini mencoba menangkap semua fitur penting dan menarik yang mungkin ada di dataset Anda terkait variabel keluaran.

  • Pertama, algoritma menduplikasi dataset, dan mengacak nilai di setiap kolom. Nilai-nilai ini disebut shadow features. * Lalu, algoritma melatih sebuah classifier, seperti Random Forest Classifier, pada dataset. Dengan melakukan ini, Anda mendapatkan gambaran kepentingan—melalui Mean Decrease Accuracy atau Mean Decrease Impurity—untuk setiap fitur pada dataset Anda. Semakin tinggi skornya, semakin baik atau semakin penting.
  • Kemudian, algoritma memeriksa untuk setiap fitur asli apakah kepentingannya lebih tinggi. Artinya, apakah fitur tersebut memiliki Z-score yang lebih tinggi daripada Z-score maksimum dari shadow features-nya, yaitu yang terbaik di antara shadow features. Jika ya, ini dicatat dalam sebuah vektor. Ini disebut hits. Berikutnya, algoritma melanjutkan ke iterasi lain. Setelah sejumlah iterasi yang telah ditentukan, Anda akan mendapatkan sebuah tabel berisi hits ini. Ingat: Z-score adalah jumlah simpangan baku suatu titik data dari mean; untuk info lebih lanjut klik di sini.
  • Pada setiap iterasi, algoritma membandingkan Z-score dari salinan teracak fitur dengan fitur asli untuk melihat apakah yang terakhir berkinerja lebih baik daripada yang pertama. Jika ya, algoritma akan menandai fitur tersebut sebagai penting. Intinya, algoritma berupaya memvalidasi kepentingan fitur dengan membandingkannya dengan salinan teracak, yang meningkatkan ketangguhan. Ini dilakukan dengan membandingkan berapa kali sebuah fitur berkinerja lebih baik daripada shadow features menggunakan distribusi binomial.

seleksi fitur R boruta

  • Jika sebuah fitur tidak tercatat sebagai hit misalnya dalam 15 iterasi, Anda menolaknya dan juga menghapusnya dari matriks asli. Setelah sejumlah iterasi—atau jika semua fitur telah dikonfirmasi atau ditolak—Anda berhenti.

Algoritma Boruta di R

Mari gunakan algoritma Boruta pada salah satu dataset yang paling umum tersedia: data Bank Marketing. Data ini merepresentasikan kampanye pemasaran langsung (panggilan telepon) dari sebuah institusi perbankan di Portugal. Tujuan klasifikasinya adalah memprediksi apakah klien akan berlangganan deposito berjangka atau tidak.

Tip: jangan lupa membaca deskripsi rinci berbagai fitur di sini.

read_file <- read.csv('./bank_bank.csv',header=TRUE,sep=';',stringsAsFactors = F) #read csv into a dataframe
str(read_file)

## 'data.frame':    4521 obs. of  17 variables:
##  $ age      : int  30 33 35 30 59 35 36 39 41 43 ...
##  $ job      : chr  "unemployed" "services" "management" "management" ...
##  $ marital  : chr  "married" "married" "single" "married" ...
##  $ education: chr  "primary" "secondary" "tertiary" "tertiary" ...
##  $ default  : chr  "no" "no" "no" "no" ...
##  $ balance  : int  1787 4789 1350 1476 0 747 307 147 221 -88 ...
##  $ housing  : chr  "no" "yes" "yes" "yes" ...
##  $ loan     : chr  "no" "yes" "no" "yes" ...
##  $ contact  : chr  "cellular" "cellular" "cellular" "unknown" ...
##  $ day      : int  19 11 16 3 5 23 14 6 14 17 ...
##  $ month    : chr  "oct" "may" "apr" "jun" ...
##  $ duration : int  79 220 185 199 226 141 341 151 57 313 ...
##  $ campaign : int  1 1 1 4 1 2 1 2 2 1 ...
##  $ pdays    : int  -1 339 330 -1 -1 176 330 -1 -1 147 ...
##  $ previous : int  0 4 1 0 0 3 2 0 0 2 ...
##  $ poutcome : chr  "unknown" "failure" "failure" "unknown" ...
##  $ y        : chr  "no" "no" "no" "no" ...

Gunakan fungsi summary() untuk merangkum statistik deskriptif umum dari berbagai fitur dalam dataset Anda.

summary(read_file)

##       age            job              marital           education        
##  Min.   :19.00   Length:4521        Length:4521        Length:4521       
##  1st Qu.:33.00   Class :character   Class :character   Class :character  
##  Median :39.00   Mode  :character   Mode  :character   Mode  :character  
##  Mean   :41.17                                                           
##  3rd Qu.:49.00                                                           
##  Max.   :87.00                                                           
##    default             balance        housing              loan          
##  Length:4521        Min.   :-3313   Length:4521        Length:4521       
##  Class :character   1st Qu.:   69   Class :character   Class :character  
##  Mode  :character   Median :  444   Mode  :character   Mode  :character  
##                     Mean   : 1423                                        
##                     3rd Qu.: 1480                                        
##                     Max.   :71188                                        
##    contact               day           month              duration   
##  Length:4521        Min.   : 1.00   Length:4521        Min.   :   4  
##  Class :character   1st Qu.: 9.00   Class :character   1st Qu.: 104  
##  Mode  :character   Median :16.00   Mode  :character   Median : 185  
##                     Mean   :15.92                      Mean   : 264  
##                     3rd Qu.:21.00                      3rd Qu.: 329  
##                     Max.   :31.00                      Max.   :3025  
##     campaign          pdays           previous         poutcome        
##  Min.   : 1.000   Min.   : -1.00   Min.   : 0.0000   Length:4521       
##  1st Qu.: 1.000   1st Qu.: -1.00   1st Qu.: 0.0000   Class :character  
##  Median : 2.000   Median : -1.00   Median : 0.0000   Mode  :character  
##  Mean   : 2.794   Mean   : 39.77   Mean   : 0.5426                     
##  3rd Qu.: 3.000   3rd Qu.: -1.00   3rd Qu.: 0.0000                     
##  Max.   :50.000   Max.   :871.00   Max.   :25.0000                     
##       y            
##  Length:4521       
##  Class :character  
##  Mode  :character  
##                    
##                    
##  

Fungsi summary() memberikan ukuran tendensi pusat untuk fitur kontinu seperti mean, median, kuantil, dan lain-lain. Jika ada fitur kategorikal dalam dataset Anda, Anda juga akan melihat Class dan Mode dari fitur-fitur tersebut.

Sekarang mari ubah fitur kategorikal menjadi tipe data factor:

convert <- c(2:5, 7:9,11,16:17)
read_file[,convert] <- data.frame(apply(read_file[convert], 2, as.factor))
str(read_file)

## 'data.frame':    4521 obs. of  17 variables:
##  $ age      : int  30 33 35 30 59 35 36 39 41 43 ...
##  $ job      : Factor w/ 12 levels "admin.","blue-collar",..: 11 8 5 5 2 5 7 10 3 8 ...
##  $ marital  : Factor w/ 3 levels "divorced","married",..: 2 2 3 2 2 3 2 2 2 2 ...
##  $ education: Factor w/ 4 levels "primary","secondary",..: 1 2 3 3 2 3 3 2 3 1 ...
##  $ default  : Factor w/ 2 levels "no","yes": 1 1 1 1 1 1 1 1 1 1 ...
##  $ balance  : int  1787 4789 1350 1476 0 747 307 147 221 -88 ...
##  $ housing  : Factor w/ 2 levels "no","yes": 1 2 2 2 2 1 2 2 2 2 ...
##  $ loan     : Factor w/ 2 levels "no","yes": 1 2 1 2 1 1 1 1 1 2 ...
##  $ contact  : Factor w/ 3 levels "cellular","telephone",..: 1 1 1 3 3 1 1 1 3 1 ...
##  $ day      : int  19 11 16 3 5 23 14 6 14 17 ...
##  $ month    : Factor w/ 12 levels "apr","aug","dec",..: 11 9 1 7 9 4 9 9 9 1 ...
##  $ duration : int  79 220 185 199 226 141 341 151 57 313 ...
##  $ campaign : int  1 1 1 4 1 2 1 2 2 1 ...
##  $ pdays    : int  -1 339 330 -1 -1 176 330 -1 -1 147 ...
##  $ previous : int  0 4 1 0 0 3 2 0 0 2 ...
##  $ poutcome : Factor w/ 4 levels "failure","other",..: 4 1 1 4 4 1 2 4 4 1 ...
##  $ y        : Factor w/ 2 levels "no","yes": 1 1 1 1 1 1 1 1 1 1 ...

Karena titik data diacak untuk membuat shadow features dan Z-score dihitung untuk masing-masingnya, penting untuk menangani nilai hilang atau kosong sebelum menggunakan paket boruta, jika tidak akan memunculkan error.

(Tidak) beruntungnya, dataset ini tidak memiliki keduanya. Namun, untuk tujuan pembelajaran, Anda akan menambahkan beberapa NA ke dalam data.

Mari sisipkan nilai hilang dalam dataset Anda menggunakan fungsi prodNA(). Anda dapat mengakses fungsi ini dengan memasang paket missForest.

Ingat bahwa Anda dapat menggunakan install.packages() untuk memasang paket yang belum ada jika diperlukan!

library(missForest)

# Generate 5% missing values at random
bank.mis <- prodNA(read_file, noNA = 0.05)  

Anda bisa kembali memanggil fungsi summary() pada data frame baru untuk melihat jumlah NA yang diimputasi, tapi mari kita sedikit lebih kreatif!

Mari visualisasikan missingness dalam data menggunakan kode ggplot2 berikut:

library(reshape2)
library(ggplot2)
library(dplyr)
ggplot_missing <- function(x){

  x %>% 
    is.na %>%
    melt %>%
    ggplot(data = .,
           aes(x = Var2,
               y = Var1)) +
    geom_raster(aes(fill = value)) +
    scale_fill_grey(name = "",
                    labels = c("Present","Missing")) +
    theme_minimal() + 
    theme(axis.text.x  = element_text(angle=45, vjust=0.5)) + 
    labs(x = "Variables in Dataset",
         y = "Rows / observations")
}
ggplot_missing(bank.mis)

Garis-garis putih pada plot menunjukkan secara visual bahwa Anda telah menyisipkan nilai hilang di setiap fitur, tetapi Anda lihat betapa rumitnya menulis fungsi ggplot_missing itu? Nah, paket Amelia di R, yang akan Anda gunakan di bagian berikutnya, menyediakan alternatif satu baris untuk memplot gambar serupa:

library(Amelia)
missmap(bank.mis)

Cobalah sendiri!

Mengimputasi Nilai Hilang dengan Amelia

Sekarang Anda dapat mengimputasi nilai hilang dengan beberapa cara, seperti imputasi dengan mean, median, atau mode (untuk fitur kategorikal), tetapi mari gunakan paket kuat lainnya untuk imputasi nilai hilang, yaitu Amelia.

Amelia mengambil sampel bootstrap sebanyak m dan menerapkan algoritma EMB (expectation-maximization dengan bootstrapping) pada setiap sampel. Estimasi mean dan varians sebanyak m akan berbeda-beda. Terakhir, set estimasi pertama digunakan untuk mengimputasi set nilai hilang pertama menggunakan regresi, kemudian set estimasi kedua untuk set kedua, dan seterusnya. Multiple imputation membantu mengurangi bias dan meningkatkan efisiensi. Selain itu, Amelia mendukung imputasi paralel menggunakan CPU multicore.

Amelia memiliki 3 parameter penting:

  • m: jumlah dataset terimputasi yang akan dibuat.
  • idvars: pertahankan semua variabel ID dan variabel lain yang tidak ingin Anda imputasi.
  • noms: masukkan variabel nominal di sini.
library(Amelia)
amelia_bank <- amelia(bank.mis, m=3, parallel = "multicore",noms=c('job','marital','education','default','housing','loan','contact','month','poutcome','y'))

## -- Imputation 1 --
## 
##   1  2  3  4  5  6
## 
## -- Imputation 2 --
## 
##   1  2  3  4  5  6
## 
## -- Imputation 3 --
## 
##   1  2  3  4  5

Untuk mengakses data frame yang telah diimputasi, Anda dapat menggunakan subsetting berikut:

amelia_bank$imputations[[1]]

Untuk mengekspor dataset terimputasi ke file csv gunakan:

write.amelia(amelia_bank, file.stem = "imputed_bank_data_set")

Paket R Boruta

Sekarang mari gunakan algoritma Boruta pada salah satu dataset terimputasi. Anda dapat memanfaatkan paket Boruta untuk melakukan ini:

library(Boruta)
set.seed(111)
boruta.bank_train <- Boruta(y~., data = amelia_bank$imputations[[1]], doTrace = 2)
print(boruta.bank_train)

## Boruta performed 99 iterations in 18.97234 mins.
##  10 attributes confirmed important: age, contact, day, duration,
## housing and 5 more;
##  3 attributes confirmed unimportant: education, job, marital;
##  3 tentative attributes left: balance, campaign, default;

Boruta memberikan keputusan mengenai signifikansi fitur dalam sebuah dataset. Banyak yang sudah diklasifikasikan sebagai penting dan tidak penting, tetapi Anda melihat ada beberapa yang dimasukkan ke kategori tentative.

Lalu apa artinya ini?

Fitur tentative memiliki kepentingan yang sangat dekat dengan shadow features terbaiknya sehingga Boruta tidak dapat membuat keputusan dengan keyakinan yang diinginkan dalam jumlah run Random Forest default.

Lalu apa yang dapat Anda lakukan?

Anda dapat mempertimbangkan untuk meningkatkan parameter maxRuns jika masih ada fitur tentative. Namun, perhatikan bahwa Anda juga dapat memberikan nilai untuk parameter mtry dan ntree, yang akan diteruskan ke fungsi randomForest(). Parameter pertama memungkinkan Anda menentukan jumlah variabel yang diambil secara acak sebagai kandidat di setiap split, sedangkan yang kedua digunakan untuk menentukan jumlah pohon yang ingin Anda bangun. Dengan argumen-argumen ini, classifier Random Forest akan mencapai konvergensi pada nilai minimal dari out-of-bag error.

Ingat bahwa out-of-bag error adalah estimasi untuk mengukur error prediksi dari classifier yang menggunakan bootstrap aggregation untuk mengambil subsampel data pelatihan. Ini adalah rata-rata error prediksi pada setiap sampel pelatihan X dengan hanya menggunakan pohon-pohon yang tidak memiliki X dalam sampel bootstrap-nya.

Alternatifnya, Anda juga dapat mengatur argumen doTrace ke 1 atau 2, yang memungkinkan Anda mendapatkan laporan kemajuan proses.

Paket boruta juga memiliki fungsi TentativeRoughFix(), yang dapat digunakan untuk mengisi keputusan yang belum ada dengan perbandingan sederhana antara median Z-score fitur dengan median Z-score dari shadow feature yang paling penting:

#take a call on tentative features
boruta.bank <- TentativeRoughFix(boruta.bank_train)
print(boruta.bank)

## Boruta performed 99 iterations in 18.97234 mins.
## Tentatives roughfixed over the last 99 iterations.
##  12 attributes confirmed important: age, campaign, contact, day,
## default and 7 more;
##  4 attributes confirmed unimportant: balance, education, job,
## marital;

Boruta kini telah menyelesaikan tugasnya: ia berhasil mengklasifikasikan setiap fitur sebagai penting atau tidak penting.

Sekarang Anda dapat memplot chart kepentingan variabel Boruta dengan memanggil plot(boruta.bank). Namun, label sumbu x akan horizontal. Ini kurang rapi.

Karena itu, Anda akan menambahkan label fitur pada sumbu x secara vertikal, seperti pada potongan kode berikut:

plot(boruta.bank, xlab = "", xaxt = "n")
lz<-lapply(1:ncol(boruta.bank$ImpHistory),function(i)
boruta.bank$ImpHistory[is.finite(boruta.bank$ImpHistory[,i]),i])
names(lz) <- colnames(boruta.bank$ImpHistory)
Labels <- sort(sapply(lz,median))
axis(side = 1,las=2,labels = names(Labels),
at = 1:ncol(boruta.bank$ImpHistory), cex.axis = 0.7)

Label sumbu y Importance merepresentasikan skor Z dari setiap fitur dalam dataset yang diacak.

Boxplot biru merepresentasikan nilai minimal, rata-rata, dan maksimal Z-score dari sebuah shadow feature, sedangkan boxplot merah dan hijau merepresentasikan Z-score dari fitur yang ditolak dan yang dikonfirmasi. Seperti yang dapat Anda lihat, boxplot merah memiliki Z score yang lebih rendah daripada Z score maksimum shadow feature, yang menjadi alasan mereka dimasukkan ke kategori tidak penting.

Anda dapat mengonfirmasi kepentingan fitur dengan mengetik:

getSelectedAttributes(boruta.bank, withTentative = F)

##  [1] "age"      "default"  "housing"  "loan"     "contact"  "day"     
##  [7] "month"    "duration" "campaign" "pdays"    "previous" "poutcome"

bank_df <- attStats(boruta.bank)
print(bank_df)

##              meanImp  medianImp     minImp    maxImp   normHits  decision
## age       11.4236197 11.3760979  8.4250222 15.518420 1.00000000 Confirmed
## job        0.0741753  0.3002281 -1.7651336  1.566687 0.01010101  Rejected
## marital    1.8891283  2.0043568 -1.0276720  4.804499 0.22222222  Rejected
## education  1.5969540  1.6188117 -1.6836346  4.629572 0.28282828  Rejected
## default    2.3721979  2.3472820 -0.1434933  5.044653 0.50505051 Confirmed
## balance    2.3349682  2.3214378 -0.8098151  5.567993 0.51515152  Rejected
## housing    8.4147808  8.4384240  4.7392059 10.404609 1.00000000 Confirmed
## loan       4.1872186  4.2797591  2.0325838  6.263155 0.87878788 Confirmed
## contact   18.9482180 18.9757719 16.0937657 22.121461 1.00000000 Confirmed
## day        9.5645192  9.5828766  6.1842233 13.495442 1.00000000 Confirmed
## month     24.1475736 24.2067940 20.0621966 27.200679 1.00000000 Confirmed
## duration  71.5232213 71.1785055 64.3941499 78.249830 1.00000000 Confirmed
## campaign   2.6221456  2.6188180 -0.4144493  4.941482 0.65656566 Confirmed
## pdays     26.5650528 26.7123730 23.7902945 29.067476 1.00000000 Confirmed
## previous  20.9569022 20.9703991 18.7273357 23.117672 1.00000000 Confirmed
## poutcome  28.5166889 28.4885934 25.9855974 31.527154 1.00000000 Confirmed

Anda dapat dengan mudah memvalidasi hasilnya karena fitur duration diberi kepentingan tertinggi, yang sudah disebutkan dalam deskripsi data (klik di sini) jika Anda membacanya dengan saksama!

Kesimpulan

Voila! Anda telah berhasil menyaring fitur paling penting dari dataset Anda hanya dengan mengetik beberapa baris kode. Dengan ini Anda telah mengurangi noise dari data yang akan sangat bermanfaat bagi classifier mana pun untuk memberikan label pada suatu observasi. Melatih model pada fitur-fitur penting ini pasti akan meningkatkan kinerja model Anda—yang memang menjadi tujuan seleksi fitur sejak awal!

Jika Anda ingin melihat sumber yang digunakan untuk membuat tutorial ini, lihat berikut ini:

Topik

Terkait

blogs

Tutorial Korelasi di R

blogs

Spaghetti Plot dan Jalur Badai

blogs

40 Pertanyaan Wawancara DBMS Teratas di 2026