# clear-up the environment
rm(list = ls())

# chunk options
knitr::opts_chunk$set(
  message = FALSE,
  warning = FALSE,
  fig.align = "center",
  comment = "#>"
)

# scientific notation
options(scipen = 9999)

1 Deep Learning with Keras

Ada banyak frameworks (template) yang bisa kita gunakan untuk membangun neural network/deep learning model:

  • Tensor Flow
  • Keras
  • Pytorch
  • Caffe
  • Theano, etc.

Untuk selanjutnya, kita akan menggunakan Keras dalam membuat arsitektur dan melakukan training model. Keras adalah package yang membantu kita mengimplementasikan model Deep Learning dengan cepat. Keras memanfaatkan tensorflow, sebuah tools open source yang dikembangkan oleh Google untuk implementasi Deep Learning.

1.1 Verify keras installation

library(keras)
library(tensorflow)
model <- keras_model_sequential()

2 MNIST Data Analysis

Seperti yang sudah disampaikan di awal, deep learning merupakan sebuah framework yang sering digunakan ketika menghadapi data unstructured. Maka dari itu mari kita lakukan image classification menggunakan deep learning dengan framework keras. Kita akan gunakan data MNIST yang berisi data image tentang tulisan tangan untuk beragam angka. Kita akan coba klasifikasikan setiap tulisan tangan ke label angka yang sesuai.

Gambar itu terdiri dari kumpulan pixel-pixel. Dimana nantinya dari setiap gabungan pixel yang ada, nantinya akan mewakili sebuah element yang jika digabungkan akan membuat sebuah gambar.

Data Source: kaggle

2.1 Read Data

Data MNIST ada pada folder data_input/mnist:

  • train.csv -> utk cross validation (train-test)
  • test.csv -> utk data validation
# Read mnist train
mnist_train <- read.csv("data_input/mnist/train.csv")

# Read mnist train
mnist_test <- read.csv("data_input/mnist/test.csv")

Pada data yang kita miliki, data yang sudah kita miliki sudah dalam bentuk pixel dan nantinya dari data tersebut kita akan mencoba untuk memprediksi apakah tulisan dari data tersebut.

Lalu bagaimana jika data yang kita miliki adalah gambar asli. Nantinya kita perlu memecahnya menjadi bentuk pixel terelbih dahulu. Intuisinya kenapa kita perlu mengubah nya menjadi pixel itu sama seperti ketika kita megubah data text menjadi nilai corpus yang hanya 1 & 0 saja

2.2 Exploratory Data

Pada tahapan eksplorasi data kali ini, mari coba jawab beberapa pertanyaan di bawah ini.

library(tidyverse)
  • Berapa dimensi data pada data train kita?
# Please type your answer
mnist_train %>% 
  dim()
#> [1] 42000   785

Dimensi data kita:

  • Row: 42000

  • Column: 785 (Include 1 kolom buat target variable)

  • Berapa banyak category yang ada pada kelas target? dan bagaimana proporsinya?

# Please type your answer
mnist_train$label %>% 
  unique()
#>  [1] 1 0 4 7 3 5 8 9 2 6

Terdapat 10 kategori pada kolom kelas target kita

mnist_train$label %>% 
  table() %>% 
  prop.table()
#> .
#>          0          1          2          3          4          5          6 
#> 0.09838095 0.11152381 0.09945238 0.10359524 0.09695238 0.09035714 0.09850000 
#>          7          8          9 
#> 0.10478571 0.09673810 0.09971429

Jika kita lihat, proporsi target variable kita cukup seimbang

  • Berapa banyak prediktor yang ada?
# Please type your answer
mnist_train %>% 
  ncol() - 1
#> [1] 784
  • Berapa range nilai dari prediktor yang digunakan?
# Please type your answer
mnist_train %>% 
  select(-label) %>% 
  range()
#> [1]   0 255
  • Bila 1 baris data merepresentasikan 1 gambar dengan bentuk persegi (sisi*sisi) maka berapa ukuran gambar tesebut?

