1 Pendahuluan

Laporan ini bertujuan untuk mengklasifikasikan apakah suatu pesan teks termasuk SPAM atau HAM berdasarkan kontennya. Tujuan dari laporan ini adalah untuk menganalisis dan memodelkan data pesan teks sehingga dapat memprediksi dengan akurat apakah pesan tersebut adalah SPAM atau HAM.

1.1 Latar Belakang

Dalam era digital saat ini, pesan teks atau SMS menjadi salah satu bentuk komunikasi yang umum digunakan. Namun, seringkali kita menerima pesan-pesan yang tidak diinginkan atau yang lebih dikenal sebagai SPAM. SPAM dapat mengganggu dan membuang waktu kita. Oleh karena itu, penting untuk memiliki alat yang dapat memfilter pesan-pesan SPAM tersebut secara otomatis. Dalam laporan ini, saya akan menggunakan analisis teks dan teknik pemodelan untuk mengklasifikasikan pesan teks apakah termasuk SPAM atau HAM, sehingga membantu kita mengelola pesan-pesan yang masuk dengan lebih efisien.

1.2 Tujuan

Tujuan dari pembuatan laporan ini adalah untuk mengklasifikasikan apakah suatu pesan teks termasuk SPAM atau HAM. Dalam laporan ini, saya akan menggunakan teknik pemrosesan teks dan pemodelan yang tepat untuk menganalisis pesan-pesan teks yang ada. Beberapa target dan prediktor yang mungkin kita gunakan untuk mencapai tujuan ini antara lain:

  • Target : status.
  • Prediktor : Semua variabel, kecuali status.

2 Read Data

Dalam bagian ini, kita akan membaca data dan melakukan proses pembersihan (jika diperlukan).

2.1 Memuat dataset latih dan dataset uji

Pertama, kita akan memuat dataset latih dan dataset uji yang akan digunakan dalam analisis. Dataset latih akan digunakan untuk melatih model dan dataset uji akan digunakan untuk menguji performa model yang telah dilatih.

# Memuat dataset latih
train_data <- read.csv("data/data-train.csv")

# Memuat dataset uji
test_data <- read.csv("data/data-test.csv")

3 Exploratory Data Analysis

3.1 Mengeksplorasi struktur data dan variabel

Selanjutnya, kita akan melakukan eksplorasi terhadap struktur data dan variabel yang ada.

đź’ˇData trainmemiliki 2,004 rows dengan total Features/columns 3.

  • datetime : Merupakan timestamp waktu pengiriman pesan.
  • text : Berisi konten pesan teks.
  • status : Merupakan label SPAM/HAM untuk setiap pesan.

3.2 Melihat tipe data

## 'data.frame':    2004 obs. of  3 variables:
##  $ datetime: chr  "2017-02-15T14:48:00Z" "2017-02-15T15:24:00Z" "2017-02-15T16:07:00Z" "2017-02-15T16:59:00Z" ...
##  $ text    : chr  "Telegram code 53784" "Rezeki Nomplok Dompetku Pengiriman Uang! Kirim uang di Alfamart & dptkan hadiah jutaan rupiah setiap hari.Perio"| __truncated__ "WhatsApp code 123-994.\n\nYou can also tap on this link to verify your phone: v.whatsapp.com/123994" "Transaksi travel online pakai CIMB Clicks gratis perlindungan kecelakaan & tiket nonton di Pasarpolis.com. Ayo "| __truncated__ ...
##  $ status  : chr  "ham" "spam" "ham" "ham" ...

đź’ˇ Tipe datanya sudah sesuai dengan kasus kali ini, namun perlu dilakukkan penyesuaian lebih lanjut.

3.3 Cek missing values

Kemudian, kita akan memeriksa apakah terdapat missing values atau nilai yang hilang pada dataset. Jika ada, kita akan menangani missing values tersebut dengan melakukan imputasi atau penghapusan nilai yang hilang, sesuai dengan kebutuhan analisis kita.

# Memeriksa missing values pada dataset latih
colSums(is.na(train_data))
## datetime     text   status 
##        0        0        0

đź’ˇ tidak ada missing values dalam dataset.

3.4 Mengeksplor variabel target (spam/ham)

# Mengubah kolom datetime menjadi tipe data waktu
train_data$datetime <- as.POSIXct(train_data$datetime)

