1 Introduction

Klasifikasi sebuah gambar/foto/objek visual lainnya menjadi hal penting dalam era modern saat ini. Peneliti, pekerja sering kali perlu mengklasifikasikan sebuah objek visual yang banyak dalam waktu yang singkat. Pekerjaan berulang ini dapat diotomasikan dengan membuat model, kemudian memprediksi gambar berdasarkan model yang sudah dibuat.

Pada kesempatan kali ini, akan dibuat sebuah prediksi klasifikasi dari sebuah gambar angka ke dalam 10 klasifikasi yakni angka 0 ~ 9.

2 Import Library & Setup

#Import library machine learning
library(tensorflow)
library(keras)

#Import library data wrangling
library(tidyverse)

#Import library untuk processing gambar
library(imager)


#Import library evaluasi (confusionmatrix)
library(caret)

#Import library untuk menghubungkan python & r
library(reticulate)

#Pemilihan virtual environment anaconda
use_condaenv("r_tf_image")

options(scipen = 999)

3 Data Wrangling & Exploratory Data Analysis (EDA)

Pertama - tama kita perlu panggil data train dan cek klasifikasinya berdasarkan folder yang telah dibuat. Dalam hal ini terdapat 10 klasifikasi

Kemudian kita simpan pada objek folder_list Tujuan akhir dari penyimpanan ini adalah untuk memanggil file image berdasarkan path dari masing-masing file tersebut

#Simpan klasifikasi folder ke object "folder_list"
folder_list_train <- list.files("archive (4)/trainingSet/trainingSet/")
folder_list_train
##  [1] "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"

Selanjutnya, klasifikasi folder tersebut akan kita tempelkan pada path tempat folder tersebut berada kita berada. Simpan pada objek folder_path

folder_path <- paste0("archive (4)/trainingSet/trainingSet/", folder_list_train, "/")
head(folder_path)
## [1] "archive (4)/trainingSet/trainingSet/0/"
## [2] "archive (4)/trainingSet/trainingSet/1/"
## [3] "archive (4)/trainingSet/trainingSet/2/"
## [4] "archive (4)/trainingSet/trainingSet/3/"
## [5] "archive (4)/trainingSet/trainingSet/4/"
## [6] "archive (4)/trainingSet/trainingSet/5/"

Untuk memanggil semua file image dari masing - masing folder yang telah kita klasifikasikan, akan kita panggil menggunakan function dibawah ini menggunakan function map

#Panggil & tempel semua file image menggunakan map
file_name <- map(folder_path, 
                 function(x) paste0(x, list.files(x))
                 ) %>% 
  unlist()

#Cek data teratas
head(file_name)
## [1] "archive (4)/trainingSet/trainingSet/0/img_1.jpg"    
## [2] "archive (4)/trainingSet/trainingSet/0/img_10007.jpg"
## [3] "archive (4)/trainingSet/trainingSet/0/img_10010.jpg"
## [4] "archive (4)/trainingSet/trainingSet/0/img_10017.jpg"
## [5] "archive (4)/trainingSet/trainingSet/0/img_10032.jpg"
## [6] "archive (4)/trainingSet/trainingSet/0/img_10039.jpg"
#Cek data terbawah
tail(file_name)
## [1] "archive (4)/trainingSet/trainingSet/9/img_9921.jpg"
## [2] "archive (4)/trainingSet/trainingSet/9/img_9943.jpg"
## [3] "archive (4)/trainingSet/trainingSet/9/img_9953.jpg"
## [4] "archive (4)/trainingSet/trainingSet/9/img_9961.jpg"
## [5] "archive (4)/trainingSet/trainingSet/9/img_997.jpg" 
## [6] "archive (4)/trainingSet/trainingSet/9/img_9999.jpg"
#Cek total data
length(file_name)
## [1] 42000

Selanjutnya, kita akan melihat dimensi dari sample data kita Kita akan menggunakan function load.image untuk membaca file image

Pertama - tama akan kita cek apakah file image berhasil dipanggil

#Tentukan kerandoman data
set.seed(100)

sample_image <- sample(file_name,6)

