Artikel ini merupakan salah satu bagian dari supervised machine learning workflow yang akan diterapkan pada dashboard “AKSATA”; Sebuah dashboard yang didesain untuk melakukan Social Network Analysis dan Sentiment Analysis. Artikel ini akan membahas langkah-langkah menyiapkan model text classification, yang mana merupakan dasar dari sentiment analysis.
Pada umumnya mungkin kita sering melihat berbagai report sentiment analysis yang mengklasifikasi setiap kalimat/frasa berdasarkan kategori: (1) Positif, (2) Netral, (3) Negatif.
Secara khusus untuk project AKSATA, saya ingin memberi label kategori tersebut berdasarkan aktor yang paling sering memenuhi suatu topik di jejaring sosial Twitter, yakni: (1) Personal, (2) Buzzer, (3) Media.
Tujuan dari pembedaan diatas adalah, saya ingin agar pengguna dashboard dapat diyakinkan, bahwa suatu trending topic tidak selalu tercipta atas respon kolektif para pengguna twitter. Dapat saja suatu topik menjadi trending karena aktivitas yang terorganisir (buzzer).
Dataset saya dapatkan dari API Twitter, dengan beberapa query yang didominasi oleh Buzzer seperti #Wadas_melawan, #Lurah_ham, #PecatYaqut, dsb; beberapa query yang didominasi oleh Personal seperti “quweenjojo”, “Pak Marsani”, dsb; dan beberapa tweet dari akun media seperti CNN Indonesia, Tribun (daerah), Tempo.
aksata_data <- readRDS("for_model.rds")str(aksata_data)#> Classes 'tbl_df', 'tbl' and 'data.frame': 15000 obs. of 2 variables:
#> $ full_text: chr "CS: Pastiian HPnya masih ada pulsa ya Pak.\n\nOrang kaya beneran<U+2705>:\nTenang mba, pascabayar kok\n\nOrang "| __truncated__ "Mau hp lo iPhone berlapis emas juga kalau gak ada pulsa ya gak bisa masuk kode OTP. Bener kata costumer service"| __truncated__ "@jaydiristui @DRespati3 \"you can buy stuff, but you cannot buy class\" -aming" "Tp rumah lo jelek bgt bang.<U+0001F44C> https://t.co/7jxXCwBgOy https://t.co/ZBX7jg4CAK" ...
#> $ Label : chr "Personal" "Personal" "Personal" "Personal" ...
library(dplyr)
# Normalize
aksata_enc <- aksata_data %>%
mutate(Label = as.factor(Label)) %>%
mutate(Label = as.integer(Label)-1)
aksata_enc[10001,]aksata_enc[1,]Kelas nomor 1: Media
Kelas nomor 2: Personal
Kelas nomor 0: Buzzer
library(tidyverse)
library(katadasaR)
library(tm)
library(stringr)# Menghapus pattern mention
buzzer <- aksata_enc %>%
group_by(Label) %>%
filter(Label == 0) %>%
mutate(full_text = str_replace_all(
string = full_text,
pattern = "^@[A-Za-z]+|.+ @[A-Za-z]+",
replacement = ""
)) %>%
mutate(full_text = str_replace_all(
string = full_text,
pattern = "@([^@ ]+){5,}",
replacement = ""
)) %>%
mutate(full_text = str_replace_all(
string = full_text,
pattern = "^([^@]*@){5,}[^@]*$",
replacement = "")) chr_buzzer <- as.character(buzzer$full_text)library(rtweet)
library(textclean)
chr_buzzer <- replace_emoji(chr_buzzer)
chr_buzzer <- gsub( ","," ", chr_buzzer)
chr_buzzer <- gsub( "/n"," ", chr_buzzer)
chr_buzzer <- gsub( "#wadasmelawan"," ", chr_buzzer)
chr_buzzer <- gsub( "#usutwadas"," ", chr_buzzer)
chr_buzzer <- replace_hash(chr_buzzer)
chr_buzzer <- replace_html(chr_buzzer, symbol = FALSE)
# chr_buzzer <- replace_incomplete(chr_buzzer, replacement = "")
chr_buzzer <- replace_number(chr_buzzer, remove = TRUE)
chr_buzzer <- replace_url(chr_buzzer, replacement = "")
chr_buzzer <- tolower(chr_buzzer)
chr_buzzer <- removePunctuation(chr_buzzer)
#later: replace internet slang
chr_buzzer[100:110]#> [1] " akhir nya kebobrokan rezim terkuak "
#> [2] " "
#> [3] " mah kan "
#> [4] " sikap muhammadiyah layak diacungi jempol peka terhadap permasalahan sosial kemanusiaan "
#> [5] " "
#> [6] "menurut catatan greenpeace indonesia penambangan batu andesit di wadas ini akan mengancam ekosistem alam di desa wadas "
#> [7] "wadas adalah bukti kongkrit betapa pemimpin pencitraan tidak melulu memikirkan rakyat mereka hanya berbusa saat kampanye tapi tak menganggap rakyat setelah berkuasa "
#> [8] "selamat pagi gaesss lebih percaya mana nih leterangan warga atau keterangan penguasa "
#> [9] "semakin aneh "
#> [10] "musang berbulu ayam "
#> [11] "sebelum daerah lain akan seperti desa wadas maka bersatulah dan dan kawan mari support "
Cleansing membuat banyak tweet bernilai NA. mencari alternatif lain
buzzer2 <- aksata_enc %>%
group_by(Label) %>%
filter(Label == 0) tes <- as.character(buzzer2$full_text)
# tes <- textclean::replace_number(tes, remove = TRUE)
# tes <- str_squish(tes)
str(tes)#> chr [1:5000] "Catet...<U+0001F60C>\n\n#UsutWadas \n#UsutWadas\n\nhttps://t.co/tCcxb3qEi1" ...
tes[1:10]#> [1] "Catet...<U+0001F60C>\n\n#UsutWadas \n#UsutWadas\n\nhttps://t.co/tCcxb3qEi1"
#> [2] "Tolong permintaan warga @Wadas_Melawan mengenai pencabutan IPL pertambangannya dilaksanakan ya,pak.. @ganjarpranowo \n\n#Wadas\n#FaktaWadas #UsutWadas # https://t.co/N7O3HjilOB"
#> [3] "Proyek oligarki meminjam tangan penguasaan, telah merampas hak rakyat. BPN 'menjadi martir' Proyek Pembangunan Waduk Bener .\n#UsutWadas \n#UsutWadas\nhttps://t.co/m2kbWnYPw6"
#> [4] "Kau siapkan karpet merah para pemburu rente yang bergentayangan. Kau mudahkan dan kau buka lebar semua jalan, bahkan hak hidup pekerja pun kau abaikan.\n#UsutWadas \n#UsutWadas https://t.co/UYcQzjz6NW"
#> [5] "Ketakutan yg kau tebarkan, intimidasi yg kau hujamkan pada setiap mereka yg berseberangan menjadi berbalik bak senjata makan tuan. Kekesalan,kesakitan, rasa diremehkan, disepelekan,dilupakan dan dihinakan, menggumpal-mengkristal meneguhkan satu kata LAWAN!\n#UsutWadas \n#UsutWadas https://t.co/trkFBNybS2"
#> [6] "Ya Allah...\n\n#UsutWadas \n#UsutWadas https://t.co/hONX6MMTQG"
#> [7] "Ada bau busuk..\n\n#UsutWadas \n#UsutWadas https://t.co/zZSI0xwPJS"
#> [8] "Fadli Zon: Sebenarnya pembangunan ini untuk siapa.?\n\n#UsutWadas \n#UsutWadas https://t.co/L3AVRXfoam"
#> [9] "Ansor Banser menuntut 4 hal kepada presiden Joko Widodo dan gubernur Jawa Tengah Ganjar Pranowo untuk Warga Wadas Purworejo.\n@AlissaWahid\n@GUSDURians\n\nHidup NU !!!\nHidup Rakyat Indonesia !!!\nHidup Warga Wadas !!!\n\n#SaveWadas\n#WadasMelawan\n#UsutWadas\n#WadasMeradang https://t.co/Ft0BjQMYCW"
#> [10] "Hanya Terjadi Di 62..\nRakyat Pemilik Sah Atas Tanah Diusir Dengan Dengan Cara Cara Yang Tidak Berprikemanusiaan \n\n#UsutWadas\n#UsutWadas https://t.co/ZeJZWCatIo"
library(rtweet)
tes <- replace_emoji(tes) # replace emoji
tes <- replace_hash(tes) # replace hashtags
tes <- replace_html(tes, symbol = FALSE) # replace HTML Markups
tes <- replace_number(tes, remove = TRUE) # replace number
tes <- replace_url(tes, replacement = " ") # replace all http tag
tes <- gsub( ":"," ", tes)
tes <- gsub( "\n"," ", tes)
tes <- gsub( "^@[A-Za-z]+|.+ @[A-Za-z]+" ," ", tes)
tes <- gsub( "@([^@ ]+){5,}" ," ", tes)
tes <- gsub( "^([^@]*@){5,}[^@]*$" ," ", tes)
tes <- removePunctuation(tes)
tes <- gsub( "relieved face"," ", tes)
tes <- gsub( "winking face with tongue"," ", tes)
tes <- gsub( "face"," ", tes)
tes <- gsub( "rt"," ", tes)
tes <- gsub( "oii"," ", tes)
tes <- gsub( "wadas"," ", tes)
tes <- tolower(tes)
tes[1:10]#> [1] "catet "
#> [2] " "
#> [3] "proyek oligarki meminjam tangan penguasaan telah merampas hak rakyat bpn menjadi ma ir proyek pembangunan waduk bener "
#> [4] "kau siapkan karpet merah para pemburu rente yang bergentayangan kau mudahkan dan kau buka lebar semua jalan bahkan hak hidup pekerja pun kau abaikan "
#> [5] "ketakutan yg kau tebarkan intimidasi yg kau hujamkan pada setiap mereka yg berseberangan menjadi berbalik bak senjata makan tuan kekesalankesakitan rasa diremehkan disepelekandilupakan dan dihinakan menggumpalmengkristal meneguhkan satu kata lawan "
#> [6] "ya allah "
#> [7] "ada bau busuk "
#> [8] "fadli zon sebenarnya pembangunan ini untuk siapa "
#> [9] " hidup nu hidup rakyat indonesia hidup warga wadas "
#> [10] "hanya terjadi di 62 rakyat pemilik sah atas tanah diusir dengan dengan cara cara yang tidak berprikemanusiaan "
Selanjutnya, proses cleansing saya lanjutkan dengan menghilangkan stopwords. Untuk diketahui juga bahwa operasi ini mengubah bentuk data menjadi list.
#Removing Stopwords, untuk menghilangkan kata-kata yang tidak diperlukan karena kemunculannya sangat sering (kata hubung, sambung, dsb)
library(stopwords)
library(tokenizers)
unfaedah_words <- readLines("stopwords_new.txt")
# buzzer_list <- as.list(chr)
to_cloud_buzzer <- tokenize_words(tes, stopwords = unfaedah_words)
to_cloud_buzzer[1:10]#> [[1]]
#> [1] "catet"
#>
#> [[2]]
#> [1] NA
#>
#> [[3]]
#> [1] "proyek" "oligarki" "meminjam" "tangan" "penguasaan"
#> [6] "merampas" "hak" "rakyat" "bpn" "ma"
#> [11] "ir" "proyek" "pembangunan" "waduk" "bener"
#>
#> [[4]]
#> [1] "kau" "siapkan" "karpet" "merah"
#> [5] "pemburu" "rente" "bergentayangan" "kau"
#> [9] "mudahkan" "kau" "buka" "lebar"
#> [13] "jalan" "hak" "hidup" "pekerja"
#> [17] "kau" "abaikan"
#>
#> [[5]]
#> [1] "ketakutan" "kau" "tebarkan"
#> [4] "intimidasi" "kau" "hujamkan"
#> [7] "berseberangan" "berbalik" "senjata"
#> [10] "makan" "tuan" "kekesalankesakitan"
#> [13] "diremehkan" "disepelekandilupakan" "dihinakan"
#> [16] "menggumpalmengkristal" "meneguhkan" "lawan"
#>
#> [[6]]
#> [1] "ya" "allah"
#>
#> [[7]]
#> [1] "bau" "busuk"
#>
#> [[8]]
#> [1] "fadli" "zon" "pembangunan"
#>
#> [[9]]
#> [1] "hidup" "nu" "hidup" "rakyat" "indonesia" "hidup"
#> [7] "warga" "wadas"
#>
#> [[10]]
#> [1] "62" "rakyat" "pemilik"
#> [4] "sah" "tanah" "diusir"
#> [7] "tidak" "berprikemanusiaan"
library(wordcloud)
sudah_chr <- as.character(to_cloud_buzzer)
set.seed(100)
cloud_buzzer <- wordcloud(sudah_chr)cloud_buzzer#> NULL
Berdasarkan wordcloud, kita dapat melihat bahwa Buzzer lebih banyak menggaungkan hal-hal yang bersifat politik, pemerintahan, sara, dengan narasi-narasi negatif terhadap suatu topik. Kita nantinya dapat melihat cara kerja model dengan berpedoman pada hasil visualisasi wordcloud ini.
disimpan pada object buzzer_clean
tes_list <- list(
text_all = tes,
label = buzzer$Label
)
buzzer_clean <- as_tibble(tes_list) %>%
drop_na()
tail(buzzer_clean,5)personal <- aksata_enc %>%
group_by(Label) %>%
filter(Label == 2)
cleansing_personal <- as.character(personal$full_text)
cleansing_personal[1:10]#> [1] "CS: Pastiian HPnya masih ada pulsa ya Pak.\n\nOrang kaya beneran<U+2705>:\nTenang mba, pascabayar kok\n\nOrang kaya jadi2an<U+26D4><U+FE0F>:\n*ngamuk di twitter sambil pamer harta* https://t.co/U46xI8jnQV"
#> [2] "Mau hp lo iPhone berlapis emas juga kalau gak ada pulsa ya gak bisa masuk kode OTP. Bener kata costumer service, emang itu sistemnya, dia gak sedang merendahkan lo sebagai orang kaya yg punya mobil harga stengah M. https://t.co/59jWGtvwRb"
#> [3] "@jaydiristui @DRespati3 \"you can buy stuff, but you cannot buy class\" -aming"
#> [4] "Tp rumah lo jelek bgt bang.<U+0001F44C> https://t.co/7jxXCwBgOy https://t.co/ZBX7jg4CAK"
#> [5] "@afrkml @DRespati3 Bacain replyannya ners, dia ga seneng cara penyampainnya. Mungkin harapan dia gini kali ya.\n\"Yang mulia baginda nasabah kami? Sudikah kiranya yang mulia baginda memastikan dlm handphone iphonenya yang keluaran terbaru paling mahal terdapat pulsa?\""
#> [6] "@DRespati3 Jangan2 mobil yg setengah M juga jarang dibawa kemari menghindari biar gak tersinggunh <U+0001F62A><U+0001F62A><U+0001F62A> https://t.co/LKgP0184Dk"
#> [7] "@DRespati3 10 things money can’t buy:\n1. Manners\n2. Morals\n3. Respect\n4. Character\n5. Common sense\n6. Trust\n7. Patience\n8. Class\n9. Integrity\n10. Love"
#> [8] "@Annezui1 @DRespati3 Kelumpuhan yang dialaminya sejak menderita penyakit meningitis hidrosefalus, menimbulkan nyeri yang luar biasa. Tapi Devi tidak menyerah.<U+2063>\n<U+2063>\n#orangbaik, mari bantu berikan semangat untuk Devi dan berdonasi <U+0001F64F>\nhttps://t.co/iucxwGrurX"
#> [9] "@DRespati3 Gua pikir Anda sedang bercanda, rupanya emg benar2 tersinggung. Kok bs tersinggung ya? Itu kan komunikasi yg penting utk mengonfirmasi ada pulsa atau tidak karena prosedur urus m-banking memang pakai pulsa utk SMS. Gak ada hubungannya sama IPHONE apalagi agenda pamermu itu!!! <U+0001F629>"
#> [10] "@DRespati3 It’s just joke https://t.co/CyoiZ0Ss8w"
cleansing_personal <- replace_emoji(cleansing_personal) # replace emoji
cleansing_personal <- replace_hash(cleansing_personal) # replace hashtags
cleansing_personal <- replace_html(cleansing_personal, symbol = FALSE) # replace HTML Markups
cleansing_personal <- replace_number(cleansing_personal, remove = TRUE) # replace number
cleansing_personal <- replace_url(cleansing_personal, replacement = " ") # replace all http tag
cleansing_personal <- gsub( ":"," ", cleansing_personal)
cleansing_personal <- gsub( "\n"," ", cleansing_personal)
cleansing_personal <- gsub( "^@[A-Za-z]+|.+ @[A-Za-z]+" ," ", cleansing_personal)
cleansing_personal <- gsub( "@([^@ ]+){5,}" ," ", cleansing_personal)
cleansing_personal <- gsub( "^([^@]*@){5,}[^@]*$" ," ", cleansing_personal)
cleansing_personal <- removePunctuation(cleansing_personal)
# cleansing2 <- gsub( "relieved face"," ", cleansing2)
# cleansing2 <- gsub( "winking face with tongue"," ", cleansing2)
# cleansing2 <- gsub( "face"," ", cleansing2)
# cleansing2 <- gsub( "rt"," ", cleansing2)
# cleansing2 <- gsub( "oii"," ", cleansing2)
cleansing_personal <- tolower(cleansing_personal)
cleansing_personal <- gsub( "pak marsani"," ", cleansing_personal)
cleansing_personal <- gsub( "steno ricardo"," ", cleansing_personal)
cleansing_personal <- gsub( "mawar afi"," ", cleansing_personal)
cleansing_personal <- gsub( "3"," ", cleansing_personal)cleansing_personal[1:10]#> [1] "cs pastiian hpnya masih ada pulsa ya pak orang kaya beneran white heavy check mark tenang mba pascabayar kok orang kaya jadi2an no entry ngamuk di twitter sambil pamer harta "
#> [2] "mau hp lo iphone berlapis emas juga kalau gak ada pulsa ya gak bisa masuk kode otp bener kata costumer service emang itu sistemnya dia gak sedang merendahkan lo sebagai orang kaya yg punya mobil harga stengah m "
#> [3] " you can buy stuff but you cannot buy class aming"
#> [4] "tp rumah lo jelek bgt bang ok hand "
#> [5] " bacain replyannya ners dia ga seneng cara penyampainnya mungkin harapan dia gini kali ya yang mulia baginda nasabah kami sudikah kiranya yang mulia baginda memastikan dlm handphone iphonenya yang keluaran terbaru paling mahal terdapat pulsa"
#> [6] " jangan2 mobil yg setengah m juga jarang dibawa kemari menghindari biar gak tersinggunh sleepy face sleepy face sleepy face "
#> [7] " things money can t buy 1 manners 2 morals respect 4 character 5 common sense 6 trust 7 patience 8 class 9 integrity 10 love"
#> [8] " kelumpuhan yang dialaminya sejak menderita penyakit meningitis hidrosefalus menimbulkan nyeri yang luar biasa tapi devi tidak menyerah mari bantu berikan semangat untuk devi dan berdonasi folded hands "
#> [9] " gua pikir anda sedang bercanda rupanya emg benar2 tersinggung kok bs tersinggung ya itu kan komunikasi yg penting utk mengonfirmasi ada pulsa atau tidak karena prosedur urus mbanking memang pakai pulsa utk sms gak ada hubungannya sama iphone apalagi agenda pamermu itu weary face "
#> [10] " it s just joke "
to_cloud_personal <- tokenize_words(cleansing_personal, stopwords = unfaedah_words)
sudah_chr_pers <- as.character(to_cloud_personal)
set.seed(100)
cloud_personal <- wordcloud(sudah_chr_pers)cloud_personal#> NULL
Disimpan pada object personal_clean
personal_list <- list(
text_all = cleansing_personal,
label = personal$Label
)
personal_clean <- as_tibble(personal_list) %>%
drop_na()
tail(personal_clean, 5)media <- aksata_enc %>%
group_by(Label) %>%
filter(Label == 1)
cleansing_media <- as.character(media$full_text)
cleansing_media[1]#> [1] "Akustik Lagu Sunda Dalam Rangka Menghibur Anak Yatim, Kolaborasi antara ... https://t.co/BdFhd1Pu9F via @YouTube"
cleansing_media <- replace_emoji(cleansing_media) # replace emoji
cleansing_media <- replace_hash(cleansing_media) # replace hashtags
cleansing_media <- replace_html(cleansing_media, symbol = FALSE) # replace HTML Markups
cleansing_media <- replace_number(cleansing_media, remove = TRUE) # replace number
cleansing_media <- replace_url(cleansing_media, replacement = " ") # replace all http tag
cleansing_media <- gsub( ":"," ", cleansing_media)
cleansing_media <- gsub( "\n"," ", cleansing_media)
# cleansing3 <- gsub( "^@[A-Za-z]+|.+ @[A-Za-z]+" ," ", cleansing3)
# cleansing3 <- gsub( "@([^@ ]+){5,}" ," ", cleansing3)
# cleansing3 <- gsub( "^([^@]*@){5,}[^@]*$" ," ", cleansing3)
cleansing_media <- removePunctuation(cleansing_media)
# cleansing2 <- gsub( "relieved face"," ", cleansing2)
# cleansing2 <- gsub( "winking face with tongue"," ", cleansing2)
# cleansing2 <- gsub( "face"," ", cleansing2)
# cleansing2 <- gsub( "rt"," ", cleansing2)
# cleansing2 <- gsub( "oii"," ", cleansing2)
cleansing_media <- tolower(cleansing_media)
# cleansing3 <- gsub( "pak marsani"," ", cleansing3)
cleansing_media <- gsub( "viral"," ", cleansing_media)
cleansing_media <- gsub( "via youtube"," ", cleansing_media)
cleansing_media <- gsub( "intipseleb"," ", cleansing_media)cleansing_media[2050:2060]#> [1] "kpk gelontorkan nyaris rp1 miliar untuk sms blast ingatkan setor lhkpn "
#> [2] "airlangga blakblakan soal jht dan jkp "
#> [3] "tim konsesi motogp adalah tim pabrikan yang kurang berprestasi atau gagal meraih podium sepanjang satu musim apa saja keuntungan yang diperoleh tim konsesi simak selengkapnya di edisi terbaru "
#> [4] "foto susah dapat jodoh partai komunis china jadi mak comblang "
#> [5] "tahanan kasus narkoba kabur dari rutan sampang "
#> [6] "ratusan aparat diterjunkan amankan bentrok di tengah hutan maluku "
#> [7] "menkes indonesia berada di puncak kasus kematian covid19 "
#> [8] "happy salma hingga kamila andini mejeng di berlinale "
#> [9] "wfo ppkm level dilonggarkan jadi persen "
#> [10] "sejarah saudi ramaikan valentine berawal dari dekrit pangeran mbs "
#> [11] "tes motogp mandalika alex marquez ubah kali setelan motor "
to_media_personal <- tokenize_words(cleansing_media, stopwords = unfaedah_words)
sudah_chr_media <- as.character(to_media_personal)
set.seed(100)
media_personal <- wordcloud(sudah_chr_media)media_personal#> NULL
Disimpan pada object media_clean
media_list <- list(
text_all = cleansing_media,
label = media$Label
)
media_clean <- as_tibble(media_list)
tail(media_clean, 5)train_ready <- rbind(buzzer_clean, personal_clean, media_clean) %>%
drop_na()# saveRDS(train_ready, file = "datatrain_clean.rds")data <- read_rds("datatrain_clean.rds")
head(data,10)# library(dplyr)
data <- data %>%
na.omit() %>%
mutate(label = as.factor(label))
glimpse(data)#> Rows: 14,977
#> Columns: 2
#> $ text_all <chr> "catet ", " ", "proyek oligarki meminjam tangan pe~
#> $ label <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0~
keraslibrary(keras)
use_condaenv("r-tensorflow329")num_words <- 2000 # proporsi sekitar 7 persen dari total unique words, mengikuti contoh di algotech
# prepare for tokenizers
tokenize <- keras::text_tokenizer(num_words = num_words,
lower = TRUE) %>%
keras::fit_text_tokenizer(as.character(data$text_all))
paste("number of unique words:", length(tokenize$word_counts))#> [1] "number of unique words: 24992"
tokenize$word_index[100:150]#> $setelah
#> [1] 100
#>
#> $ibu
#> [1] 101
#>
#> $para
#> [1] 102
#>
#> $lain
#> [1] 103
#>
#> $rumah
#> [1] 104
#>
#> $sendiri
#> [1] 105
#>
#> $hanya
#> [1] 106
#>
#> $soal
#> [1] 107
#>
#> $nya
#> [1] 108
#>
#> $hukum
#> [1] 109
#>
#> $tau
#> [1] 110
#>
#> $index
#> [1] 111
#>
#> $pointing
#> [1] 112
#>
#> $adalah
#> [1] 113
#>
#> $jd
#> [1] 114
#>
#> $bagi
#> [1] 115
#>
#> $bakal
#> [1] 116
#>
#> $jalan
#> [1] 117
#>
#> $sekarang
#> [1] 118
#>
#> $o
#> [1] 119
#>
#> $suka
#> [1] 120
#>
#> $desa
#> [1] 121
#>
#> $emang
#> [1] 122
#>
#> $malah
#> [1] 123
#>
#> $pas
#> [1] 124
#>
#> $lalu
#> [1] 125
#>
#> $kerja
#> [1] 126
#>
#> $grinning
#> [1] 127
#>
#> $minyak
#> [1] 128
#>
#> $secara
#> [1] 129
#>
#> $bgt
#> [1] 130
#>
#> $proyek
#> [1] 131
#>
#> $kalau
#> [1] 132
#>
#> $bener
#> [1] 133
#>
#> $jokowi
#> [1] 134
#>
#> $presiden
#> [1] 135
#>
#> $masa
#> [1] 136
#>
#> $oleh
#> [1] 137
#>
#> $tears
#> [1] 138
#>
#> $hands
#> [1] 139
#>
#> $joy
#> [1] 140
#>
#> $pake
#> [1] 141
#>
#> $soeha
#> [1] 142
#>
#> $anjing
#> [1] 143
#>
#> $tanah
#> [1] 144
#>
#> $perlu
#> [1] 145
#>
#> $backhand
#> [1] 146
#>
#> $semoga
#> [1] 147
#>
#> $allah
#> [1] 148
#>
#> $suara
#> [1] 149
#>
#> $rp
#> [1] 150
to_split <- data %>%
mutate(label = as.numeric(label),
label = label - 1)
head(to_split) set.seed(100)
rows <- sample(nrow(to_split))
split <- to_split[rows, ]
split[200:210,2]Data sudah teracak, dilihat melalui label yang sudah tidak berurutan.
table(split$label) %>%
prop.table()#>
#> 0 1 2
#> 0.3338452 0.3338452 0.3323095
library(rsample)
set.seed(100)
intrain <- initial_split(data = to_split, prop = 0.8, strata = "label")
data_train <- training(intrain)
data_val <- testing(intrain)library(stringr)
maxlen <- max(str_count(to_split$text_all, "\\w+")) + 1
paste("maximum length of words in data:", maxlen)#> [1] "maximum length of words in data: 105"
# prepare x
data_train_x <- texts_to_sequences(tokenize, data_train$text_all) %>%
pad_sequences(maxlen = maxlen)
data_val_x <- texts_to_sequences(tokenize, data_val$text_all) %>%
pad_sequences(maxlen = maxlen)
# prepare y
data_train_y <- to_categorical(data_train$label, num_classes = 3)#, num_classes = 3)
data_val_y <- to_categorical(data_val$label, num_classes = 3)keras_model_sequentialmodel <- keras_model_sequential()embedding –> dropout –> LSTM –> dense
tensorflow::tf$random$set_seed(100)
# model
model %>%
# layer input
layer_embedding(
name = "input",
input_dim = num_words,
input_length = maxlen,
output_dim = 32,
embeddings_initializer = initializer_random_uniform(minval = -0.05, maxval = 0.05, seed = 2)
) %>%
# layer dropout
layer_dropout(
name = "embedding_dropout",
rate = 0.3
) %>%
# layer lstm 1
layer_lstm(
name = "lstm1",
units = 256,
dropout = 0.2,
recurrent_dropout = 0.2,
return_sequences = FALSE,
recurrent_initializer = initializer_random_uniform(minval = -0.05, maxval = 0.05, seed = 2),
kernel_initializer = initializer_random_uniform(minval = -0.05, maxval = 0.05, seed = 2)
) %>%
# layer output
layer_dense(
name = "output",
units = 3,
activation = "softmax",
kernel_initializer = initializer_random_uniform(minval = -0.05, maxval = 0.05, seed = 2)
)# compile the model
model %>% compile(
optimizer = "adam",
metrics = "accuracy",
loss = "categorical_crossentropy"
)
# model summary
summary(model)#> Model: "sequential"
#> ________________________________________________________________________________
#> Layer (type) Output Shape Param #
#> ================================================================================
#> input (Embedding) (None, 105, 32) 64000
#> ________________________________________________________________________________
#> embedding_dropout (Dropout) (None, 105, 32) 0
#> ________________________________________________________________________________
#> lstm1 (LSTM) (None, 256) 295936
#> ________________________________________________________________________________
#> output (Dense) (None, 3) 771
#> ================================================================================
#> Total params: 360,707
#> Trainable params: 360,707
#> Non-trainable params: 0
#> ________________________________________________________________________________
Dengan epoch = 10 dan batch_size = 512
epochs <- 10
batch_size <- 512
# fit the model
history <- model %>% fit(
data_train_x, data_train_y,
batch_size = batch_size,
epochs = epochs,
verbose = 1,
validation_data = list(
data_val_x, data_val_y
)
)
# history plot
plot(history)# predict on train
options(scipen = 123)
data_train_pred <- model %>%
predict_classes(data_train_x) %>%
as.vector()
# predict on val
data_val_pred <- model %>%
predict_classes(data_val_x) %>%
as.vector()# accuracy on data train
library(yardstick)
accuracy_vec(
truth = factor(data_train$label,labels = c("buzzer", "media", "personal")),
estimate = factor(data_train_pred, labels = c("buzzer", "media", "personal"))
)#> [1] 0.940823
# accuracy on data validation
accuracy_vec(
truth = factor(data_val$label,labels = c("buzzer", "media", "personal")),
estimate = factor(data_val_pred, labels = c("buzzer", "media", "personal"))
)#> [1] 0.8931909
# sensitivity on data validation
sensitivity_vec(
truth = factor(data_val$label,labels = c("buzzer", "media", "personal")),
estimate = factor(data_val_pred, labels = c("buzzer", "media", "personal"))
)#> [1] 0.893162
specificity_vec(
truth = factor(data_val$label, labels = c("buzzer", "media", "personal")),
estimate = factor(data_val_pred, labels = c("buzzer", "media", "personal"))
)#> [1] 0.9465965
# precision on data validation
ppv_vec(
truth = factor(data_val$label, labels = c("buzzer", "media", "personal")),
estimate = factor(data_val_pred, labels = c("buzzer", "media", "personal"))
)#> [1] 0.8933404
Saya memiliki sebuah data twitter search query yang didominasi oleh personal statement. Saya ingin melihat bagaimana mesin dapat memprediksi bahwa teks-teks tersebut ditulis oleh perseorangan.
test <- read_rds("geprek bensu-AKSATA.rds")test_5000 <- test %>%
select(full_text) %>%
mutate(label = NA) %>%
distinct() %>%
head(5000)
nrow(test_5000)#> [1] 1776
data_test_x <- texts_to_sequences(tokenize, test_5000$full_text) %>%
pad_sequences(maxlen = maxlen)# predict on data test
data_test_pred <- model %>%
predict_classes(data_test_x) %>%
as.vector()
# create submission data
submission <- test_5000 %>%
mutate(label = data_test_pred)
inspect_buzzer_pred <- submission %>%
filter(label == 0)
inspect_personal_pred <- submission %>%
filter(label == 2)
inspect_media_pred <- submission %>%
filter(label == 1)
inspect_media_pred# to_submit <- ifelse(submission$bully == 1, "yes", "no")
#
# submission <- datatest %>%
# mutate(bully = to_submit)inspect_buzzer_predinspect_personal_predPada Model 1 (Long Short Term Memmory menggunakan Tensorflow), kita dapat melihat bahwa dari total entri data test sebanyak 1776 tweets, model dapat memprediksi 1153 tweets murni sebagai personal statement. Sedangkan, berbicara mengenai dataset, kita dapat melihat bahwa test_5000 tidak sepenuhnya diisi oleh tweet perseorangan. Ada beberapa tweet yang betul-betul dikemukakan oleh Media, misalnya.
Sedangkan untuk class buzzer pada kasus ini, Mesin mengalami bias klasifikasi karena kelas buzzer pada data train lebih banyak mengandung kalimat bernada negatif. Sehingga ketika terdapat tweet perseorangan namun bernada negatif pula (pada data test), mesin mempelajarinya sebagai buzzer.