1 . Pendahuluan

Project ini dilakukan untuk tugas Learning By Building sebagai pendalaman dan pembelajaran mandiri atas materi Neural Network. Data set yang digunakan adalah images pizza dan ice cream yang diperoleh dari situs Kaggle. Analisis yang dilakukan untuk mencoba klasifikasi kedua kelas gambar tersebut.


2 . Setting Python Environment

Previous on Terminal: * Active the conda environment: r-tensorflow * Install the pillow package: pip install pillow * Install the pillow package: pip install Scipy

The next step is call conda environment:

use_condaenv("r-tensorflow")


3 . Data Preprocess and Explanatory Data Analysis

3.1 Import Data

Seluruh data berada pada folder archive/data_train, diambil nama file dari setiap image-nya.

folder_list <- list.files("archive/dataset/train_valid/")

folder_list
## [1] "icecream" "pizza"

Mengkombinasikan nama folder dengan path/directory dari folder archive/data train dengan tujuan untuk mengakses konten gambar yang ada pada setiap folder.

folder_path <- paste0("archive/dataset/train_valid/", folder_list, "/")

folder_path
## [1] "archive/dataset/train_valid/icecream/"
## [2] "archive/dataset/train_valid/pizza/"

Menggunakan method map() untuk mengiterasi dan mengumpulkan nama file pada setiap folder (pizza, icecream). Map () akan mengembalikan list sehingga kita perlu untuk mengombinasikan list dengan nama filde dari 2 folder yang berbeda dengan menggunakan fungsi unlist().

file_name <- map(folder_path, 
                 function(x) paste0(x, list.files(x))
                 ) %>% 
  unlist()

head(file_name)
## [1] "archive/dataset/train_valid/icecream/000_jpg.rf.918ed1e12516d64db054b957724595d5.jpg"  
## [2] "archive/dataset/train_valid/icecream/0010_jpg.rf.99e844b71704288b2bbb516937a68735.jpg" 
## [3] "archive/dataset/train_valid/icecream/00101_jpg.rf.6c81e823f2c23a7b175638a103161ae3.jpg"
## [4] "archive/dataset/train_valid/icecream/00102_jpg.rf.b2dbb157d1bfc492793781d6d3f16734.jpg"
## [5] "archive/dataset/train_valid/icecream/00103_jpg.rf.a350a7fd51d959bdc8ef5fcad37d6839.jpg"
## [6] "archive/dataset/train_valid/icecream/00104_jpg.rf.2fb3dc95a94210eeb7639c3afd7de28e.jpg"
tail(file_name)
## [1] "archive/dataset/train_valid/pizza/0091_jpg.rf.b1f7a1fd4b3c02dcae526ebabb3a2518.jpg"
## [2] "archive/dataset/train_valid/pizza/0093_jpg.rf.894a2dbd5e6b9e3acfe36a35bd1ed82b.jpg"
## [3] "archive/dataset/train_valid/pizza/0094_jpg.rf.b8a288f62e08a04ff410aced6d4729a2.jpg"
## [4] "archive/dataset/train_valid/pizza/0096_jpg.rf.df9d2f6fa9dad8834387da362bff83d2.jpg"
## [5] "archive/dataset/train_valid/pizza/0097_jpg.rf.5875349832fae2a100963236ba10a737.jpg"
## [6] "archive/dataset/train_valid/pizza/0098_jpg.rf.4103370d19a6dd7fb5783f8ef871bdc9.jpg"

Pemeriksaan jumlah image pada data training

length(file_name)
## [1] 887

Pemeriksaan konten file menggunakan fungsi load.image() dari package imager

# Randomly select image
set.seed(123)
sample_image <- sample(file_name, 6)

# Load image into R
img <- map(sample_image, load.image)

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

## [[1]]
## Image. Width: 626 pix Height: 356 pix Depth: 1 Colour channels: 3 
## 
## [[2]]
## Image. Width: 417 pix Height: 626 pix Depth: 1 Colour channels: 3 
## 
## [[3]]
## Image. Width: 417 pix Height: 626 pix Depth: 1 Colour channels: 3 
## 
## [[4]]
## Image. Width: 626 pix Height: 417 pix Depth: 1 Colour channels: 3 
## 
## [[5]]
## Image. Width: 626 pix Height: 417 pix Depth: 1 Colour channels: 3 
## 
## [[6]]
## Image. Width: 626 pix Height: 458 pix Depth: 1 Colour channels: 3