img <- map(sample_image, load.image)

par(mfrow= c(2,3)) #create 2 x 3 image grid
map(img, plot)

## [[1]]
## Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1 
## 
## [[2]]
## Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1 
## 
## [[3]]
## Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1 
## 
## [[4]]
## Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1 
## 
## [[5]]
## Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1 
## 
## [[6]]
## Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1

Setelah file berhasil kita panggil, maka kita akan membuat dataframe yang berisikan dimensi dari masing - masing gambar tersebut.

get_dim <- function(x){
  img <- load.image(x) 
  
  df_img <- data.frame(height = height(img),
                       width = width(img),
                       filename = x
                       )
  
  return(df_img)
}

get_dim(file_name[1])

Untuk memastikan range dimensi dari data kita, menggunakan fungsi yang sama seperti sebelumnya, kita akan mengambil range/distribusi dimensi dari data gambar kita, untuk selanjutnya dibuat menjadi dataframe.

set.seed(123)
sample_file <- sample(file_name, 500)

# Run the get_dim() function for each image
file_dim <- map_df(sample_file, get_dim)

head(file_dim, 10)

Berdasarkan range dimensi, terlihat terdapat range dimensi dari 500 file yakni semuanya 28 x 28, maka akan kita gunakan dimensi 28 x 28 pixel

#Cek range
summary(file_dim)
##      height       width      filename        
##  Min.   :28   Min.   :28   Length:500        
##  1st Qu.:28   1st Qu.:28   Class :character  
##  Median :28   Median :28   Mode  :character  
##  Mean   :28   Mean   :28                     
##  3rd Qu.:28   3rd Qu.:28                     
##  Max.   :28   Max.   :28

Mari kita define pixel dalam objek target_size kemudian dalam hal ini kita tentukan batch sizenya bernilai batch_size = 30

target_size <- c(28, 28)

# Batch size for training the model
batch_size <- 30

4 Data Pre-Processing

Karena data yang kita miliki terbatas, kita akan menciptakan data buatan menggunakan metode Image Agumentation. Image augmentation adalah teknik untuk mendapatkan data image tambahan dengan menerapakan modifikasi seperti melakukan scaling, membolak-balik gambar, memutar gambar, melakukan zoom, crop, dan lain-lain. Dengan cara ini, model akan mempelajari data tambahan sehingga model memiliki akurasi yang lebih baik.

Untuk melakukan image augmentation, kita akan melakukan fit data pada sebuah generator image menggunakan function image_data_generator. Karena data yang kita miliki sudah sebanyak 42000 dengan data gambar yang sudah dilakukan berputar-putar, zoom in & zoom out, maka modifikasi yang akan dilakukan yakni sebagai berikut:

  • Scaling data sehingga memiliki range 0~1
  • Splitting data 80:20 menjadi data train & data validation
# Image Generator
train_data_gen <- image_data_generator(rescale = 1/255, # Scaling pixel value
                                       validation_split = 0.2 # 20% data as validation data
                                       )

Sekarang kita dapat memasukkan data generator menggunakan function fow_images_from_directory(). Data generator akan diterapkan pada data training train_image_array_gen dan juga data validationakan val_image_array_gen.

Terlihat pada output, bahwa data sudah dibagi sesuai splitting dati data generator Found 33604 images belonging to 10 classes. Found 8396 images belonging to 10 classes.

# Training Dataset
train_image_array_gen <- flow_images_from_directory(directory = "archive (4)/trainingSet/trainingSet/", # Folder of the data
                                                    target_size = target_size, # target of the image dimension (28 x 28)  
                                                    color_mode = "rgb", # use grayscale color
                                                    batch_size = batch_size , 
                                                    seed = 123,  # set random seed
                                                    subset = "training", # declare that this is for training data
                                                    generator = train_data_gen
                                                    )

# Validation Dataset
val_image_array_gen <- flow_images_from_directory(directory = "archive (4)/trainingSet/trainingSet/",
                                                  target_size = target_size, 
                                                  color_mode = "rgb", 
                                                  batch_size = batch_size ,
                                                  seed = 123,
                                                  subset = "validation", # declare that this is the validation data
                                                  generator = train_data_gen
                                                  )