# Membuat kolom jam
train_data$hour <- format(train_data$datetime, "%H")

# Menghitung frekuensi total per jam untuk setiap status
hourly_freq <- table(train_data$hour, train_data$status)

# Mengubah tipe data kolom hour menjadi numerik
hourly_freq <- as.data.frame(hourly_freq)
hourly_freq$Var1 <- as.numeric(as.character(hourly_freq$Var1))

# Menghitung jumlah frekuensi untuk kategori ham dan spam
ham_freq <- sum(hourly_freq$Freq[hourly_freq$Var2 == "ham"])
spam_freq <- sum(hourly_freq$Freq[hourly_freq$Var2 == "spam"])

# Menghitung persentase untuk kategori ham dan spam
ham_percentage <- ham_freq / (ham_freq + spam_freq) * 100
spam_percentage <- spam_freq / (ham_freq + spam_freq) * 100

# Membuat data frame untuk pie chart
pie_data <- data.frame(Category = c("ham", "spam"),
                       Frequency = c(ham_freq, spam_freq),
                       Percentage = c(ham_percentage, spam_percentage))

# Membuat plot pie chart
ggplot(pie_data, aes(x = "", y = Frequency, fill = Category)) +
  geom_bar(stat = "identity", width = 1, alpha = 0.7) +
  coord_polar("y", start = 0) +
  labs(x = NULL, y = NULL, fill = "Status") +
  ggtitle("Perbandingan Frekuensi Total antara Ham dan Spam") +
  theme_minimal() +
  theme(axis.line = element_blank(),
        axis.text = element_blank(),
        axis.title = element_blank(),
        panel.grid = element_blank(),
        plot.title = element_text(hjust = 0.5)) +
  geom_text(aes(label = paste0(round(Percentage, 1), "%")), position = position_stack(vjust = 0.5))

đź’ˇ Terdapat 842 pesan yang tergolong dalam kategori spam, dengan persentase sebesar 42 % dari total frekuensi pesan. Hal ini menarik karena menunjukkan bahwa pesan spam cukup signifikan dalam dataset.

3.5 Distrbusi Waktu

library(lubridate)

# Mengubah kolom datetime menjadi tipe data waktu
train_data$datetime <- as.POSIXct(train_data$datetime)

# Ekstrak tanggal dan jam dari kolom datetime
train_data$date <- as.Date(train_data$datetime)
train_data$hour <- hour(train_data$datetime)

#  Memvisualisasikan distribusi tanggal dengan box plot
ggplot(train_data, aes(x = date, y = hour)) +
  geom_boxplot(fill = "lightblue", color = "black", alpha = 0.7) +
  labs(title = "Distribusi Jam Berdasarkan Tanggal", x = "Tanggal", y = "Jam") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

đź’ˇDapat dilihat bahwa mayoritas data berada dalam rentang periode Juli 2017 hingga Januari 2018, Median berada di akhir tahun 2017, menunjukkan bahwa jam tertentu pada periode tersebut paling sering muncul.

3.6 Menganalisis karakteristik teks terkait spam

kali ini saya mau melihat karakteristik teks yang terkait dengan pesan spam menggunakan visualisasi wordcloud

# Filter data untuk kategori "spam"
spam_text <- train_data$text[train_data$status == "spam"]

# Menggabungkan semua teks spam menjadi satu string
spam_text <- paste(spam_text, collapse = " ")

# Menghapus kata-kata yang tidak relevan atau umum dalam teks spam
custom_stopwords <- c("dan", "atau", "ini", "itu", "dari", "dengan", "ke", "di", "untuk")

# Membuat wordcloud untuk teks spam dengan batasan kata
wordcloud(spam_text, stopwords = custom_stopwords, max.words = 100)

Dari hasil wordcloud tersebut, terlihat bahwa pesan spam cenderung berisi promosi terkait “pulsa,” “kuota,” “info,” “sms,” “internet”. Kata-kata seperti “promo,” “diskon,” “gratis,” dan “hadiah” juga sering muncul dalam pesan spam, menunjukkan adanya penawaran khusus untuk menarik perhatian penerima pesan.

4 Data Preprocessing

