1 Deep Neural Network

Halo. Disini saya akan membahas topik RNN. Saya menulis catatan ini sebagai tolak ukur apa yang telah saya pelajari. Berikut beberapa blog yang menurut saya baik mengenai RNN, Christopher Olah, Michael Nguyen.

Sebelumn jauh pergi ke RNN, saya ingin sedikit membahas konsep dari Deep Learning, dimana ide utamanya ada di Neural Network. Jadi NN itu adalah salah satu cabang dari machine learning yang dimana proses pembelajarannya mengimitasi cara neuron di otak manusia bekerja. intip sedikit intro DL yang saya rangkum disini. Di NN kita mengenal bebrapa istilah, misal input layer, hidden layer, dan output layer. lalu yang membedakan arsitektur NN dan DL yaitu berada di jumlah hidden layer yang ditetapkan. NN sederhana hanya memiliki 1 hidden layer, sedangkan DL memiliki lebih dari 1 hidden layer, makin banyak jumlah hidden layernya makin deep. hehe. kemudian sebenarnya tujuan utama dari NN yakni, memperoleh weight yang dapat menghasilkan galat/error terkecil. proses memperoleh weight itu melalui beberapa iterasi/epoch. Tahapnya dinamakan backpropogation tujuannya untuk update & adjust weight tiap nodes. arsitektur NN selain dapat dibedakan melalui jumlah neuron dan layer, ternyata dapat juga di karakteristikan berdasarkan arah sinyal (direction of signal) proses trainingnya: feed-forward dan recurrent. tapi yang akan dibahas di catatan ini yaitu recurrent neural network (RNN)

nah oke ketika berbicara terkait arsitektur DL yang sangat deep tadi itu, dan dengan tipe feed-forward network dan Recurrent Neural Network ternyata berpeluang mengalami masalah yang dinamakan… vanishing gradient dan atau exploding gradient.

1.1 Vanishing/Exploding Gradient Problem

Seperti yang sudah disinggung diatas, proses backproppogation bertujuan untuk meng-update weight di tiap network-nya. Berikut tulisan dari mas samuel sena untuk penjelasan yang sederhana tahap backpropogation. Ketika backprop dari output layer melewati hidden layer sampai ke input layer, pada simple Neural Network (1 hidden layer) kita mungkin tidak akan menemui masalah update weights. namun ketika kita membangun arsitektur dengan jumlah hidden layer yang banyak (deep neural network) kita akan menemui beberapa masalah update weight (dengan gradient descent):

  1. kemungkinan nilai yang diperoleh akan secara eksponensial mengecil ketika menuju ke input layer yang mengakibatkan masalah vanishing gradient. Berikut ilustrasinya, yang saya ambil dari tulisan Michael Nguyen.
  • Ilustrasi proses training Deep Feed Forward Network (Deep Neural Network):


Ada tiga langkah utama: pertama, melakukan forward pass, untuk membuat prediksi. Kedua, membandingkan hasil prediksi dengan nilai aktualnya dengan cost function/lost function. Akan dihasilkan nilai error modelnya. Terakhir, menggunakan error tersebut untuk backpropogation yang menghitung gradient tiap node untuk update weigth dan bias dengan tujuan memperoleh hasil error terkecil.

  • ilustrasi masalah vanishing gradient:


Fungsi gradient adalah untuk menyesuaikan weights dan bias yang memungkinankan model NN belajar. sifat dari gradient yaitu, makin besar nilai gradient pada layer saat ini (current layer), akan mengakibatkan penyesuaian di layer setelahnya semakin besar. begitupun sebaliknyua. disinilah permasalahannya. saat melakiukan backprop, setiap node akan menghitung nilai gradient dan mengupdate weightnya sesuai dengan efek gradient di layer sebelumnya. jadi jika di layer sebelumnya kecil, maka penyesuaian weights pada layer saat iniakan semakn kecil. itu meyebabkan gradient menysut secara exponensial mengecil ketika menuju ke input layer. sehingga ketika berada di input layer gagal melakukan pembelajaran karena masalah vanishing gradient. sehingga model gagal untuk belajar saat dilakukan pass forward kembali untuk membuat prediksi.

  1. Kemungkinan nilai yang diperoleh akan secara exponensial membesar ketika menuju ke input layer yang mengakibatkan maslaah exploding gradient. Ciri-ciri model mengalami masalah exploding gradient, yaitu ketika ketika hasil cost function-nya adalah NaN (Not a Number)

2 Recurrent Neural Network

Berangkat dari permasalahan vasishing/exploding gradient yang sudah disinggung diatas, muncul perkembangan metode dari RNN yaitu LSTM dan GRU mampu menangani masalah tersebut. akan dibahas dibawah. RNN sendiri belum mampu menangani vanishing gradient karena masalah short-term memory.

Lalu RNN itu apa? RNN adalah model deep learning yang biasa digunakan untuk data-data sekuen (berurutan). Apasih data sekuensial itu? Berikut adalah beberapa contoh kasus data sekuensial:

  1. Rangkaian kata-kata pada klasifikasi sentiment. input: teks -> output: rating/kelas sentimen.
  2. Rangkaian kata penerjemah bahasa. input: teks -> output: text translation.
  3. Data Runtun Waktu (Time Series). input: numerik -> output: forecasting result.
  4. Speech recognation. input: audio -> output: teks. dan masih banyak lagi contohnya.