4.1 Explore label

Setelah membagi data train dan validation, selanjutnya kita akan memeriksa proporsi target variable yang mana adalah klasifikasi angka 0 ~ 9

Pada output terlilhat bahwa data train memiliki label yang masih proporsional, dimana nilainya masih berkisar pada nilai 10% dengan detil sebagai berikut

Angka 0 memiliki proporsi 9.8% Angka 1 memiliki proporsi 11.1% Angka 2 memiliki proporsi 9.9% Angka 3 memiliki proporsi 10.3% Angka 4 memiliki proporsi 9.7% Angka 5 memiliki proporsi 9% Angka 6 memiliki proporsi 9.8% Angka 7 memiliki proporsi 10.4% Angka 8 memiliki proporsi 9.7% Angka 9 memiliki proporsi 9.9%

# Number of training samples
train_samples <- train_image_array_gen$n

# Number of validation samples
valid_samples <- val_image_array_gen$n

# Number of target classes/categories
output_n <- n_distinct(train_image_array_gen$classes)

# Get the class proportion
table("\nFrequency" = factor(train_image_array_gen$classes)
      ) %>% 
  prop.table()
## 
## Frequency
##          0          1          2          3          4          5          6 
## 0.09838115 0.11153434 0.09945245 0.10358886 0.09695274 0.09034639 0.09850018 
##          7          8          9 
## 0.10477919 0.09674444 0.09972027

5 Model Fitting

5.1 Model Architecture

Pertama - tama, kita akan menciptakan 1 model simpel dengan layer - layer sebagai berikut:

  1. Convolutional layer untuk mengekstrak gambar (image features) menjadi 2d image dengan relu activation function, matring kernel 3 x 3, & 16 filters
  2. Max Pooling layer untuk downsample image features dengan matrix pooling 2x2
  3. Flatten layer untuk mengubah data dari 2D menjadi 1D
  4. Dense layer untuk mengambil informasi dari flatten layer
  5. Dense layer sebagai output dengan softmax activation function dan unit sejumlah jenis klasifikasi target
# Set Initial Random Weight
tensorflow::tf$random$set_seed(123)

model <- keras_model_sequential() %>% 
  
  # Convolution Layer
  layer_conv_2d(filters = 16,
                kernel_size = c(3,3),
                padding = "same",
                activation = "relu",
                input_shape = c(target_size, 3) 
                ) %>% 

  # Max Pooling Layer
  layer_max_pooling_2d(pool_size = c(2,2)) %>% 
  
  # Flattening Layer
  layer_flatten() %>% 
  
  # Dense Layer
  layer_dense(units = 16,
              activation = "relu") %>% 
  
  # Output Layer
  layer_dense(units = output_n,
              activation = "softmax",
              name = "Output")
  
model
## Model: "sequential"
## ________________________________________________________________________________
##  Layer (type)                       Output Shape                    Param #     
## ================================================================================
##  conv2d (Conv2D)                    (None, 28, 28, 16)              448         
##  max_pooling2d (MaxPooling2D)       (None, 14, 14, 16)              0           
##  flatten (Flatten)                  (None, 3136)                    0           
##  dense (Dense)                      (None, 16)                      50192       
##  Output (Dense)                     (None, 10)                      170         
## ================================================================================
## Total params: 50,810
## Trainable params: 50,810
## Non-trainable params: 0
## ________________________________________________________________________________

5.2 Train Model

Setelah meenyusun model, selanjutnya kita akan melatih model kita dengan data train.

Untuk training akan dipilih loss function categorica_crossentropy sesuai dengan target variable yang berbentuk kategorikal. Kemudian untuk parameter optimizer akan digunakan optimizer adam dengan learning rate sebesar 0.01.

Kita akan melakukan repetisi/epoch sebanyak 30. Kita juga akan melakukan evaluasi terhadap validation data

model %>% 
  compile(
    loss = "categorical_crossentropy",
    optimizer = optimizer_adam(lr = 0.01),
    metrics = "accuracy"
  )

