Case Study : Analisis Sentimen dengan SMOTE SVM (Python)

Pendahuluan

Analisis sentimen telah menjadi alat yang membantu untuk memahami emosi sosial terhadap suatu topik, merek, produk, maupun layanan tertentu. Mengklasifikasikan sebuah teks memiliki sentimen positif, negatif, maupun netral memang dapat dilakukan oleh manusia. Namun, bagaimana jika ada lebih dari 1.000 data yang perlu diklasifikasikan? Tentu akan memakan waktu yang sangat lama.

Untuk mengatasi keterbatasan waktu dan tenaga yang dimiliki manusia, analisis sentimen secara otomatis dimanfaatkan. Sebagai contoh, untuk menganalisis ulasan pengguna suatu produk atau layanan, opini dalam sebuah survei, maupun dalam sebuah percakapan di media sosial. Analisis sentimen menjadi semakin populer seiring dengan berkembangnya Artificial Intelligence, Deep Learning, Machine Learning, dan Natural Language Processing.

Pada post kali ini, saya akan coba membahas bagaimana cara melakukan analisis sentimen dengan menggunakan SMOTE SVM menggunakan Python.

Dataset

Ada banyak topik yang dapat kita gunakan sebagai dataset. Begitu pula dengan media yang dapat kita gunakan untuk memperoleh dataset tersebut, seperti Twitter, Facebook, Instagram, dan lain-lain. Selanjutnya dataset tersebut akan diberi label menjadi positif, negatif, atau netral dan digunakan oleh mesin untuk mempelajari karakteristik dan pola dalam sebuah teks untuk diklasifikasikan ke dalam sentimen tertentu. Pertanyaannya, siapa yang akan memberi label terhadap kumpulan dataset tersebut? Apakah kita bisa memberikan label terhadap kumpulan dataset tersebut? Atau ada library khusus yang dapat digunakan untuk memberi label terhadap dataset tersebut?

Jika kumpulan dataset yang kita miliki dalam Bahasa Inggris, kita dapat memanfaatkan library Vader untuk memberikan label. Namun, bagaimana dengan dataset dalam Bahasa Indonesia? Sejauh ini saya belum menemukan library yang dapat digunakan utuk labeling otomatis pada dataset Bahasa Indonesia. Kita bisa saja memberikan label terhadap kumpulan dataset tersebut berdasarkan hasil analisis kita. Namun, hal tersebut akan menimbulkan bias. Akan lebih baik menggunakan ahli bahasa untuk meminimalisir bias pada labeling dataset.

Alternatif lain yang bisa dimanfaatkan adalah dengan menggunakan dataset yang telah dilabeling. Kita bisa memperoleh dataset yang telah diberi label pada Github, Kaggle, atau melalui Drone Emprit. Pada percobaan kali ini, saya mengambil dataset dari Drone Emprit dengan kategori Bantuan Langsung Tunai dalam rentang waktu 1 April 2022 sampai dengan 1 Mei 2022.

Dalam rentang waktu tersebut, terdapat 43.602 tweet yang terdiri dari 27.961 tweet dengan sentimen positif, 14.173 tweet dengan sentimen negatif, dan 1.468 tweet dengan sentimen netral. Selanjutnya data ini diunduh dan dibersihkan.

Gambar di atas merupakan tampilan dataset yang diunduh dari Drone Emprit dan dibuka melalui google-sheet. Dari dataset yang terunduh, terdapat 5.000 data yang terdiri dari 3.625 data bersentimen positif, 1.268 data bersentimen negatif, dan 107 data bersentimen netral. Masing-masing baris pada data memiliki Type tersendiri yang bisa diklasifikasikan menjadi mention, RT, dan reply. Jika kita perhatikan gambar di atas, pada 3 cell yang terselect, terdapat duplikasi pada data. Selain itu, data yang diperlukan untuk melakukan analisis sentimen kali ini hanya Mentions dan Sentiment. Sehingga perlu dilakukan preprocessing terlebih dahulu.

Preprocessing

Berikut adalah tahapan preprocessing yang dilakukan:

  1. Menghapus baris data yang bertipe RT
  2. Menghapus kolom No, Type, Date, Link, Media, Author, Followers, Comments, Likes, Shares, Retweeted, Replied, dan Favourited. Sehingga yang tersisa hanya kolom Mentions dan Sentiment.
  3. Menghapus tweet duplikat pada kolom mentions
    *) Langkah 1, 2, dan 3 dapat dilakukan melalui google sheet dengan menggunakan tools filter dan Hapus duplikat pada data
    Sehingga tersisa 2.975 baris data. Namun, jika kita perhatikan, masih ada duplikat pada data yang gagal terhapus, karena tautan pada masing-masing tweet berbeda seperti pada gambar di bawah ini. Cell 11, 12, dan 13 pada kolom A memiliki isi tweet yang sama, hastag yang sama, namun tautannya berbeda.