đź’ˇDalam kasus ini, saya akan melakukan preprocesing pada kolom text menggunakan pustaka textclean. Preprocess data teks melibatkan langkah-langkah seperti tokenisasi, pembersihan tanda baca & emoticon, mengubah ke huruf kecil, dan lainnya untuk mempersiapkan data sebelum dilakukan analisis teks lebih lanjut.

4.1 Cek apakah ada emoticon dan punctuation?

# Mengecek keberadaan emoticon dalam kolom "text"
has_emoticon <- any(stringr::str_detect(train_data$text, ":\\)|:\\(|;\\)|;\\)|:D"))

# Mengecek keberadaan punctuation dalam kolom "text"
has_punctuation <- any(stringr::str_detect(train_data$text, "[[:punct:]]"))

# Output hasil pengecekan
has_emoticon
## [1] FALSE
has_punctuation
## [1] TRUE

đź’ˇSetelah melakukan pengecekan pada data, tidak ditemukan adanya emotikon. Namun, ada tanda baca punctuation yang perlu ditangani.

4.2 Persiapkan paket untuk preprocessing

# library(katadasaR)
library(textclean) #meembersihkan teks
library(tokenizers) #membagi teks menjadi token2
library(wordcloud) #membuat wodcloud
library(stringr) #memaipulasi teks / regex
library(tm)
head(train_data$text)
## [1] "Telegram code 53784"                                                                                                                                                                                                                       
## [2] "Rezeki Nomplok Dompetku Pengiriman Uang! Kirim uang di Alfamart & dptkan hadiah jutaan rupiah setiap hari.Periode s.d. 28Feb17.Info: http://bit.ly/dmpurna MFI1"                                                                           
## [3] "WhatsApp code 123-994.\n\nYou can also tap on this link to verify your phone: v.whatsapp.com/123994"                                                                                                                                       
## [4] "Transaksi travel online pakai CIMB Clicks gratis perlindungan kecelakaan & tiket nonton di Pasarpolis.com. Ayo transaksi & nikmati manfaatnya! Info S&K 14041."                                                                            
## [5] "Apakah Anda mencoba mengakses akun Anda dari perangkat lain? Jika ya, mohon klik tautan ini https://api.gojek.co.id/customers/device?token=f192293e-3117-46e9-bac3-1d1473c23113 dalam 72 jam ke depan. Jika tidak, mohon abaikan pesan ini"
## [6] "Apakah Anda mencoba mengakses akun Anda dari perangkat lain? Jika ya, mohon klik tautan ini https://api.gojek.co.id/customers/device?token=f192293e-3117-46e9-bac3-1d1473c23113 dalam 72 jam ke depan. Jika tidak, mohon abaikan pesan ini"

4.3 Melakukan preprocess data untuk teks, langhkahnya :

  • Mengubah teks menjadi huruf kecil.
  • Mengganti kontraksi kata menjadi bentuk lengkap.
  • Menghilangkan tanda baca dan karakter khusus dari teks, dll.
# Mengubah teks menjadi huruf kecil.
preprocessed_text <- tolower(train_data$text)

# Mengganti kontraksi kata menjadi bentuk lengkap.
preprocessed_text <- replace_contraction(preprocessed_text)

# Menghilangkan karakter selain alphanumeric dan spasi.
preprocessed_text <- gsub("[^[:alnum:][:space:]]", "", preprocessed_text)

# Menghilangkan tanda baca.
preprocessed_text <- gsub("[[:punct:]]", "", preprocessed_text)

# Menghilangkan angka.
preprocessed_text <- gsub("\\d+", "", preprocessed_text)

# Membersihkan teks dari karakter yang tidak diinginkan.
preprocessed_text <- strip(preprocessed_text)

head(preprocessed_text)
## [1] "telegram code"                                                                                                                                                                                
## [2] "rezeki nomplok dompetku pengiriman uang kirim uang di alfamart dptkan hadiah jutaan rupiah setiap hariperiode sd febinfo httpbitlydmpurna mfi"                                                
## [3] "whatsapp code you can also tap on this link to verify your phone vwhatsappcom"                                                                                                                
## [4] "transaksi travel online pakai cimb clicks gratis perlindungan kecelakaan tiket nonton di pasarpoliscom ayo transaksi nikmati manfaatnya info sk"                                              
## [5] "apakah anda mencoba mengakses akun anda dari perangkat lain jika ya mohon klik tautan ini httpsapigojekcoidcustomersdevicetokenfeebacdc dalam jam ke depan jika tidak mohon abaikan pesan ini"
## [6] "apakah anda mencoba mengakses akun anda dari perangkat lain jika ya mohon klik tautan ini httpsapigojekcoidcustomersdevicetokenfeebacdc dalam jam ke depan jika tidak mohon abaikan pesan ini"