4 . Check Image Dimension

# Full Image Description
img <- load.image(file_name[1])
img
## Image. Width: 417 pix Height: 626 pix Depth: 1 Colour channels: 3
# Function for acquiring width and height of an image
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])
##   height width
## 1    626   417
##                                                                               filename
## 1 archive/dataset/train_valid/icecream/000_jpg.rf.918ed1e12516d64db054b957724595d5.jpg


4.1 Mendapatkan ukuran tinggi dan lebar gambar.

file_dim <- map_df(file_name, get_dim)

head(file_dim, 10)
##    height width
## 1     626   417
## 2     417   626
## 3     626   417
## 4     626   417
## 5     417   626
## 6     626   418
## 7     626   584
## 8     469   626
## 9     626   417
## 10    417   626
##                                                                                  filename
## 1    archive/dataset/train_valid/icecream/000_jpg.rf.918ed1e12516d64db054b957724595d5.jpg
## 2   archive/dataset/train_valid/icecream/0010_jpg.rf.99e844b71704288b2bbb516937a68735.jpg
## 3  archive/dataset/train_valid/icecream/00101_jpg.rf.6c81e823f2c23a7b175638a103161ae3.jpg
## 4  archive/dataset/train_valid/icecream/00102_jpg.rf.b2dbb157d1bfc492793781d6d3f16734.jpg
## 5  archive/dataset/train_valid/icecream/00103_jpg.rf.a350a7fd51d959bdc8ef5fcad37d6839.jpg
## 6  archive/dataset/train_valid/icecream/00104_jpg.rf.2fb3dc95a94210eeb7639c3afd7de28e.jpg
## 7  archive/dataset/train_valid/icecream/00105_jpg.rf.ae64e0f65a00c9ec86ef0a891bb5244e.jpg
## 8  archive/dataset/train_valid/icecream/00106_jpg.rf.2c2c3d68541a5a0828fdfd6de5f7d302.jpg
## 9  archive/dataset/train_valid/icecream/00107_jpg.rf.10d547221c69fa45b6b63980ab6d43ea.jpg
## 10 archive/dataset/train_valid/icecream/00108_jpg.rf.13ea8649d0644fa4fab335d3385353d1.jpg


4.2 Mendapatkan sebaran statistik image dimension

summary(file_dim)
##      height          width         filename        
##  Min.   :228.0   Min.   :353.0   Length:887        
##  1st Qu.:417.0   1st Qu.:501.0   Class :character  
##  Median :417.0   Median :626.0   Mode  :character  
##  Mean   :490.9   Mean   :573.4                     
##  3rd Qu.:626.0   3rd Qu.:626.0                     
##  Max.   :626.0   Max.   :626.0

Dari data di atas terlihat bahwa gambar memiliki range minimum dan maximum yang cukup beragam dari tinggi dan lebarnya. Untuk tingginya range min-max berada pada rentang 228-626 pixels sementara untuk lebarnya range min-max berada pada rentang 353 - 626 pixels. Dimensi data menjadi hal yang penting untuk diperhatikan, karena informasi distribusi dari dimensi gambar ini akan digunakan untuk menentukan input dimensi yang sesuai pada model yang akan dibangun (target_size).


5 . Data Augmentation

Seluruh gambar input harus berada pada dimensi yang sama. Untuk itu kita dapat menentukan input size gambarnya. Pada referensi disebutkan bahwa proses ini sama dengan melakukan resize gambar. Semakin besari dimensi akan memberikan lebih banyak fitur namun akan lebih memakan waktu untuk prosesnya, dan sebaliknya jika gambar terlalu kecil akan ada kemungkinan kehilangan informasi dari datanya.Hal itu yang membuat sangat penting untuk menentukan dimensi yang menyeimbangkan kedua hal tersebut. Untuk kasus kali, dengan juga mempertimbangkan nilai range image yang ada sebelumnya maka dimensi yang digunakan 64 X 64 pixels.

