R Markdown

https://www.kaggle.com/polavr/twoclass-weather-classification

Pada project ini, saya megambil data dari kaggle yang bernama Two-class weather classification. Disini saya membuat model mengganakan aristektur CNN, dan memakai image data generator

# Data Pre-Processing
library(tidyverse)

# Image manipulation
library(imager)

# Deep learning
library(keras)
library(tensorflow)

# Model Evaluation
library(caret)

List.files dibawah ini berfungsi untuk men-listing file yang ada di dalam folder train

folder_list <- list.files("dataset/WeatherClassification/train/")

folder_path digunakan untuk menambil path dari setiap folder class

folder_path <- paste0("dataset/WeatherClassification/train/", folder_list, "/")

file_path digunakan untuk menyimpan file path dari setiap gambar

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

head(file_path)
## [1] "dataset/WeatherClassification/train/cloudy/c0001.jpg"
## [2] "dataset/WeatherClassification/train/cloudy/c0002.jpg"
## [3] "dataset/WeatherClassification/train/cloudy/c0003.jpg"
## [4] "dataset/WeatherClassification/train/cloudy/c0004.jpg"
## [5] "dataset/WeatherClassification/train/cloudy/c0005.jpg"
## [6] "dataset/WeatherClassification/train/cloudy/c0006.jpg"

Disini kita mengambil sample dari file path dan menjadikan image, sehingga kita tau image seperti apa yang akan kita gunakan

set.seed(2021)

sample_img <- sample(x = file_path, size = 2)
img <- map(sample_img, load.image)

map(img, plot)

## [[1]]
## Image. Width: 200 pix Height: 200 pix Depth: 1 Colour channels: 3 
## 
## [[2]]
## Image. Width: 200 pix Height: 200 pix Depth: 1 Colour channels: 3

Setelah mengetahui image yang yang kita gunakan seperti apa, kita akan membuat funtion yang dimana akan mengambil dimension setiap image

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

get_dim(file_path[1])
##   height width                                            file_name
## 1    200   200 dataset/WeatherClassification/train/cloudy/c0001.jpg

Kita akan gunakan function yang sudah kita buat sebelumnya agar mendapatkan dimension seluruh image, dan melakukan summary.

dim_img <- map_df(file_path, get_dim)
summary(dim_img)
##      height        width      file_name        
##  Min.   :200   Min.   :200   Length:10000      
##  1st Qu.:200   1st Qu.:200   Class :character  
##  Median :200   Median :200   Mode  :character  
##  Mean   :200   Mean   :200                     
##  3rd Qu.:200   3rd Qu.:200                     
##  Max.   :200   Max.   :200

Dari summary di atas, kita dapat lihat bahwa dimension keseluruhan iamge ialah 200x200. maka kita akan menggunakan target size berdimensi 200x200 dan kita memakai batch size sebesar 64

target_size <- c(200,200)
batch_size <- 64

Disini kita akan memakai image datta generator dan juga flow image from directory untuk mengambil semua gambar dan class dari folder.

train_img_array_gen <- flow_images_from_directory(directory = "dataset/WeatherClassification/train/",
                                                  generator = image_data_generator(
                                                                                   horizontal_flip = T,
                                                                                   vertical_flip = T,
                                                                                   zoom_range = 0.25,
                                                                                   rotation_range = 90,
                                                                                   rescale = 1/255),
                                                  target_size = target_size,
                                                  batch_size = batch_size,
                                                  color_mode = "rgb",
                                                  
                                                  seed = 2021)

valid_img_array_gen <- flow_images_from_directory(directory = "dataset/WeatherClassification/test/",
                                                  generator = image_data_generator(
                                                                                   horizontal_flip = T,
                                                                                   vertical_flip = T,
                                                                                   zoom_range = 0.25,
                                                                                   rotation_range = 90,
                                                                                   rescale = 1/255),
                                                  target_size = target_size,
                                                  batch_size = batch_size,
                                                  color_mode = "rgb",
                                                  
                                                  seed = 2021)

setelah kita mengambil gambar dan juga kelas nya, kita akan mengambil total kelas, gambar training, dan gambar validation. Disini kita juga melihat proporsi dari target classnya.

n_output <- n_distinct(train_img_array_gen$classes)
train_sample <- train_img_array_gen$n
valid_sample <- valid_img_array_gen$n

table(as.factor(train_img_array_gen$classes))
## 
##    0    1 
## 5000 5000

setelah dilihat dari proporsi kelasnya, kelar tersebut bisa dinyatakan balanced. Disini kita akan membuat arsitektur CNN nya.

Untuk model base nya sendiri, saya memakai 1x layer conv 2d, 1x max pooling, dan 2 hidel layer

untuk layer conv 2d nya disini memakai kernel size 3x3, activation relu, filters 8, padding same. untuk max pooling nya menggunakan pool size 2x2. untuk hiden layer pertama menggunakan units 64, dan activation relu untuk hiden layer kedua menggunakan konfigurasi yang sama seperti pertama dan untuk outputnya menggunakan activation sigmod dan units 2 karena target kita hanya mempunyai 2 class