Jika kita ingat, gambar yang akan kita coba prediksi berbentuk kotak. Untuk mengetahui berapa ukuran sisi dari gambar tersebut kita bisa menghitugnnya dengan rumus sqrt()

# Please type your answer
sqrt(784)
#> [1] 28

Jadinya gambar kita memiliki ukuran 28 x 28 pixel

  • Apa yang anda ketahui terkait pixel?

  • resolusi gambar

  • bagian terkecil dari suatu gambar

  • range dari pixel adalah 0 - 255

    • semakin kecil semakin putih dan semakin besar semakin hitam (greyscale)
    • Semakin kecil semakin muda waranya dan semakin besar semakin gelap waranya (berwaran)
  • Visualisasi gambar

vizTrain <- function(input) {
    
    dimmax <- sqrt(ncol(mnist_train[, -1]))
    
    dimn <- ceiling(sqrt(nrow(input)))
    par(mfrow = c(dimn, dimn), mar = c(0.1, 0.1, 0.1, 
        0.1))
    
    for (i in 1:nrow(input)) {
        m1 <- matrix(input[i, 2:ncol(input)], nrow = dimmax, 
            byrow = T)
        m1 <- apply(m1, 2, as.numeric)
        m1 <- t(apply(m1, 2, rev))
        
        image(1:dimmax, 1:dimmax, m1, col = grey.colors(255), 
            xaxt = "n", yaxt = "n")
        text(2, 20, col = "white", cex = 1.2, mnist_train[i, 
            1])
    }
    
}

# visualisasi
vizTrain(mnist_train[1:25, ])

2.3 Cross Validation

Lakukan cross validation menggunakan initial_split dengan proporsi 80% data untuk data training.

library(rsample)
set.seed(100)

# Spliting
mnist_split <- initial_split(data = mnist_train, 
                             prop = 0.8,
                             strata = "label")

# Data train
mnist_train <- training(mnist_split)

# Data validation
mnist_validation <- testing(mnist_split)

2.4 Data pre-processing

Sebelum membuat model dengan Keras, ada beberapa hal yang perlu dilakukan untuk mempersiapkan data:

  1. Memisahkan prediktor dengan target variabel -> Sama denan membuat mmodel KNN
  2. Scalling data
  3. Mengubah format data menjadi array. Dari data.frame -> matrix -> array
  4. Melakukan one-hot encoding apabila target variabel adalah kategori

2.4.1 Pisahkan target-prediktor, ubah data menjadi matrix serta melakukan scalling

Scalling dengan membagi data dengan angka 255 agar range data yang dimiliki menjadi 0 hingga 1.

Tujuan dari dilakukannya scalling adalah untuk meringankan komputasi, karena perhitungannya hanya menjadi 0-1 saja

# ambil prediktor dan lakukan scaling
#prediktor
train_x <- mnist_train %>% 
  select(-label) %>% 
  as.matrix()/255

validation_x <- mnist_validation %>% 
  select(-label) %>% 
  as.matrix()/255

#target
train_y <- mnist_train %>% 
  select(label)

validation_y <- mnist_validation %>% 
  select(label)

2.4.2 Processing prediktor: Mengubah matrix menjadi array

Framework keras menerima data dalam bentuk array. Sehingga data prediktor dalam bentuk matrix perlu diubah ke dalam bentuk array menggunakan array_reshape().

Keras : mengenal tipe data yang ada di python (array)

# prediktor
train_x_keras <- train_x %>% 
  array_reshape(dim = dim(train_x))

validation_x_keras <- validation_x %>% 
  array_reshape(dim = dim(validation_x))

2.4.3 Processing target: One Hot Encoding

Ubah target (data kategorik) menjadi variable One Hot Encoding menggunakan fungsi to_categorical():

Jika kita langsung mengubahnya dalam bentuk categorical tidak bisa, karena fungsi to_categorical() hanya bisa menerima sebuah bentuk matrix

# Please type your answer
train_y_keras <-  train_y %>% 
  as.matrix() %>% 
  to_categorical()

validation_y_keras <-validation_y %>% 
  as.matrix() %>% 
  to_categorical()