# Fit data into model
history <- model %>% 
  fit(
  # training data
  train_image_array_gen,

  # training epochs
  steps_per_epoch = as.integer(train_samples / batch_size), 
  epochs = 10, 
  
  # validation data
  validation_data = val_image_array_gen,
  validation_steps = as.integer(valid_samples / batch_size)
)

plot(history)

5.3 Model Evaluation

5.3.1 Graph Value

Kita akan melakukan evaluasi berdasarkan hasil training terlebih dahulu sesuai dengan plot yang telah dibentuk

Terlihat pada hasil dibawah, model menghasilkan accuracy sebesar 98% untuk data train, dan juga accuracy sebesar 96% untuk data validation. Hal ini dapat dikatakan sangat baik, dimana nilai accuracy sudah memiliki nilai diatas 95%, selain itu untuk train & unseen data memiliki perbedaan yang tidak melebihi 10%

#Nilai loss train data
history$metrics$loss[10]
## [1] 0.0603594
#Nilai accuracy train data
history$metrics$accuracy[10]
## [1] 0.9817418
#Nilai loss unseen data
history$metrics$val_loss[10]
## [1] 0.1682155
#Nilai accuracy unseen data
history$metrics$val_accuracy[10]
## [1] 0.9657109

5.3.2 Confussion matrix

Kita akan melakukan evaluasi menggunakan confusion matrix menggunakan data validation atau unseen data.

Untuk menghasilkan evaluasi berdasarkan nama kategori, kita akan mengekstrak categorical label terlebih dahulu

#Ekstrak categorical label
val_data <- data.frame(file_name = paste0("trainingSet/trainingSet/", val_image_array_gen$filenames)) %>% 
  mutate(class = str_extract(file_name, "0|1|2|3|4|5|6|7|8|9"))

#Cek hasil
head(val_data, 30)

Untuk melakukan prediksi, sebuah gambar harus bisa dibaca terlebih dahulu baru kemudian dapat diprediksi, karena itu perlu dilakukan pengubahan dari image menjadi data array dengan function image_to_array. Berikut adalah function untuk mengubah data yang akan diprediksi menjadi array

# Function to convert image to array
image_prep <- function(x) {
  arrays <- lapply(x, function(path) {
    img <- image_load(path, target_size = target_size, 
                      grayscale = F # Set FALSE if image is RGB
                      )
    
    x <- image_to_array(img)
    x <- array_reshape(x, c(1, dim(x)))
    x <- x/255 # rescale image pixel
  })
  do.call(abind::abind, c(arrays, list(along = 1)))
}
val_data_name <- data.frame(file_name = paste0("archive (4)/trainingSet/trainingSet/", val_image_array_gen$filenames)) %>% 
  mutate(class = str_extract(file_name, "0|1|2|3|4|5|6|7|8|9"))

head(val_data_name,20)

Setelah membuat function konversi image to array, selanjutnya akan kita simpan pada objek test_x Selain itu kita akan melakukan pemeriksaan pada dimensi dari objek test_x

test_x <- image_prep(val_data_name$file_name)

# Check dimension of testing data set
dim(test_x)
## [1] 8396   28   28    3

Selanjutnya kita akan melakukan prediksi menggunakan model model terhadap data test_x yang mana adalah data validation

#Prediksi data
pred_test <- predict(model, test_x) %>% k_argmax()

#Cek hasil
head(pred_test, 10)
## tf.Tensor([0 0 0 0 0 0 0 0 0 0], shape=(10), dtype=int64)

Terlihat dari hasil prediksi diatas, label yang dihasilkan masih berupa angka dan belum berupa kategorikal karakter sesuai ekspektasi kita. Kita akan melakukan encoding dahulu, secara default hasil prediksi dibuat sesuai dengan urutan angka 0 -> 9

# Convert encoding to label
decode <- function(x){
  case_when(x == 0 ~ "0",
            x == 1 ~ "1",
            x == 2 ~ "2",
            x == 3 ~ "3",
            x == 4 ~ "4",
            x == 5 ~ "5",
            x == 6 ~ "6",
            x == 7 ~ "7",
            x == 8 ~ "8",
            x == 9 ~ "9",
            )
}