model <- keras_model_sequential(name = "Model_base") %>% 
  layer_conv_2d(input_shape = c(target_size, 3),
                filters = 8,
                kernel_size = c(3,3),
                padding = "same",
                activation = "relu") %>% 
  layer_max_pooling_2d(pool_size = c(2,2)) %>% 
  layer_flatten() %>% 
  layer_dense(units = 64,
              activation = "relu",
              name = "satu") %>% 
  layer_dense(units = 64,
              activation = "relu",
              name = "dua") %>% 
  layer_dense(activation = "sigmoid",
              units = n_output,
              name = "output")

untuk model base sendiri, saya menggunakan optimizer adam dengan learning rate default atau 0.001

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

disini saya memakai epoch 10

set.seed(2021)

history_base <- model %>% 
  fit(train_img_array_gen,
      epoch = 10,
      steps_per_epoch = as.integer(train_sample / batch_size),
      validation_data = valid_img_array_gen,
      validation_step = as.integer(valid_sample/ batch_size))

plot(history_base)
## `geom_smooth()` using formula 'y ~ x'

dari hasil yang di dapatkan model_base, sebenernya hasil tersebut sudah cukup bagus dengan accuracy kurang lebih 85% dan val accuracy di 80%

disini kita mau membuat model tuning lagi, dengan konfugaris yang berbeda dari sebelumnya.

untuk model tune sendiri disini menggunakan layer conv 2x, max pooling 2x, hiden layer 3x, dan output layer 1x,

model_tune <- keras_model_sequential(name = "model_tuning") %>% 
  layer_conv_2d(input_shape = c(target_size, 3),
                kernel_size = c(3,3),
                filters = 16,
                padding = "same",
                activation = "relu") %>% 
  layer_max_pooling_2d(pool_size = c(3,3)) %>% 
  layer_conv_2d(kernel_size = c(2,2),
                filters = 8,
                padding = "same",
                activation = "relu") %>% 
  layer_max_pooling_2d(pool_size = c(3,3)) %>% 
  layer_flatten() %>% 
  layer_dense(units = 256,
              activation = "relu") %>% 
  layer_dense(units = 128,
              activation = "relu") %>% 
  layer_dense(units = 64,
              activation = "relu") %>% 
  layer_dense(units = 2,
              activation = "sigmoid",
              name = "Output")

mode tune sendiri menggunakan optimizer adam dengan menggunakan learning rate sebesar 0.0001.

untuk fit nya menggunakan epoch 10

model_tune %>% 
  compile(loss = "binary_crossentropy",
          metrics = "accuracy",
          optimizer = optimizer_adam(learning_rate = 0.0001))

history_tune <- model_tune %>% 
  fit(train_img_array_gen,
      epoch = 10,
      steps_per_epoch = as.integer(train_sample / batch_size),
      validation_data = valid_img_array_gen,
      validation_step = as.integer(valid_sample/ batch_size))

plot(history_tune)
## `geom_smooth()` using formula 'y ~ x'

setelah training, kita mendapatkan hasil yang bagus menggunakan model tune

setelah itu kita mengambil data test tanpa menggunakan data generator, dan file path dimasukan kedalam test_data beserta classnya

test_img_data_gen <- flow_images_from_directory(directory = "dataset/WeatherClassification/test/",
                                                target_size = target_size,
                                                batch_size = batch_size,
                                                color_mode = "rgb")

test_data <- data.frame(file_name = paste0("dataset/WeatherClassification/test/", test_img_data_gen$filenames)) %>% 
  mutate(class = str_extract(file_name, "cloudy|sunny"))

disini kita membuat function yang digunakan untuk mengambil gambar dan dirubah kedalam array

image_prep <- function(x){
  array <- lapply(x, function(path){
    img <- image_load(path, target_size = target_size,
                      grayscale = F)
    x <- image_to_array(img)
    x <- array_reshape(x, c(1, dim(x)))
    x <- x/255
  })
  do.call(abind::abind, c(array, list(along = 1)))
}

test_x <- image_prep(test_data$file_name)

disini kita memprediksi gambar yang sudah dirubah kedalam array.

pred_test <- predict(model_tune, test_x) %>% k_argmax() %>% as.array()

disini kita membuat function untuk merubah class dari 0 1 menjadi class sebenarnya

encode_label <- function(x) {
  ifelse(x == 0, "cloudy", "sunny")
}

dan terakhir kita dapat melihat nilai akurasi, recall, dan precission

pred_test <- sapply(pred_test, encode_label)

confusionMatrix(as.factor(pred_test), as.factor(test_data$class))
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction cloudy sunny
##     cloudy     96    40
##     sunny       4   113
##                                           
##                Accuracy : 0.8261          
##                  95% CI : (0.7737, 0.8707)
##     No Information Rate : 0.6047          
##     P-Value [Acc > NIR] : 2.308e-14       
##                                           
##                   Kappa : 0.6576          
##                                           
##  Mcnemar's Test P-Value : 1.317e-07       
##                                           
##             Sensitivity : 0.9600          
##             Specificity : 0.7386          
##          Pos Pred Value : 0.7059          
##          Neg Pred Value : 0.9658          
##              Prevalence : 0.3953          
##          Detection Rate : 0.3794          
##    Detection Prevalence : 0.5375          
##       Balanced Accuracy : 0.8493          
##                                           
##        'Positive' Class : cloudy          
## 

Dan hasilnya sama sama bagus untuk model base dan tune, kita dapat memakai salah saatunya.