2.5 Model Building

Tahapan pembuatan model neural network/deep learning di Keras:

  1. Define Architecture
  2. Compile with training parameters
  3. Fitting (training) Model
  4. Evaluate
  5. Predict

2.5.1 Define Architecture

Tahapan yang pertama kali kita lakukan adalah membangun arsitektur dari model deep learning kita. Berikut, ada beberapa ketentuan dalam membantu arsitektur model deep learning dengan menggunakan Keras.

Step 1 Selalu diawali keras_model_sequential()

# keras initialization
model <- keras_model_sequential()

Note: - keras_model_sequential() adalah inisialisasi awal pembuatan model - Ketika ingin mengubah sedikit saja parameter dari model, maka harus di run ulang dari keras_model_sequential()

Step 2 Membangun setiap layer (Input, Hidden & Output)

Dalam membangun setiap layer yang ada, Keras akan mengimplementasikan sistem layer by layer. Dimana, layer pertama yang dibuat akan menjadi hidden layer pertama dan layer terakhir yang dibuat akan menjadi output layer.

Fungsi yang dapat kita gunakan dalam membangun setiap layer adalah layer_dense(), pada fungsi tersebut nantinya akan kita isi dengan beberapa parameter berikut ini.

  • input_shape: mendefinisikan jumlah input nodes.
    • Parameter ini hanya digunakan pada fungsi layer_dense() yang paling pertama.
  • units: mendefinisikan jumlah nodes dalam layer tersebut.
  • activation: mendefinisikan Activation Function yang digunakan.
    • linear: Activation ini digunakan untuk kasus regregsi (range: -inf ~ inf)
    • logistic: Activation ini digunakan untuk kasus klasifikasi biner (range: 0 ~ 1)
    • softmax: Activation ini digunakan untuk kasus klasifikasi multiclass (range 0 ~ 1)
    • reLu: Activation ini cocok di hidden layer untuk kasus data image (range: 0 ~ inf)
    • tanh:Activation ini cocok di hidden layer bila nilai prediktor banyak yang negatif (range: -1 ~ 1)
  • name: untuk penamaan layer (demi kemudahan visualisasi/summary).
# Untuk mengunci random
tensorflow::tf$random$set_seed(123)
 
# Please type your answer
model %>% 
  layer_dense(input_shape = 784,
              units = 128,
              activation = "relu",
              name = "Hidden_1") %>% 
  layer_dense(units = 64,
              activation = "relu",
              name = "Hidden_2") %>% 
  layer_dense(units = 32,
              activation = "relu",
              name = "Hidden_3") %>% 
  layer_dense(units = 10,
              activation = "softmax",
              name = "Output")

# Untuk melihat hasil arsitektur yang sudah dibuat
summary(model)
#> Model: "sequential_1"
#> ________________________________________________________________________________
#>  Layer (type)                       Output Shape                    Param #     
#> ================================================================================
#>  Hidden_1 (Dense)                   (None, 128)                     100480      
#>  Hidden_2 (Dense)                   (None, 64)                      8256        
#>  Hidden_3 (Dense)                   (None, 32)                      2080        
#>  Output (Dense)                     (None, 10)                      330         
#> ================================================================================
#> Total params: 111,146
#> Trainable params: 111,146
#> Non-trainable params: 0
#> ________________________________________________________________________________

Keterangan:

  • Total params = Jumlah bobot yang dimiliki oleh model
  • Trainable param = parameter/koneksi yang bobotnya bisa berubah sesuai dengan proses training
  • Non-trainable param = nilai bobot/parameter tidak berubah atau dikunci nilainya

2.5.2 Compile a Model

Pada tahapan membangun arsitektur, kita belum memberikan perhitungan error yang akan digunakan. Pada tahapan inilah kita akan memberikan cara perhitungan error, dengan menggunakan fungsi compile(). Jenis perhitungan error dapat kita definisikan dengan menggunakan parameter loss =. Pada parameter tersebut dapat kita isi dengan 3 value, seperti di bawah ini.

  • klasifikasi multiclass (3 target variable): categorical_crossentropy
  • klasifikasi biner (2 target variable): binary_crossentropy.
  • regresi: mean_square_error