# Height and width of images yang diinginkan
target_size <- c(64, 64)

# Batch size for training the model
batch_size <- 80

Image augmentation adalah teknik yang digunakan untuk meningkatkan size dari dari training set tanpa perlu menambahkan gambar baru. Tujuannya adalah agar model belajar bukan hanya dari gambar asli namun juga dari gambar hasil modifikasi. Beberapa settingan modifikasi (true) yang digunakan pada kasus kali ini adalah:

  • Scaling pixel dengan membagi ke angka 255
  • Flip horizontal
  • Flip vertical
  • Rotate dari 0 ke 45 degrees
  • Zoom in/zoom out sebesar 25%
  • Dan membagi data dngan kriteria 20% sebagai validation dataset
# Image Generator
train_data_gen <- image_data_generator(rescale = 1/255,
                                       horizontal_flip = T,
                                       vertical_flip = T,
                                       rotation_range = 45,
                                       zoom_range = 0.25,
                                       validation_split = 0.2)
## Loaded Tensorflow version 2.0.0


6 . Persiapan Data Training vs Cross Validation

Setelah membuat data generator untuk image augmentasi, lalu data image dimasukkan ke dalam generator tersebut menggunakan flow_images_from_directory(). Perlu diingat kembali pada geneartor digunakan validation_split = 0.2, yang nantinya akan mebagi data training 80% dan data validasi 20%. Melalui proses ini akan dihasilakan data gambar hasil augmentasi yang sudah dikelompokkan menjadi data training dan data validasi mengikuti proporsi tersebut. Data perlu dikelompokkan agar model hanya belajar menggunakan data training yang memang dikhususkan, namun selanjutnya model akan melakukan prediksi dari hasil belajar menggunakan data yang benar-benar baru. Hal ini penting untuk melihat kemampuan prediksi model dengan data-data baru yang belum pernah dilihat sebelumnya.

# Training Dataset
train_image_array_gen <- flow_images_from_directory(
  directory = "archive/dataset/train_valid/",
  target_size = target_size,
  color_mode = "rgb",
  batch_size = batch_size,
  seed = 123,
  subset = "training",
  generator = train_data_gen)

# Validation Dataset
val_image_array_gen <- flow_images_from_directory(
  directory = "archive/dataset/train_valid/",
  target_size = target_size,
  color_mode = "rgb",
  batch_size = batch_size,
  seed = 123,
  subset = "validation",
  generator = train_data_gen)


7 . Periksa Class Proportion

# 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 
## 0.5253521 0.4746479

Eksplorasi proporsi setiap kelas dilakukan dengan menggunakan prop.tabel. Di kedua kelas yang ada terlihat bahwa proporsinya cukup seimbang di kisaran angka 50%. Memiliki proporsi yang balance dari setiap kelas sangat penting agar model yang dibuat dapat mempelajari semua kelas dengan komposisi jumlah data yang sama sehingga pemahaman model tarhadap seluruh data akan sama. dan sebaliknya imbalance akan membuat model timpang dalam mempelajari sebuah kelas dibandingkan kelas lainnya.


8 . Convolutional Neural Network

8.1 Model Architecture

Convolutional Neural Network merupakan bagian dari deep neural network, yakni jenis jaringan saraf tiruan yang umumnya digunakan dalam pengenalan dan pemrosesan gambar, misalnya: image classification. Algoritma ini dirancang khusus untuk memproses data piksel dan citra visual.

Neural network yang umum biasanya mengubah input dengan meletakkannya melalui rangkaian hidden layer. Setiap layer terdiri dari sekumpulan neuron, dimana setiap layer terhubung secara penuh dengan semua neuron pada layer sebelumnya. Terakhir, lapisan yang sudah terhubung sepenuhnya (output layer) digunakan untuk mewakili prediksi.

