forked from teamalgoritma/FAQ-dvml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
05-C1.Rmd
320 lines (215 loc) · 14.9 KB
/
05-C1.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# Classification 1
```{r setup, include=FALSE}
# clear-up the environment
rm(list = ls())
# chunk options
knitr::opts_chunk$set(
message = FALSE,
warning = FALSE,
fig.align = "center",
comment = "#>"
)
# scientific notation
options(scipen = 9999)
```
```{r echo=FALSE, warning=FALSE, message=FALSE}
# libraries
library(tidyverse)
library(rsample)
library(caret)
library(recipes)
```
## Classification in General
### **Pada kasus klasifikasi, penentuan kelas didasarkan pada peluang. Bagaimana jika peluang yang diperoleh sama besar, misalnya pada kasus klasifikasi biner diperoleh peluang masuk ke kelas positif adalah 0.5 dan peluang masuk ke kelas negatif juga 0.5?**
Hal tersebut bergantung pada user yang menentukan threshold/batasan probability untuk masuk ke kelas positif atau masuk ke kelas negatif. Namun, pada umumnya jika diperoleh probability $>= 0.5$ maka observasi tersebut akan masuk ke kelas positif.
### **Permasalahan apa yang paling sering ditemui pada kasus klasifikasi?**
Permasalahan yang sering ditemui pada kasus klasifikasi adalah proporsi target variabel yang tidak seimbang. Pada data di lapangan, nyatanya jumlah kelas positif jauh lebih sedikit dibandingkan kelas negatif. Contohnya:
- Perbankan: fraud detection, loan default
- Penerbangan: delay prediction
- Digital marketing: customer churn
- Kesehatan: cancer detection
- HR: employee attrition
- dan masih banyak lagi
Misalkan pada kasus fraud detection, dari 1000 transaksi yang terjadi, hanya 10 diantaranya fraud. Hal tersebut akan berpengaruh terhadap kemampuan model untuk memprediksi target, karena model klasifikasi sangat bergantung pada jumlah setiap level target dalam proses learning-nya. Model klasifikasi cenderung lebih pintar dalam memprediksi kelas mayoritas. Hal ini menjadi masalah yang cukup serius, sehingga perlu dilakukan penanganan lebih lanjut.
![](assets/04-C1/imbalance proportion.png)
```{r}
attrition <- read_csv("data/04-C1/attrition.csv") %>%
mutate(attrition = as.factor(attrition))
prop.table(table(attrition$attrition)) %>% round(2)
```
Salah satu cara yang paling umum untuk menyeimbangkan proporsi target variabel adalah dengan metode sampling, yaitu **downsampling** dan **upsampling**.
- **Downsampling** adalah proses sampling pada observasi kelas mayoritas sebanyak jumlah observasi pada kelas minoritas. Proses downsampling akan mengurangi jumlah observasi pada kelas mayoritas, sehingga memungkinkan terjadinya kehilangan informasi.
- **Upsampling** adalah proses sampling pada observasi kelas minoritas sebanyak jumlah observasi pada kelas mayoritas. Proses upsampling akan menambah jumlah observasi pada kelas minoritas, sehingga hanya menduplikasi data yang terdapat pada kelas minoritas.
Berikut contoh downsampling dan upsampling dengan menggunakan fungsi pada library `caret` dan `recipes`:
Sebelum menerapkan downsampling dan upsampling terlebih dahulu dilakukan cross validation, yaitu membagi data menjadi **training set** untuk proses pemodelan dan **testing set** untuk melakukan evaluasi. Cross validation dilakukan dengan menggunakan fungsi `initial_split()` dari library `rsample`. Fungsi tersebut akan melakukan proses sampling dengan metode **stratified random sampling**, sehingga proporsi target variabel pada data awal dipertahankan dengan baik pada training set maupun testing set.
```{r}
# define seed
set.seed(100)
# menentukan indeks untuk train dan test
splitted <- initial_split(data = attrition,
prop = 0.75,
strata = "attrition")
# mengambil indeks data train
train <- training(splitted)
# mengambil indeks data test`
test <- testing(splitted)
```
```{r}
# proporsi data train
prop.table(table(train$attrition)) %>% round(2)
```
```{r}
# proporsi data test
prop.table(table(test$attrition)) %>% round(2)
```
> Downsampling dan upsampling **hanya dilakukan pada data train** karena proses pembuatan model klasifikasi hanya dilakukan pada data train. Data test dianggap sebagai unseen data yang hanya digunakan untuk mengevaluasi model.
- Cara downsampling menggunakan `downSample()` dari library `caret`
```{r}
train_down <- downSample(x = train[, -1],
y = train$attrition,
yname = "attrition")
```
```{r}
prop.table(table(train_down$attrition)) %>% round(2)
```
- Cara upsampling menggunakan `upSample()` dari library `caret`
```{r}
train_up <- upSample(x = train[, -1],
y = train$attrition,
yname = "attrition")
```
```{r}
prop.table(table(train_up$attrition)) %>% round(2)
```
Berikut dokumentasi official dari library `caret`: [downSample: Down- and Up-Sampling Imbalanced Data](https://rdrr.io/cran/caret/man/downSample.html)
- Cara downsampling/upsampling dengan `recipes`
Seperti saat menggunakan fungsi pada library `caret`, ketika menggunakan fungsi dari library `recipes` juga harus dilakukan cross validation terlebih dahulu. Perbedaan ketika menggunakan fungsi dari library `recipes` adalah data train dan test tidak di-assign ke dalam sebuah objek melainkan dilakukan downsampling atau upsampling terlebih dahulu.
```{r}
set.seed(417)
splitted_rec <- initial_split(data = attrition,
prop = 0.8,
strata = "attrition")
splitted_rec
```
Gunakan fungsi `step_downsample()` atau `step_upsample()` yang didefinisikan dalam sebuah **recipe**.
```{r}
rec <- recipe(attrition ~ ., training(splitted)) %>%
# `step_downsample()` dapat diganti dengan `step_upsample()`
step_downsample(attrition, ratio = 1, seed = 100) %>%
prep()
```
```{r}
# membuat data train dengan fungsi `juice()`
train_rec <- juice(rec)
# membuat data test dengan fungsi `bake()`
test_rec <- bake(rec, testing(splitted))
```
```{r}
prop.table(table(train_rec$attrition)) %>% round(2)
```
Berikut dokumentasi official dari library `recipes`: [tidymodels/recipes](https://github.com/tidymodels/recipes)
## Logistic Regression
### **Bagaimana model logistic regression menggunakan variabel kategorik sebagai prediktor?**
Sama seperti kasus linear regression, pada logistic regression variabel kategorik harus diubah menjadi dummy variabel. Pada fungsi `glm()` sudah otomatis melakukan transformasi dummy variabel untuk kolom yang bertipe data character atau factor.
### **Bagaimana jika terdapat salah satu level pada prediktor kategorik yang tidak signifikan (p-value > alpha)? Apakah prediktor tersebut masih dianggap signifikan mempengaruhi target?**
Level yang menjadi basis akan dianggap signifikan, sedangkan untuk level lainnya yang tidak signifikan artinya level tersebut tidak memberikan pengaruh terhadap target variabel. Solusi yang dapat dilakukan adalah:
- Binning, yaitu level tersebut digabungkan dengan level lainnya yang mirip dan signifikan
- Menambahkan jumlah observasi pada level yang tidak signifikan tersebut.
### **Pada fungsi `lm()` sudah otomatis melakukan transformasi data kategorik dengan level pertama yang dijadikan basis. Apakah pengubahan urutan level (reorder) akan mengubah hasil pemodelan?**
Nilai p-value pada setiap level tidak akan berubah ketika kita melakukan reorder level. Interpretasi untuk variabel kategorik bergantung pada level yang dijadikan basis.
### **Apa pengertian dari Null Deviance dan Residual Deviance pada summary model?**
- Null deviance menunjukkan seberapa baik model memprediksi target variabel hanya berdasarkan nilai intercept, tidak menggunakan predictor apapun.
- Residual deviance menunjukkan seberapa baik model memprediksi target variabel berdasarkan nilai intercept dan semua prediktor yang digunakan dalam model. Umumnya nilai Residual deviance lebih kecil dibandingkan null deviance.
Note: Null dan residual deviance hanya sebagian kecil tools yang bisa kita gunakan untuk mengevaluasi model mana yang paling baik. Namun perlu dijadikan catatan bahwa semakin banyak prediktor yang digunakan, nilai residual deviance pasti lebih kecil sehingga evaluasi menjadi bias. Pada praktiknya, confusion matrix lebih sering digunakan untuk melakukan evaluasi model klasifikasi.
Berikut link eksternal yang dapat dijadikan sebagai bahan referensi: [Null deviance & Residual deviance](https://www.theanalysisfactor.com/r-glm-model-fit/)
### **Apa itu Fisher Scoring pada summary model?**
Fisher scoring adalah turunan dari metode Newton untuk mengatasi Maximum Likelihood. Fisher scoring memberikan informasi berapa banyak iterasi yang dilakukan pada model sehingga diperoleh nilai parameter pada summary.
### **Apa itu Maximum Likelihood Estimator (MLE)?**
Nilai estimate pada model logistic regression diperoleh dengan pendekatan MLE. MLE merupakan pendekatan statistik untuk mendapatkan nilai estimate yang optimum pada model.
### **Apa yang dimaksud dari nilai Akaike Information Criterion (AIC)?**
AIC menggambarkan seberapa banyak informasi yang hilang pada model tersebut. Nilai AIC sendiri tidak dapat diinterpretasi, berbeda dengan R-squared, karena tidak memiliki range tertentu. Sehingga Nilai AIC digunakan untuk membandingkan kualitas dari beberapa model. Semakin kecil nilai AIC, semakin sedikit informasi yang hilang, yang artinya semakin baik model kita dalam menangkap pola data.
### **Bagaimana cara untuk mengindikasi adanya perfect separation pada model?**
- Tidak ada prediktor yang signifikan padahal nilai AIC sangat kecil
- Terdapat 1 nilai estimate yang nilainya cukup besar dibandingkan yang lain
- Gunakan parameter `method = "detect_separation"` untuk mendeteksi adanya perfect separation pada model:
```{r}
honors <- read.csv("data/04-C1/sample.csv")
```
```{r}
library(brglm2)
glm(hon ~ female + read + math + write,
data = honors,
family = "binomial",
method = "detect_separation")
```
Output `Separation: TRUE` menandakan adanya perfect separation pada model. Untuk mengetahui variabel mana yang merupakan perfect separation, kita perlu amati output dari `summary()` model.
### **Bagaimana Logistic Regression untuk kasus multiclass classification?**
Multiclass classification adalah kasus klasifikasi dengan lebih dari 2 levels pada target variable. Contohnya pada data `iris` kita ingin mengklasifikasi apakah sebuah bunga termasuk kelas setosa, versicolor, atau virginica.
```{r}
levels(iris$Species)
```
Lakukan train-test splitting dengan proporsi 80-20 persen.
```{r}
set.seed(100)
idx <- sample(nrow(iris), 0.8*nrow(iris))
iris_train <- iris[idx,]
iris_test <- iris[-idx,]
```
Pembuatan model multiclass classification dapat menggunakan fungsi `multinom()` dari library `nnet`, dengan menyertakan parameter `formula` dan `data`.
```{r}
library(nnet)
iris_multi <- multinom(formula = Species ~ ., data = iris_train)
summary(iris_multi)
```
Dari model summary di atas, kita dapat menuliskan formula logistic regression sebagai berikut:
$$log-odds(versicolor) = 30.78627 - 9.027769 Sepal.Length - 6.721883 Sepal.Width + 16.02937 Petal.Length - 8.216299 Petal.Width$$
$$log-odds(virginica) = -52.10505 - 45.676726 Sepal.Length - 55.277893 Sepal.Width + 88.90439 Petal.Length + 48.300727 Petal.Width$$
dengan $$Prob(setosa) = 1 - Prob(versicolor) - Prob(virginica)$$
Gunakan fungsi `predict()` dengan parameter `type = "probs"` untuk mengembalikan nilai probabilitas untuk masing-masing kelas.
```{r}
iris_pred_prob <- predict(iris_multi,
newdata = iris_test,
type = "probs")
data.frame(iris_pred_prob) %>% round(8)
```
## Model Evaluation
### **Apa yang dimaksud dengan False Positive dan False Negative?**
- False positive adalah kasus dimana observasi di kelas negatif terprediksi oleh model sebagai positif. Contohnya, pasien yang sebenarnya mengidap kanker jinak, terprediksi oleh model sebagai kanker ganas.
- False negative adalah kasus dimana observasi di kelas positif terprediksi oleh model sebagai negatif. Contohnya, pasien yang sebenarnya mengidap kanker ganas, terprediksi oleh model sebagai kanker jinak.
### **Pada kasus klasifikasi, mengapa metric accuracy tidak cukup menjelaskan seberapa baik model yang diperoleh?**
Untuk mengetahui seberapa baik perfomance model klasifikasi, tidak cukup dengan melihat nilai accuracy nya saja, karena accuracy menganggap sama penting untuk kasus False Positive (FP) dan False Negative (FN). Apabila kasus FP dan Kita membutuhkan metric lain seperti Precision dan Recall.
Contoh pertama: pada kasus prediksi pasien apakah mengidap kanker jinak atau ganas. Tentunya akan lebih berbahaya apabila pasien dengan kanker ganas namun terprediksi menjadi jinak. Hal ini dapat membahayakan keselamatan pasien karena tidak ditangani dengan serius oleh pihak medis. Pada kasus ini ingin diminimalisir kasus terjadinya False Negative, maka kita mengharapkan nilai Recall yang lebih tinggi dibandingkan metric lainnya.
$$Recall = \frac{TP}{TP+FN}$$
Contoh kedua: pada kasus prediksi email apakah termasuk spam atau ham (tidak spam). Akan lebih berbahaya apabila email yang sebenarnya tidak spam namun terprediksi sebagai spam. Hal ini mengakibatkan email tidak spam akan masuk ke folder spam sehingga email penting tidak terbaca oleh pengguna. Pada kasus ini ingin diminimalisir kasus False Positive, maka kita mengharapkan nilai Precision yang tinggi dibandingkan metric lainnya.
$$Precision = \frac{TP}{TP+FP}$$
Apabila kedua metric Recall dan Precision sama-sama ingin diharapkan tinggi, dapat menggunakan metric F-score yang merupakan rata-rata harmonik dari Recall dan Precision:
$$F = 2 \times \frac{Precision \times Recall}{Precision + Recall}$$
### **Bagaimana cara melakukan perubahan threshold pada kasus binary classification?**
Secara default, fungsi `predict()` menggunakan nilai 0.5 sebagai threshold dalam mengklasifikasi kelas positif dan negatif. Kita dapat menggeser nilai threshold tersebut untuk mendapatkan nilai Precision-Recall yang kita inginkan.
- Semakin besar threshold, maka Precision naik, Recall turun
- Semakin kecil threshold, maka Precision turun, Recall naik
Akan lebih praktis apabila kita dapat memvisualisasikan nilai Precision-Recall untuk setiap thresholdnya. Silahkan install package `cmplot` yang dikembangkan oleh [Ahmad Husain](https://github.com/ahmadhusain), salah satu instructor di Algoritma.
```{r eval=FALSE}
install.packages("remotes")
remotes::install_github("ahmadhusain/cmplot")
```
```{r include=FALSE}
model <- readRDS("model/04-C1/model_sms_spam.RDS")
```
Membuat plot "Tradeoff model performance" dari kasus apakah sebuah SMS diklasifikasi sebagai spam (kelas positif) atau ham (not spam, kelas negatif).
```{r}
library(cmplot)
confmat_plot(prob = model$predicted_prob,
ref = model$actual_label,
postarget = "spam",
negtarget = "ham")
```
Misal dari segi bisnis dibutuhkan Precision minimal 98%, maka menurut plot di atas kita bisa set threshold 0.6324, kemudian lakukan evaluasi ulang dengan confusion matrix ataupun ROC-AUC.
```{r}
threshold <- 0.6324
pred_class <- as.factor(ifelse(model$predicted_prob > threshold,
"spam", "ham"))
confusionMatrix(data = pred_class,
reference = model$actual_label,
positive = "spam")
```