SMS (Short Message Service) adalah salah satu fasilitas dari teknologi GSM (Global System for Mobile) yang memungkinkan mengirim dan menerima pesan-pesan singkat berupa teks dengan kapasitas maksimal 160 karakter. Meski sudah jarang digunakan sebagai media komunikasi sehari-hari, SMS tetap menjadi media penting untuk beberapa orang. Selain itu proses verifikasi hal penting seringkali dilakukan melalui SMS. Namun tidak dapat dipungkiri bahwa SMS yang kita terima bukanlah sebuah pesan yang penting atau bisa disebut sebagai (SPAM). Pada project kali ini akan dibuat sebuah model untuk memprediksi sebuah SMS apakah SPAM atau HAM dengan menggunakan dua jenis model yaitu Naive Bayes dan Random Forest.
Setup yang digunakan pada project ini:
Berikut ini serangkaian library yang digunakan untuk proses pemodelan.
# library
library(dplyr)
library(lubridate)
library(tm)
library(textclean)
library(devtools)
library(katadasaR)
library(tokenizers)
library(wordcloud)
library(stopwords)
library(e1071) #Naive Bayes
library(caret) #Confussion Matrix
library(ROCR)
library(lime)
library(ggplot2)
library(tidyr)
library(tibble)
read.csv("data/data-train.csv",stringsAsFactors = FALSE, encoding = "UTF-8") sms <-
glimpse(sms)
#> Rows: 2,004
#> Columns: 3
#> $ datetime <chr> "2017-02-15T14:48:00Z", "2017-02-15T15:24:00Z", "2017-02-1...
#> $ text <chr> "Telegram code 53784", "Rezeki Nomplok Dompetku Pengiriman...
#> $ status <chr> "ham", "spam", "ham", "ham", "ham", "ham", "ham", "spam", ...
Data diatas masih memiliki type data yang belum sesuai, maka dari itu akan dilakukan transformasi type data:
datetime
-> datetimestatus
-> factor sms %>%
sms <- mutate(
datetime = ymd_hms(datetime),
status = as.factor(status)
)
%>%
sms mutate(hour = hour(datetime)) %>%
group_by(hour) %>%
summarise(
spam = sum(ifelse(status == "spam", 1, 0)),
ham = sum(ifelse(status == "spam", 0, 1)),
%>%
) ungroup() %>%
pivot_longer(
cols=c(ham, spam)
%>%
) ggplot(
aes(
x=hour,
y=value,
fill=name
)+
) geom_col(
stat="identity"
+
) scale_fill_manual(values=c("#0890a8", "#a80813")) +
labs(title = "Perbandingan Densitas SMS ham vs spam")+theme(panel.background = element_rect(fill = "white"),
plot.title = element_text(size = 16, colour = "#280759"),
plot.background = element_rect(fill = "transparent"))
Plot diatas menunjukkan distribusi ham dan spam sepanjang waktu. Dari grafik diatas dapat diamati bahwa terjadi peningkatan jumlah sms yang diterima pada pukul 5 hingga pukul 9. Lalu kemudian menurun secara perlahan mengikuti larutnya hari. Dalam kasus ini waktu (datetime) tidak berpengaruh pada pengelompokkan spam dan ham, maka kolom datetime
dapat kita keluarkan dari data.
sms %>%
sms <- select(-datetime)
Pemahaman data yang kita gunakan amatlah penting mengingat hal tersebut sangat mempengaruhi proses apa yang akan kita lakukan selanjutnya. Salah satu cara memahami data adalah dengan mengkarakterisasi data. Karena tujuan projek ini adalah sebuah model yang dapat melakukan klasifikasi Spam/Ham pada SMS maka akan tepat bila kita dapat mengkarakterisasi kata atau keyword apa saja yang banyak muncul pada masing-masing status.
Berikut ini adalah kata-kata yang sering muncul pada data train yang digunakan.
library(wordcloud)
subset(sms, status == "spam")
spam <-wordcloud(spam$text, max.words = 60, colors = brewer.pal(5, "Dark2"), random.order = FALSE)
%>%
sms filter(status == "spam") %>%
tail()
Data sms yang tergolong ke dalam SPAM
kebanyakan merupakan pesan iklan, promosi, dan penawaran produk. Kata kunci yang sering muncul antara lain adalah: “bonus”, “menang”, “gratis”.
%>%
sms filter(status == "ham") %>%
tail()
Sedangkan yang tergolong ke dalam HAM
biasanya berhubungan dengan verifikasi, pesan operator, dan pesan pribadi. Kata kunci yang sering muncul antara lain: “dimaana”, “code”.
Keterbatasan jumlah karakter yang dimiliki fitur SMS berakibat pada sering terjadinya penyingkattan kata pada pesan yang dikirim. Hal tersebut mudah dipahami manusia namun tidak oleh komputer. Selain itu penggunaan kata yang tidak baku juga seringkali terjadi antara percakapan kita dengan keluarga maupun orang terdekat. Atas dasar hal-hal tersebut Text Cleansing
menjadi sebuah tahapan yang sangat penting agar kata-kata potensial yang terdapat dalam text pesan dapat dikenali dan di proses dengan baik oleh model dan mesin yang dibuat.
sms %>%
sms.corpus <- # Convert to corpus
VectorSource() %>%
VCorpus()
sms.corpus %>%
sms.corpus <- tm_map(content_transformer(tolower)) %>%
tm_map(removeNumbers) %>%
tm_map(removeWords, stopwords("id", source="stopwords-iso")) %>%
tm_map(removePunctuation) %>%
tm_map(function(x) { stemDocument(x, language="indonesian") }) %>%
tm_map(stripWhitespace)
Untuk dapat membuat model prediksi, tidak dapat digunakan type data text. Dalam kasus ini biasanya data text di konversi menjadi Document-Term Matrix
(DTM).
sms.corpus %>%
sms.dtm <- DocumentTermMatrix()
sms.dtm
#> <<DocumentTermMatrix (documents: 2, terms: 2821)>>
#> Non-/sparse entries: 2822/2820
#> Sparsity : 50%
#> Maximal term length: 79
#> Weighting : term frequency (tf)
Kolom atau kata yang kita punya untuk prediksi sangat banyak. Untuk mengurangi noise (kata-kata yang jarang muncul), kita akan gunakan kata-kata yang cukup sering muncul, setidaknya muncul dalam 20 dokumen (sms). Proses ini juga dapat memangkas waktu training model.
findFreqTerms(sms.dtm, lowfreq = 20)
sms.freq <-
sms.dtm[,sms.freq] sms.dtm <-
Nilai pada matrix masih berupa nilai frekuensi. Untuk perhitungan peluang, frekuensi akan diubah menjadi hanya kondisi muncul (1) atau tidak (0). Salah satu caranya dengan menggunakan Bernoulli Converter
. Berikut contoh transformasi yang dilakukan dengan menggunakan Bernoulli Converter.
function(x) {
bernoulli_conv <- as.factor(ifelse(x > 0, 1, 0))
x <-return(x)
}
bernoulli_conv(c(0,1,3))
#> [1] 0 1 1
#> Levels: 0 1
Sekarang kita aplikasikan pada data yang kita miliki.
sms.dtm %>%
sms.dtm <- apply(MARGIN = 2, FUN = bernoulli_conv)
1:2, 1:20] sms.dtm[
#> Terms
#> Docs aja aks aktif aktifkan aplikasi app aspen axi axisnet ayo bala bank beba
#> 1 "1" "1" "1" "1" "1" "1" "1" "1" "1" "1" "1" "1" "1"
#> 2 "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"
#> Terms
#> Docs beli berhasil berita berlaku bersifat biaya blm
#> 1 "1" "1" "1" "1" "1" "1" "1"
#> 2 "0" "0" "0" "0" "0" "0" "0"
Proses tokenization berperan memisahkan sebuah text menjadi pecahan kata yang terpisah dan dapat diindentifikasi satu-persatu.
function(x, is_bernoulli = TRUE) {
tokenize_text <- x %>%
data_dtm <- # Convert to corpus
VectorSource() %>%
VCorpus() %>%
# text cleaning
tm_map(content_transformer(tolower)) %>%
tm_map(removeNumbers) %>%
tm_map(removeWords, stopwords("id", source="stopwords-iso")) %>%
tm_map(removePunctuation) %>%
tm_map(stemDocument) %>%
tm_map(stripWhitespace) %>%
# Convert DTM
DocumentTermMatrix()
findFreqTerms(data_dtm, lowfreq = 20)
data_freq <-
if (is_bernoulli) {
%>%
data_dtm[,data_freq] apply(MARGIN = 2, FUN = bernoulli_conv) %>%
return()
else {
} %>%
data_dtm[,data_freq] return()
} }
Cleansing data telah selesai dilakukan, selanjutnya dilakukan pembagian data train dan data test dengan proporsi secara berurutan adalah 75% dan 25%.
RNGkind(sample.kind = "Rounding")
set.seed(100)
sample(nrow(sms), nrow(sms)*0.75)
index <-
tokenize_text(sms$text)
sms_clean <-
sms_clean[index,]
data_train_clean <- sms_clean[-index,]
data_test_clean <-
sms[index, "status"]
label_train <- sms[-index, "status"] label_test <-
sms[index,]
data_train <- sms[-index,] data_test <-
Pada pemodelan ini akan digunakan dua jenis tipe model agar dapat dilakukan perbandingan, tipe model tersebut antara lain adalah Naive Bayes
dan Random Forest
naiveBayes(
model_nb <-x = data_train_clean,
y = label_train,
laplace = 1
)
set.seed(521)
trainControl(method="repeatedcv", number = 5, repeats = 3)
ctrl <-
train(
model_forest <-x = data_train_clean,
y = label_train,
method = "rf",
trControl = ctrl
)
saveRDS(model_forest, "spam_forest.RDS") # save model
readRDS("spam_forest.RDS") model_forest <-
predict(model_nb, newdata = data_test_clean, type="class")
sms_pred_naive <-head(sms_pred_naive)
#> [1] ham spam spam ham ham ham
#> Levels: ham spam
predict(model_forest, newdata = data_test_clean, type="raw")
sms_pred_rf <-head(sms_pred_rf)
#> [1] ham spam spam ham spam spam
#> Levels: ham spam
Model evaluation dilakukan untuk dapat mengetahui performa masing-masing model yang digunakan untuk prediksi.
Salah satu cara mengevaluasi model yang kita gunakan adalah dengan menggunakan Confussion Matrix
.
confusionMatrix(data = sms_pred_naive, reference = label_test, positive = "spam")
#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction ham spam
#> ham 257 17
#> spam 27 200
#>
#> Accuracy : 0.9122
#> 95% CI : (0.8839, 0.9355)
#> No Information Rate : 0.5669
#> P-Value [Acc > NIR] : <0.0000000000000002
#>
#> Kappa : 0.8221
#>
#> Mcnemar's Test P-Value : 0.1748
#>
#> Sensitivity : 0.9217
#> Specificity : 0.9049
#> Pos Pred Value : 0.8811
#> Neg Pred Value : 0.9380
#> Prevalence : 0.4331
#> Detection Rate : 0.3992
#> Detection Prevalence : 0.4531
#> Balanced Accuracy : 0.9133
#>
#> 'Positive' Class : spam
#>
confusionMatrix(data = sms_pred_rf, reference = label_test, positive = "spam")
#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction ham spam
#> ham 270 6
#> spam 14 211
#>
#> Accuracy : 0.9601
#> 95% CI : (0.939, 0.9754)
#> No Information Rate : 0.5669
#> P-Value [Acc > NIR] : <0.0000000000000002
#>
#> Kappa : 0.9191
#>
#> Mcnemar's Test P-Value : 0.1175
#>
#> Sensitivity : 0.9724
#> Specificity : 0.9507
#> Pos Pred Value : 0.9378
#> Neg Pred Value : 0.9783
#> Prevalence : 0.4331
#> Detection Rate : 0.4212
#> Detection Prevalence : 0.4491
#> Balanced Accuracy : 0.9615
#>
#> 'Positive' Class : spam
#>
Hasil evaluasi menggunakan confussion matrix menunjukkan bahwa akurasi prediksi dengan model Naive Bayes
adalah 91% sedangkan prediksi dengan model Random Forest
menghasilkan akurasi sebesar 96%.
Karena akurasi yang dihasilkan dengan menggunakan model random forest lebih besar, model inilah yang akn dipilih.
Mencari tahu di bagian mana model kita salah memprediksi.
data_test %>%
pred.false <- mutate(
pred.rf = sms_pred_rf,
%>%
) filter(pred.rf != status)
%>% filter(pred.rf == "spam") pred.false
::varImp(model_forest, 20)$importance %>%
caret as.data.frame() %>%
rownames_to_column() %>%
arrange(-Overall) %>%
mutate(rowname = forcats::fct_inorder(rowname))
Dari tabel diatas kita dapat mengetahui bahwa kata yang memiliki bobot pengaruh paling besar adalah info
. Sebaliknya, kata yang memiliki bobot pengaruh paling kecil ialah mempengaruhi
Local Interpretable Model-agnostic Explanations (LIME) merupakan salah satu teknik interpretasi hasil prediksi yang dilakukan sebuah model. LIME dapat memprediksi model appaun dan menganggapnya sebagai black box model. Pada kesempatan kali ini LIME akan digunakan untuk meninterpretasikan model naive bayes.
function(x){
model_type.naiveBayes <-return("classification")
}
function(x, newdata, type = "raw") {
predict_model.naiveBayes <- predict(x, newdata, type = "raw") %>% as.data.frame()
res <-return(res)
}
Persipkan data input untuk LIME.
data_train$text %>% as.character()
text_train <- data_test$text
text_test <-
lime(
explainer <-
text_train,model=model_nb,
preprocess=tokenize_text
)
set.seed(123)
explain(
explanation <-1:5],
text_test[explainer = explainer,
n_labels = 1, # show only 1 label (recommend or not recommend)
n_features = 5,
feature_select = "none", # use all terms to explain the model
single_explanation = F
)
Visualisasi LIME
plot_text_explanations(explanation)
Teks berlabel biru berarti kata support / tingkatkan kemungkinan menjadi SPAM, dengan pengaruh kata promo dan belaku yang paling berpengaruh.
Teks berlabel merah berarti kata tersebut bertentangan / mengurangi probabilitas review menjadi HAM, seperti your, hari atau nasi.
read.csv("data/data-test.csv") submission <-
tokenize_text(submission$text)
submission.clean <-1:5,1:10] submission.clean[
#> Terms
#> Docs aplikasi axi axisnet bala beli berlaku bonus bronet dgn diblokir
#> 1 "0" "0" "0" "0" "1" "0" "0" "0" "1" "0"
#> 2 "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"
#> 3 "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"
#> 4 "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"
#> 5 "0" "0" "0" "0" "0" "0" "1" "0" "0" "0"
function(x, train_data) {
trimRfPredictor <-%>%
x as.data.frame() %>%
fncols(colnames(train_data)) %>%
select(colnames(train_data)) %>%
mutate_all(as.factor) %>%
as.matrix.data.frame() %>%
return()
}
function(data, cname) {
fncols <-!cname%in%names(data)]
add <-cname[
if(length(add)!=0) data[add] <- as.factor("0")
data }
trimRfPredictor(submission.clean, data_train_clean)
submission.clean.df <-1:5,1:20] submission.clean.df[
#> aja aks aktif aktifkan aplikasi app aspen axi axisnet ayo bala bank beba
#> [1,] "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"
#> [2,] "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"
#> [3,] "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"
#> [4,] "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"
#> [5,] "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"
#> beli berhasil berita berlaku bersifat biaya blm
#> [1,] "1" "0" "0" "0" "0" "0" "0"
#> [2,] "0" "0" "0" "0" "0" "0" "0"
#> [3,] "0" "0" "0" "0" "0" "0" "0"
#> [4,] "0" "0" "0" "0" "0" "0" "0"
#> [5,] "0" "0" "0" "0" "0" "0" "0"
submission %>%
submission.nb <- select(datetime)
$status <- predict(model_nb, newdata = submission.clean.df, type="class")
submission.nb
head(submission.nb)
write.csv(submission.nb, "data/submission_nb.csv")
submission %>%
submission.rf <- select(datetime)
$status <- predict(model_forest, newdata = submission.clean.df, type="raw")
submission.rf
head(submission.rf)
write.csv(submission.rf, "data/submission_rf.csv")
Pembuatan model klasifikasi sebuah text pada SMS adalah sebuah SPAM atau HAM sudah berhasil dibuat. Pada kasus ini digunakan dua model berbeda yakni Naive Bayes dan Random Forest. Pada percobaan dengan menggunakan data test (25% dari data train) dihasilkan akurasi Naive Bayes 91% dan Random Forest 96%. Hal tersebut menunjukkan bahwa performa yang lebih baik dihasilkan dengan menggunakan model Random Forest. Hal tersebut juga ikut dibuktikan oleh hasil test dengan data test langsung yang menghasilkan akurasi lebih baik pada model Random Forest.
Model klasifikasi ini sangat berpotensi untuk pengembangan bisnis online. Dengan automasi dan klasifikasi menggunakan machine learning, sebuah usaha akan sangat dimudahkan dalam memanajemen pesanan maupun keluhan dari pelanggannya.