Arsitektur pada algoritma CNN terdiri atas 2 tugas, yakni ekstraksi fitur dan klasifikasi. Model arsitektur yang dibangun menggunakan 5 jenis layer sebagai berikut:

  1. Convolutional layer: mengekstrak features dari 2D image menggunakan relu activation function.

  2. Max Pooling layer: melakukan downsample image features

  3. Flattening layer: melakukan flatten data dari 2D array ke 1D array. Atau dengan kata lain tahapan flattening adalah merubah dari matriks yang ada di pooling layer menjadi satu kolom saja (sebuah vektor tunggal).

  4. Dense layer untuk menangkap lebih banyak informasi.

  5. Dense layer untuk output menggunakan sigmoid activation function.

# input shape of the image
c(target_size, 3)
## [1] 64 64  3
# Set Initial Random Weight
tensorflow::tf$random$set_seed(123)

model <- keras_model_sequential(name = "model") %>% 
  
  # 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 = "sigmoid",
              name = "Output")
  
model
## Model: "model"
## ________________________________________________________________________________
## Layer (type)                        Output Shape                    Param #     
## ================================================================================
## conv2d (Conv2D)                     (None, 64, 64, 16)              448         
## ________________________________________________________________________________
## max_pooling2d (MaxPooling2D)        (None, 32, 32, 16)              0           
## ________________________________________________________________________________
## flatten (Flatten)                   (None, 16384)                   0           
## ________________________________________________________________________________
## dense (Dense)                       (None, 16)                      262160      
## ________________________________________________________________________________
## Output (Dense)                      (None, 2)                       34          
## ================================================================================
## Total params: 262,642
## Trainable params: 262,642
## Non-trainable params: 0
## ________________________________________________________________________________


8.2 Model Fitting

Compile model dengan menggunakan:

  • loss functionnya adalah binary cross-entropy karena ini adalah classification 2 kelas prediksi.

  • Optimizer menggunakan Adam dengan learning rate 0.01. Untuk optimizer dapat diganti dan disesuaikan agar mendapatkan nilai prediksi terbaik. Selain Adam, ada beberapa pilihan lainnya yang dapat digunakan, misalnya: SGD, Adagrad, Adadelta, RMSprop, AdaMax, and Nadam (sumber: internet).

  • metrics model yang digunakan adalah accuracy, dimana seluruh kelas adalah sama-sama penting.

model %>% 
  compile(
    loss = "binary_crossentropy",
    optimizer = optimizer_adam(learning_rate = 0.01),
    metrics = "accuracy"
    )

Untuk model awal menggunakan 4 epochs

# 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 = 4, 
  
  # validation data
  validation_data = val_image_array_gen,
  validation_steps = as.integer(valid_samples / batch_size)
)

plot(history)


8.3 Model Evaluation

Evalusi model menggunakan confusion matrix untuk data validasi. Namun sebelumnya kita akan mendapatkan file name yang akan digunakan sebagai data untuk validasi berdasarkan hasil dari generator yang sebelumnya sudah dibuat. Dari file nama tersebut juga kita akan mengekstrak label sebagai target variabelnya.

val_data <- data.frame(file_name = paste0("archive/dataset/train_valid/", val_image_array_gen$filenames)) %>% 
  mutate(class = str_extract(file_name, "icecream|pizza"))

head(val_data,5)
##                                                                                 file_name
## 1   archive/dataset/train_valid/icecream\\000_jpg.rf.918ed1e12516d64db054b957724595d5.jpg
## 2 archive/dataset/train_valid/icecream\\00101_jpg.rf.6c81e823f2c23a7b175638a103161ae3.jpg
## 3 archive/dataset/train_valid/icecream\\00102_jpg.rf.b2dbb157d1bfc492793781d6d3f16734.jpg
## 4 archive/dataset/train_valid/icecream\\00103_jpg.rf.a350a7fd51d959bdc8ef5fcad37d6839.jpg
## 5 archive/dataset/train_valid/icecream\\00104_jpg.rf.2fb3dc95a94210eeb7639c3afd7de28e.jpg
##      class
## 1 icecream
## 2 icecream
## 3 icecream
## 4 icecream
## 5 icecream
# 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)))
}
test_x <- image_prep(val_data$file_name)