2.1 Jenis Pemrosesan RNN

Terdapat beberapa tipe pemrosesan RNN yang masing-masing memiliki karakteristik studi kasus sendiri.





Persegi panjang adalah sebuah vektor representatif, dan garis panah yang menghubungkan antar persegi panjang adalah fungsi aktivasinya. Persegi panjang berwarna merah melambangkan sebuah input, warna biru sebagai output, sedangkan hijau adalah state memory RNN.

1. One-to-One: Pada jenis model membutuhkan sebuah input dan dihasilkan satu output, prosesnya belum menerapkan RNN. Contoh penggunaannya pada image classification.
2. One-to-Many: Pada jenis ini, model membutuhkan sebuah input kemudian diproses untuk menghasilkan banyak output. Contoh penggunaannya adalah image captioning. menerima input sebuah image dan outputnya adalah kata-kata (caption).
3. Many-to-One: Pada jenis ini, model membutuhkan beberapa input yang kemudian dihasilkan sebuah output. Contoh penggunaannya adalah sentiment classification. Menerima input beberapa sekuen kata, yang kemudian diperoleh hasil deteksi kelas sentimennya.
4. Many-to-Many: Pada jenis ini model membutuhkan beberapa input, dan dihasilkan beberapa output. Contoh penggunaannya pada sebuah machine translator. Menerima input sekuen kata, dan dihasilkan output sekuen kata sesuai dengan terjemahannya.
5. Many-to-Many: Pada jenis ini model menerima input dan output yang disinkronisasikan. Contoh penerapannya pada kasus video classification: Self driving cars, dan medical image processing.

2.2 Konsep RNN

Ide utama dari RNN yaitu memanfaatkan pemrosesan sebuah informasi yang berurutan (sekuensial). Dalam NN kita mengamsusikan bahwa seluruh input tidak bergantung satu sama lain atau biasa dekenal sebagai IID (Independent Identical Distributed), sehingga kurang tepat jika digunakan untuk melakukan pemrosesan data sekuensial. Misal dalam kasus sentiment analysis, model NN yang naif ketika diberikan sebuah kalimat, “tidak ada yang bisa membuatku bahagia, senang, dan gembira selain kamu.” akan menduga itu termasuk sebuah sentiment positif. Karena terdapat banyak sekali kata positif didalamnya. Namun model itu tidak menangkap konteks negasi “tidak” diawal kalimat yang dapat merubah makna kalimat yang sebenarnya. disitulah mengapa RNN diperlukan.

RNN disebut berulang karena melakukan sebuah tugas yang sama untuk setiap elemen yang berurutan, dengan output tergantung pada perhitungan sebelumnya.





Diagram diatas adalah arsitektur RNN setelah dibuka buka lipatannya (unfold). Dengan membuka lipatannya tersebut kita dapat melihat arsitektur layer secara penuh. Misal kita mempunyai sebuah kalimat yang terdiri dari 10 kata, berarti akan ada 10 layer-NN yang terbentuk secara terpisah. Masing-masing layer merepresentasikan tiap kata. Adapun berikut adalah beberapa penjelasan notasi pada diagram diatas:

  1. \(x_t\) adalah sebuah input pada saat (waktu) \(t\). Misal, \(x_1\) adalah sebuah one-hot vector yang sesuai dengan kata setelahnya.
  2. Dalam tiap pemrosesan, RNN menyimpan sebuah state internal yaitu \(S_t\). inilah “memori” pada RNN. \(S_t\) dihitung berdasarkan hidden state sebelumnya dan input pada langkah saat itu. \(S_t=f(U_{x_t}+W_{S_t-1})\). Fungsi aktivasi dari f biasanya nonlinear seperti tanh atau ReLU.
  3. \(O_t\) adalah output dari step \(t\).

2.2.1 Forward Pass

Diawal tadi sudah dibahas, bahwa akan ada beberapa istilah dari NN sederhana yang terdapat pada RNN. Misal diambil sebuah studi kasus terkait tentang sentiment classification. Maka inputnya akan berisi one-hot vektor dari kata-kata, dan outputnya merupakan kelas sentimen.

Kemudian bagaimana proses training modelnya? pertama dilakukan forward pass seperti penjelasan dibawah ini:





Keterangan:
1. Masing-masing layer menjelaskan tiap kata.
2. Untuk penjelasan konsep Forward pass, kita tinjau pada layer pada saat \(t\) (berada di tengah).

  • Ingat bahwa RNN memperhatikan data perhitungan output pada \(t-_1\). Sehingga awalnya kita harus menghitung state pada \(s_t\) terlebih dahulu. Melibatkan perkalian matriks input \(x_t\) dengan parameter \(U\) dan dijumlahkan dengan hasil perkalian \(s_{t-_1}\). Lalu hasilnya diproses dengan fungsi aktivasi tanh. Detail perhitungannya adalah sebagai berikut:
\(s_t=tanh(U.x_t+W.s_{t-_1})\)


  • Hasil dari \(s_t\) kemudian diteruskan ke output \(\hat{y}_t\) dengan melakuka perkalian matrik terhadap paramter \(V\). Kemudian diteruskan dengan fungsi aktivasi softmax. Detailnya sebagai berikut:
