library(rsample)

# untuk persiapan data
library(dplyr)
library(readxl)

# untuk keperluan machine learning
library(partykit)
library(randomForest)
library(caret)

# untuk keperluan evaluasi 
library(ROCR)

Tree Based Model

Setelah berkenalan dengan Naive Bayes, terdapat model lainnya yang menganut konsep tree based. Model tree based ini dianggap sebagai salah satu model yang robust dan menjadi fondasi model-model terbaik sekarang.

Akan ada 2 model yang kita pelajari, yaitu:

  • Decision Tree
  • Random Forest

Decision Tree

Decision tree merupakan tree-based model yang cukup sederhana dengan performa yang robust/powerful untuk membuat prediksi. Seperti namanya, decision tree menghasilkan struktur pohon yang tersusun dari pertanyaan-pertanyaan yang berkaitan dengan variabel prediktor. Pertanyaan-pertanyaan ini akan menjadi guidance dalam menentukan hasil prediksi.

🗒️ Note: Selain klasifikasi, decision tree juga dapat digunakna nuntuk kasus regresi.

Struktur

  • Root Node
    • Root node merupakan node paling atas pada suatu decision tree.
    • Root node merepresentasikan prediktor yang paling dasar untuk memisahkan data.
    • Root node pada gambar di atas: Outlook.
  • Interior Node
    • Percabangan selanjutnya yang menggunakan predictor lain apabila root node tidak cukup dalam menentukan target.
    • Interior node pada gambar di atas: Humidity.
  • Terminal/Leaf Node
    • Keputusan akhir berupa nilai target yang diprediksi.

Bagaimana cara membangun sebuah decision tree dari prediktor-prediktor yang terdapat pada data?

  • Tingkat homogen ini dapat diukur menggunakan entropy dan information gain.

  • Secara intuitif, decision tree memilih predictor yang sebisa mungkin menyeragamkan (homogen) target variabel pada leaf node.

Ilustrasi DT

Berikut adalah contoh visualisasi decision tree dari kasus di atas.

dine <- read.csv("data_input/dineout.csv",
                 sep = ";",
                 stringsAsFactors = T)

dine_tree <- ctree(formula = Dine.Out ~ .,
                     data = dine,
                     control = ctree_control(mincriterion = 0.5,
                                             minsplit = 0,
                                             minbucket = 0))

plot(dine_tree, type = "simple")

Study Case: Credit

Dalam kesempatan kali ini, kita akan menggunakan kembali data pada material C1 minggu kemarin. Tujuan dari penggunaan data ini adalah agar kita dapat membandingkan peforma dari model Logistic Regression & K-NN, dengan model tree based.

# Read Data
credit <- read_xlsx("data_input/credit_taiwan_clean.xlsx")

# Mengubah format data
credit <- 
credit %>% 
  select(-id) %>% 
  mutate_at( vars(sex, education, marriage, gb_flag), as.factor )

Dataset memiliki 25 kolom dan 30,952 dengan penjelasan sebagai berikut:

  • id = id debitur
  • limit_bal = Besaran kredit limit yang diberikan dalam dolar NT
  • sex = jenis kelamin
    • man = laki-laki -> 0
    • woman = perempuan -> 1
  • education = Pendidikan terakhir
    • postgraduate (s2 & s3) -> 1
    • undergraduate (s1) -> 2
    • highschool (SMA) -> 3
    • others = lain-lain -> 4
  • marriage = Status pernikahan
    • unknown = tidak terdapat status -> 0
    • married = menikah -> 1
    • single = lajang -> 2
    • others = lainnya -> 3
  • age = Usia dalam tahun
  • pay_* = Status pembayaran dalam bulan April (1) - September (6).
    • 0 = pembayaran tepat waktu
    • 1 = keterlambatan pembayaran satu bulan
    • 2 = keterlambatan pembayaran dua bulan
    • 8 = keterlambatan pembayaran delapan bulan atau lebih
  • bill_amt* = Jumlah tagihan pada bulan April (1) - September (6) dalam dolar NT
  • pay_amt* = Jumlah pembayaran/pengeluaran sebelumnya pada bulan April (1) - September(6) dalam dolar NT
  • gb_flag = Flagging pembayaran default (gagal bayar) pada bulan berikutnya
    • 1 = default
    • 0 = not default

Additional Data Preprocessing

Pada material kali ini, kita juga akan menambah wawasan dalam melakukan proses data preprocessing, berikut adalah beberapa tambahannya

Cross Validation

Dalam melakukan pembagian data menjadi data train dan test, terdapat pendekatan lainnya yang dapat digunakan, yaitu initial_split()

