-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy path06-C2.Rmd
449 lines (312 loc) · 19.7 KB
/
06-C2.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
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
# Classification 2
```{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(e1071)
library(tm)
library(katadasaR)
library(textclean)
library(rsample)
library(caret)
library(MLmetrics)
library(recipes)
```
## Classification in General
### **Dari berbagai metode klasifikasi yang telah dipelajari, yaitu Logistic Regression, KNN, Naive Bayes, Decision Tree, dan Random Forest, bagaimana pemilihan dalam penggunaan metode tersebut?**
Pemilihan metode klasifikasi bergantung pada tujuan analisis yang dilakukan. Secara umum, tujuan pemodelan klasifikasi adalah melakukan analisa terkait hubungan prediktor dengan target variabel atau melakukan prediksi.
Jika tujuannya adalah untuk melakukan analisa terkait hubungan antara prediktor dengan target variabel dapat menggunakan Logistic Regression atau Decision Tree. Berikut kelebihan dan kekurangan dari kedua metode tersebut.
**Logistic Regression:**
* (+) model klasifikasi yang cukup sederhana dan komputasinya cepat
* (+) interpretabilitas yang tinggi dari formula yang dibentuk
* (+) tidak memerlukan scaling data
* (+) baseline yang baik sebelum mencoba model yang lebih kompleks
* (-) perlu ketelitian saat melakukan feature engineering karena sangat bergantung pada data yang fit
* (-) tidak dapat mengevaluasi hubungan yang non-linear antara log of odds dengan prediktornya
* (-) mengharuskan antar prediktornya tidak saling berkaitan (tidak ada multikolinearitas)
**Decision Tree:**
* (+) tidak memerlukan scaling data
* (+) dapat mengevaluasi hubungan yang non-linear
* (+) antar prediktornya boleh saling berkaitan
* (+) interpretabilitas dari visualisasi pohon
* (-) sangat sensitif terhadap perubahan data, sehingga cenderung tidak stabil
* (-) waktu komputasi relatif lebih lama
* (-) rentan overfitting
Jika tujuannya adalah untuk melakukan prediksi dengan harapan performa yang baik, maka dapat menggunakan **Random Forest**. Metode klasifikasi ini merupakan gabungan dari beberapa Decision Tree, sehingga cukup robust (tidak sensitif) terhadap outlier, antar prediktor boleh saling berkaitan, bahkan mengatasi overfitting. Namun, kekurangannya Random Forest adalah model black-box yang artinya tidak dapat diinterpretasi secara detail.
**KNN** digunakan ketika prediktor-prediktor numerik masuk akal ketika kita melakukan klasifikasi berdasarkan kemiripan antar observasi. Namun KNN tidak melakukan pembuatan model sehingga waktu komputasi akan lama apabila kita berhadapan dengan data yang jumlahnya besar.
**Naive Bayes** sangat umum digunakan ketika terdapat sangat banyak prediktor karena komputasinya yang relatif lebih cepat dibanding model lain. Misalkan pada kasus text classification, yang prediktornya merupakan kata-kata yang unique pada dataset. Berikut merupakan link eksternal yang dapat dijadikan sebagai bahan referensi [The Naive Bayes Classifier](https://towardsdatascience.com/the-naive-bayes-classifier-e92ea9f47523)
### **Bagaimana cara mengembalikan hasil prediksi berupa probability pada metode klasifikasi?**
Pada dasarnya, semua metode klasifikasi akan menghasilkan nilai probability, bukan langsung kelas. Secara default, metode klasifikasi akan mengembalikan kelas dengan threshold probabilitas 0.5. Tambahkan parameter `type` saat melakukan `predict()` untuk menghasilkan nilai probability. Berikut beberapa `type` untuk metode klasifikasi yang dipelajari:
- `type = "response"` untuk Logistic Regression
- `type = "raw"` untuk Naive Bayes
- `type = "probability"` untuk Decision Tree dan Random Forest
### **Bagaimana cara handling imbalance data dengan metode SMOTE di R?**
Untuk melakukan balancing data menggunakan metode SMOTE di R dapat dilakukan dengan fungsi `SMOTE()` dari library `DMwR` seperti berikut
```{r}
data(iris)
data <- iris[, c(1, 2, 5)]
data$Species <- factor(ifelse(data$Species == "setosa","rare","common"))
# checking the class distribution
prop.table(table(data$Species))
## now using SMOTE to create a more "balanced problem"
library(DMwR)
newData <- SMOTE(Species ~ ., data, perc.over = 600, perc.under=100)
prop.table(table(newData$Species))
```
Berikut dokumentasi dari fungsi `SMOTE()` [SMOTE](https://www.rdocumentation.org/packages/DMwR/versions/0.4.1/topics/SMOTE)
## Text Cleansing
### **Bagaimana cara menghapus stopwords dalam Bahasa Indonesia?**
Daftar stopwords Bahasa Indonesia dapat diunduh terlebih dahulu melalui [GitHub](https://github.com/stopwords-iso/stopwords-id) berikut. Import file `stopwords id.txt` tersebut dengan menggunakan fungsi `readLines()`.
```{r}
# import Indonesia stopwords
stop_id <- readLines("data/05-C2/stopwords_id.txt")
# generate data frame
text <- data.frame(sentence = c("saya tertarik belajar data science di @algoritma :)",
"anda tinggal di Jakarta",
"Ingin ku merat🔥 na👍",
"selamat tahun baru #2020 !",
"pingin makan yang kek gitu"))
```
Mengubah text berbentuk data frame ke dalam bentuk corpus dengan menggunakan fungsi `VectorSource()` dan `VCorpus()` dari library `tm`. Setelah itu, aplikasikan fungsi `tm_map()` dan `removeWords()` untuk menghapus stopwords dari objek `stop_id`.
```{r}
text_clean1 <- text %>%
pull(sentence) %>%
VectorSource() %>%
VCorpus() %>%
tm_map(removeWords, stop_id)
text_clean1[[1]]$content
```
### **Bagaimana cara mengubah kata berimbuhan menjadi kata dasar dalam Bahasa Indonesia?**
Untuk mengubah kata berimbuhan menjadi kata dasar dalam bahasa Indonesia dapat menggunakan fungsi `katadasaR()` dari library `katadasaR`. Namun, fungsi tersebut hanya menerima 1 inputan kata saja sehingga dibutuhkan fungsi `sapply()` untuk mengaplikasikan fungsi tersebut ke dalam 1 kalimat.
```{r}
# membuat fungsi untuk mengubah kata berimbuhan menjadi kata dasar
kata_dasar <- function(x) {
paste(sapply(words(x), katadasaR), collapse = " ")
}
```
Menggunakan fungsi di atas dengan menggabungkan fungsi `tm_map()` dan `content_transformer()`.
```{r}
text_clean2 <- text %>%
pull(sentence) %>%
VectorSource() %>%
VCorpus() %>%
tm_map(content_transformer(kata_dasar))
text_clean2[[1]]$content
```
### **Bagaimana cara menghapus emoticon dan emoji?**
Untuk menghapus emoticon dan emoji dapat menggunakan fungsi `replace_emoji()` dan `replace_emoticon()` dari library `textclean`. Kedua fungsi tersebut hanya menerima kalimat dengan tipe data berupa karakter, sehingga harus diubah terlebih dahulu tipe datanya jika masih belum karakter.
```{r}
text_clean3 <- text %>%
mutate(sentence = as.character(sentence)) %>%
pull(sentence) %>%
replace_emoji() %>%
replace_emoticon()
text_clean3
```
### **Bagaimana cara menghapus mention dan hashtag?**
Untuk menghapus mention dan hashtag dapat menggunakan fungsi `replace_hash()` dan `replace_tag()` dari library `textclean`.
```{r}
text_clean4 <- text %>%
mutate(sentence = as.character(sentence)) %>%
pull(sentence) %>%
replace_hash() %>%
replace_tag()
text_clean4
```
### **Bagaimana cara menghapus slang words dalam Bahasa Indonesia?**
Daftar slang words Bahasa Indonesia dapat diunduh terlebih dahulu melalui [GitHub](https://github.com/nasalsabila/kamus-alay/blob/master/colloquial-indonesian-lexicon.csv) berikut.
```{r}
slang_id <- read.csv("data/05-C2/colloquial-indonesian-lexicon.csv")
```
Untuk menghapus slang words dapat menggunakan fungsi `replace_internet_slang()` dari library `textclean`.
```{r}
text_clean5 <- text %>%
mutate(sentence = as.character(sentence)) %>%
pull(sentence) %>%
replace_internet_slang(slang = paste0('\\b', slang_id$slang, '\\b') ,
replacement = slang_id$formal,
ignore.case = T)
text_clean5
```
Berikut link eksternal yang dapat dijadikan sebagai bahan referensi dalam melakukan cleaning text:
- [Text Cleaning Bahasa Indonesia](https://algotech.netlify.com/blog/text-cleaning-bahasa/)
- [Text Cleaning Bahasa Inggris](https://algotech.netlify.com/blog/textclean/)
### **Selain proses stemming apakah bisa menggunakan lemmatization untuk memisahkan kata dasar dengan imbuhan?**
Proses stemming bertujuan untuk menghilangkan imbuhan dari suatu kata berimbuhan. Pada stemming bahasa inggris misalnya fungsi stemming akan menghapus suku kata d, ed, s, es, ing, etc. Sehingga, ada kemungkinan terdapat kata-kata yang hasil stemmingnya kurang tepat seperti "balance". Namun, hal ini tidak akan berpengaruh terhadap hasil pemodelan karena kata yang dihasilkan sama/seragam (dianggap sebagai satu prediktor yang sama). Hal ini karena komputer/machine learning tidak memahami/mengerti konteks/makna dari kata tersebut. Namun, hal ini akan cukup menggangu ketika melakukan visualisasi seperti membuat wordcloud.
Solusinya dapat mencoba alternatif lain, yaitu lemmatization. Berbeda dengan stemming, lemmatization dapat memisahkan kata dasar dengan kata imbuhannya secara tepat. Hal ini karena, secara sederhana lemmatization bekerja seperti ketika kita menghapus stopwords, dimana kita sebenarnya mempunyai kamus besar yang berisi berbagai macam jenis kata berimbuhan dan kata dasarnya.
Penjelasan secara detail tentang stemming dan lemmatization dapat dibaca melalui [Stemming and lemmatization](https://nlp.stanford.edu/IR-book/html/htmledition/stemming-and-lemmatization-1.html) dan [textstem package](https://cran.r-project.org/web/packages/textstem/README.html)
### **Bagaiaman proses tokenization untuk pasangan kata?**
Tokenization pasangan kata, seperti "saya pergi", "tidak suka", etc. adalah salah satu tahapan yang umum dilakukan di n-gram analysis (bigram analysis bila terdapat 2 pasangan kata). Hal tersebut umumnya dilakukan pada kasus analisis korelasi antar kata. Berikut beberapa artikel yang mengaplikasikan proses tokenization pasangan kata:
- [4 Relationships between words: n-grams and correlations](https://www.tidytextmining.com/ngrams.html)
- [TEXT GENERATION WITH MARKOV CHAINS](https://algotech.netlify.app/blog/text-generating-with-markov-chains/)
- [Topic Modelling with LDA](https://rpubs.com/Argaadya/topic_lda)
## Naive Bayes
### **Apakah metode Naive Bayes dapat diterapkan untuk prediktor bertipe numerik?**
Naive bayes dapat diterapkan pada berbagai permasalahan klasifikasi, tidak terbatas pada klasifikasi text. Jika prediktor yang digunakan bertipe numerik, Naive Bayes akan menghitung rata-rata (mean) dan standard deviation (sd) untuk setiap level target. Peluang didapatkan dengan mengasumsikan bahwa prediktor numerik memiliki distribusi normal. Tipe Naive Bayes ini disebut sebagai Gaussian Naive Bayes. Berikut contoh naive bayes pada data `iris`.
```{r}
naiveBayes(Species ~ ., iris)
```
## Tree-based Model
```{r echo=FALSE}
diab <- read.csv("data/05-C2/diabetes.csv")
diab_forest_100 <- readRDS("model/05-C2/diab_forest_100.RDS")
diab_forest_tune <- readRDS("model/05-C2/diab_forest_tune.RDS")
diab_forest_pr <- readRDS("model/05-C2/diab_forest_pr.RDS")
insurance_forest <- readRDS("model/05-C2/insurance_forest.RDS")
```
### **Apa yang dimaksud dengan ensemble method?**
Ensemble method merupakan gabungan prediksi dari beberapa model menjadi prediksi tunggal. Random forest merupakan salah satu jenis ensemble method untuk kasus klasifikasi yang memanfaatkan konsep *Bagging*, yaitu gabungan dari **Bootstrap** dan **Aggregation**.
- Bootstrap merupakan proses pengambilan sampel dengan pengembalian, adanya pengembalian memiliki kemungkinan data yang diambil berulang (baris terduplikasi). Setiap observasi memiliki peluang yang sama untuk dijadikan sampel.
- Aggregation, dari beberapa model yang telah terbentuk dikumpulkan semua hasil prediksi untuk menentukan hasil prediksi tunggal. Untuk klasifikasi, maka dilakukan `majority voting` dimana kelas yang paling banyak diprediksi akan menjadi targetnya. Sedangkan untuk regresi akan diperoleh nilai rata-rata targetnya dari setiap model.
### **Secara default, Random Forest akan membangun sebanyak 500 tree. Bagaimana cara mengubahnya?**
Gunakan parameter `ntree` pada fungsi `train()`. Misalnya kita hanya ingin membuat 100 trees, agar komputasinya lebih cepat. Pada finalModel kita dapat lihat bahwa Number of trees yang dipakai adalah 100.
```{r eval=FALSE}
set.seed(100)
diab_forest_100 <- train(diabetes ~ .,
data = diab,
method = "rf",
ntree = 100)
```
```{r}
diab_forest_100$finalModel$param
```
### **Secara default, Random Forest akan mencoba 3 nilai `mtry`. Bagaimana cara mengubahnya?**
Gunakan parameter `tuneGrid` pada fungsi `train()` dengan menyiapkan dataframe dari `expand.grid()` berupa daftar kemungkinan nilai dari parameter `mtry`. Misalkan saya hanya ingin Random Forest yang menggunakan mtry 4 dan 5:
```{r}
grid <- expand.grid(mtry = c(4, 5))
grid
```
```{r eval=FALSE}
set.seed(100)
diab_forest_tune <- train(diabetes ~ .,
data = diab,
method = "rf",
tuneGrid = grid)
```
```{r}
diab_forest_tune
```
### Secara default, Random Forest akan memilih model dengan Accuracy terbaik. Bagaimana cara mengubahnya agar menggunakan metric Precision atau Recall atau bahkan AUC?
Terdapat parameter pada fungsi `trainControl()`:
- `summaryFunction = prSummary` untuk menghitung nilai precision-recall
- `classProbs = TRUE` untuk perhitungan nilai AUC
Kemudian gunakan parameter `metric` pada fungsi `train()` untuk memilih `metric` mana yang ingin diunggulkan: `"AUC"`, `"Precision"`, `"Recall"`, atau `"F"`. Misalkan untuk kasus prediksi diabetes, karena ingin meminimalisir kasus False Negative berarti kita berharap untuk mendapatkan model dengan metric Recall tertinggi.
```{r eval=FALSE}
set.seed(100)
ctrl <- trainControl(summaryFunction = prSummary,
classProbs = TRUE)
diab_forest_pr <- train(diabetes ~ .,
data = diab,
method = "rf",
trControl = ctrl,
metric = "Recall")
```
```{r}
diab_forest_pr
```
### **Bagaimana implementasi model regresi pada Random Forest?**
Import data yang akan digunakan:
```{r}
insurance <- read.csv("data/05-C2/insurance.csv")
head(insurance)
```
Lakukan cross validation dengan fungsi `initial_split` pada library `rsample`:
```{r}
set.seed(100)
idx <- initial_split(insurance, prop = 0.8)
# check train dataset
train <- training(idx)
# check test dataset
test <- testing(idx)
```
Cara membuat model regresi dengan Random Forest tidak berbeda dengan kasus klasifikasi, ketika target variabel yang digunakan bertipe numerik, otomatis model akan menghasilkan model regresi.
```{r eval=FALSE}
set.seed(100)
ctrl <- trainControl(method = "repeatedcv",
number = 5,
repeats = 3)
insurance_forest <- train(charges ~ .,
data = train,
method = "rf",
trControl = ctrl)
```
```{r}
insurance_forest
```
Melakukan prediksi pada data test dan evaluasi model menggunakan nilai RMSE:
```{r}
test$pred <- predict(object = insurance_forest, newdata = test)
RMSE(y_pred = test$pred, y_true = test$charges)
```
## Model Evaluation
### **Jelaskan kegunaan dari ROC dan AUC?**
Kurva ROC (Receiver Operating Characteristic) menggambarkan seberapa baik kinerja model klasifikasi biner. Kurva ROC dibentuk dari nilai TPR (True Positive Rate) dan FPR (False Positive Rate) untuk semua nilai threshold dari 0 hingga 1. AUC (Area Under the Curve) adalah luas daerah dari kurva ROC. Nilai AUC mendekati 1 artinya model sangat baik, ketika nilai AUC berada di sekitar 0.5 maka model tersebut memiliki performance yang tidak baik dan hanya menebak secara random.
### **Apakah k-fold cross validation dapat digunakan untuk metode klasifikasi selain Random Forest?**
k-fold cross validation dapat digunakan untuk semua metode klasifikasi bahkan di luar metode yang telah dipelajari. Namun, karena k-fold cross validation tidak memperlihatkan hasil pemodelan untuk semua subset data (hanya mengambil model dengan performa terbaik), maka tetap perlu dilakukan cross validation untuk melakukan evaluasi model. Berikut contoh k-fold cross validation untuk metode Decision Tree:
```{r eval=FALSE}
set.seed(417)
ctrl <- trainControl(method = "repeatedcv", number = 5, repeats = 3)
# parameter method dapat disesuaikan dengan metode klasifikasi yang digunakan
model <- train(attrition ~ .,
data = train,
method = "ctree",
trControl = ctrl)
```
## Mathematics Concept
### **Bayes Theorem**
```{r echo=F}
knitr::include_graphics("assets/09-C2/bayes.PNG")
```
```{r echo = F}
knitr::include_graphics("assets/09-C2/bayes2.PNG")
```
### **Independent Event**
Ketika ada 2 kejadian yang terjadi secara bersamaan, peluang satu kejadian tidak mempengaruhi kejadian yang lain. Maka, peluang terjadi 2 kejadian yang tidak saling berhubungan adalah hasil **perkalian** masing-masing peluang kejadian tersebut.
$$P(A \cap B) = P(A) \ P(B)$$
### **Dependent Event**
Peluang satu kejadian dipengaruhi atau berubah sesuai dengan informasi tentang kejadian lainnya. Untuk menghitung peluangnya, kita bisa menggunakan **Bayes Theorem**.
$$P(A|B) = \frac{P(B|A) P(A)}{P(B)} = \frac{P(B|A) P(A)}{P(B|A) P(A)\ +\ P(B|\neg A) P(\neg A)}$$
- $P(A|B)$: Peluang terjadi A apabila diketahui B telah terjadi.
- $P(B|A)$: Peluang terjadi B apabila diketahui A telah terjadi.
- $P(B|\neg A)$: Peluang terjadi B apabila diketahui A tidak terjadi.
- $P(A)$: Peluang terjadi A
- $P(\neg A)$: Peluang tidak terjadi A
### **Entropy**
Entropy adalah ukuran ketidakteraturan (measure of disorder) yang bisa digunakan untuk mewakili seberapa beragam kelas yang ada dalam suatu variabel.
$$Entropy = \Sigma_{i=1}^c -p_i \ log_2 \ p_i$$
Nilai entropy apabila dalam satu variabel terdapat 2 kelas atau nilai:
$$Entropy = -\ p_1 \ log_2 \ p_1 -p_2 \ log_2 \ p_2$$
- $p_i$: proporsi kelas ke-*i* (jumlah observasi kelas *i* dibagi total seluruh observasi)
### **Information Gain**
Information Gain digunakan untuk mengukur perubahan Entropy dan tingkat keragaman kelas setelah dilakukan percabangan. Ketika kita memisahkan 1 data menjadi 2 cabang menggunakan variabel tertentu, information gain dipakai untuk menentukan variabel mana yang dapat memberikan penurunan Entropy yang paling besar.
$$Information \ Gain = Entropy(awal) - (P_1 \ Entropy_1 + P_2 \ Entropy_2)$$
- $P_1$: proporsi data pada cabang kiri
- $Entropy_1$: nilai entropy pada cabang kiri
- $P_2$: proporsi data pada cabang kanan
- $Entropy_2$: nilai entropy pada cabang kanan
Untuk mencari variabel terbaik yang bisa digunakan untuk memisahkan dua kelas supaya entropy-nya semakin kecil, kita cari nilai Information Gain untuk tiap variabel dan pilih variable yang memberikan Information Gain terbesar sebagai percabangannya.
### **Gini Index**
Gini index adalah alternatif dari nilai Entropy. Secara komputasi, Gini lebih cepat dibandingkan Entropy karena hanya berupa perkalian, sedangkan Entropy menggunakan fungsi logaritma.
$$Gini = \Sigma_{i=1}^C\ p(i)\ (1-p(i))$$
Sehingga Gini untuk 2 kelas:
$$Gini = p(a)\ (1-p(a))\ +\ p(b)\ (1-p(b))$$
### **Variable Importance**
Variable Importance yang dihitung oleh Random Forest didapatkan dari rumus Gini Importance, yang konsepnya sama dengan Information Gain. Namun Gini Importance menggunakan Gini Index, bukan nilai Entropy.
$$Gini\ Importance = Gini_{awal} - (P_1\ Gini_1 + P_2\ Gini_2)$$
### **ROC Curve**
Berikut ini ilustrasi yang menggambarkan kondisi ROC (Receiver Operating characteristic) Curve yang diinginkan ketika kelas imbalance adalah mendapatkan *True Positive* setinggi mungkin dan *False Positive* serendah mungkin.
```{r echo=F}
knitr::include_graphics("assets/09-C2/roc-intuition.png")
```