Akan tetapi, setelah kita mendapatkan hasil dari perhitungan error yang terjadi, model kita perlu untuk melakukan back propagation untuk memperbaiki bobo agar errornya berkurang. Untuk melakukan hal tersebut kita juga dapat menambahkan parameter optimizer =. Pada parameter optimizer terdapat 2 fungsi yang dapat kita isi, yaitu optimizer_sgd() dan optimizer_adam().

  • Optimizer SGD/optimizer_sgd(): Merupakan pembaharuaan dari cara perhitungan Gradient Decent yang sudah di-implementasikan pada fungsi neuralnet().
  • Optimizer Adam/optimizer_adam(): Merupakan pembaharuaan dari metode Optimizer SGD.
    • Para setiap fungsi optimzer dapat kita tambahkan dengan sebuah parameter baru, yaitu learning_rate = untuk mengatur seberapa cepat penurunan bobotnya.

Parameter terakhir yang dapat kita tambahkan juga ke fungsi compile() adalah parameter metric =. Parameter tersebut dapat kita isi denagn accuracy untuk melihat nilai accuracy-nya yang dihasilkan.

# Please type your answer
model %>% 
  compile(loss = "categorical_crossentropy",
          optimizer = optimizer_adam(learning_rate = 0.001),
          metrics = "accuracy")

2.5.3 Fit (Training Model)

Setelah arsitektur dari model Deep Learning kita sudah siap, kita akan memulai tahapan trainingnya. Untuk melakukan training model, kita akan memnggunakan fungsi fit(). Pada fungsi tersebut dapat kita isi dengan beberapa parameter berikut ini,

  • x: Prediktor
  • y: Target variabel
  • epochs: Jumlah epoch atau jumlah iterasi yang dilakukan untuk model belajar
    • Intuisinya sama dengan jumlah step yang dihasilkan dari fungsi neuralnet()
  • validation_data: Data testing/unseen data untuk dilihat metrik evaluasinya
  • verbose: Apakah ingin menampilkan visualisasi selama training berlangsung
  • batch_size: Model akan mengupdate bobot setiap n data

Apa itu batch size?

Terminologi pada neuralnet yang kita pelajari sebelumnya & keras agak berbeda. Pada keras, model yang dibuat hanya 1 buah, namun proses trainingnya dapat dilakukan beberapa kali. Pada tiap proses training, dilakukan pembagian data ke beberapa batch melalui random sampling. Model akan ditraining menggunakan data pada batch 1 terlebih dahulu, kemudian batch selanjutnya, hingga digunakan seluruh data (1 epoch).

Model dapat ditraining lebih dari 1 epoch (berarti melakukan pembagian batch dengan random sampling yang berbeda dari sebelumnya). Pada tiap epoch, error dan accuracy (metrics) model dapat di track. Bila ditinjau, semakin bertambah epoch, model akan memiliki error semakin kecil dan accuracy yang semakin tinggi.

knitr::include_graphics("assets/batch.png")

Hal yang perlu di HIGHLIGHT:

  • Semakin banyak batch maka proses training semakin lama (karena proses optimasi model semakin banyak)
  • Semakin banyak epoch maka error bisa lebih kecil, namun proses training semakin lama dan rentan overfitting.
    • dapat ditentukan epoch yang tidak terlalu tinggi dahulu
    • perubahan error tiap epoch dapat dilihat melalui plot
# JANGAN RUN 2X! Karena akan melanjutkan proses training. Bila ingin run lagi, ulang dari `keras_model_sequential()`

history <- model %>% 
           fit(x = train_x_keras,
               y = train_y_keras,
               epoch = 10,
               validation_data = list(validation_x_keras, validation_y_keras),
               verbose = 1,
               batch_size = 100
               )

Batch(1): 100 Batch(2): 100 Batch(3): 100 Batch(4): 100 Batch(5): 100 Batch(336): 100 Total = 336 * 100

Plotting Model:

plot(history)