\(\hat{y}_t=softmax(V.s_t)\)


proses diatas sering diilustrasikan dengan gambar:



2.2.2 Backpropogation Through Time

Tujuan training model RNN adalah untuk menemukan parameter \(U\), \(V\), dan \(W\) yang menghasilkan error minimum. Istilah BTT muncul karena pada arsitektur RNN memperhatikan deret waktu sebelumnya. Maka untuk mengalkulasi gradien pada langkah waktu \(t\), kita harus menghitung tuturanan pada langkah \(t-1\), \(t-2\), \(t-3\) hingga berada pada saat \(t=1\). Kalau anda penasaran bagaimana algoritma ini bekerja, saya sarankan untuk baca lebih lanjut tulisan part : 3 Backpropogarion & Vanishing Gradient.



Seperti yang telah disinggung pada bab sebelumnya, model RNN juga mempunyai masalah vanishing gradient, karena tidak mampu menangkap long-term dependencies. karena jumlah layer yang telalu panjang, membuat proses backprop menghasilkan nilai gradient yang semakin kecil dan bahkan mendekati nol atau dikatakan menghilang ketika sampai pada layer-layer awal.

Hal ini disebabkan oleh sifat perkalian antar bilangan pecahan. Bayangkan misalkan suatu bilangan pecahan 1/4, dikalikan dengan bilangan pecahan lain misalnya 1/3, maka dalam satu operasi ini nilainya sudah menjadi 1/12. Dikalikan pecahan lain misalnya 1/5, nilainya menjadi 1/60, dst. Nilai ini akan mengecil secara eksponensial, dan dengan nilai pecahan yang kecil dan banyaknya operasi perkalian, nilainya akan mendekati nol.

source: https://indoml.com/2018/04/04/pengenalan-rnn-bag-1/.

untuk mengatasi permasalahan ini, muncul perkembangan varian model RNN yaitu Long-Term Short Memory (LSTM)

3 LSTM

Sama seperti RNN, LSTM memiliki model runtun waktu yang di ilustrasikan dengan kotak hijau. jika di-unfold arsitekturnya menjadi seperti dibawah ini:



yang menjadi perbedaan dengan RNN adlah LSTM memiliki tambahan informasi sinyal yang diberikan dari satu langkah waktu ke langkah waktu berikutnya yang biasa disebut dengan “cell memory”. LSTM didesain untuk mengatasi permasalahan vanishing gradient, dengan menggunakan mekanisme gerbang (gate).

3.1 LSTM Network

Jadi komponen yang terdapat di LSTM antara lain:

  1. Forget Gate f (NN dengan fungsi sigmoid).
  2. Candidate Layer g (NN dengan fungsi tanh).
  3. Input Gate I (NN dengan fungsi sigmoid).
  4. Output Gate O (NN dengan fungsi sigmoid).
  5. Hidden State H (vektor).
  6. Memory State C (vektor).

berikut adalah diagram LSTM pada saat langkah waktu ke-t.



  1. \(X_t\) = Vektor input saat ke-t.
  2. \(H_{t-_1}\) = Hidden state sebelumnya.
  3. \(C_{t-_1}\) = Memori state sebelumnya.
  4. \(H_t\) = Hidden state saat ini.
  5. \(C_t\) = Memori state saat ini.
  6. [*] = operasi perkalian elemen.
  7. [+] = operasi penjumlahan elemen.

jadi input dari tiap modul LSTM adalah \(X_t\) (current input), \(H_{t-_1}\), dan \(C_{t-_1}\). kemudian outputnya adalah \(H_t\), dan \(C_t\).

Keterangan:

disarikan dari tulisan Denny Britz yang merupakan hasil rangkuman dari tulisan Christopher Olah: Understanding LSTM Networks.

  1. I, f, O adalah gerbang Input, Forget, dan Output. Baik input, forget, dan output memiliki rumusan fungsi yang sama (sigmoid), yang membedakan hanya parameter matriksnya saja (perhatikan rumus dibawah). Artinya keluaran gate ini memiliki vektor nilai antara 0 hingga 1. nol artinya informasi diblok total, dan satu artinya seluruh informasi diikutsertakan. Input gate mengatur berapa banyak state yang baru saja dikomputasi untuk current input yang ingin anda biarkan lewat. Forget gate mengatur berapa state sebelumnya (previous) yang ingin anda biarkan lewat. Terakhir output gate mengatur berapa banyak internal state yang ingin anda expose ke jaringan (network) (higher layer & next time step). Semua gate memiliki dimensi yang sama dengan dimension hidden state (\(d_{st}\)) sebagai ukuran untuk hidden state. Keluaran dari gerbang sigmoid akan dikalikan dengan suatu nilai lain untuk mengontrol seberapa banyak nilai tersebut dipakai.
\(I= \sigma(x_tU^I + s_{t-_1}W^I)\)
\(f= \sigma(x_tU^f + s_{t-_1}W^f)\)
\(O= \sigma(x_tU^O + s_{t-_1}W^O)\)