4.4 Membuat document-term matrix (DTM)

Setelah itu, saya membuat document-term matrix (DTM) untuk mewakili data teks dengan menggunakan Corpus dan DocumentTermMatrix. DTM membantu mengubah teks menjadi bentuk matriks, Dengan adanya DTM, kita dapat melanjutkan dengan analisis teks lebih lanjut, seperti pemodelan teks menggunakan algoritma seperti Naive Bayes, Random Forest, atau SVM untuk mengklasifikasikan pesan teks menjadi SPAM atau HAM.

# Mengubah teks menjadi Corpus.
corpus <- Corpus(VectorSource(preprocessed_text))

# Membuat Document Term Matrix (DTM).
dtm <- DocumentTermMatrix(corpus)

# Menampilkan hasil DTM
dtm
## <<DocumentTermMatrix (documents: 2004, terms: 3148)>>
## Non-/sparse entries: 22277/6286315
## Sparsity           : 100%
## Maximal term length: 79
## Weighting          : term frequency (tf)

DTM (DocumentTermMatrix) menunjukkan bahwa dalam dataset berisi 2004 pesan teks, terdapat 3148 kata yang berbeda yang muncul. Kepadatan DTM ini adalah 100%, artinya setiap sel dalam matriks memiliki nilai (tidak ada entri kosong). DTM ini memberikan gambaran detail tentang distribusi kata dalam dataset teks, yang bisa digunakan untuk analisis teks lebih lanjut.

5 Model and Evaluation

Kali ini saya akan coba membandingkan beberapa pendekatan metode untuk tugas klasifikasi teks (misalnya: Naive Bayes, Random Forest, SVM)

5.1 Cross Validation

Dataset dibagi menjadi 70% untuk train dan 30% untuk test dengan fungsi createDataPartition.

# Memuat library yang diperlukan
library(caret)
library(randomForest)
library(e1071)
library(kernlab)  # Untuk SVM
library(tm)       # Untuk Text Mining (membuat Corpus dan DTM)

# Convert response variable to a factor
train_data$status <- factor(train_data$status)

# Split data menjadi data training dan data testing(validasi)
set.seed(123)
train_index <- createDataPartition(train_data$status, p = 0.7, list = FALSE)
train_set <- train_data[train_index, ]
test_set <- train_data[-train_index, ]

5.2 Preprocessing pada data yang telah dibagi dan data test

pada tahap ini saya melakukan beberapa pendekatan untuk membersihkan data yang sudah di split, seperti : konversi teks menjadi huruf kecil, penghilangan kontraksi, menghapus angka, tanda baca, dan kata-kata umum menggunakan *stopword*. kali ini saya tidak melakukan normalisasi karena model yang digunakan untuk klasifikasi teks (Naive Bayes, Random Forest, dan Support Vector Machine) tidak memerlukan normalisasi pada data masukan (DTM).

library(tm) 

test_data <- read.csv("data/data-test.csv", stringsAsFactors = FALSE)


# Preprocessing pada teks dataset yang akan diprediksi
preprocess_text <- function(text) {
  # Mengubah teks menjadi huruf kecil
  text <- tolower(text)
  
  # Mengganti kontraksi kata menjadi bentuk lengkap
  text <- replace_contraction(text)
  
  # Menghilangkan karakter selain alphanumeric dan spasi
  text <- gsub("[^[:alnum:][:space:]]", "", text)
  
  # Menghilangkan tanda baca
  text <- gsub("[[:punct:]]", "", text)
  
  # Menghilangkan angka
  text <- gsub("\\d+", "", text)
  
  # Membersihkan teks dari karakter yang tidak diinginkan
  text <- strip(text)
  
  return(text)
}


# Membuat Corpus dari data training dan data testing
corpus_train <- Corpus(VectorSource(train_set$text))
corpus_test <- Corpus(VectorSource(test_set$text)) #corpus_valid
test_corpus <- Corpus(VectorSource(test_data$text))

# Preprocessing Corpus