Untuk membersihkan duplikasi data seperti di atas, kita bisa melakukan beberapa tahapan berikut:

  1. Mengubah seluruh teks ke bentuk lowercase
  2. Menghapus emoticon dan non ASCII
  3. Menghapus tag, hastag, tanda baca
  4. Menghapus duplikat dari data yang sudah bersih

Keempat tahapan di atas dapat dilakukan dengan Python. Berikut source code untuk tahap 1, 2, dan 3.

def clean_tweet(text):
  # Mengubah teks ke lowercase
  text = text.lower()

  # Menghapus emoticon
  emoji_pattern = re.compile("["
                               u"\U0001F600-\U0001F64F"  # emoticons
                               u"\U0001F300-\U0001F5FF"  # symbols & pictographs
                               u"\U0001F680-\U0001F6FF"  # transport & map symbols
                               u"\U0001F1E0-\U0001F1FF"  # flags (iOS)
                               u"\U00002500-\U00002BEF"  # chinese char
                               u"\U00002702-\U000027B0"
                               u"\U00002702-\U000027B0"
                               u"\U000024C2-\U0001F251"
                               u"\U0001f926-\U0001f937"
                               u"\U00010000-\U0010ffff"
                               u"\u2640-\u2642"
                               u"\u2600-\u2B55"
                               u"\u200d"
                               u"\u23cf"
                               u"\u23e9"
                               u"\u231a"
                               u"\ufe0f"  # dingbats
                               u"\u3030"
                               "]+", flags=re.UNICODE)
  text = emoji_pattern.sub(r'', text)

  # Menghapus non ASCII character
  encoded_string = text.encode("ascii", "ignore")
  text = encoded_string.decode()

  # Menghapus tag, hastag, dan tanda baca
  text = ' '.join(re.sub("([@#_][A-Za-z0-9]+)|(\w+:\/\/\S+)|[0-9]|[@#_]|[\"#$%&'()*+,-./?!:;<=>@[\]^_`{|}~]","", text).split())
  
  return(text)

df["Mentions"] = df["Mentions"].apply(lambda x: clean_tweet(x))

Berikut adalah tampilan dataset setelah fungsi clean_tweet dieksekusi dan data diexport ke bentuk excel. Data yang sudah dibersihkan dengan fungsi clean_tweet tidak harus diimport ke excel, kita dapat langsung menghapus duplikat melalui code Python. Namun saya mengimport ke excel terlebih dahulu agar mudah untuk menunjukkan hasil preprocessing dalam post ini.

Bisa kita lihat tag, hastag, dan url telah terhapus, sehingga dapat dengan mudah dilakukan penghapusan terhadap duplikasi data dengan tool Filter. Setelah duplikasi pada data terhapus, tersisa 1.974 data yang terdiri dari 1.560 data bersentimen positif, 363 data bersentimen negatif, dan 51 data bersentimen netral.

Dalam beberapa artikel, dapat kita temukan tahapan preprocessing terdiri dari mengubah huruf ke lowercase, menghapus url, menghapus tag, menghapus hastag, menghapus tanda baca, menghapus emoticon, menghapus kata yang termasuk stopword, melakukan stemming, dan normalisasi.

Dalam penelitian yang berjudul “Multi-label Classification of Indonesian Hate Speech on Twitter Using Support Vector Machines”, kegiatan stemming dapat menghilangkan imbuhan yang memengaruhi arti kata dan penghapusan terhadap kata yang termasuk ke dalam stopword dapat menghilangkan kata-kata penting yang mempengaruhi klasifikasi. Sehingga pada percobaan kali ini, saya melewatkan tahapan stemming dan menghapus kata yang termasuk ke dalam stopword. Selain itu, saya juga tidak melakukan tahapan normalisasi karena keterbatasan waktu untuk menbuat kamus normalisasi kata.

Maka tahapan yang selanjutnya dilakukan adalah mentransformasikan teks ke bentuk yang dapat dipahami oleh mesin. Namun, sebelum mentransformasikan dataset yang kita miliki, saya akan menjabarkan mengenai TF-IDF, sampling, dan algoritma yang digunakan.