# Check dimension of testing data set
dim(test_x)
## [1] 177  64  64   3

validasi data terdiri dari 177 images dengan dimensi 64 x 64 pixels dan 3 color channels (RGB). Proses kemudian dilanjutkan proses prediksi dengan menggunakan model yang sudah ada.

pred_test <- predict_classes(model, test_x)

pred_test
##   [1] 1 1 1 0 1 1 1 0 1 0 1 1 0 0 1 0 0 1 0 0 0 0 0 1 1 0 1 0 0 1 1 1 0 1 1 1 0
##  [38] 1 0 1 1 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 0 1 1 1 1 0 0 1 0 0 0 1 1 1 0 1
##  [75] 1 0 1 0 1 1 1 0 0 1 0 1 0 0 0 0 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [112] 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1
## [149] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

Supaya hasil prediksi lebih mudah dipahami dan diinterpretasi, maka akan dilakukan konversi melalui encoding ke label kelas yang ada. Namun jika tidak ingin menggunakan encoding, juga dapat menggunakan fungsi if else.

# Convert encoding to label
decode <- function(x){
  case_when(x == 0 ~ "icecream",
            x == 1 ~ "pizza"
            )
}

pred_test <- sapply(pred_test, decode) 

head(pred_test, 10)
##  [1] "pizza"    "pizza"    "pizza"    "icecream" "pizza"    "pizza"   
##  [7] "pizza"    "icecream" "pizza"    "icecream"

Evaluasi model menggunakan confusion matrix.

confusionMatrix(as.factor(pred_test),
                as.factor(val_data$class))
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction icecream pizza
##   icecream       38     4
##   pizza          55    80
##                                           
##                Accuracy : 0.6667          
##                  95% CI : (0.592, 0.7356) 
##     No Information Rate : 0.5254          
##     P-Value [Acc > NIR] : 0.00009675865517
##                                           
##                   Kappa : 0.3507          
##                                           
##  Mcnemar's Test P-Value : 0.00000000007543
##                                           
##             Sensitivity : 0.4086          
##             Specificity : 0.9524          
##          Pos Pred Value : 0.9048          
##          Neg Pred Value : 0.5926          
##              Prevalence : 0.5254          
##          Detection Rate : 0.2147          
##    Detection Prevalence : 0.2373          
##       Balanced Accuracy : 0.6805          
##                                           
##        'Positive' Class : icecream        
## 

Dari hasil confusion matrix terlihat bahwa performa model masih belum cukup baik, dimana nilai accuracy masih cukup rendah, yakni di angka 67%. Karena itu akan dilakukan tuning dengan memperbaiki arsitektur model agar performa prediksi bisa lebih baik.


9 . Tuning the Model

Model yang pertama dirasa baru menangkap sedikit informasi pada convolutional layernya. Arah tuning dilakukan adalah dengan tujuan mendapatkan lebih banyak informasi. Hal ini dilakukan dengan menambahkan beberapa layer pada model architecturenya, sehingga akan lebih banyak informasi yang diekstrak sebelum akhirnya dilakukan flattening. Selain itu juga ditambahkan CNN layers sebelum dilakukan max pooling.

model
## Model: "model"
## ________________________________________________________________________________
## Layer (type)                        Output Shape                    Param #     
## ================================================================================
## conv2d (Conv2D)                     (None, 64, 64, 16)              448         
## ________________________________________________________________________________
## max_pooling2d (MaxPooling2D)        (None, 32, 32, 16)              0           
## ________________________________________________________________________________
## flatten (Flatten)                   (None, 16384)                   0           
## ________________________________________________________________________________
## dense (Dense)                       (None, 16)                      262160      
## ________________________________________________________________________________
## Output (Dense)                      (None, 2)                       34          
## ================================================================================
## Total params: 262,642
## Trainable params: 262,642
## Non-trainable params: 0
## ________________________________________________________________________________
tensorflow::tf$random$set_seed(123)