# Buat daftar kata bawaan untuk bahasa Indonesia // tidak tau library/fungsinya

stopwords_id <- c("dan", "atau", "adalah", "dari", "di", "ke", "sebuah", "itu", "ini", "yang")


corpus_train <- tm_map(corpus_train, content_transformer(tolower))
corpus_train <- tm_map(corpus_train, content_transformer(replace_contraction))
corpus_train <- tm_map(corpus_train, removeNumbers)
corpus_train <- tm_map(corpus_train, removePunctuation)
corpus_train <- tm_map(corpus_train, removeWords, stopwords_id) # bagaimmana indonesia?
corpus_train <- tm_map(corpus_train, stripWhitespace)

corpus_valid <- tm_map(corpus_test, content_transformer(tolower))
corpus_valid <- tm_map(corpus_test, content_transformer(replace_contraction))
corpus_valid <- tm_map(corpus_test, removeNumbers)
corpus_valid <- tm_map(corpus_test, removePunctuation)
corpus_valid <- tm_map(corpus_test, removeWords, stopwords_id)
corpus_valid <- tm_map(corpus_test, stripWhitespace)

test_corpus <- tm_map(test_corpus, content_transformer(tolower))
test_corpus <- tm_map(corpus_test, content_transformer(replace_contraction))
test_corpus <- tm_map(test_corpus, removePunctuation)
test_corpus <- tm_map(test_corpus, removeNumbers)
test_corpus <- tm_map(test_corpus, removeWords, stopwords_id)
test_corpus <- tm_map(test_corpus, stripWhitespace)

# Membuat Document Term Matrix (DTM).
dtm_train <- DocumentTermMatrix(corpus_train)
dtm_valid <- DocumentTermMatrix(corpus_valid, control = list(dictionary = Terms(dtm_train))) # data_valid

# Buat Document Term Matrix (DTM) untuk dataset uji
dtm_test <- DocumentTermMatrix(test_corpus, control = list(dictionary = Terms(dtm_train)))
dtm_test_mat <- as.matrix(dtm_test)

# Konversi DTM menjadi matriksmethod
dtm_train_mat <- as.matrix(dtm_train)
dtm_valid_mat <- as.matrix(dtm_valid)

5.3 Modeling

saya menggunakan 3 model klasifikasi, yaitu Naive Bayes, Random Forest, dan Support Vector Machine (SVM). Evaluasi model dilakukan dengan menghitung akurasi pada data training dan data testing menggunakan predict dan mean.

Pemilihan model untuk klasifikasi pesan spam atau ham didasarkan pada pertimbangan beberapa metode. Pertama, Naive Bayes dipilih karena sederhana dan efisien dalam mengatasi data teks. Kedua, Random Forest digunakan karena kekuatan sebagai model ensemble yang mengatasi overfitting dan cocok untuk data teks kompleks. Terakhir, SVM dipertimbangkan karena kemampuannya menemukan batas keputusan optimal dan dapat menangani data yang tidak linier.

# Memilih model untuk dibandingkan
models <- c("Naive Bayes", "Random Forest", "Support Vector Machine")

# Inisialisasi list untuk menyimpan hasil model
model_results <- list()

# Looping untuk membangun dan mengevaluasi model
for (model_name in models) {
  # Membangun model menggunakan caret
  model <- NULL
  if (model_name == "Naive Bayes") {
    model <- naiveBayes(as.matrix(dtm_train), train_set$status)
  } else if (model_name == "Random Forest") {
    model <- randomForest(as.matrix(dtm_train), train_set$status)
  } else if (model_name == "Support Vector Machine") {
    model <- svm(as.matrix(dtm_train), train_set$status)
  }

  # Evaluasi model pada data training
  train_pred <- predict(model, dtm_train_mat)
  train_acc <- mean(train_pred == train_set$status)
  
  # Evaluasi model pada data validasi
  valid_pred <- predict(model, dtm_valid_mat)
  valid_acc <- mean(valid_pred == test_set$status)
  
  # Simpan hasil evaluasi ke dalam list
  model_results[[model_name]] <- list(train_accuracy = train_acc, valid_accuracy = valid_acc)
}

# Tampilkan hasil evaluasi model
result_df <- data.frame(Model = character(), Training_Accuracy = numeric(), Validation_Accuracy = numeric(), stringsAsFactors = FALSE)