Cara kerjanya sendiri kita akan menggunakan parameter

  • data =: untuk memasukan data yang akan dibagi
  • prop =: jumlah proporsi data yang akan dijadikan data training

Selain dari fungsi tersebut untuk implemetnasinya kita akan menggunakan fungsi training() & testing().

RNGkind(sample.kind = "Rounding")
set.seed(100)

splitter <- initial_split(data = credit, prop = 0.8)

credit_train <- training(splitter)
credit_test <- testing(splitter)
# Mengecek ulang
credit_train$gb_flag %>% 
  table()
## .
##     0     1 
## 13349 11412

Imbalanced Data Train Proportion

Dalam machine learning, upsampling dan downsampling digunakan untuk menangani imbalanced data, yaitu ketika satu kelas memiliki jauh lebih banyak sampel dibandingkan kelas lainnya.

  • Upsampling adalah teknik menambah jumlah sampel kelas minoritas agar lebih seimbang dengan kelas mayoritas. Caranya bisa dengan menduplikasi data yang sudah.

  • Downsampling adalah kebalikan dari upsampling, yaitu mengurangi jumlah sampel kelas mayoritas agar lebih seimbang dengan kelas minoritas.

Fungsi untuk Upsampling dan Downsampling

  • upSample() : fungsi untuk upsampling.
  • downSample(): fungsi untuk downsampling.
  • Parameter:
    • x : predictor.
    • y : target.
    • yname : nama kolom target.
# upsampling
RNGkind(sample.kind = "Rounding")
set.seed(100)

loan_train_up <- upSample(x = credit_train %>% select(-gb_flag),
                          y = credit_train$gb_flag,
                          yname = "gb_flag")

loan_train_up$gb_flag %>% table() %>% prop.table()
## .
##   0   1 
## 0.5 0.5

Modeling Fitting

Untuk membuat model decision tree, dapat digunakan fungsi ctree() dari library partykit.

🧪 Formula: ctree(formula, data)

  • formula = y ~ x
    • y: variabel target.
    • x: variabel prediktor.
  • data: dataframe yang berisikan variabel target dan prediktor.
# tuning model decision tree
tree_credit <- ctree(formula = gb_flag ~ .,
                         data = loan_train_up
                         # control = ctree_control(mincriterion = 0.95,
                         #                         minsplit = 20,
                         #                         minbucket = 10)
                         )
# visualisasi decision tree hasil tuning
plot(tree_credit, type = "simple")

Model Evaluation

Confusion Matrix

# prediksi kelas di data training
pred_tuned_train <- predict(tree_credit, 
                           loan_train_up, 
                           type = "response")


# confusion matrix data training
confusionMatrix(pred_tuned_train,
                loan_train_up$gb_flag,
                positive = "1")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction     0     1
##          0 10868   885
##          1  2481 12464
##                                                
##                Accuracy : 0.8739               
##                  95% CI : (0.8699, 0.8779)     
##     No Information Rate : 0.5                  
##     P-Value [Acc > NIR] : < 0.00000000000000022
##                                                
##                   Kappa : 0.7478               
##                                                
##  Mcnemar's Test P-Value : < 0.00000000000000022
##                                                
##             Sensitivity : 0.9337               
##             Specificity : 0.8141               
##          Pos Pred Value : 0.8340               
##          Neg Pred Value : 0.9247               
##              Prevalence : 0.5000               
##          Detection Rate : 0.4669               
##    Detection Prevalence : 0.5598               
##       Balanced Accuracy : 0.8739               
##                                                
##        'Positive' Class : 1                    
## 
# prediksi kelas di data testing
pred_tuned_test <- predict(tree_credit, 
                           credit_test, 
                           type = "response")


# confusion matrix data testing
confusionMatrix(pred_tuned_test,
                credit_test$gb_flag,
                positive = "1")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction    0    1
##          0 2663  271
##          1  673 2584
##                                                
##                Accuracy : 0.8475               
##                  95% CI : (0.8383, 0.8564)     
##     No Information Rate : 0.5388               
##     P-Value [Acc > NIR] : < 0.00000000000000022
##                                                
##                   Kappa : 0.6963               
##                                                
##  Mcnemar's Test P-Value : < 0.00000000000000022
##                                                
##             Sensitivity : 0.9051               
##             Specificity : 0.7983               
##          Pos Pred Value : 0.7934               
##          Neg Pred Value : 0.9076               
##              Prevalence : 0.4612               
##          Detection Rate : 0.4174               
##    Detection Prevalence : 0.5261               
##       Balanced Accuracy : 0.8517               
##                                                
##        'Positive' Class : 1                    
## 