model_tuning <- keras_model_sequential() %>% 
  
  # First convolution layer
  layer_conv_2d(filters = 32,
                kernel_size = c(5,5), # 5 x 5 filters
                padding = "same",
                activation = "relu",
                input_shape = c(target_size, 3)
                ) %>% 
  
  # Second convolutional layer
  layer_conv_2d(filters = 32,
                kernel_size = c(3,3), # 3 x 3 filters
                padding = "same",
                activation = "relu"
                ) %>% 
  
  # Max pooling layer
  layer_max_pooling_2d(pool_size = c(2,2)) %>% 
  
  # Third convolutional layer
  layer_conv_2d(filters = 64,
                kernel_size = c(3,3),
                padding = "same",
                activation = "relu"
                ) %>% 

  # Max pooling layer
  layer_max_pooling_2d(pool_size = c(2,2)) %>% 
  
  # Fourth convolutional layer
  layer_conv_2d(filters = 128,
                kernel_size = c(3,3),
                padding = "same",
                activation = "relu"
                ) %>% 
  
  # Max pooling layer
  layer_max_pooling_2d(pool_size = c(2,2)) %>% 

  # Fifth convolutional layer
  layer_conv_2d(filters = 256,
                kernel_size = c(3,3),
                padding = "same",
                activation = "relu"
                ) %>% 
  
  # Max pooling layer
  layer_max_pooling_2d(pool_size = c(2,2)) %>% 
  
  # Flattening layer
  layer_flatten() %>% 
  
  # Dense layer
  layer_dense(units = 64,
              activation = "relu") %>% 
  
  # Output layer
  layer_dense(name = "Output",
              units = 2, 
              activation = "sigmoid")

model_tuning
## Model: "sequential"
## ________________________________________________________________________________
## Layer (type)                        Output Shape                    Param #     
## ================================================================================
## conv2d_5 (Conv2D)                   (None, 64, 64, 32)              2432        
## ________________________________________________________________________________
## conv2d_4 (Conv2D)                   (None, 64, 64, 32)              9248        
## ________________________________________________________________________________
## max_pooling2d_4 (MaxPooling2D)      (None, 32, 32, 32)              0           
## ________________________________________________________________________________
## conv2d_3 (Conv2D)                   (None, 32, 32, 64)              18496       
## ________________________________________________________________________________
## max_pooling2d_3 (MaxPooling2D)      (None, 16, 16, 64)              0           
## ________________________________________________________________________________
## conv2d_2 (Conv2D)                   (None, 16, 16, 128)             73856       
## ________________________________________________________________________________
## max_pooling2d_2 (MaxPooling2D)      (None, 8, 8, 128)               0           
## ________________________________________________________________________________
## conv2d_1 (Conv2D)                   (None, 8, 8, 256)               295168      
## ________________________________________________________________________________
## max_pooling2d_1 (MaxPooling2D)      (None, 4, 4, 256)               0           
## ________________________________________________________________________________
## flatten_1 (Flatten)                 (None, 4096)                    0           
## ________________________________________________________________________________
## dense_1 (Dense)                     (None, 64)                      262208      
## ________________________________________________________________________________
## Output (Dense)                      (None, 2)                       130         
## ================================================================================
## Total params: 661,538
## Trainable params: 661,538
## Non-trainable params: 0
## ________________________________________________________________________________


9.1 Model Fitting

Fitting model yang baru juga dilakukan beberapa perubahan untuk meningkatkan performanya. Beberapa hal yang diubah adalah:

  • Perubahan learning rate dari 0.01 menjadi 0.001

  • Jumlah Epoch ditambah menjadi 50

model_tuning %>% 
  compile(
    loss = "binary_crossentropy",
    optimizer = optimizer_adam(learning_rate = 0.001),
    metrics = "accuracy")