for (model_name in models) {
  cat("Model:", model_name, "\n")
  cat("Training Accuracy:", model_results[[model_name]]$train_accuracy, "\n")
  cat("Validation Accuracy:", model_results[[model_name]]$valid_accuracy, "\n\n")
  
  # Simpan hasil evaluasi ke dalam dataframe
  result_df <- rbind(result_df, data.frame(Model = model_name,
                                           Training_Accuracy = model_results[[model_name]]$train_accuracy,
                                           Validation_Accuracy = model_results[[model_name]]$valid_accuracy,
                                           stringsAsFactors = FALSE))
}
## Model: Naive Bayes 
## Training Accuracy: 0.3860399 
## Validation Accuracy: 0.42 
## 
## Model: Random Forest 
## Training Accuracy: 0.9900285 
## Validation Accuracy: 0.955 
## 
## Model: Support Vector Machine 
## Training Accuracy: 0.9900285 
## Validation Accuracy: 0.5966667
# Tampilkan dataframe hasil evaluasi
print(result_df)
##                    Model Training_Accuracy Validation_Accuracy
## 1            Naive Bayes         0.3860399           0.4200000
## 2          Random Forest         0.9900285           0.9550000
## 3 Support Vector Machine         0.9900285           0.5966667

Hasil evaluasi menunjukkan bahwa Random Forest memiliki akurasi yang sangat tinggi pada data training (99.0%) namun sedikit menurun pada data validasi (95.0%), mengindikasikan kemungkinan adanya overfitting. Meskipun begitu, model ini masih memiliki performa yang lebih baik pada data testing dibandingkan dengan model SVM dan Naive Bayes. Dengan demikian, model Random Forest mungkin merupakan pilihan terbaik untuk mengklasifikasikan teks dalam kasus ini.

5.4 Mengidentifikasi kata-kata penting

Berikut kata-kata yang memiliki pengaruh besar dalam hasil prediksi menggunakan model Random Forest. Informasi ini sangat berharga karena dapat membantu kita dalam memahami faktor-faktor apa saja yang berperan dalam mempengaruhi hasil prediksi dan dapat digunakan untuk mengoptimalkan atau meningkatkan performa model.

# Melatih model Random Forest pada data training

model_rf <- randomForest(as.matrix(dtm_train), train_set$status)

# Mendapatkan tingkat penting variabel dari model Random Forest
rf_var_imp <- importance(model_rf)

# Buat data frame untuk variable importance
rf_var_imp_df <- data.frame(word = row.names(rf_var_imp), importance = rf_var_imp[, 1])

# Mengurutkan dan memilih 15 kata-kata penting teratas
rf_var_imp_df <- rf_var_imp_df %>%
  arrange(desc(importance)) %>%
  head(15)

# Tampilkan kata-kata penting dari model Random Forest
print(rf_var_imp_df)
##                  word importance
## info             info  23.299473
## ooredoo       ooredoo  12.366892
## ayo               ayo  11.604051
## berlaku       berlaku  10.004051
## kuota           kuota   9.967524
## hanya           hanya   9.931377
## kamu             kamu   9.898857
## iring           iring   9.689708
## internetan internetan   8.944873
## bls               bls   8.722977
## pulsa           pulsa   8.494814
## gratis         gratis   8.126968
## beli             beli   8.027739
## bronet         bronet   7.608694
## cek               cek   7.408633

đź’ˇ Tingkat penting variabel menggambarkan seberapa besar kontribusi setiap kata terhadap prediksi. Semakin tinggi tingkat kepentingannya, semakin berpengaruh kata tersebut dalam proses pengambilan keputusan model.

6 Prediction Performance

6.1 Mengukur performa model pada dataset validasi

# Mengukur performa model pada dataset validasi
valid_pred <- predict(model_rf, dtm_valid_mat)
valid_cm <- confusionMatrix(valid_pred, test_set$status)  # Ganti test_set dengan valid_data

# Menghitung akurasi, sensitivitas, spesifisitas, dan presisi pada dataset validasi
valid_accuracy <- valid_cm$overall["Accuracy"]
valid_sensitivity <- valid_cm$byClass["Sensitivity"]
valid_specificity <- valid_cm$byClass["Specificity"]
valid_precision <- valid_cm$byClass["Precision"]