Another Confusion Matrix Consideration

Dalam machine learning, performa model bergantung pada seberapa baik model dapat menggeneralisasi terhadap data baru. Tiga kondisi utama yang sering terjadi adalah underfitting, overfitting, dan just right.

  • Overfitting (Model Terlalu Kompleks)
    • Error rendah pada data latih, tetapi tinggi pada data uji.
    • Biasanya terjadi karena model terlalu kompleks.
  • Underfitting (Model Terlalu Sederhana)
    • Error tinggi pada data latih dan data uji.
    • Biasanya karena model gagal dalam menangkap pola di data.
  • Just Right (Model Optimal)
    • Performa baik di data latih dan data uji.

Bagaimana perbandingan dari confusion matrix di atas?

Decision Tree Weakness

Kekurangan dari decision tree adalah kecenderungannya untuk overfitting. Hal ini terjadi karena decision tree melakukan percabangan data hingga amat detail (bahkan hingga dalam leaf node hanya terdapat 1 observasi).

Untuk mengatasinya, decision tree perlu tahu kapan ia berhenti membuat cabang. Dengan demikian, pohon yang dihasilkan tidak terlalu kompleks. Pemotongan/pencegahan cabang pohon disebut pruning. Pruning terbagi menjadi 2 cara:

  • Pre-pruning: pencegahan pembuatan cabang (memasang parameter sebelum model dihasilkan).
  • Post-pruning: pemangkasan cabang (memotong cabang setelah model terbentuk).

Implementasi

Mari kita lakukan tuning terhadap model decision tree dengan mengubah beberapa nilai berikut:

  • Parameter mincriterion
    • Parameter ini menunjukkan nilai (1 - p-value) yang harus dilewati agar proses percabangan dapat terjadi.
    • Contoh, saat mincriterion = 0.95, nilai p-value harus < 0.05 agar sebuah node dapat membuat cabang (berkaitan dengan signifikansi prediktor).
    • Semakin besar parameter ini, semakin besar tingkat signifikansi sebuah prediktor agar dapat dilakukan percabangan.
    • Default: mincriterion = 0.95.
  • Parameter minsplit
    • Parameter ini mengatur banyak observasi minimal pada node agar percabangan dapat terjadi.
    • Default: minsplit = 20.
  • Parameter minbucket
    • Parameter ini mengatur banyak observasi minimal pada node setelah terjadi percabangan.
    • Default: minbucket = 7.

💡️ Tips: semakin besar ketiga parameter di atas, semakin sederhana decision tree yang dihasilkan (semakin ketat kriteria dalam melakukan percabangan).

# tuning model decision tree
tree_credit_tuned <- ctree(formula = gb_flag ~ .,
                         data = loan_train_up,
                         control = ctree_control(mincriterion = 0.95,
                                                 minsplit = 300,
                                                 minbucket = 150)
                         )
# visualisasi decision tree hasil tuning
plot(tree_credit_tuned, type = "simple")

# Ensemble Method: Random Forest

Concept

Ensemble method merupakan pendekatan machine learning untuk melakukan prediksi dengan mengombinasikan prediksi dari beberapa model menjadi sebuah prediksi. Dengan kombinasi dari beberapa prediktor tersebut, ensemble method dapat meningkatkan performa prediksi model. Contoh ensemble method adalah random forest.

🌳🌳 Random forest terbentuk dari sekumpulan decision tree. Masing-masing decision tree memiliki karakteristik yang berbeda dan tidak saling berhubungan.

Specil Cross Validation Method

Salah satu hal yang menarik lainnya yang akan kita pelajari adalah bagaimana melakukan cross validation

knitr::include_graphics("assets/kfold.png")

# # JANGAN DI RUN
# # definisikan training control untuk repeated k-fold cross validation
# train_ctrl <- trainControl(method = "repeatedcv",
#                            number = 5, # seberapa banyak kita ingin membagi data
#                            repeats = 3)
# 
# # training model random forest dengan train()
# credit_forest <- train(gb_flag ~ .,
#                    data = loan_train_up,
#                    method = "rf", # pilih metode random forest
#                    trControl = train_ctrl)

Salah satu kelemahan random forest adalah pembuatan model yang membutuhkan waktu yang cukup lama. Practice yang baik selesai melakukan training adalah menyimpan model tersebut ke dalam bentuk file RDS dengan function saveRDS() agar model dapat langsung digunakan tanpa harus training dari awal.