2.5.4 Predict

Melakukan prediksi pada data test test_x_keras dengan menggunakan fungsi predict()

# Please type your answer
predict_class <- predict(model, validation_x_keras) %>% 
  k_argmax() %>% 
  as.array() %>% 
  as.factor()
# melihat hasil prediksi
predict_class %>% 
  head()
#> [1] 1 3 9 3 7 6
#> Levels: 0 1 2 3 4 5 6 7 8 9

2.5.5 Evaluation

Evaluasi kebaikan model dalam memprediksi menggunakan confusionMatrix():

# Please type your answer
caret::confusionMatrix(data  = predict_class, reference = as.factor(mnist_validation$label))
#> Confusion Matrix and Statistics
#> 
#>           Reference
#> Prediction   0   1   2   3   4   5   6   7   8   9
#>          0 814   0   7   3   0   2   1   0   4   1
#>          1   0 913   0   0   2   1   2   0   2   0
#>          2   1   4 829   1   4   0   1   5   6   2
#>          3   0   0   0 844   0   8   0   1   7   7
#>          4   0   0   2   0 767   0   1   2   2   6
#>          5   2   0   1   8   1 735   4   2   9   3
#>          6   4   0   1   1   6   4 803   0   5   0
#>          7   2   1   6   7   7   1   0 884   4  14
#>          8   3   3   1   4   0   1   1   0 752   1
#>          9   2   1   2   4  26   2   0   6  11 815
#> 
#> Overall Statistics
#>                                                
#>                Accuracy : 0.9707               
#>                  95% CI : (0.9669, 0.9742)     
#>     No Information Rate : 0.1097               
#>     P-Value [Acc > NIR] : < 0.00000000000000022
#>                                                
#>                   Kappa : 0.9675               
#>                                                
#>  Mcnemar's Test P-Value : NA                   
#> 
#> Statistics by Class:
#> 
#>                      Class: 0 Class: 1 Class: 2 Class: 3 Class: 4 Class: 5
#> Sensitivity           0.98309   0.9902  0.97644   0.9679  0.94342  0.97480
#> Specificity           0.99762   0.9991  0.99682   0.9969  0.99829  0.99608
#> Pos Pred Value        0.97837   0.9924  0.97186   0.9735  0.98333  0.96078
#> Neg Pred Value        0.99815   0.9988  0.99735   0.9963  0.99396  0.99751
#> Prevalence            0.09855   0.1097  0.10105   0.1038  0.09676  0.08974
#> Detection Rate        0.09688   0.1087  0.09867   0.1005  0.09129  0.08748
#> Detection Prevalence  0.09902   0.1095  0.10152   0.1032  0.09284  0.09105
#> Balanced Accuracy     0.99036   0.9947  0.98663   0.9824  0.97085  0.98544
#>                      Class: 6 Class: 7 Class: 8 Class: 9
#> Sensitivity           0.98770   0.9822  0.93766   0.9600
#> Specificity           0.99723   0.9944  0.99816   0.9929
#> Pos Pred Value        0.97451   0.9546  0.98172   0.9379
#> Neg Pred Value        0.99868   0.9979  0.99345   0.9955
#> Prevalence            0.09676   0.1071  0.09545   0.1010
#> Detection Rate        0.09557   0.1052  0.08950   0.0970
#> Detection Prevalence  0.09807   0.1102  0.09117   0.1034
#> Balanced Accuracy     0.99247   0.9883  0.96791   0.9764

NOTE: Bila hasil metrics masih ingin ditingkatkan, kita dapat melakukan model tuning kembali pada pembuatan model nn maupun tahap pre-processing lainnya.

2.5.6 Save & Load Model

Dikarenakan dalam proses training model memerlukan waktu yang tidak sebentar, kita akan menyimpan terlebih dahulu model yang sudah semmpat kita buat dengan menggunakan fungsi save_model_tf()

# save
model %>% 
  save_model_tf(filepath = "model_nn_rhea")

Untuk memanggil hasil model yang disimpan dengan menggunakan fungsi save_model_tf(), kita dapat menggunakan funsi load_model_tf()