# Melaporkan performa model pada dataset validasi
cat("Performa model pada dataset validasi:\n")
## Performa model pada dataset validasi:
cat("Akurasi:", valid_accuracy, "\n")
## Akurasi: 0.9516667
cat("Sensitivitas:", valid_sensitivity, "\n")
## Sensitivitas: 0.9626437
cat("Spesifisitas:", valid_specificity, "\n")
## Spesifisitas: 0.9365079
cat("Presisi:", valid_precision, "\n\n")
## Presisi: 0.954416

6.2 Mengukur performa model pada dataset uji (datatest)

test_data$datetime <- format(test_data$datetime, format = "%Y-%m-%d %H:%M:%S")

# Evaluasi model terbaik pada dataset uji
test_pred <- predict(model_rf, dtm_test_mat)
test_pred <- factor(test_pred, levels = levels(test_set$status))

# Ubah format datetime menjadi POSIXct
test_data$datetime <- as.POSIXct(test_data$datetime, format = "%Y-%m-%d %H:%M:%S")

# Jika level faktor test_pred dan test_data$status tidak sama, atur levelnya agar sama
levels(test_pred) <- levels(test_set$status)

test_cm <- confusionMatrix(test_pred, test_set$status)

# Menghitung akurasi, sensitivitas, spesifisitas, dan presisi pada dataset uji
test_accuracy <- test_cm$overall["Accuracy"]
test_sensitivity <- test_cm$byClass["Sensitivity"]
test_specificity <- test_cm$byClass["Specificity"]
test_precision <- test_cm$byClass["Precision"]

# Melaporkan performa model pada dataset uji
cat("Performa model pada dataset uji:\n")
## Performa model pada dataset uji:
cat("Akurasi:", test_accuracy, "\n")
## Akurasi: 0.9666667
cat("Sensitivitas:", test_sensitivity, "\n")
## Sensitivitas: 0.9683908
cat("Spesifisitas:", test_specificity, "\n")
## Spesifisitas: 0.9642857
cat("Presisi:", test_precision, "\n")
## Presisi: 0.9739884

6.3 Presdiksi kata yang tidak benar dalam datatest

Pada kode berikut, pengaturan jumlah kata yang muncul dalam wordcloud dilakukan dengan menggunakan fungsi min.freq. Argumen ini menentukan frekuensi minimum kata yang harus muncul agar dimasukkan ke dalam wordcloud. Kata-kata yang muncul dengan frekuensi lebih rendah dari nilai min.freq akan diabaikan.

# Mengidentifikasi SMS yang diprediksi dengan tidak benar
misclassified_sms <- test_data[test_pred != test_set$status, "text"]

head(misclassified_sms)
## [1] "GRATIS UNLIMITED YOUTUBE+INTERNET 10GB+CHAT&SOSMED+SMS+NELPON selama 30hari.Data Rollover.PROMO 100Rb (Normal 115rb). MAU? Tekan C25 kirim SMS ke 929 sekarang"
## [2] "Proses PEMBLOKIRAN kartu bagi yg blm registrasi sdg berjalan,segera registrasi kartu Anda,dapatkan bonus 250MB+250mnt+250SMS.Ketik ULANG#NIK#No.KK# SMS ke4444"
## [3] "Paket kamu akan diperpanjang otomatis HARI INI ke PAKET BRONET 24JAM 100MB 1hr Rp3500. Pastikan pulsa kamu min5rb ya. Info838"                                 
## [4] "AGAR TDK DIBLOKIR,sgr registrasi ulang kartu prabayar Anda.Tlp dan SMS keluar diblokir mulai 1-31 Maret 2018.Ketik ULANG#NIK#No.KK SMS ke 4444.Info 838"       
## [5] "PAKET BRONET 4G OWSEM 4GB sudah aktif. 1GB kuota utama+3GB kuota 4G berlaku di jam 00-23.59 s.d 19-04-2018.Info838"                                            
## [6] "DISKON main & makan cantik di weekend, tiket masuk Trans Studio Bandung, Kidzania, Sindu Kusuma Edupark, Kebab Baba Rafi, dll. Download AXISnet axisnet.id/app"
# Buat Wordcloud dari teks yang diprediksi tidak benar
wordcloud(misclassified_sms, scale = c(5, 0.5), min.freq = 2, random.order = FALSE, colors = brewer.pal(8, "Dark2"))