Nilai 1 artinya “benar-benar jaga elemen ini” sementara 0 artinya “benar-benar singkirkan elemen ini.

  1. g adalah “kandidat” hidden state yang dikomputasi berbasis input pada saat ini (ke-t atau current input) dan hidden state sebelumnya.

  2. \(c_t\) adalah memori interna. merupakan hasil kombinasi perhitungna memori sebelumnya \(c_{t-_1}\) dikalikan dengan forget state dan dijumlahkan dengan hasil perkalian Input gate dengan candidate gate. Jadi ini sebenarnya adalah bagaimana cara kita menggabungkan antara memori sebelumnya dan input yang baru untuk menghasilkan memori saat ini.

\(c_t=f_t*c_{t-_1} + I_t*g\)

3.2 Build LSTM from the scratch

will be update.

4 Implementasi LSTM using Keras

Pada contoh ini saya mengambil studi kasus sentiment tweet mengenai maskapai penerbangan di US yang diperoleh dari kaggle dataset. klasifikasi sentimen yaitu sebuah teknik untuk menyimpulkan padanan kata dari tweet/ulasan/komentar/yang terkait apakah termasuk kedalam kalimat positif atau negatif. Metode Machine Learning pada umumnya seperti Naive Bayes Classifier,Support Vector machine bisa digunakan untuk klasifikasi sentiment. Namun kali ini akan dicoba untuk menggunakan deep learning dengan metode LSTM.

# clear environment
rm(list = ls())

# load packages required
pacman::p_load(caret, keras, magrittr, textclean, tidyverse, tidytext, tm)

#set seed keras for reproducible result
use_session_with_seed(2)

# set conda env
use_condaenv("tensorflow")

4.1 Text Pre-Processing

system.time(
  data <- read_csv("data/Tweets.csv") %>%
  mutate(
    text = text %>%
      replace_url()  %>% 
      # replace_emoji() %>%
      # replace_emoticon() %>%
      replace_html() %>% 
      str_remove_all("@([0-9a-zA-Z_]+)") %>% # remove username
      str_remove_all("#([0-9a-zA-Z_]+)") %>% # remove hashtag
      str_replace_all("[\\?]+", " questionmark ") %>% # optional
      str_replace_all("[\\!]+", " exclamationmark ") %>% # optional
      str_remove_all('[\\&]+') %>% # optional
      str_remove_all('[\\"]+') %>% # optional
      replace_contraction() %>%
      replace_word_elongation() %>% 
      replace_internet_slang() %>% 
      str_remove_all(pattern = "[[:digit:]]") %>% # remove number
      str_remove_all(pattern = "[[:punct:]]") %>% # remove semua tanda baca selain !,&,""
      str_remove_all(pattern = "\\$") %>% # remove dollar sign
      str_to_lower() %>% # transform menjadi huruf kecil
      str_squish(), # remove extra whitespace
    label = base::factor(airline_sentiment, levels = c("negative", "neutral", "positive")) %>% 
                     as.numeric() %>% {. - 1}
  ) %>% 
  select(text, label) %>% 
  na.omit() # remove NA
)
## Parsed with column specification:
## cols(
##   tweet_id = col_double(),
##   airline_sentiment = col_character(),
##   airline_sentiment_confidence = col_double(),
##   negativereason = col_character(),
##   negativereason_confidence = col_double(),
##   airline = col_character(),
##   airline_sentiment_gold = col_character(),
##   name = col_character(),
##   negativereason_gold = col_character(),
##   retweet_count = col_integer(),
##   text = col_character(),
##   tweet_coord = col_character(),
##   tweet_created = col_character(),
##   tweet_location = col_character(),
##   user_timezone = col_character()
## )
##    user  system elapsed 
##   37.72    0.10   38.00
Keterangan beberapa command yang digunakan:
1. replace_url() = replace url dengan “” atau sama artinya dengan remove, misal https://blabla.com akan dihapus.
2. replace_html() = replace simbol html dengan “” atau sama artinya dengan remove, misal

akan dihapus.
3. str_replace_all("[\\?]+", " questionmark ") = replace tanda baca “?” dengan “questionmark”. karena RNN memperhatikan data t-1 dan t+1 maka tanda baca tdk diremove, tapi direplace dengan nama tanda baca.
4. replace_contraction() = replace abbreviation ke versi panjangnya. misal isn’t jadi is not.
5. replace_word_elongation() = replace kata panjang ke bentuk umumnya. misal whyyyy menjadi why.
6. replace_internet_slang() = replace slang menjadi kata umum, ada dictionary-nya.
7. replace_emoticon() = replace emoticon dengan kata yang sesuai. misal :-D akan di transform menjadi laughing.

remove stopwords atau kata umum yang sebenarnya tidak terlalu penting disertakan pada saat membangun model. ex: the,you,I,we,they,be

system.time(
  rm.stopwords <- VCorpus(VectorSource(data$text)) %>%
  tm_map(removeWords,
         stopwords("en")) %>%
  tm_map(stripWhitespace) %>% 
  sapply(as.character) %>%
  as.data.frame(stringsAsFactors = FALSE)
)
data.clean <- bind_cols(rm.stopwords, data[,2]) %>%
  `colnames<-`(c("text","label"))

perbandingan hasil sebelum dan sesudah dilakukan praproses.