pred_test <- sapply(pred_test, decode) 

tail(pred_test, 30)
##  [1] "9" "9" "9" "9" "9" "0" "9" "9" "9" "9" "9" "9" "9" "9" "9" "9" "9" "9" "9"
## [20] "9" "9" "9" "9" "9" "3" "9" "9" "9" "9" "9"

Setelah berhasil mengubah data dari angka menjadi kategorik yang bentuknya sesuai ekspektasi, maka selanjutnya kita akan melakukan evaluasi berdasarkan accuracy dari confussion matrix menggunakan function confusionMatrix

confusionMatrix(as.factor(pred_test), 
                as.factor(val_data$class)
                )
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1   2   3   4   5   6   7   8   9
##          0 805   0   2   0   0   1   4   1   4   4
##          1   1 921   2   1   2   0   2   1   1   0
##          2   3   3 798   4   3   0   3   7   3   0
##          3   0   1  14 853   0  14   1  11  10   2
##          4   0   2   5   0 775   0   1   0   2   4
##          5   3   1   0   7   0 734   7   1  11  10
##          6   7   1   1   0   3   6 808   0   5   0
##          7   0   4   4   1   4   0   0 838   0   2
##          8   6   3   7   1   8   3   1   9 769   8
##          9   1   0   2   3  19   1   0  12   7 807
## 
## Overall Statistics
##                                                
##                Accuracy : 0.9657               
##                  95% CI : (0.9616, 0.9695)     
##     No Information Rate : 0.1115               
##     P-Value [Acc > NIR] : < 0.00000000000000022
##                                                
##                   Kappa : 0.9619               
##                                                
##  Mcnemar's Test P-Value : NA                   
## 
## Statistics by Class:
## 
##                      Class: 0 Class: 1 Class: 2 Class: 3 Class: 4 Class: 5
## Sensitivity           0.97458   0.9840  0.95569   0.9805  0.95209  0.96706
## Specificity           0.99789   0.9987  0.99656   0.9930  0.99815  0.99476
## Pos Pred Value        0.98051   0.9893  0.96845   0.9415  0.98226  0.94832
## Neg Pred Value        0.99723   0.9980  0.99511   0.9977  0.99487  0.99672
## Prevalence            0.09838   0.1115  0.09945   0.1036  0.09695  0.09040
## Detection Rate        0.09588   0.1097  0.09505   0.1016  0.09231  0.08742
## Detection Prevalence  0.09778   0.1109  0.09814   0.1079  0.09397  0.09219
## Balanced Accuracy     0.98623   0.9913  0.97612   0.9867  0.97512  0.98091
##                      Class: 6 Class: 7 Class: 8 Class: 9
## Sensitivity           0.97703  0.95227  0.94704  0.96416
## Specificity           0.99696  0.99800  0.99393  0.99405
## Pos Pred Value        0.97232  0.98242  0.94356  0.94718
## Neg Pred Value        0.99749  0.99443  0.99433  0.99602
## Prevalence            0.09850  0.10481  0.09671  0.09969
## Detection Rate        0.09624  0.09981  0.09159  0.09612
## Detection Prevalence  0.09898  0.10160  0.09707  0.10148
## Balanced Accuracy     0.98699  0.97514  0.97049  0.97910

Dapat dilihat, bahwa accuracy yang diperoleh mempunyai nilai sebesar 96%

6 Model Tuning

Meskipun nilai accuracy kita untuk unseen data (validation data) sudah memiliki accuracy diatas 95%, namun berdasarkan model sebelumnya dapat kita perhatikan bahwa model melakukan prediksi pada saat dimensi masih bernilai 14x14 dimana ini berarti masih banyak informasi yang sebetulnya masih dapat diekstrak. Karena itu perubahan yang akan kita lakukan hanya terfokus pada jumlah layer kita akan membiarkan nilai learning rate & nilai epoch

6.1 Model Architecture

