Sebelum mengenal tentang classification, mari sedikit membahas tentang machine learning itu sendiri. Machine Learning adalah suatu bidang ilmiah yang mempelajari tentang algoritma dan model statistik yang digunakan oleh sistem komputer dengan mengandalkan pola. Machine learning atau dalam Bahasa Indonesia Pembelajaran Mesin juga memberikan kemampuan pada sistem untuk secara otomatis belajar dan meningkat dari pengalaman tanpa diprogram secara eksplisit. Pembelajaran mesin berfokus pada pengembangan program komputer yang dapat mengakses data dan menggunakannya untuk belajar sendiri.
Lalu kita masuk kedalam pembahasan apa itu classification? Classification dapat didefinisikan sebagai proses memprediksi kelas atau kategori dari nilai yang diamati atau titik data yang diberikan. Output yang dikategorikan dapat memiliki bentuk seperti “Black” atau “White” atau “spam” atau “no spam”. Secara matematis, classification adalah tugas mendekati fungsi pemetaan (f) dari variabel input (X) ke variabel output (Y). Ini pada dasarnya milik pembelajaran mesin yang diawasi di mana target juga disediakan bersama dengan set data input.
BERBAGAI ALGORITMA CLASSIFICATION
Berikut ini adalah beberapa algoritma classification :
Logistic Regression
Support Vector Machine (SVM)
Decision Tree
Naïve Bayes
Random Forest
Tujuan utama dari kursus ini adalah untuk memberikan pengenalan yang komprehensif untuk classification in machine learning dengan menggunakan data yang ada.
Mempelajari algoritma klasifikasi dari dasar, menyelidiki dasar matematika yang mendukung algoritma logistic regression dan algoritma nearest neighbors.
Load Library
library(dplyr)
library(class)
library(caret)Load Data
turn_over <- read.csv("turnover_balance.csv")
turn_overData turnover terdiri dari 10 kolom dan 7142 baris. Data ini merupakan data Human Resource (HR) yang menunjukkan rekam historis karakteristik karyawan yang telah mengundurkan diri dan yang tidak. Di bawah ini merupakan informasi detail terkait variabel yang terdapat pada dataset:
satisfaction_level: tingkat kepuasan karyawan selama bekerja di perusahaanlast_evaluation: tingkat kepuasan karyawan pada evaluasi terakhirnumber_project: jumlah projek yang diterima oleh karyawanaverage_monthly_hours: rata-rata jam kerja per bulantime_spend_company: lama waktu bekerja di perusahaan (dalam tahun)work_accident: ada atau tidaknya kecelakaan kerja, 0 = tidak ada, 1 = adapromotion_last_5years: apakah karyawan pernah mendapatkan promosi dalam 5 tahun terakhir, 0 = tidak, 1 = yadivision: nama divisi atau departemensalary: tingkat pendapatan, dibedakan menjadi low, medium, dan highleft: apakah karyawan mengundurkan diri, 0 = tidak, 1 = ya (target)Kita akan memprediksi kecenderungan karyawan untuk mengundurkan diri atau tidak yang tersimpan dalam kolom left sebagai variabel target. Kita akan ubah tipe data pada kolom Work_accident, promotion_last_5years, division, salary dan left agar menjadi tipe data factor seperti seharusnya.
str(turn_over)#> 'data.frame': 7142 obs. of 10 variables:
#> $ satisfaction_level : num 0.82 0.79 0.73 0.92 0.69 0.98 0.52 0.51 0.88 0.76 ...
#> $ last_evaluation : num 0.68 0.67 0.95 0.78 1 0.97 0.9 0.73 0.99 0.85 ...
#> $ number_project : int 3 5 3 3 5 3 4 4 3 3 ...
#> $ average_monthly_hours: int 140 156 149 218 237 209 285 229 190 192 ...
#> $ time_spend_company : int 2 2 2 3 3 3 2 3 5 3 ...
#> $ Work_accident : int 0 0 0 0 0 0 0 0 0 0 ...
#> $ promotion_last_5years: int 0 0 0 0 0 0 0 0 0 0 ...
#> $ division : chr "sales" "product_mng" "support" "technical" ...
#> $ salary : chr "low" "low" "low" "low" ...
#> $ left : int 0 0 0 0 0 0 0 0 0 0 ...
Ubah Tipe Data
turn_over_clean <- turn_over %>%
mutate(
Work_accident = as.factor(Work_accident),
promotion_last_5years = as.factor(promotion_last_5years),
division = as.factor(division),
salary = as.factor(salary),
left = as.factor(left)
)
str(turn_over_clean)#> 'data.frame': 7142 obs. of 10 variables:
#> $ satisfaction_level : num 0.82 0.79 0.73 0.92 0.69 0.98 0.52 0.51 0.88 0.76 ...
#> $ last_evaluation : num 0.68 0.67 0.95 0.78 1 0.97 0.9 0.73 0.99 0.85 ...
#> $ number_project : int 3 5 3 3 5 3 4 4 3 3 ...
#> $ average_monthly_hours: int 140 156 149 218 237 209 285 229 190 192 ...
#> $ time_spend_company : int 2 2 2 3 3 3 2 3 5 3 ...
#> $ Work_accident : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
#> $ promotion_last_5years: Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
#> $ division : Factor w/ 10 levels "accounting","hr",..: 8 6 9 10 10 9 2 8 10 5 ...
#> $ salary : Factor w/ 3 levels "high","low","medium": 2 2 2 2 1 2 2 2 1 2 ...
#> $ left : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
Misal, sebagai seorang staff HR, kita diminta untuk menginvestigasi divisi yang memiliki rekam jejak karyawan yang mengundurkan diri berdasarkan rata-rata jam kerja per bulan. Silahkan lakukan agregasi data berdasarkan average_monthly_hours untuk masing-masing divisi. Karena Anda hanya fokus pada karyawan yang mengundurkan diri, Anda harus menyeleksi data berdasarkan kondisi yang dibutuhkan.
avg_hour_agg <- turn_over_clean %>%
group_by(division) %>% # mengelompokkan berdasarkan kolom division
summarise(mean_hour = mean(average_monthly_hours)) %>% # merata2kan untuk setiap channel_title
ungroup() %>%
arrange(desc(mean_hour))
avg_hour_aggBerdasarkan agregasi data yang telah dibuat, divisi yang merupakan 3 divisi dengan rata-rata jam kerja per bulan tertinggi adalah technical, IT, dan RnD.
colSums(is.na(turn_over_clean))#> satisfaction_level last_evaluation number_project
#> 0 0 0
#> average_monthly_hours time_spend_company Work_accident
#> 0 0 0
#> promotion_last_5years division salary
#> 0 0 0
#> left
#> 0
Dataset yang ada sudah baik, karena tidak ada nilai missing value.
Setelah melakukan eksplorasi data, kita akan melanjutkan pada tahap pre-proses sebelum membangun model klasifikasi. Lihatlah proporsi kelas pada variabel target yaitu kolom left dengan menggunakan fungsi prop.table(table(data)).
prop.table(table(turn_over_clean$left))#>
#> 0 1
#> 0.5 0.5
Variabel target kita terlihat memiliki proporsi kelas yang seimbang.
Sebelum kita membangun model, kita harus memisahkan data menjadi data train dan test agar dapat memvalidasi performa model klasifikasi yang dibuat. Pisahkan data turn_over_clean dengan pembagian proporsi 80% untuk data train dan 20% untuk data test menggunakan fungsi sample(). Gunakan set.seed() dengan besaran 100. Simpan hasil pemisahan data pada objek train dan test.
RNGkind(sample.kind = "Rounding")
set.seed(100)
# index sampling
index <- sample(x = nrow(turn_over_clean),
size = nrow(turn_over_clean)*0.8)
# splitting
turn_over_train <- turn_over_clean[index,] # mengambil 80% dari total data untuk digunakan sebagai data train
turn_over_test <- turn_over_clean[-index,] # 20% sisanya digunakan sebagai data testMari kita lihat proporsi kelas target pada data train menggunakan fungsi prop.table(table(data)) untuk memastikan data train memiliki proporsi kelas yang seimbang.
round(prop.table(table(turn_over_train$left)), digits = 2)#>
#> 0 1
#> 0.5 0.5
Variabel target kita terlihat memiliki proporsi kelas yang seimbang.
Setelah membagi data menjadi data train dan test, mari kita modelkan variabel left dengan menggunakan seluruh variabel sebagai prediktor dengan regresi logistik.
model_logistic <- glm(formula = left ~ .,
data = turn_over_train,
family = "binomial")Berdasarkan model_logistic yang telah dibuat, mari kita lihat ringkasan model menggunakan fungsi summary().
summary(model_logistic)#>
#> Call:
#> glm(formula = left ~ ., family = "binomial", data = turn_over_train)
#>
#> Deviance Residuals:
#> Min 1Q Median 3Q Max
#> -3.2893 -0.7749 -0.0707 0.8286 2.8346
#>
#> Coefficients:
#> Estimate Std. Error z value Pr(>|z|)
#> (Intercept) -0.9916588 0.2572100 -3.855 0.000116 ***
#> satisfaction_level -4.6637026 0.1554522 -30.001 < 2e-16 ***
#> last_evaluation 1.3486580 0.2441434 5.524 3.31e-08 ***
#> number_project -0.4619596 0.0345888 -13.356 < 2e-16 ***
#> average_monthly_hours 0.0046603 0.0008423 5.533 3.16e-08 ***
#> time_spend_company 0.5433006 0.0302083 17.985 < 2e-16 ***
#> Work_accident1 -1.5683412 0.1248489 -12.562 < 2e-16 ***
#> promotion_last_5years1 -1.8527726 0.3521320 -5.262 1.43e-07 ***
#> divisionhr 0.1506603 0.1944332 0.775 0.438417
#> divisionIT -0.1697472 0.1801030 -0.943 0.345936
#> divisionmanagement -0.6447231 0.2268468 -2.842 0.004482 **
#> divisionmarketing -0.0871136 0.1933533 -0.451 0.652320
#> divisionproduct_mng -0.3322115 0.1877558 -1.769 0.076830 .
#> divisionRandD -0.5623727 0.2053928 -2.738 0.006181 **
#> divisionsales -0.1007493 0.1501597 -0.671 0.502254
#> divisionsupport 0.0185694 0.1599557 0.116 0.907580
#> divisiontechnical 0.1114474 0.1569055 0.710 0.477528
#> salarylow 2.0031532 0.1716264 11.672 < 2e-16 ***
#> salarymedium 1.5014787 0.1729044 8.684 < 2e-16 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> (Dispersion parameter for binomial family taken to be 1)
#>
#> Null deviance: 7919.9 on 5712 degrees of freedom
#> Residual deviance: 5773.6 on 5694 degrees of freedom
#> AIC: 5811.6
#>
#> Number of Fisher Scoring iterations: 5
Interpretasi:
Mengubah log of odds ke nilai odds
exp(model_logistic$coefficient)#> (Intercept) satisfaction_level last_evaluation
#> 0.370960820 0.009431476 3.852252357
#> number_project average_monthly_hours time_spend_company
#> 0.630047803 1.004671202 1.721680060
#> Work_accident1 promotion_last_5years1 divisionhr
#> 0.208390582 0.156801811 1.162601623
#> divisionIT divisionmanagement divisionmarketing
#> 0.843878131 0.524807857 0.916572975
#> divisionproduct_mng divisionRandD divisionsales
#> 0.717335564 0.569855369 0.904159693
#> divisionsupport divisiontechnical salarylow
#> 1.018742927 1.117894976 7.412392267
#> salarymedium
#> 4.488320943
Regresi logistik adalah salah satu model yang dapat diinterpretasikan. Kita dapat menjelaskan bagaimana masing-masing variabel memberikan pengaruh pada hasil prediksi. Berdasarkan ringkasan model di atas, beberapa contoh yang dapat diinterpretasikan dari koefisiennya: > - Karyawan yang berada di divisi HR 1.16 kali lebih mungkin untuk mengundurkan diri dibandingkan karyawan di divisi Accounting. - Karyawan yang mendapatkan medium salary 4.48 kali lebih mungkin untuk mengundurkan diri dibandingkan karyawan yang menerima high salary.
Sekarang, kita akan membuat model dengan menggunakan feature selection “stepwise-backward”:
# stepwise
model_step1 <- step(object = model_logistic,
direction = "backward",
trace = 0)
summary(model_step1)#>
#> Call:
#> glm(formula = left ~ satisfaction_level + last_evaluation + number_project +
#> average_monthly_hours + time_spend_company + Work_accident +
#> promotion_last_5years + division + salary, family = "binomial",
#> data = turn_over_train)
#>
#> Deviance Residuals:
#> Min 1Q Median 3Q Max
#> -3.2893 -0.7749 -0.0707 0.8286 2.8346
#>
#> Coefficients:
#> Estimate Std. Error z value Pr(>|z|)
#> (Intercept) -0.9916588 0.2572100 -3.855 0.000116 ***
#> satisfaction_level -4.6637026 0.1554522 -30.001 < 2e-16 ***
#> last_evaluation 1.3486580 0.2441434 5.524 3.31e-08 ***
#> number_project -0.4619596 0.0345888 -13.356 < 2e-16 ***
#> average_monthly_hours 0.0046603 0.0008423 5.533 3.16e-08 ***
#> time_spend_company 0.5433006 0.0302083 17.985 < 2e-16 ***
#> Work_accident1 -1.5683412 0.1248489 -12.562 < 2e-16 ***
#> promotion_last_5years1 -1.8527726 0.3521320 -5.262 1.43e-07 ***
#> divisionhr 0.1506603 0.1944332 0.775 0.438417
#> divisionIT -0.1697472 0.1801030 -0.943 0.345936
#> divisionmanagement -0.6447231 0.2268468 -2.842 0.004482 **
#> divisionmarketing -0.0871136 0.1933533 -0.451 0.652320
#> divisionproduct_mng -0.3322115 0.1877558 -1.769 0.076830 .
#> divisionRandD -0.5623727 0.2053928 -2.738 0.006181 **
#> divisionsales -0.1007493 0.1501597 -0.671 0.502254
#> divisionsupport 0.0185694 0.1599557 0.116 0.907580
#> divisiontechnical 0.1114474 0.1569055 0.710 0.477528
#> salarylow 2.0031532 0.1716264 11.672 < 2e-16 ***
#> salarymedium 1.5014787 0.1729044 8.684 < 2e-16 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> (Dispersion parameter for binomial family taken to be 1)
#>
#> Null deviance: 7919.9 on 5712 degrees of freedom
#> Residual deviance: 5773.6 on 5694 degrees of freedom
#> AIC: 5811.6
#>
#> Number of Fisher Scoring iterations: 5
Pada bagian ini, cobalah untuk memprediksi data test menggunakan model_logistic untuk menghasilkan nilai probabilitas. Gunakan fungsi predict() dengan mengatur parameter type = "response".
turn_over_test$prob_value <- predict(object = model_step1,
newdata = turn_over_test,
type = "response")Karena hasil prediksi pada model regresi logistik berupa probabilitas, kita harus mengubah nilai tersebut menjadi kategori/ kelas target kita. Dengan menggunakan threshold 0.55, cobalah untuk mengklasifikan mana karyawan yang akan mengundurkan diri atau tidak. Silahkan gunakan fungsi `ifelse().
turn_over_test$pred_value <- ifelse(test = turn_over_test$prob_value > 0.55,
yes = 1,
no = 0)
turn_over_test$pred_value <- as.factor(turn_over_test$pred_value)# lihat hasil prediksi
turn_over_test %>%
select(prob_value,
pred_value,
left)table(predict = turn_over_test$pred_value,
actual = turn_over_test$left)#> actual
#> predict 0 1
#> 0 546 171
#> 1 163 549
Pada tahap ini, buatlah confusion matrix dari model regresi logistik menggunakan label aktual dari data test dan hasil prediksi kemudian atur kelas positif yaitu “1” (positive = "1").
confusionMatrix(data = turn_over_test$pred_value,
reference = turn_over_test$left,
positive = "1")#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction 0 1
#> 0 546 171
#> 1 163 549
#>
#> Accuracy : 0.7663
#> 95% CI : (0.7434, 0.788)
#> No Information Rate : 0.5038
#> P-Value [Acc > NIR] : <2e-16
#>
#> Kappa : 0.5326
#>
#> Mcnemar's Test P-Value : 0.7017
#>
#> Sensitivity : 0.7625
#> Specificity : 0.7701
#> Pos Pred Value : 0.7711
#> Neg Pred Value : 0.7615
#> Prevalence : 0.5038
#> Detection Rate : 0.3842
#> Detection Prevalence : 0.4983
#> Balanced Accuracy : 0.7663
#>
#> 'Positive' Class : 1
#>
left: apakah karyawan mengundurkan diri, 0 = tidak, 1 = ya.
Resiko nya:
FN - Recall / Sensitivity - Prediksi stay (0), nyatanya resign (1).
FP - Precision / Pos Pred Value - Prediksi resign (1), nyatanya stay (0).
Sekarang mari kita mengeksplorasi algoritma klasifikasi k-Nearest Neighbor. Pada algoritma k-Nearest Neighbor, kita perlu melakukan satu tahap data pre-proses tambahan. Untuk setiap data train dan test yang kita miliki, hilangkan variabel kategorik kecuali variabel left. Pisahkan variabel prediktor dan target dari data train dan test.
str(turn_over_clean)#> 'data.frame': 7142 obs. of 10 variables:
#> $ satisfaction_level : num 0.82 0.79 0.73 0.92 0.69 0.98 0.52 0.51 0.88 0.76 ...
#> $ last_evaluation : num 0.68 0.67 0.95 0.78 1 0.97 0.9 0.73 0.99 0.85 ...
#> $ number_project : int 3 5 3 3 5 3 4 4 3 3 ...
#> $ average_monthly_hours: int 140 156 149 218 237 209 285 229 190 192 ...
#> $ time_spend_company : int 2 2 2 3 3 3 2 3 5 3 ...
#> $ Work_accident : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
#> $ promotion_last_5years: Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
#> $ division : Factor w/ 10 levels "accounting","hr",..: 8 6 9 10 10 9 2 8 10 5 ...
#> $ salary : Factor w/ 3 levels "high","low","medium": 2 2 2 2 1 2 2 2 1 2 ...
#> $ left : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
turn_clean <- turn_over_clean %>%
select(-c(Work_accident, promotion_last_5years, division, salary))
str(turn_clean)#> 'data.frame': 7142 obs. of 6 variables:
#> $ satisfaction_level : num 0.82 0.79 0.73 0.92 0.69 0.98 0.52 0.51 0.88 0.76 ...
#> $ last_evaluation : num 0.68 0.67 0.95 0.78 1 0.97 0.9 0.73 0.99 0.85 ...
#> $ number_project : int 3 5 3 3 5 3 4 4 3 3 ...
#> $ average_monthly_hours: int 140 156 149 218 237 209 285 229 190 192 ...
#> $ time_spend_company : int 2 2 2 3 3 3 2 3 5 3 ...
#> $ left : Factor w/ 2 levels "0","1": 1 1 1 1 1 1 1 1 1 1 ...
RNGkind(sample.kind = "Rounding")
set.seed(100)
# index sampling
index1 <- sample(x = nrow(turn_clean),
size = nrow(turn_clean)*0.8)
# splitting
turn_train <- turn_clean[index1,] # mengambil 80% dari total data untuk digunakan sebagai data train
turn_test <- turn_clean[-index1,] # 20% sisanya digunakan sebagai data tes# variabel prediktor pada `train`
train_x <- select(turn_train, -left)
# variabel prediktor pada `test`
test_x <- select(turn_test, -left)
# variabel target pada `train`
train_y <- turn_train[,"left"]
# variabel target pada `test`
test_y <- turn_test[,"left"]Ingatlah bahwa pengukuran jarak pada kNN sangat bergantung pada skala data dari variabel prediktor yang dimasukkan sebagai input model. Adanya prediktor yang memiliki range nilai yang amat berbeda dari prediktor lainnya dapat menyebabkan masalah pada model klasifikasi. Oleh karena itu, mari lakukan normalisasi data untuk menyamakan skala dari tiap variabel prediktor agar memiliki range nilai yang standar.
Untuk menormalisasi data train_x, silahkan gunakan fungsi scale(). Sementara itu, untuk menormalisasi data test, silahkan gunakan fungsi yang sama namun menggunakan atribut center dan scale yang didapat dari data train_x.
turn_train_x_scale <- scale(x = train_x)
# scale train_x data
turn_test_x_scale <- scale(x = test_x, # melakukan scale untuk test dari scale train
center = attr(turn_train_x_scale, "scaled:center"),
scale = attr(turn_train_x_scale, "scaled:scale"))k-NN tidak membuat model sehingga langsung ke predict.
Find K - Optimum
sqrt(nrow(turn_train))#> [1] 75.58439
apakah karyawan mengundurkan diri? 0 = tidak, 1 = ya, Target variable ada 2: kelas (1-) = ya & (0-) = tidak
sehingga K- nya ganjil : 75 / 77
Dengan menggunakan nilai k yang telah kita dapatkan, cobalah untuk memprediksi test_y dengan menggunakan data train_x dan train_y. Untuk membuat model kNN, silahkan gunakan fungsi knn() dan simpanlah hasil prediksi pada objek model_knn.
model_knn <- knn(train = turn_train_x_scale,
test = turn_test_x_scale,
cl = train_y,
k = 77)
# cek hasil prediksi
head(model_knn)#> [1] 0 0 0 0 0 0
#> Levels: 0 1
Membuat confusion matrix untuk hasil prediksi model_knn dan label aktual test_y.
confusionMatrix(data = model_knn, #hasil prediksi
reference = test_y, #
positive = "1")#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction 0 1
#> 0 646 57
#> 1 63 663
#>
#> Accuracy : 0.916
#> 95% CI : (0.9004, 0.9299)
#> No Information Rate : 0.5038
#> P-Value [Acc > NIR] : <2e-16
#>
#> Kappa : 0.832
#>
#> Mcnemar's Test P-Value : 0.6481
#>
#> Sensitivity : 0.9208
#> Specificity : 0.9111
#> Pos Pred Value : 0.9132
#> Neg Pred Value : 0.9189
#> Prevalence : 0.5038
#> Detection Rate : 0.4640
#> Detection Prevalence : 0.5080
#> Balanced Accuracy : 0.9160
#>
#> 'Positive' Class : 1
#>
left: apakah karyawan mengundurkan diri, 0 = tidak, 1 = ya.
Resiko nya:
FN - Recall / Sensitivity - Prediksi stay (0), nyatanya resign (1).
FP - Precision / Pos Pred Value - Prediksi resign (1), nyatanya stay (0).
Dengan Model Logistic Regression:
Accuracy = 76.63%
Sensitivity / Recall = 76.25%
Pos Pred Value / Precision = 77.11%
Dengan Model KNN:
Accuracy = 91.60%
Sensitivity / Recall = 92.08%
Pos Pred Value / Precision = 91.32%
Kesimpulan untuk evaluasi Model KNN lebih baik dalam melakukan klasifikasi daripada model Logistic Regression.