suppressMessages(
  cbind("before" = read_csv("data/Tweets.csv") %>% 
          select(text, airline_sentiment) %>% 
          na.omit() %$% 
          text[3] ,
       "after" = data$text[3])
)
##      before                                                                   
## [1,] "@VirginAmerica I didn't today... Must mean I need to take another trip!"
##      after                                                                  
## [1,] "i did not today must mean i need to take another trip exclamationmark"

4.2 Tokenizer

Tahap ini untuk memisahkan tiap kata di seluruh dokumen menjadi bentuk token. Parameter num_words untuk mengatur jumlah maksimum kata yang akan digunakan, di sort berdasarkan urutan frekuensi yang terbesar. kata yang jarang muncul akan dihilangkan. dari total 11216 kata unique yg terdapat di data teks, saya reduksi menjadi 1024 saja yang akan digunakan untuk membuat model. Parameter lower adalah sebuah logika kondisi, jika TRUE maka seluruh kata akan di transformasi ke huruf kecil (tolower).

num_words <- 1024 
# prepare tokenizers
tokenizer <- text_tokenizer(num_words = num_words,
                            lower = TRUE) %>% 
  fit_text_tokenizer(data$text)
paste("total kata unique pada data text:", length(tokenizer$word_counts))
## [1] "total kata unique pada data text: 11216"

4.2.1 Intuition

docs = c('Well done!',
        'Good work',
        'Great effort',
        'nice work',
        'Excellent!')
tokendocs <- text_tokenizer(num_words = 4, 
                            lower = TRUE) %>% 
  fit_text_tokenizer(docs)

Misal saya punya 5 buah dokumen teks yang di simpan ke dalam objek docs. Kemudian saya membuat token dengan maksimum words/term (kata) yang digunakan yaitu 4. Artinya kata yang jarang muncul tidak akan digunakan pada saat train model. Untuk melihat jumlah kata unique yang tersimpan di dictionary dokumen, gunakan command token$word_counts. Untuk melihat list kata dengan frekuensi kemunculan tertinggi, gunakan command token$index_word.

#token$word_counts
paste("jumlah kata unique di dictionary:",length(tokendocs$word_counts))
## [1] "jumlah kata unique di dictionary: 8"
tokendocs$index_word[1:4] # 4 kata yang akan digunakan.
## $`1`
## [1] "work"
## 
## $`2`
## [1] "well"
## 
## $`3`
## [1] "done"
## 
## $`4`
## [1] "good"

4.3 Splitting Data

Disini akan dilakukan splitting data menjadi 3 bagian yaitu train, validation, dan test. Proporsinya sebesar 60% untuk train dan sisanya 40% di partisi untuk data validation dan testing.





Data Train adalah data yang akan kita gunakan untuk melatih model (menyesuaikan weight dan bias). Data Validation untuk evaluasi tuning hyperparameter di model (mengataur hidden layer, optimizer, learning rate dll). Sedangkan data test sebagai evaluator dari model yang kita buat.

Bayangkan ketika kita punya 50 soal matematika. kemudian kita gunakan 30 soal untuk digunakan sebagai bahan ajar adik kita. 20 soal sisanya saya pisah lagi 10 soal untuk validasi dan 10 soal untuk menguji pemahaman hasil belajar adik saya. Saat saya mengajar adik saya, saya menggunakan 30 soal untuk belajar + 10 soal validasi. Data validasi ini tujuannya agar saya tahu, kekuragan adik saya dibagian mana. sehigga saya bisa improve kemampuan belajar adik saya sebelum dia mengerjakan soal ujian (10 soal/data uji)

set.seed(100)
intrain <- sample(1:nrow(data), nrow(data) * 0.6)

data_train <- data[intrain, ]
data_test <- data[-intrain, ]

set.seed(100)
inval <- sample(1:nrow(data_test), nrow(data_test) * 0.5)

data_val <- data_test[inval, ]
data_test <- data_test[-inval, ]

paste("jumlah kata terpanjang di satu observasi/tweet: ",  max(str_count(data$text, "\\w+")))
## [1] "jumlah kata terpanjang di satu observasi/tweet:  33"
maxlen <- max(str_count(data$text, "\\w+")) + 1 
# prepare x
data_train_x <- texts_to_sequences(tokenizer, data_train$text) %>%
  pad_sequences(maxlen = maxlen)

data_val_x <- texts_to_sequences(tokenizer, data_val$text) %>%
  pad_sequences(maxlen = maxlen)

data_test_x <- texts_to_sequences(tokenizer, data_test$text) %>%
  pad_sequences(maxlen = maxlen)

# prepare y
data_train_y <- to_categorical(data_train$label, num_classes = 3)
data_val_y <- to_categorical(data_val$label, num_classes = 3)
data_test_y <- to_categorical(data_test$label, num_classes = 3)

4.3.1 Intuition

Command texts_to_sequence() untuk membuat matriks hasil transformasi text ke bentuk urutan angka (integer). Setelah itu diwrap dengan command pad_sequences yang tujuannya menyamakan dimensi dari panjang seluruh dokumen. Bayangkan input layer sebuah matriks, maka harus mempunya row dan column yang sama. Maka dari itu perlu dilakukan padding. Secara default parameter value akan diatur menjadi 0. Artinya jika terdapat kata yang tidak terdapat di token kita (yang telah dibatasi num_words) maka akan ditransform menjadi 0. berikut ilustrasinya:

texts_to_sequences(tokendocs, c("Excellent!", 
                                "Good job bro, keep hard work", 
                                "well done")) %>% 
  pad_sequences(maxlen = 5)
##      [,1] [,2] [,3] [,4] [,5]
## [1,]    0    0    0    0    0
## [2,]    0    0    0    0    1
## [3,]    0    0    0    2    3

Hasil dari text_to_sequences adalah sebuah matriks berukuran \(n*maxlen\). Contoh diatas terdiri dari 3 dokumen teks dan diatur maxlen = 5. maka akan menghasilkan matriks \(3x5\) yang tiap indeksnya adalah sebuah integer representatif kata yang sama dengan tokendocs pada urutan list ke-i. Ingat, kata done berada di list ke-3 pada token kita kan? maka dari itu hasil matriks diatas pada dokumen ketiga urutan integer paling akhir adalah 3. Kenapa munculnya di indeks paling akhir? karena di pad_sequences kita tidak mengatur parameter tipe padding apakah "pre" atau "post" dan by default adalah "pre".

tokendocs$word_index[3]
## $done
## [1] 3

4.4 Architecture

4.4.1 Embedding Layer:

Embedding Layer hanya dapat digunakan di layer awal/pertama pada arsitektur Deep Learning. Pada berbagai macam framework deep learning seperti Keras, embedding layer berfungsi untuk training data teks menjadi vektor numerik yang merupakan representatif kedekatan makna tiap kata. Proses ini disebut word embedding. Lihat kesini untuk pembahasan word embedding. Proses word embedding ada dua opsi, dapat dilakukan pada saat training model. Atau dapat pula dilakukan pre-trained untuk menghasilkan model word embedding (word2vec/glove model), kemudian model tersebut kita jadikan input di embedding layer. Di catatan ini, saya menggunakan opsi yang pertama.

Embedding layer menerima beberapa parameter. Beberapa contohnya yaitu: input_dim yaitu ukuran maksimum dimensi dari kata-kata (vocabulary) yang telah dijelaskan di bagian num_words. Kemudian ada parameter input_length yaitu batas maksimum panjang dari sekuen kata pada input dokumen. Kemudian ada output_dim dimana merupakan dimensi embedding dari output layer yang akan diterukan ke layer selanjutnya. umunya adalah 32, namun dapat lebih tergantung dengan masalah yang dihadapi.

input yang diterima berupa vektor 2D dengan bentuk: {batch_size, sequence_length}, sedangkan output yang diterima adalah tensor 3D dengan bentuk {batch_size, sequence_length, output_dim}.

4.4.2 Deep Network Layer:

Deep Network Layer menerima matriks embedding sebagai input dan kemudian dikonversi menjadi dimensi yang lebih kecil. Hasil kompresi dimensi tersebut telah mewakili informasi dari data. Pada kasus data teks, arsitektur deep learning yang biasa digunakan yaitu RNN > LSTM/GRU. Parameter dropout ditambahkan untuk mengurangi risiko overfit. range nilai dropout antara 0 hingga 1. yang umum digunakan yaitu 0.2 hingga 0.5. makin mendekati 0 akan cenderung overfit, sedangkan makin mendekati 1 memiliki risiko underfit.

Lebih detailnya terkait masing-masing layer sekuensial silahkan kunjungi website keras documentation.

4.4.3 Output Layer

Output layer ini adalah layer terakhir pada arsitektur deep learning. Di keras menggunakan command layer_dense dimana kita perlu mengatur parameter units atau berapa banyak neuron yang ingin kita bangun. Di kasus ini saya menggunakan 3 unit, karena kelas yang kita miliki ada 3 (negative, neutral, positive).

4.4.4 Activation Function

Sederhananya, fungsi aktivasi ini bertujuan untuk transformasi nilai input pada tiap neuron. bayangkan jika kasus klasifikasi maka di output layer nilainya adalah antara 0, 1, atau 2. jika misal diperoleh nilai input sebesar 13, kemudian diteruskan secara linear di output layer maka selisih prediksi dnegan aktualnya sangat besar, sehingga butuh waktu train lebih lama lagi. namun jika di output layer tersebut menggunakan fungsi activation, dia akan memberikan sinyal pada tiap neuron yang ada di output layer untuk aktif mentransformasi nilai input (hasil perhitungan operasi bobot pada layer sebelumnya) menjadi sebuah nilai yang range nya lebih kecil. ada beberapa pilihan activation function, antara lain:

  1. sigmoid : hasil transformasi nilainya diantara 0-1 cocok digunakan untuk binary class.
  2. softmax : hasil transformasi menjadi nilai probabilitas antara 0-1 cocok dgunakan untuk multiclass.
  3. tanh : hasil transformasi nilainya diantara -1 hingga 1.

dan masih banyak lagi pilihannya keras documentation.

4.4.6 Lost function and Optimizer