history_tuning <- model_tuning %>% 
  fit_generator(
  # training data
  train_image_array_gen,
  
  # epochs
  steps_per_epoch = as.integer(train_samples / batch_size), 
  epochs = 50, 
  
  # 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_tuning)


9.2 Model Evaluation

Proses kemudian dilanjutkan proses prediksi dengan menggunakan model yang sudah ada.

pred_test_tuning <- predict_classes(model_tuning, test_x) 
pred_test_tuning
##   [1] 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0
##  [38] 1 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0
##  [75] 1 0 0 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [112] 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [149] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

Supaya hasil prediksi lebih mudah dipahami dan diinterpretasi, maka akan dilakukan konversi melalui encoding ke label kelas yang ada. Namun jika tidak ingin menggunakan encoding, juga dapat menggunakan fungsi if else.

# Convert encoding to label
decode <- function(x){
  case_when(x == 0 ~ "icecream",
            x == 1 ~ "pizza"
            )
}

pred_test_tuning <- sapply(pred_test_tuning, decode) 

head(pred_test_tuning, 10)
##  [1] "icecream" "icecream" "icecream" "icecream" "icecream" "icecream"
##  [7] "pizza"    "icecream" "icecream" "icecream"

Evaluasi model menggunakan confusion matrix.

confusionMatrix(as.factor(pred_test_tuning), 
                as.factor(val_data$class))
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction icecream pizza
##   icecream       79     2
##   pizza          14    82
##                                               
##                Accuracy : 0.9096              
##                  95% CI : (0.8574, 0.9474)    
##     No Information Rate : 0.5254              
##     P-Value [Acc > NIR] : < 0.0000000000000002
##                                               
##                   Kappa : 0.82                
##                                               
##  Mcnemar's Test P-Value : 0.00596             
##                                               
##             Sensitivity : 0.8495              
##             Specificity : 0.9762              
##          Pos Pred Value : 0.9753              
##          Neg Pred Value : 0.8542              
##              Prevalence : 0.5254              
##          Detection Rate : 0.4463              
##    Detection Prevalence : 0.4576              
##       Balanced Accuracy : 0.9128              
##                                               
##        'Positive' Class : icecream            
## 

Dari hasil confusion matrix terlihat bahwa performa model_tuning sudah jauh lebih baik dimana nilai accuracy di angka 91%. Sehingga disimpulkan model sudah sesuai.


10 . Predict Data in Testing Dataset

Setelah melatih model dan sudah mendapatkan performa yang diinginkan pada validasi data, selanjutnya adalah menerapkan model pada data test. Data test adalah data yang benar-benar baru yang belum pernah dipakai pada model.

10.1 Import Data Test

folder_list_test <- list.files("archive/dataset/test/")

folder_path_test <- paste0("archive/dataset/test/", folder_list, "/")

# Get file name
file_name_test <- map(folder_path_test, 
                 function(x) paste0(x, list.files(x))
                 ) %>% 
  unlist()

# first 6 file name
head(file_name_test)
## [1] "archive/dataset/test/icecream/00110_jpg.rf.8e2342aa2d175b85bdc6dd27649f3f99.jpg"
## [2] "archive/dataset/test/icecream/00119_jpg.rf.814e5835e122b325ffc49f2671f6b7f0.jpg"
## [3] "archive/dataset/test/icecream/00124_jpg.rf.cbfd9b294fb345cac66fea8d648861dd.jpg"
## [4] "archive/dataset/test/icecream/00126_jpg.rf.d8bd0dacaa776c92bac0f6087c81a58f.jpg"
## [5] "archive/dataset/test/icecream/00132_jpg.rf.403736aa08b4664e66095811823d6ea3.jpg"
## [6] "archive/dataset/test/icecream/00146_jpg.rf.c9421eb0b6eb46d80fdc889f2d18af1e.jpg"

Konversi data image test ke array

file_name_test_df <- as.data.frame(file_name_test)

test_x2 <- image_prep(file_name_test_df$file_name_test)

dim(test_x2)
## [1] 106  64  64   3

validasi data terdiri dari 106 images dengan dimensi 64 x 64 pixels dan 3 color channels (RGB). Proses kemudian dilanjutkan proses prediksi dengan menggunakan model yang sudah ada.


10.2 Prediction

pred_test_2 <- predict_classes(model_tuning, test_x2)

pred_test_2
##   [1] 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
##  [38] 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [75] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1


Supaya hasil prediksi lebih mudah dipahami dan diinterpretasi, maka akan dilakukan konversi melalui encoding ke label kelas yang ada. Namun jika tidak ingin menggunakan encoding, juga dapat menggunakan fungsi if else.

# Convert encoding to label
decode <- function(x){
  case_when(x == 0 ~ "icecream",
            x == 1 ~ "pizza"
            )
}

pred_test_2 <- sapply(pred_test_2, decode) 

head(pred_test_2, 10)
##  [1] "icecream" "icecream" "icecream" "pizza"    "icecream" "icecream"
##  [7] "pizza"    "icecream" "icecream" "icecream"
val_data_test <- as.data.frame(file_name_test) %>%
  mutate(class = str_extract(file_name_test, "icecream|pizza"))

head(val_data_test,5)
##                                                                    file_name_test
## 1 archive/dataset/test/icecream/00110_jpg.rf.8e2342aa2d175b85bdc6dd27649f3f99.jpg
## 2 archive/dataset/test/icecream/00119_jpg.rf.814e5835e122b325ffc49f2671f6b7f0.jpg
## 3 archive/dataset/test/icecream/00124_jpg.rf.cbfd9b294fb345cac66fea8d648861dd.jpg
## 4 archive/dataset/test/icecream/00126_jpg.rf.d8bd0dacaa776c92bac0f6087c81a58f.jpg
## 5 archive/dataset/test/icecream/00132_jpg.rf.403736aa08b4664e66095811823d6ea3.jpg
##      class
## 1 icecream
## 2 icecream
## 3 icecream
## 4 icecream
## 5 icecream

Evaluasi model menggunakan confusion matrix.

confusionMatrix(as.factor(pred_test_2),
                as.factor(val_data_test$class))
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction icecream pizza
##   icecream       53     0
##   pizza           6    47
##                                               
##                Accuracy : 0.9434              
##                  95% CI : (0.8809, 0.9789)    
##     No Information Rate : 0.5566              
##     P-Value [Acc > NIR] : < 0.0000000000000002
##                                               
##                   Kappa : 0.8868              
##                                               
##  Mcnemar's Test P-Value : 0.04123             
##                                               
##             Sensitivity : 0.8983              
##             Specificity : 1.0000              
##          Pos Pred Value : 1.0000              
##          Neg Pred Value : 0.8868              
##              Prevalence : 0.5566              
##          Detection Rate : 0.5000              
##    Detection Prevalence : 0.5000              
##       Balanced Accuracy : 0.9492              
##                                               
##        'Positive' Class : icecream            
## 

Dari hasil confusion matrix terlihat bahwa performa model_tuning untuk data test performanya sedikit lebih baik dibandingkan pada data validasi namun masih taraf yang sesuai dengan nilai accuracy di angka 94%.


11 . Kesimpulan

Convolutional Neural Network merupakan salah satu jenis algoritme Deep Learning yang dapat menerima input berupa gambar, menentukan aspek atau obyek apa saja dalam sebuah gambar yang bisa digunakan mesin untuk “belajar” mengenali gambar, dan membedakan antara satu gambar dengan yang lainnya.

Dalam kasus ini CNN digunakan untuk melakukan klasifikasi gambar yang diambil dari google dengan 2 kelas, yakni: pizza dan ice cream. Melihat hasil prediksinya, spesifik pada nilai akurasi terlihat bahwa model CNN ini mampu untuk menjalankan prediksi dengan baik sampai pada nilai akurasi 90%. Dengan kata lain, untuk kasus klasifikasi binary dengan unstructured data, CNN menjadi salah satu alternatif yang dapat digunkan dengan tingkat akurasi yang baik.

Image Classification memiliki sejumlah manfaat yang dapat diimplementasikan dalam bisnis. Salah satu yang saya ketahui misalnya adalah face/image recognition, dimana penggunaannya banyak dilakukan ketika saat ini pendaftaran atau registrasi dokumen-dokumen fisik sudah mulai dilakukan secara digital. Setiap dokumen diinput sebagai gambar, dan cukup banyak dokumen yang terkumpul dan harus dapat dikenali dan bahakan dikategorikan. Hal ini membuat kebutuhan akan alat/model machine learning yang mampu memproses data-data unstructured menjadi vital.