TF-IDF

Vektorisasi terhadap teks dapat dilakukan dengan berbagai metode, seperti Bag of Words, Count Vectorizer, TF-IDF, dan lain-lain. TF-IDF merupakan text vectorizers yang populer. Perhitungannya simple dan mudah untuk dipahami. Untuk menggunakan TF-IDF, kita dapat menggunakan library sklearn.

Sampling

Sampling dapat digunakan untuk mengatasi unbalanced pada dataset. Seperti yang sudah disebutkan sebelumnya, jumlah data antara kelas positif, negatif, dan netral tidak balanced, sehingga teknik sampling dapat digunakan untuk mengatasinya. Ada berbagai teknik sampling yang dapat dimanfaatkan, seperti Random oversampling, Randong undersampling, ADASYN, dan SMOTE. Pada post kali ini kita akan mencoba menggunakan SMOTE untuk melakukan sampling pada data kelas minoritas.

Support Vector Machine (SVM)

SVM menjadi algoritma yang cukup handal untuk melakukan klasifikasi. Selain itu, SMOTE dan SVM terbukti cukup efektif dalam dua buah jurnal berikut “An Evaluation of SVM and Naive Bayes with SMOTE on Sentiment Analysis Data Set” dan “Komparasi Metode Oversampling untuk Klasifikasi Teks Ujaran Kebencian”.

Pengujian: Lalu bagaimana hasilnya?

Source code TF-IDF, SMOTE, dan SVM akan saya gabungkan ke dalam teknik validasi stratified K-Fold cross validation. Stratified K-Fold cross validation dan teknik split test data menjadi teknik pengujian yang dapat ditemukan dibanyak artikel. Namun, jika kita menggunakan teknik pengujian split test data, pengaturan terhadap random number dapat menimbulkan bias. Berikut adalah source code yang digunakan:

X = df["Mentions"]
y = df["Sentiment"]
kf = StratifiedKFold(n_splits=5, shuffle=True)
oversample = SMOTE(sampling_strategy='minority')

accuracy_list, precision_list, recall_list, f1_list =[], [], [], []

for train_index, test_index in kf.split(X, y):
      X_train, X_test = X[train_index], X[test_index] 
      y_train, y_test = y[train_index], y[test_index]
      
      vect = TfidfVectorizer()

      X_train_dtm = vect.fit_transform(X_train)
      X_over, y_over = oversample.fit_resample(X_train_dtm, y_train)
      X_test_dtm = vect.transform(X_test)

      model = SVC(kernel='linear')
      model.fit(X_over, y_over)
      y_pred_class = model.predict(X_test_dtm)

      accuracy = accuracy_score(y_test, y_pred_class)*100
      precision = precision_score(y_test, y_pred_class, average='weighted')
      recall = recall_score(y_test, y_pred_class, average='weighted')
      f1 = f1_score(y_test, y_pred_class, average='weighted')

      accuracy_list.append(accuracy)
      precision_list.append(precision)
      recall_list.append(recall)
      f1_list.append(f1)

print("Rata-rata Akurasi =", round(statistics.mean(accuracy_list), 3))
print("Rata-rata Precision =", round(statistics.mean(precision_list), 3))
print("Rata-rata Recall =", round(statistics.mean(recall_list), 3))
print("Rata-rata F1 Score =", round(statistics.mean(f1_list), 3))

*) Jumlah split pada stratified k-fold dapat ditingkatkan menjadi 10 jika dataset yang dimiliki cukup banyak.

Dari percobaan yang dilakukan, didapatkan matriks evaluasi sebagai berikut:

Penutup

Kita telah mencoba membangun model analisis sentimen dengan Python. Dalam percobaan kali ini, pengklasifikasian terhadap tweet menjadi positif, negatif, dan netral. F1 score yang diperoleh adalah 0.692. F1 score digunakan sebagai acuan evaluasi, karena data yang kita miliki unbalanced.

Dataset dan source code dapat dilihat pada https://github.com/aquemos/Sentiment-Analysis-SMOTE-SVM

Referensi

Sentiment Analysis: Concept, Analysis and Applications

Sentiment Analysis: A Definitive Guide

Sentiment Analysis in 2022: Benefits, Tools, and more!

How to Fix k-Fold Cross-Validation for Imbalanced Classification

10 Techniques to deal with Imbalanced Classes in Machine Learning

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout /  Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout /  Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout /  Ubah )

Connecting to %s