model_loaded <- load_model_tf(filepath = "model_nn_rhea")
summary(model_loaded)
#> Model: "sequential_2"
#> ________________________________________________________________________________
#>  Layer (type)                       Output Shape                    Param #     
#> ================================================================================
#>  Hidden_1 (Dense)                   (None, 128)                     100480      
#>  Hidden_2 (Dense)                   (None, 64)                      8256        
#>  Hidden_3 (Dense)                   (None, 32)                      2080        
#>  Output (Dense)                     (None, 10)                      330         
#> ================================================================================
#> Total params: 111,146
#> Trainable params: 111,146
#> Non-trainable params: 0
#> ________________________________________________________________________________

3 Dive Deeper

Buat model deep learning baru menggunakan data train yang sama dan simpan ke dalam objek model_nama-anda.

  • Buat arsitektur arsitektur model dengan:
    • input layer: jumlah node: ncol(train_x_keras) = 784
    • hidden layer 1: jumlah node …; activation function ReLu
    • hidden layer 2: jumlah node …; activation function ReLu
    • hidden layer 3: jumlah node …; activation function ReLu
    • output layer: jumlah node 10; activation function softmax
  • Compile dengan training parameter:
    • loss (cost function) : …
    • optimizer …; learning rate = …
    • metric = accuracy
  • Fitting:
    • Gunakan epoch & batch size sesuai dengan keinginan anda.

Step 1: Build architecture

Step 2: Compile model

Step 3: Fit (training) model

Step 4: Plot model

Step 5: Lakukan prediksi ke data test:

Step 6: Evaluasi

4 Summary

Data Pre-processing untuk Keras

  • Data dalam bentuk data.frame diubah terlebih dahulu menjadi array karena keras menerima input dalam bentuk array. Tahapan: data.frame -> matrix -> array.
  • Pengubahan data dalam bentuk matrix ke array:
    • data prediktor diubah menggunakan fungsi array_reshape()
    • data target (label) diubah menggunakan fungsi to_categorical()

Tahapan Model Building di Keras:

  1. Define Model Architecture:
  • layers & node
  • activation function
  1. Compile a Model:
  • optimizer (metode back propagation)
  • cost/loss function
  • metrics
  1. Fit (train model):
  • batch size
  • epochs
  1. Predict
  2. Evaluate

Panduan Model Building & Tuning:

  • Nodes pada input layer: sejumlah prediktor yang kita miliki.
  • Nodes pada output layer:
    • regresi = 1
    • klasifikasi = sejumlah kelas variable target
  • Hidden layer:
    • jumlah hidden layer (how deep):
      • model lebih kompleks
      • waktu training lebih lama
      • percobaan pertama bisa buat 2 layer dulu
    • jumlah node pada hidden layer (how wide):
      • terlalu banyak bisa menghasilkan overfitting.
      • bisa pakai angka kelipatan 2^n (2,4,8,16,32,64,128, dst..), kisaran 2/3 jumlah prediktor
      • jumlah mengerucut semakin ke output layer
  • Cost function:
    • regresi: sum of squared error (sse)
    • klasifikasi: cross-entropy (ce)
  • Activation function:
    • output layer:
      • linear: regresi
      • logistic: klasifikasi biner
      • softmax: klasifikasi multiclass
    • hidden layer:
      • ReLu: data image
      • tanh: data yang nilai prediktornya banyak yang negatif
  • Metode back propagation (optimizer):
    • gradient descent (gunakan optimizer_sgd() di Keras)
    • adam (gunakan optimizer_adam())
  • Batch yang banyak (batch_size = kecil) mencegah komputasi terlalu besar dalam 1 waktu
  • Epoch yang semakin banyak maka:
    • proses training semakin lama
    • error bisa lebih kecil
    • terlalu banyak akan rentan overfitting
    • dapat ditentukan epoch yang tidak terlalu tinggi dahulu: 10 atau 15
    • perubahan error tiap epoch dapat dilihat melalui plot; di cut pada perubahan accuracy (metrics) yang sudah landai.