Kita akan melakukan tuning model dimana informasi yang diekstrak akan lebih banyak jika dibandingkan dengan model model

Model yang akan kita buat akan disimpan pada objek model_big dengan parameter sebagai berikut

  1. Convolutional layer untuk mengekstrak gambar (image features) menjadi 2d image dengan relu activation function, matring kernel 3 x 3, & 16 filters
  2. Max Pooling layer untuk downsample image features dengan matrix pooling 2x2
  3. Convolutional layer untuk mengekstrak gambar (image features) menjadi 2d image dengan relu activation function, matring kernel 3 x 3, & 32 filters
  4. Max Pooling layer untuk downsample image features dengan matrix pooling 2x2
  5. Flatten layer untuk mengubah data dari 2D menjadi 1D
  6. Dense layer untuk mengambil informasi dari flatten layer dengan unit sebanyak 32 buah
  7. Dense layer sebagai output dengan softmax activation function dan unit sejumlah jenis klasifikasi target
tensorflow::tf$random$set_seed(123)

model_big <- keras_model_sequential() %>% 
  
   # Convolution Layer 1
  layer_conv_2d(filters = 16,
                kernel_size = c(3,3),
                padding = "same",
                activation = "relu",
                input_shape = c(target_size, 3) 
                ) %>% 

  
   # Max Pooling Layer 1
  layer_max_pooling_2d(pool_size = c(2,2),
                       strides = c(2,2)) %>%
    # Convolution Layer 2
  layer_conv_2d(filters = 32,
                kernel_size = c(3,3),
                padding = "same",
                activation = "relu"
                ) %>% 
  
# Max Pooling Layer 2
  layer_max_pooling_2d(pool_size = c(2,2),
                       strides = c(2,2)) %>%

  # Flattening Layer
  layer_flatten() %>% 
  
  # Dense Layer
  layer_dense(units = 32,
              activation = "relu") %>%
  
  # Output Layer
  layer_dense(units = output_n,
              activation = "softmax",
              name = "Output")

model_big
## Model: "sequential_1"
## ________________________________________________________________________________
##  Layer (type)                       Output Shape                    Param #     
## ================================================================================
##  conv2d_2 (Conv2D)                  (None, 28, 28, 16)              448         
##  max_pooling2d_2 (MaxPooling2D)     (None, 14, 14, 16)              0           
##  conv2d_1 (Conv2D)                  (None, 14, 14, 32)              4640        
##  max_pooling2d_1 (MaxPooling2D)     (None, 7, 7, 32)                0           
##  flatten_1 (Flatten)                (None, 1568)                    0           
##  dense_1 (Dense)                    (None, 32)                      50208       
##  Output (Dense)                     (None, 10)                      330         
## ================================================================================
## Total params: 55,626
## Trainable params: 55,626
## Non-trainable params: 0
## ________________________________________________________________________________

6.2 Train Model

Setelah meenyusun model model_big, selanjutnya kita akan melatih model kita dengan data train.

Untuk training akan dipilih loss function categorica_crossentropy sesuai dengan target variable yang berbentuk kategorikal. Kemudian untuk parameter optimizer akan digunakan optimizer adam dengan learning rate sebesar 0.01 sama seperti sebelumnya.

Kita akan melakukan repetisi/epoch sebanyak 10. Kita juga akan melakukan evaluasi terhadap validation data

model_big %>% 
  compile(
    loss = "categorical_crossentropy",
    optimizer = optimizer_adam(lr = 0.01),
    metrics = "accuracy"
  )

history_big <- model_big %>% 
  fit_generator(
  # training data
  train_image_array_gen,
  
  # epochs
  steps_per_epoch = as.integer(train_samples / batch_size), 
  epochs = 10, 
  
  # validation data
  validation_data = val_image_array_gen,
  validation_steps = as.integer(valid_samples / batch_size),
  
  # print progress but don't create graphic
  verbose = 1,
  view_metrics = 0
)

plot(history_big)

6.3 Model Evaluation

6.3.1 Confusion matrix

Kita akan melakukan prediksi terhadap validation data

pred_test_big <- predict(model_big, test_x) %>% k_argmax()