💡 Berdasarkan kata-kata dalam wordcloud dan teks yang salah terklasifikasi, terlihat adanya pola umum. Kata-kata seperti “paket,” “bronet,” “axis,” “info,” “utk” (untuk), dan “kamu” sering muncul dalam SMS yang salah terklasifikasi. Pola ini menunjukkan model kesulitan mengenali SMS tentang paket internet (paket data) dan informasi terkait. Kesimpulannya, ada pola umum di antara teks yang salah terklasifikasi terutama terkait dengan konten paket internet.

Namun, perlu dilakukan analisis lebih lanjut terhadap teks tersebut. Hal ini dapat melibatkan eksplorasi lebih dalam terhadap karakteristik teks, penggunaan kata-kata atau frasa tertentu, konteks penggunaan, atau fitur-fitur lain yang mungkin mempengaruhi prediksi model. Dengan analisis yang lebih mendalam, mungkin akan terlihat pola atau tren yang dapat memberikan wawasan tentang mengapa SMS tertentu sering salah terklasifikasi oleh model.

Insight singkat dari hasil evaluasi performa model:

  • Model klasifikasi yang dibangun berhasil mencapai performa yang sangat baik pada dataset validasi dan dataset uji. Dengan akurasi sekitar 94.83% pada dataset validasi dan 96.17% pada dataset uji, model ini dapat mengklasifikasikan pesan dengan sangat tepat. Selain itu, model juga memiliki sensitivitas dan spesifisitas yang tinggi, menunjukkan kemampuan yang baik dalam mengidentifikasi pesan positif dan negatif.
  • Performa presisi yang mencapai 95.16% pada dataset validasi dan 96.83% pada dataset uji menunjukkan tingkat ketepatan model dalam mengklasifikasikan pesan positif. Dengan demikian, model ini dapat diandalkan untuk memfilter dan mendeteksi pesan spam dengan efektif.

đź’ˇ Hasil ini menunjukkan bahwa model klasifikasi memiliki kemampuan prediksi yang kuat dan siap digunakan untuk mengatasi masalah deteksi pesan spam pada skala yang lebih besar atau dalam aplikasi nyata.

7 Penutup

7.1 Menyimpulkan proyek capstone

Proyek capstone ini bertujuan untuk mengatasi masalah klasifikasi pesan spam menggunakan machine learning. Prosesnya dimulai dari pengumpulan data pesan teks, lalu data dibagi menjadi training dan test set. Selanjutnya, dilakukan pra-pemrosesan data dengan membersihkan teks dan mengonversinya ke dalam bentuk matriks.

7.2 Menjelaskan apakah tujuan tercapai

Proyek capstone ini berhasil mencapai tujuan dalam mengatasi masalah klasifikasi pesan spam. Model Random Forest yang digunakan berhasil mencapai akurasi lebih dari 94%, serta sensitivitas dan spesifisitas yang tinggi.

7.3 Mengevaluasi apakah masalah dapat diselesaikan dengan menggunakan machine learning

Random Forest menunjukkan kinerja yang sangat baik dengan akurasi lebih dari 94%. Sensitivitas dan spesifisitas yang tinggi menunjukkan kemampuan model dalam mengklasifikasikan pesan positif dan negatif.

7.4 Melaporkan model yang digunakan dan seberapa baik kinerjanya

Model Random Forest berhasil mencapai tingkat akurasi lebih dari 94% pada dataset validasi maupun test. Selain itu, model ini juga memiliki tingkat sensitivitas dan spesifisitas yang tinggi, menandakan kemampuannya dalam mengidentifikasi pesan positif dan negatif dengan akurat. Dengan performa seperti ini, model Random Forest siap digunakan untuk mengklasifikasikan pesan spam dalam implementasi bisnis.

7.5 Mengidentifikasi potensi implementasi bisnis dari proyek capstone

Model ini dapat diimplementasikan dalam lingkungan bisnis untuk meningkatkan efisiensi komunikasi dengan memfilter pesan-pesan spam. Potensi implementasi bisnisnya termasuk filter pesan dalam platform komunikasi perusahaan, perlindungan dari pesan spam bagi pengguna individu, dan meningkatkan efektivitas kampanye pemasaran serta komunikasi pelanggan