# menyimpan model ke format RDS
#saveRDS(diab_forest, "model/credit_forest.RDS")
# memanggil model
model_rf <- readRDS("model/credit_forest.RDS")
model_rf
## Random Forest 
## 
## 26698 samples
##    23 predictor
##     2 classes: '0', '1' 
## 
## No pre-processing
## Resampling: Cross-Validated (5 fold, repeated 3 times) 
## Summary of sample sizes: 21359, 21358, 21358, 21359, 21358, 21358, ... 
## Resampling results across tuning parameters:
## 
##   mtry  Accuracy   Kappa    
##    2    0.9289335  0.8578669
##   14    0.9320799  0.8641597
##   27    0.9296203  0.8592405
## 
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 14.

💡 Penjelasan Output Model

  • ... samples: banyak bootstrap data untuk pembuatan model -> banyak DT.
  • ... predictors: banyak prediktor.
  • ... classes: banyak kelas target.
  • mtry & accuracy: banyaknya mtry yang digunakan dan accuracy model untuk masing-masing mtry. Digunakan untuk menentukan model terbaik.

Random Forest Special Evaluation

Pada tahap bootstrap sampling, terdapat data yang tidak digunakan dalam pembuatan model. Data ini yang disebut sebagai data out-of-bag (OOB).

Model random forest akan menggunakan data OOB sebagai data untuk melakukan evaluasi dengan cara menghitung error (serupa dengan data testing). Error inilah yang disebut OOB Error. Dalam kasus klasifikasi, OOB error merupakan persentase data OOB yang misklasifikasi.

Syntax: object_model$finalModel

model_rf$finalModel
## 
## Call:
##  randomForest(x = x, y = y, mtry = param$mtry) 
##                Type of random forest: classification
##                      Number of trees: 500
## No. of variables tried at each split: 14
## 
##         OOB estimate of  error rate: 5.64%
## Confusion matrix:
##       0     1 class.error
## 0 12026  1323  0.09910855
## 1   183 13166  0.01370889

Variable Importance

Meskipun random forest kerap dikatakan sebagai model yang tidak bisa diinterpretasi, kita bisa melihat prediktor apa saja yang paling penting (important) dalam pembentukan random forest.

Syntax: varImp(model)

varImp(model_rf) %>% plot()

Special Evaluation Method

ROC-AUC

Sebelumnya, kita sudah mengetahui bahwa accuracy tidak representatif apabila digunakan untuk kasus kelas target yang imbalance. Sebagai alternatif, kita dapat menggunakan precision atau recall. Akan tetapi, kedua metrics ini sangat berpengaruh pada nilai threshold.

Saat kita menggeser nilai threshold, nilai TP, TN, FP, dan FN dapat naik atau turun.

Untuk mengetahui apakah model kita baik di semua threshold, kita dapat membuat plot ROC (Receiver Operating Characteristic).

ROC merupakan plot antara TPR (True Positive Rate) dan FPR (False Positive Rate) pada setiap threshold. TPR pada sumbu y dan FPR pada sumbu x.

\[TPR = \frac{TP}{TP+FN} \qquad \qquad FPR = \frac{FP}{FP+TN}\]

Perhatikan plot di bawah ini.

Mari kita buat plot ROC

# prediksi: ambil nilai peluang
pred_test_prob <- predict(model_rf, 
                          credit_test, 
                          type = "prob")
# ambil peluang kelas positif: 0
pred_prob <- pred_test_prob[,"0"]
# membuat prediction object agar dapt menghitung nilai TPR, FPR,
rf_roc <- prediction(predictions = pred_prob, 
                        labels = credit_test$gb_flag,
                        label.ordering = c("1", "0")) # urutan: kelas negatif, positif
# membuat plot ROC
model_roc_vec <- performance(rf_roc, 
                             "tpr", # True Positive Rate (Recall)
                             "fpr") # False Positive Rate (1 - Specificity)
plot(model_roc_vec)
abline(0,1 , lty = 2) 

💡 Plot ROC Ideal

  • TPR mendekati 1.
  • FPR mendekati 0.
  • Secara grafis:

Karena bentuk ROC visual, untuk mengetahui apakah model kita sudah baik di semua threshold, kita memerlukan AUC (Area Under Curve)

AUC menunjukkan luas area di bawah kurva ROC.

💡 Kriteria AUC

  • Rentang 0-1.
    • Semakin mendekati 1, semakin baik dalam memisahkan kelas positif dan negatif.
    • Semakin mendekati 0.5, model cenderung membuat prediksi secara random.
# menghitung AUC
rf_auc <- performance(rf_roc, "auc")
rf_auc@y.values[[1]] # mengakses nilai AUC
## [1] 0.9940546