Model membutuhkan loss function dan optimizer parameter pada saat dilatih (training model). sederhananya loss function untuk mengukur efektivitas model kita dalam melakukan prediksi pada tiap epoch (iterasi) dilihat dari gap error antara prediksi dan aktualnya. pada kasus kategori 2 kelas, lost function yang digunakan adalah binary_crossentropy sedangkan untuk kasus multiclass menggunakan categorical_crossentropy. Pilihannya tidak hanya 2 saja, ada beberapa pilihan, namun yang umum ketika bekerja dengan kasus klasifikasi, 2 fungsi tersebut yang digunakan. Berikut adalah beberapa pilihan loss function dari keras documentation.

Disini algoritma optimasi yang digunakan pada saat training adalah adam paper : Adam: A method for stochastic optimization. adam menerima beberapa parameter, salah satunya adalah learning rate: yaitu tingkat pembelajaran yang kita atur pada saat training untuk update weight hingga mencapai local minimum (error terkecil). intervalnya antara 0 hingga 1 dimana semakin mendekati 0 akan membutuhkan waktu lama mencapai local minimum, tapi jika mendekati 1 akan berakibat terjebak di titik yang bukan menjadi local minimumnya.

# compile the model
model %>% compile(
  optimizer = "adam",
  metrics = "accuracy",
  loss = "categorical_crossentropy"
)

# model summary
summary(model)
## ___________________________________________________________________________
## Layer (type)                     Output Shape                  Param #     
## ===========================================================================
## input (Embedding)                (None, 34, 32)                32768       
## ___________________________________________________________________________
## embedding_dropout (Dropout)      (None, 34, 32)                0           
## ___________________________________________________________________________
## lstm (LSTM)                      (None, 256)                   295936      
## ___________________________________________________________________________
## output (Dense)                   (None, 3)                     771         
## ===========================================================================
## Total params: 329,475
## Trainable params: 329,475
## Non-trainable params: 0
## ___________________________________________________________________________

4.5 Train the Model

epoch : adalah banyak iterasi pada saat training model (update weight). umumnya, akurasi model akan meningkat dengan jumlah epoch yang lebih banyak. namun menyesuaikan dengan kasus masing-masing. bisa di evaluasi lewat grafik history train modelnya, jika kurva akurasi masih cenderung naik, maka ada kemunkinan jika ditambahkan jumlah epochnya akan meningkatkan nilai akurasi.
batch_size : penentuan jumlah sampel yang di training pada tiap epoch. umumnya, makin besar batch size proese training akan lebih cepat, tapi tidak selalu konvergen dengan cepat. kebalikannya, makin kecil batch size akan mengakibatkan training lama, namun proses konvergennya lebih cepat.

# model fit settings
epochs <- 10
batch_size <- 128

# 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)

dapat dilihat pada grafik diatas, penurunan nilai loss ekuivalen dengan peningkatan nilai akurasi pada saat training model. sebenarnya ini trial and error, belum ada kaidah khusus yang menunjukkan angka epoch dan batch size yang memperoleh akurasi tinggi. jadi jika kamu punya model lain yang dapat menghasilkan akurasi lebih tinggi, beri tahu saya ya linkedin.

4.6 Evaluate the Model

prediksi terhadap data training, validasi, dan testing.

# predict on train
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()

# predict on test
data_test_pred <- model %>%
  predict_classes(data_test_x) %>%
  as.vector()

4.6.1 Evaluasi model data training

caret::confusionMatrix(
  factor(data_train_pred,labels = c("negative", "neutral", "positive")),
  factor(data_train$label,labels = c("negative", "neutral", "positive"))
)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction negative neutral positive
##   negative     5024     412      158
##   neutral       365    1353      125
##   positive       99     131     1062
## 
## Overall Statistics
##                                           
##                Accuracy : 0.8522          
##                  95% CI : (0.8446, 0.8596)
##     No Information Rate : 0.6287          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.7203          
##  Mcnemar's Test P-Value : 0.0008835       
## 
## Statistics by Class:
## 
##                      Class: negative Class: neutral Class: positive
## Sensitivity                   0.9155         0.7136          0.7896
## Specificity                   0.8241         0.9283          0.9689
## Pos Pred Value                0.8981         0.7341          0.8220
## Neg Pred Value                0.8520         0.9211          0.9619
## Prevalence                    0.6287         0.2172          0.1541
## Detection Rate                0.5756         0.1550          0.1217
## Detection Prevalence          0.6409         0.2111          0.1480
## Balanced Accuracy             0.8698         0.8209          0.8792

4.6.2 Evaluasi model data validasi

# conf matrix for val
caret::confusionMatrix(
  factor(data_val_pred,labels = c("negative", "neutral", "positive")),
  factor(data_val$label,labels = c("negative", "neutral", "positive"))
)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction negative neutral positive
##   negative     1603     164       92
##   neutral       163     359       62
##   positive       60      60      347
## 
## Overall Statistics
##                                          
##                Accuracy : 0.7935         
##                  95% CI : (0.7783, 0.808)
##     No Information Rate : 0.6275         
##     P-Value [Acc > NIR] : < 2e-16        
##                                          
##                   Kappa : 0.6113         
##  Mcnemar's Test P-Value : 0.07951        
## 
## Statistics by Class:
## 
##                      Class: negative Class: neutral Class: positive
## Sensitivity                   0.8779         0.6158          0.6926
## Specificity                   0.7638         0.9033          0.9502
## Pos Pred Value                0.8623         0.6147          0.7430
## Neg Pred Value                0.7878         0.9037          0.9370
## Prevalence                    0.6275         0.2003          0.1722
## Detection Rate                0.5509         0.1234          0.1192
## Detection Prevalence          0.6388         0.2007          0.1605
## Balanced Accuracy             0.8209         0.7595          0.8214