head(pred_test_big, 10)
## tf.Tensor([0 0 0 0 0 0 0 0 0 0], shape=(10), dtype=int64)

Sama seperti sebelumnya, kita akan melakukan encoding terhadap hasil prediksi dimana target variable yang diprediksi masih berupa angka, belum character

pred_test_big <- sapply(pred_test_big, decode) 

head(pred_test_big, 10)
##  [1] "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"

Setelah berhasil mengubah data dari angka menjadi kategorik yang bentuknya sesuai ekspektasi, maka selanjutnya kita akan melakukan evaluasi berdasarkan accuracy dari confussion matrix menggunakan function confusionMatrix

confusionMatrix(as.factor(pred_test_big), 
                as.factor(val_data$class)
                )
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1   2   3   4   5   6   7   8   9
##          0 818   0   5   0   0   1   4   3   2   4
##          1   0 907   1   0   1   0   2   2   0   0
##          2   0   4 810   8   0   1   0   6   2   0
##          3   0   0   1 827   0   7   0   2   0   2
##          4   0   3   2   0 790   0   0   1   1  14
##          5   0   0   0  23   1 734   0   0   3   4
##          6   6   2   2   1   3  13 820   0   2   0
##          7   0   7   3   0   2   0   0 848   2   1
##          8   2  10  10   6   6   2   1   7 798  11
##          9   0   3   1   5  11   1   0  11   2 801
## 
## Overall Statistics
##                                                
##                Accuracy : 0.9711               
##                  95% CI : (0.9672, 0.9745)     
##     No Information Rate : 0.1115               
##     P-Value [Acc > NIR] : < 0.00000000000000022
##                                                
##                   Kappa : 0.9678               
##                                                
##  Mcnemar's Test P-Value : NA                   
## 
## Statistics by Class:
## 
##                      Class: 0 Class: 1 Class: 2 Class: 3 Class: 4 Class: 5
## Sensitivity           0.99031   0.9690  0.97006  0.95057  0.97052  0.96706
## Specificity           0.99749   0.9992  0.99722  0.99841  0.99723  0.99594
## Pos Pred Value        0.97730   0.9934  0.97473  0.98570  0.97411  0.95948
## Neg Pred Value        0.99894   0.9961  0.99670  0.99431  0.99684  0.99672
## Prevalence            0.09838   0.1115  0.09945  0.10362  0.09695  0.09040
## Detection Rate        0.09743   0.1080  0.09647  0.09850  0.09409  0.08742
## Detection Prevalence  0.09969   0.1087  0.09898  0.09993  0.09659  0.09111
## Balanced Accuracy     0.99390   0.9841  0.98364  0.97449  0.98387  0.98150
##                      Class: 6 Class: 7 Class: 8 Class: 9
## Sensitivity           0.99154   0.9636  0.98276  0.95699
## Specificity           0.99617   0.9980  0.99275  0.99550
## Pos Pred Value        0.96584   0.9826  0.93552  0.95928
## Neg Pred Value        0.99907   0.9958  0.99814  0.99524
## Prevalence            0.09850   0.1048  0.09671  0.09969
## Detection Rate        0.09767   0.1010  0.09505  0.09540
## Detection Prevalence  0.10112   0.1028  0.10160  0.09945
## Balanced Accuracy     0.99385   0.9808  0.98775  0.97625

Dapat dilihat bahwa setelah dilakukan tuning model, accuracy model mengalami kenaikan untuk unseen data yakni bernilai 97%

7 Conclusion

Berdasarkan hasil pembuatan model, tuning model, dan juga evaluasi hasil prediksi, dapat disimpulkan bahwa model dapat meningkatkan akurasinya dengan mengekstrak informasi lebih banyak dengan menambahkan layer convolution & juga max poolingnya, namun hal ini rentan menimbulkan overfitting model.

Jika dibandingkan dengan model model, model model_big dapat mengklasifikasikan gambar jauh lebih baik dengan mengevaluasi nilai accuracynya dimana terdapat peningkatan accuracy dari 96% menjadi 97%.