4.6.3 Evaluasi model data testing

# conf matrix for test
caret::confusionMatrix(
  factor(data_test_pred,labels = c("negative", "neutral", "positive")),
  factor(data_test$label, labels = c("negative", "neutral", "positive"))
)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction negative neutral positive
##   negative     1612     154       87
##   neutral       161     389       58
##   positive       45      54      350
## 
## Overall Statistics
##                                           
##                Accuracy : 0.8079          
##                  95% CI : (0.7931, 0.8221)
##     No Information Rate : 0.6247          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.6396          
##  Mcnemar's Test P-Value : 0.003403        
## 
## Statistics by Class:
## 
##                      Class: negative Class: neutral Class: positive
## Sensitivity                   0.8867         0.6516          0.7071
## Specificity                   0.7793         0.9053          0.9590
## Pos Pred Value                0.8699         0.6398          0.7795
## Neg Pred Value                0.8051         0.9096          0.9411
## Prevalence                    0.6247         0.2052          0.1701
## Detection Rate                0.5540         0.1337          0.1203
## Detection Prevalence          0.6368         0.2089          0.1543
## Balanced Accuracy             0.8330         0.7785          0.8330

5 Comparison Method: Naive Bayes Classifier

library(tm)
textcorpus <- VCorpus(VectorSource(data$text))

textdtm <- DocumentTermMatrix(textcorpus)
set.seed(100)
split_80 <- sample(nrow(data), nrow(data)*0.80)
trainx <- textdtm[split_80, ]
testx <- textdtm[-split_80, ]


trainy <- as.factor(data[split_80, ]$label)
testy <- as.factor(data[-split_80, ]$label)

threshold <- 0.1

min_freq = round(textdtm$nrow*(threshold/100),0)

freq_words <- findFreqTerms(textdtm, min_freq )

trainx <- trainx[ , freq_words]
testx <- testx[ , freq_words]
bernoulli_conv <- function(x){
        x <- as.factor(as.numeric(x > 0))
}

trainbn <- apply(trainx, MARGIN = 2,
                   bernoulli_conv)
testbn <- apply(testx, MARGIN = 2,
bernoulli_conv)
#Create model from the training dataset

library(e1071)
nbclassifier <- naiveBayes(trainbn, trainy, laplace = 0)

#Make predictions on test set
testpred <- stats::predict(nbclassifier, testbn)

#Create confusion matrix
confusionMatrix(data = as.factor(testpred), reference = as.factor(testy))
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction    0    1    2
##          0 1514  125   75
##          1  192  387   54
##          2   95   82  386
## 
## Overall Statistics
##                                           
##                Accuracy : 0.7859          
##                  95% CI : (0.7706, 0.8007)
##     No Information Rate : 0.6189          
##     P-Value [Acc > NIR] : < 2.2e-16       
##                                           
##                   Kappa : 0.6155          
##  Mcnemar's Test P-Value : 5.708e-05       
## 
## Statistics by Class:
## 
##                      Class: 0 Class: 1 Class: 2
## Sensitivity            0.8406   0.6515   0.7495
## Specificity            0.8197   0.8938   0.9261
## Pos Pred Value         0.8833   0.6114   0.6856
## Neg Pred Value         0.7600   0.9091   0.9450
## Prevalence             0.6189   0.2041   0.1770
## Detection Rate         0.5203   0.1330   0.1326
## Detection Prevalence   0.5890   0.2175   0.1935
## Balanced Accuracy      0.8302   0.7726   0.8378

Jika dibandingkan hasil predksi terhadap data test, model LSTM sederhana menghasilkan nilai akurasi sebesar 0,8079. lebih baik dibandingkan dengan model naive bayes 0,7859.

Anda dapat mengembangkan model LSTM untuk meningkatkan performa hasil prediksinya. beberapa cara yang dapat dilakukan antara lain:

  1. Fine tune hyperparameters : yaitu mengubah beberapa variabel yang ditetapkan sebelum training. misal, learning rate, batch size, jumlah epoch. Tuning parameternya dapat dilakukan dengan cara manual trial and error, menggunakan parameter flags berikt 1 2
  2. meningkatkan kualitas dataset pada saat pra-proses. memperbaiki kualitas data teks dapat dilakukan dengan menghapus beberapa simbol karakter yang tidak diperlukan, angka, stopwords dan lainnya. Proses stemming juga dapat dilakukan tapi saya tidak terlalu merekomendasikan.
  3. menggunakan layer dropout. teknik untuk menghindari terjadi overfitting dan berpeluang untuk meningkatkan akuraasi pada data validasi.

Referensi:

  1. Recurrent Neural Networks Tutorial, Part 1 – Introduction to RNNs
  2. The Unreasonable Effectiveness of Recurrent Neural Networks
  3. Pengenalan RNN bagian 1
  4. Deep NLP - LSTM.
  5. Christopher Olah: Understanding LSTM Networks.
  6. Illustrated Guided to Recurrent Neural Networks