1 Latar Belakang

Indonesia adalah bangsa yang besar, bangsa yang terdiri dari banyak pulau dari Sabang sampai Merauke dimana setiap pulau memiliki kebudayaan sendiri-sendiri, seperti alat musik, busana, bahkan juga makanan tradisional. Dengan ragam kultur budayanya, Indonesia memiliki banyak jenis makanan dan memiliki ciri khas yang berbeda antara daerah yang satu dengan yang lainnya. Makanan tradisional Jawa Tengah misalnya terkenal dengan cita rasanya yang manis, masyarakat dari kota Manado dan Lombok yang suka dengan rasa pedas, dan lain sebagainya. Makanan tradisional Indonesia sangat beragam, seiring dengan beragamnya etnik dan wilayah multikulturalnya. Makanan tradisional Indonesia mengandung beragam rempah-rempah, memiliki aneka teknik memasak dan berbahan-bahan lokal yang sebagian terpengaruh dari India, China, Timur Tengah, dan Eropa.

Menurut Marwanti (2000: 112), makanan tradisional mempunyai pengertian makanan rakyat sehari-hari, baik yang berupa makanan pokok, makanan selingan, atau sajian khusus yang sudah turun-temurun dari zaman nenek moyang. Cara pengolahan pada resep makanan tradisional dan cita rasanya umumnya sudah bersifat turun temurun sehingga makanan tradisional disetiap tempat atau daerah berbeda-beda.

Dari pengertian di atas, dapat dikatakan bahwa makanan tradisional merupakan makanan yang diperoleh secara turun temurun dan di setiap daerah mempunyai ciri khas yang berbeda-beda. Makanan tradisional Indonesia sangat banyak macamnya, berdasarkan tingkat eksistensinya dalam masyarakat hingga saat ini. Keanekaragaman makanan tradisional yang ada dipengaruhi oleh keadaan daerah atau tempat tinggal dan budaya yang ada di daerah tersebut. Dengan banyaknya keanekaragaman makanan tradisional dalam suatu daerah, tidak sedikit pula makanan tradisional yang hampir terlupakan oleh masyarakat saat ini.

Menurut Murdijati (2017), makanan tradisional ini dapat dikategorikan menjadi tiga kelompok, antara lain:

  1. Makanan tradisional yang hampir punah. Makanan tradisional yang hampir punah ini langka dan hampir jarang dapat ditemui mungkin disebabkan karena ketersediaan bahan dasarnya mulai sulit atau 10 masyarakat pembuatnya mulai tidak mengerjakan lagi atau terdesak oleh produk makanan lain, contohnya karangan, cethot, entog-entog, getas, es semlo, dan hawuk-hawuk.
  2. Makanan tradisional yang kurang populer. Kelompok makanan tradisional yang kurang populer adalah makanan tradisional yang masih mudah ditemui, tetapi makin tidak dikenal dan cenderung berkurang penggemarnya, dianggap mempunyai status sosial lebih rendah dalam masyarakat, contohnya kethak, adrem, wedang tahu, lemet, bothok sembukan, dan bajigur.
  3. Makanan tradisional yang populer (tetap eksis). Kelompok makanan tradisional yang populer merupakan makanan tradisional yang tetap disukai masyarakat dengan bukti banyak dijual, laku, dan dibeli oleh konsumen bahkan beberapa menjadi ikon daerah tertentu seperti gudeg, emping melinjo, gatot, thiwul, tempe benguk, kipo, dan sate klathak.

Cara pengolahan pada resep makanan tradisional dan cita rasanya umumnya sudah bersifat turun temurun, serta sedikit sekali adanya inovasi produk. Menurut Sosroningrat (1991), makanan tradisional mempunyai ciri-ciri antara lain:

  1. Resep makanan yang diperoleh secara turun-temurun dari generasi pendahulunya,
  2. Penggunaan alat tradisional tertentu di dalam pengolahan masakan tersebut (misalnya masakan harus diolah dengan alat dari tanah liat),
  3. Teknik olah masakan merupakan cara pengolahan yang harus dilakukan untuk mendapatkan rasa maupun rupa yang khas dari suatu masakan.

Bahkan berdasarkan tulisan artikel dari website Kementerian Luar Negeri berikut ini (https://kemlu.go.id/portal/id/read/4771/berita/citra-rasa-kuliner-indonesia-menjadi-daya-tarik-tersendiri-di-tengah-perayaan-warisan-budaya-asia-amerika-di-san-francisco), Indonesia dikenal dengan kekayaan kulinernya karena memiliki lebih dari 5 ribu resep kuliner tradisional sehingga menjadikan Indonesia memiliki jumlah resep makanan terbanyak di dunia.

Makanan tradisional sebagai salah satu aspek budaya suatu bangsa dapat mencirikan identitas bangsa tersebut. Kita sebagai warga negara Indonesia perlu melakukan pelestarian makanan tradisional dengan meningkatkan kualitas produk agar mampu bersaing dan memuaskan wisatawan lokal maupun asing. Selain itu, dapat juga dilakukan dengan cara memperkenalkannya kepada generasi muda ditengah-tengah banyaknya kuliner dari kebudayaan barat yang sekarang sudah merajalela di Indonesia. Bahkan kita juga harus bangga memperkenalkan makanan tradisional ini kepada wisatawan asing karena makanan Indonesia dibuat dari rempah-rempah yang kualitasnya salah satu yang terbaik di seluruh dunia.

Sebagai warga negara Indonesia yang lahir dan besar di Indonesia, saya memiliki kecintaan dan kebanggaan kepada kebudayaan Indonesia, salah satunya adalah makanan tradisional. Menurut saya, makanan tradisional Indonesia tidak hanya enak dan istimewa, tetapi juga memiliki banyak ragam rasa dan jenis yang menjadi khas daerah masing-masing dan menjadikannya sebagai hidangan tradisional populer di daerahnya. Indonesia terus berupaya menjadi salah satu destinasi wisata dunia terus berupaya meningkatkan diri agar mampu bersaing dengan negara lain.

Oleh karena itu, saya ingin sekali memperkenalkan makanan tradisional Indonesia kepada seluruh masyarakat khususnya wisatawan lokal maupun asing yang mungkin tidak tahu banyak mengenai hal ini. Dengan demikian akan semakin banyak wisatawan yang ingin berkunjung ke Indonesia dan menikmati kebudayaan khususnya makanan tradisional Indonesia.

2 Pemahaman Masalah

Seperti kita ketahui di atas bahwa makanan tradisional di Indonesia, sebagai salah satu aspek budaya Indonesia, sangat banyak dan beragam. Kita sebagai warga Indonesia mungkin hanya mengetahui beberapa jenis makanan tradisional atau makanan yang berasal dari daerah asal kita saja. Hal ini terbukti dari pengalaman pribadi saya, saat saya ingin makan salah satu makanan tradisional daerah, namun saya tidak mengetahui nama makanan tersebut. Saya percaya hal ini juga sering dialami oleh banyak orang Indonesia, apalagi generasi muda saat ini yang mungkin lebih mengenal makanan dari luar negeri.

Sebagai bentuk pelestarian budaya Indonesia yaitu makanan tradisional, saya mempunyai keinginan untuk memperkenalkan makanan tradisional Indonesia kepada masyarakat maupun wisatawan asing yang ingin mengetahui lebih dalam mengenai makanan tradisional tersebut. Inilah yang menjadi dasar bagi saya membuat sebuah dashboard yang mampu mengenali makanan tradisional Indonesia yang difoto atau di-upload gambarnya tersebut.

Dashboard yang saya buat ini akan memberitahukan nama makanan beserta informasi mengenai makanan tersebut dari gambar / foto makanan yang di-upload oleh user. Dashboard ini dikembangkan dengan menggunakan machine learning berbasis Neural Network untuk dapat melakukan klasifikasi atas gambar makanan tradisional.

3 Business Impact

Project ini menampilkan dashboard yang berisi fitur-fitur tentang makanan tradisional Indonesia beserta informasi singkat mengenai makanan tersebut dengan cara meng-upload foto atau gambar makanan ke dalam dashboard. Yang menjadi business impact utama pada dashboard ini adalah:

  • Memperkenalkan jenis makanan tradisional Indonesia melalui gambar / foto makanan yang di-upload oleh user.
  • Sosialisasi serta edukasi atas informasi makanan tradisional kepada user seperti asal daerah dan penjelasan singkat mengenai makanan tersebut.

Jika sebelumnya saat kita tidak tahu jenis makanan, kita hanya bertanya kepada orang yang tahu kemudian mencarinya di mesin pencarian seperti Google. Bahkan mungkin, saat ini Google juga sudah memiliki fitur image recognition untuk mengenali makanan yang di-upload. Namun, yang menjadi kelebihan dashboard ini adalah penggunaan dashboard ini sangat praktis karena antara fitur yang satu dengan yang lain saling terintegrasi. Hanya dengan meng-upload suatu gambar makanan maka user dapat memperoleh informasi-informasi lain yang bermanfaat untuk menambah pengetahuan seperti bagaimana mengucapkan atau melafalkan nama makanan tersebut, bagaimana nama makanan tersebut dituliskan dalam aksara-aksara bahasa lain bahkan asal daerah dari makanan tradisional tersebut yang divisualisasikan dalam bentuk maps Indonesia.

Dashboard ini sangat cocok untuk pengguna introvert dan memiliki curiosity yang tinggi pada dunia kuliner, yaitu seseorang yang sangat mencintai dunia kuliner dan selalu tertarik dengan makanan-makanan yang baru dikenal, seperti makanan tradisional Indonesia yang beragam ini. Jika orang seperti itu menemui makanan tradisional yang baru pertama kali dilihatnya saat berkunjung ke suatu daerah, dan ingin mencari tahu makanan tersebut, namun malu atau tidak berani bertanya kepada orang lain, maka dengan teknologi pada dashboard ini yang mampu mengenali makanan di depannya dan mendapatkan banyak informasi atas makanan tersebut.

Melalui fitur-fitur tersebut, dashboard ini ke depannya juga bisa dimanfaatkan dan dikembangkan juga oleh restoran di tiap-tiap kota asal makanan tradisional tersebut serta penjual bumbu masak siap saji yang menjual bumbu makanan tradisional sehingga dapat meningkatkan perekonomian. Juga dengan melihat maps Indonesia, maka user bisa mendapatkan gambaran tentang luasnya negara Indonesia yang berpotensi juga dapat mengenali kebudayaan Indonesia yang sangat kaya. Hal ini dapat membuat user tertarik untuk mempelajari kebudayaan-kebudayaan dari kota-kota di Indonesia yang nantinya dapat memungkinkan juga untuk menjalin kerjasama dengan agen travel misalnya, dimana hal tersebut dapat mendorong pariwisata Indonesia. Contohnya: jika user upload makanan Ayam Betutu dan dashboard mampu mengenali makanan Ayam Betutu tersebut, dashboard juga dapat terintegrasi dengan platform kuliner seperti gofood, sehingga memberikan rekomendasi restoran yang autentik menjual Ayam Betutu di Bali yang merupakan daerah asal dari makanan tradisional tersebut.

4 Target User

Target user untuk project ini adalah masyarakat umum khususnya generasi muda dan wisatawan asing yang ingin mengenal lebih dalam mengenai makanan tradisional indonesia baik itu informasi mengenai makanan tersebut maupun jika tertarik untuk membelinya pada saat berkunjung ke suatu daerah wisata.

Misalnya user adalah wisatawan asing yang baru pertama kali datang ke Indonesia dan mendapatkan makanan tradisional kemudian tertarik dengan makanan tersebut seperti namanya dan informasi lainnya, maka user dapat masuk ke dashboard ini kemudian mengupload foto dari makanan tersebut, kemudian dashboard ini akan menampilkan output berupa nama makanan beserta informasi-informasi mengenai makanan tersebut yang berguna untuk menambah pengetahuan user tentang kuliner tradisional Indonesia serta menimbulkan minat lebih terhadap makanan tradisional dari daerah-daerah lain di Indonesia dan tertarik juga untuk melakukan wisata atau kunjungan ke daerah tersebut sehingga dapat meningkatkan pariwisata Indonesia.

User yang tertarik mengetahui informasi mengenai makanan tradisional dapat menggunakan dashboard ini karena lebih efisien dalam memberikan informasi dibandingkan dengan melakukan pencarian via website seperti google atau bertanya kepada orang lain (dalam hal ini warga indonesia) yang mungkin saja juga tidak tahu detail mengenai makanan tradisional tersebut. Karena jika mencari via website maka hasil dari pencarian google terlalu luas karena hanya berdasarkan keyword yang dimasukkan. Tetapi jika menggunakan dashboard ini maka hanya dengan memasukkan foto suatu makanan maka dashboard ini dapat menampilkan informasi yang spesifik mengenai makanan tersebut.

Target user dapat ditambahkan yaitu restoran atau pelaku bisnis kuliner yang ingin mempromosikan makanan tradisional mereka juga bisa menjadi target yang potensial.

5 Implementasi ke Business Serupa

Implementasi dashboard pengenalan gambar untuk kebudayaan Indonesia yang lain seperti alat musik tradisional, jenis-jenis batik, dan pakaian daerah.

6 Tujuan/Output dari Project

Dashboard klasifikasi gambar untuk mengenali jenis makanan dengan langkah seperti berikut:

  • User input gambar makanan dengan cara klik tombol upload
  • Dashboard mengeluarkan output berupa nama makanan Indonesia beserta informasinya

7 Data Collection

Saya menggunakan data gambar tentang makanan tradisional Indonesia yang diambil dari website Kaggle dengan judul Traditional Food Knowledge of Indonesia (https://www.kaggle.com/datasets/arizbw/traditional-food-knowledge-of-indonesia/data). Data Food sudah terbagi ke dalam data dev, data train dan data test. Dataset ini terdiri dari 1150 baris dan 38 kolom. 1150 menunjukkan jumlah data makanan pada dataset ini yang nantinya hanya terbagi menjadi 35 jenis makanan saja. 38 kolom terdiri dari Image.Index, Patient.ID, Finding.Labels dan 35 jenis makanan.

Berikut ini merupakan data gambar dari masing-masing dataset baik itu data dev, data train, data test yang sudah dibagi ke dalam folder-folder di bawah ini:

Dan untuk setiap masing-masing folder data tersebut memiliki gambar seluruh jenis makanan yang ada pada dataset. Berikut adalah isi dari masing-masing folder tersebut:

Namun pada project ini kita hanya fokus pada data train dan data test saja.

Berikut ini salah satu contoh gambar dari salah satu jenis makanan pada dataset tersebut, yaitu gambar gado-gado.

Terkait proses berikutnya untuk memperkaya jumlah gambar agar performa model lebih baik, akan dilakukan proses data augmentation pada tahapan data pre-processing sebelum masuk pembuatan model.

Kita mulai dengan membaca dataset data train.csv dan test.csv dan kita simpan dalam variabel train dan test.

train <- read.csv("train.csv")
test <- read.csv("test.csv")

Kita coba lihat beberapa data pertama dari data train.

# your code here
head(train)

8 Data Preparation

8.1 Library And Setup

Disini kita akan menginstal package pillow di conda environment untuk memanipulasi data gambar. Berikut instruksi singkat tentang cara membuat conda environment baru dengan tensorflow dan pillow di dalamnya.

  1. Buka terminal, baik di command prompt anaconda atau langsung di RStudio.

  1. Buat conda environment baru dengan menjalankan perintah berikut:

conda create -n tf_image python=3.7

  1. Aktifkan conda environment dengan menjalankan perintah berikut:

conda activate tf_image

  1. Install package tensorflow ke environment dengan menjalankan perintah berikut:

conda install -c conda-forge tensorflow=2

  1. Pasang package pillow dengan menjalankan perintah berikut:

pip install pillow

  1. Langkah selanjutnya cukup panggil conda environment menggunakan fungsi reticulate::use_python() dan masukkan lokasi python dari environment tf_image. Path atau lokasi environment dapat ditemukan dengan cara mengetik conda env list di terminal.

library(reticulate)

# Use python in your anaconda3 environment folder
reticulate::use_python("C:/Users/hp/miniconda3/envs/tf_image", required = T)

Kita panggil setiap library yang dibutuhkan untuk membangun dan mengevaluasi klasifikasi gambar:

# EDA
library(dplyr)
library(tidyverse)

# Image manipulation
library(imager)

# Neural Network
library(keras)
library(tensorflow)
tensorflow::set_random_seed(123)

# Model evaluation
library(caret)

Untuk memastikan berapa jumlah baris dan kolom pada data train dan data test menggunakan fungsi dim().

# your code here (cek dimensi)
dim(train)
#> [1] 564  20

Kemudian kita cek setiap tipe data dengan fungsi glimpse.

glimpse(train)
#> Rows: 564
#> Columns: 20
#> $ Image.Index         <chr> "IMG_6886.jpg", "IMG_7407.jpg", "IMG_7216.jpg", "I…
#> $ Patient.ID          <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
#> $ Finding.Labels      <chr> "gado-gado", "kue-lumpur", "kue-lumpur", "rawon-su…
#> $ asinan.jakarta      <int> 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ ayam.betutu         <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ bika.ambon          <int> 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0,…
#> $ cendol              <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ gado.gado           <int> 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,…
#> $ gudeg               <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ keladi              <int> 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ klappertart         <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ kolak               <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ kue.lumpur          <int> 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ nasi.goreng.kampung <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,…
#> $ papeda              <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ rawon.surabaya      <int> 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,…
#> $ rendang             <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ sate.ayam.madura    <int> 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0,…
#> $ soerabi             <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ soto.ayam.lamongan  <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…

Membuang kolom yang tidak diperlukan yaitu kolom Patient.ID pada data train dan data test:

train <- train %>% select(-Patient.ID)
train
test <- test %>% select(-Patient.ID)
test

Berikut ini adalah penjelasan terkait variabel-variabel tersebut:

  • Image.Index : nama file dari gambar dataset
  • Finding.Labels : kelas jenis makanan
  • Kolom sisanya menjelaskan tentang kelas jenis makanan yang direpresentasikan dalam bentuk one-hot-encoding untuk setiap baris datanya.

Yang menjadi target adalah kolom Finding.Labels.

Yang menjadi prediktor adalah data gambar pada folder train yang direferensikan pada kolom Image.Index.

Disini kita akan menghitung jumlah data gambar berdasarkan kelas target yaitu Finding.Labels pada data train:

train %>% 
  group_by(Finding.Labels) %>% 
  summarise(Jumlah_gambar = n()) %>% 
  ungroup()

Disini kita akan menghitung jumlah data gambar berdasarkan kelas target yaitu Finding.Labels pada data test:

test %>% 
  group_by(Finding.Labels) %>% 
  summarise(Jumlah_gambar = n()) %>% 
  ungroup()

Data tersebut sudah sesuai dengan tujuan project yang saya buat yaitu untuk melakukan klasifikasi jenis makanan dengan menggunakan data gambar

Karena data yang tersedia adalah data gambar dalam bentuk .jpg atau .png, dan bukan dalam bentuk pixel, oleh karena itu tahapan yang dilakukan pada proses data preparation antara lain: menggunakan fungsi flow_images_from_directory.

Untuk penggunaan flow_images_from_directory akan dilakukan pada tahapan data pre-processing setelah exploratory data analysis (EDA).

Cara untuk mempersiapkan datanya adalah meletakkan gambar yang memiliki kelas yang sama di satu folder yang sama (yaitu sebanyak 34 jenis makanan sesuai dengan data). Misalnya seperti ini:

9 Data Exploration

Mari kita jelajahi datanya terlebih dahulu sebelum membuat model. Dalam masalah klasifikasi gambar, merupakan praktik umum untuk meletakkan setiap gambar pada folder terpisah berdasarkan kelas/label target. Misalnya, di dalam folder train terdapat 34 folder berbeda. Dimana penjelasan mengenai proses foldering data-data tersebut sudah dijelaskan pada tahapan data collection.

Mari kita coba mendapatkan nama file setiap gambar. Pertama, kita perlu mencari folder masing-masing kelas target. Kode berikut akan memberikan nama folder di dalam folder train.

folder_list <- list.files("data/train/")

folder_list
#>  [1] "asinan-jakarta"      "ayam-betutu"         "bika-ambon"         
#>  [4] "cendol"              "gado-gado"           "gudeg"              
#>  [7] "keladi"              "klappertart"         "kolak"              
#> [10] "kue-lumpur"          "nasi-goreng-kampung" "papeda"             
#> [13] "rawon-surabaya"      "rendang"             "sate-ayam-madura"   
#> [16] "soerabi"             "soto-ayam-lamongan"

Menggabungkan nama folder dengan path (lokasi file) atau direktori folder train untuk mengakses konten di dalam setiap folder.

folder_path <- paste0("data/train/", folder_list, "/")

folder_path
#>  [1] "data/train/asinan-jakarta/"      "data/train/ayam-betutu/"        
#>  [3] "data/train/bika-ambon/"          "data/train/cendol/"             
#>  [5] "data/train/gado-gado/"           "data/train/gudeg/"              
#>  [7] "data/train/keladi/"              "data/train/klappertart/"        
#>  [9] "data/train/kolak/"               "data/train/kue-lumpur/"         
#> [11] "data/train/nasi-goreng-kampung/" "data/train/papeda/"             
#> [13] "data/train/rawon-surabaya/"      "data/train/rendang/"            
#> [15] "data/train/sate-ayam-madura/"    "data/train/soerabi/"            
#> [17] "data/train/soto-ayam-lamongan/"

Kita akan menggunakan fungsi map() untuk mengulang atau mengulangi dan mengumpulkan nama file untuk setiap folder (rawon, gado-gado, rujak cingur).

Fungsi map() akan menghasilkan sebuah data path / letak masing-masing gambar (index gambar), seperti : IMG_7841.jpg itu berada pada lokasi / path apa? Maka fungsi map() itulah yang mengeluarkan nilai / data tersebut.

Karena nama file tersebut masih tersedia dalam bentuk list karena menggunakan fungsi list.files(x) sedangkan kita membutuhkan datanya tidak dalam bentuk list untuk setiap gambarnya / path file nya, maka kita perlu menambahkan fungsi unlist().

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

# first 6 file name
head(file_name, 100)
#>   [1] "data/train/asinan-jakarta/IMG_7841.jpg"
#>   [2] "data/train/asinan-jakarta/IMG_7843.jpg"
#>   [3] "data/train/asinan-jakarta/IMG_7844.jpg"
#>   [4] "data/train/asinan-jakarta/IMG_7846.jpg"
#>   [5] "data/train/asinan-jakarta/IMG_7847.jpg"
#>   [6] "data/train/asinan-jakarta/IMG_7848.jpg"
#>   [7] "data/train/asinan-jakarta/IMG_7849.jpg"
#>   [8] "data/train/asinan-jakarta/IMG_7850.jpg"
#>   [9] "data/train/asinan-jakarta/IMG_7852.jpg"
#>  [10] "data/train/asinan-jakarta/IMG_7853.jpg"
#>  [11] "data/train/asinan-jakarta/IMG_7854.jpg"
#>  [12] "data/train/asinan-jakarta/IMG_7855.jpg"
#>  [13] "data/train/asinan-jakarta/IMG_7857.jpg"
#>  [14] "data/train/asinan-jakarta/IMG_7858.jpg"
#>  [15] "data/train/asinan-jakarta/IMG_7859.jpg"
#>  [16] "data/train/asinan-jakarta/IMG_7860.jpg"
#>  [17] "data/train/asinan-jakarta/IMG_7861.jpg"
#>  [18] "data/train/asinan-jakarta/IMG_7909.jpg"
#>  [19] "data/train/asinan-jakarta/IMG_7910.jpg"
#>  [20] "data/train/asinan-jakarta/IMG_7911.jpg"
#>  [21] "data/train/asinan-jakarta/IMG_7912.jpg"
#>  [22] "data/train/asinan-jakarta/IMG_7913.jpg"
#>  [23] "data/train/asinan-jakarta/IMG_7914.jpg"
#>  [24] "data/train/asinan-jakarta/IMG_7916.jpg"
#>  [25] "data/train/asinan-jakarta/IMG_7918.jpg"
#>  [26] "data/train/asinan-jakarta/IMG_7919.jpg"
#>  [27] "data/train/asinan-jakarta/IMG_7922.jpg"
#>  [28] "data/train/asinan-jakarta/IMG_7923.jpg"
#>  [29] "data/train/asinan-jakarta/IMG_7926.jpg"
#>  [30] "data/train/asinan-jakarta/IMG_7929.jpg"
#>  [31] "data/train/asinan-jakarta/IMG_7930.jpg"
#>  [32] "data/train/asinan-jakarta/IMG_7931.jpg"
#>  [33] "data/train/asinan-jakarta/IMG_7932.jpg"
#>  [34] "data/train/asinan-jakarta/IMG_7934.jpg"
#>  [35] "data/train/asinan-jakarta/IMG_7935.jpg"
#>  [36] "data/train/asinan-jakarta/IMG_7936.jpg"
#>  [37] "data/train/asinan-jakarta/IMG_7937.jpg"
#>  [38] "data/train/asinan-jakarta/IMG_7938.jpg"
#>  [39] "data/train/asinan-jakarta/IMG_7939.jpg"
#>  [40] "data/train/asinan-jakarta/IMG_7940.jpg"
#>  [41] "data/train/asinan-jakarta/IMG_7941.jpg"
#>  [42] "data/train/asinan-jakarta/IMG_7942.jpg"
#>  [43] "data/train/asinan-jakarta/IMG_7945.jpg"
#>  [44] "data/train/ayam-betutu/IMG_8037.jpg"   
#>  [45] "data/train/ayam-betutu/IMG_8039.jpg"   
#>  [46] "data/train/ayam-betutu/IMG_8040.jpg"   
#>  [47] "data/train/ayam-betutu/IMG_8042.jpg"   
#>  [48] "data/train/ayam-betutu/IMG_8046.jpg"   
#>  [49] "data/train/ayam-betutu/IMG_8048.jpg"   
#>  [50] "data/train/ayam-betutu/IMG_8050.jpg"   
#>  [51] "data/train/ayam-betutu/IMG_8051.jpg"   
#>  [52] "data/train/ayam-betutu/IMG_8052.jpg"   
#>  [53] "data/train/ayam-betutu/IMG_8053.jpg"   
#>  [54] "data/train/ayam-betutu/IMG_8054.jpg"   
#>  [55] "data/train/ayam-betutu/IMG_8055.jpg"   
#>  [56] "data/train/ayam-betutu/IMG_8056.jpg"   
#>  [57] "data/train/ayam-betutu/IMG_8234.jpg"   
#>  [58] "data/train/ayam-betutu/IMG_8237.jpg"   
#>  [59] "data/train/ayam-betutu/IMG_8238.jpg"   
#>  [60] "data/train/ayam-betutu/IMG_8239.jpg"   
#>  [61] "data/train/ayam-betutu/IMG_8240.jpg"   
#>  [62] "data/train/ayam-betutu/IMG_8241.jpg"   
#>  [63] "data/train/ayam-betutu/IMG_8242.jpg"   
#>  [64] "data/train/ayam-betutu/IMG_8243.jpg"   
#>  [65] "data/train/ayam-betutu/IMG_8244.jpg"   
#>  [66] "data/train/ayam-betutu/IMG_8246.jpg"   
#>  [67] "data/train/ayam-betutu/IMG_8247.jpg"   
#>  [68] "data/train/ayam-betutu/IMG_8248.jpg"   
#>  [69] "data/train/ayam-betutu/IMG_8249.jpg"   
#>  [70] "data/train/ayam-betutu/IMG_8250.jpg"   
#>  [71] "data/train/ayam-betutu/IMG_8254.jpg"   
#>  [72] "data/train/ayam-betutu/IMG_8257.jpg"   
#>  [73] "data/train/ayam-betutu/IMG_8258.jpg"   
#>  [74] "data/train/ayam-betutu/IMG_8259.jpg"   
#>  [75] "data/train/ayam-betutu/IMG_8260.jpg"   
#>  [76] "data/train/ayam-betutu/IMG_8261.jpg"   
#>  [77] "data/train/ayam-betutu/IMG_8262.jpg"   
#>  [78] "data/train/bika-ambon/IMG_7180.jpg"    
#>  [79] "data/train/bika-ambon/IMG_7181.jpg"    
#>  [80] "data/train/bika-ambon/IMG_7182.jpg"    
#>  [81] "data/train/bika-ambon/IMG_7185.jpg"    
#>  [82] "data/train/bika-ambon/IMG_7186.jpg"    
#>  [83] "data/train/bika-ambon/IMG_7187.jpg"    
#>  [84] "data/train/bika-ambon/IMG_7188.jpg"    
#>  [85] "data/train/bika-ambon/IMG_7189.jpg"    
#>  [86] "data/train/bika-ambon/IMG_7191.jpg"    
#>  [87] "data/train/bika-ambon/IMG_7192.jpg"    
#>  [88] "data/train/bika-ambon/IMG_7193.jpg"    
#>  [89] "data/train/bika-ambon/IMG_7194.jpg"    
#>  [90] "data/train/bika-ambon/IMG_7195.jpg"    
#>  [91] "data/train/bika-ambon/IMG_7197.jpg"    
#>  [92] "data/train/bika-ambon/IMG_7199.jpg"    
#>  [93] "data/train/bika-ambon/IMG_7332.jpg"    
#>  [94] "data/train/bika-ambon/IMG_7336.jpg"    
#>  [95] "data/train/bika-ambon/IMG_7338.jpg"    
#>  [96] "data/train/bika-ambon/IMG_7341.jpg"    
#>  [97] "data/train/bika-ambon/IMG_7342.jpg"    
#>  [98] "data/train/bika-ambon/IMG_7346.jpg"    
#>  [99] "data/train/bika-ambon/IMG_7347.jpg"    
#> [100] "data/train/bika-ambon/IMG_7348.jpg"

Kita juga dapat memeriksa 6 gambar terakhir.

# last 6 file name
tail(file_name)
#> [1] "data/train/soto-ayam-lamongan/IMG_8716.jpg"
#> [2] "data/train/soto-ayam-lamongan/IMG_8720.jpg"
#> [3] "data/train/soto-ayam-lamongan/IMG_8721.jpg"
#> [4] "data/train/soto-ayam-lamongan/IMG_8722.jpg"
#> [5] "data/train/soto-ayam-lamongan/IMG_8724.jpg"
#> [6] "data/train/soto-ayam-lamongan/IMG_8725.jpg"

Mari kita periksa berapa banyak gambar yang kita miliki.

length(file_name)
#> [1] 564

Untuk memeriksa isi file, kita dapat menggunakan fungsi load.image() dari paket imager. Misalnya, mari kita visualisasikan 15 gambar dari data secara acak.

# Randomly select image
set.seed(99)
sample_image <- sample(file_name, 15)

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

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

#> [[1]]
#> Image. Width: 2484 pix Height: 2516 pix Depth: 1 Colour channels: 3 
#> 
#> [[2]]
#> Image. Width: 1600 pix Height: 1742 pix Depth: 1 Colour channels: 3 
#> 
#> [[3]]
#> Image. Width: 2148 pix Height: 2138 pix Depth: 1 Colour channels: 3 
#> 
#> [[4]]
#> Image. Width: 1587 pix Height: 1548 pix Depth: 1 Colour channels: 3 
#> 
#> [[5]]
#> Image. Width: 2043 pix Height: 1692 pix Depth: 1 Colour channels: 3 
#> 
#> [[6]]
#> Image. Width: 1326 pix Height: 711 pix Depth: 1 Colour channels: 3 
#> 
#> [[7]]
#> Image. Width: 1967 pix Height: 1200 pix Depth: 1 Colour channels: 3 
#> 
#> [[8]]
#> Image. Width: 1589 pix Height: 944 pix Depth: 1 Colour channels: 3 
#> 
#> [[9]]
#> Image. Width: 2163 pix Height: 1349 pix Depth: 1 Colour channels: 3 
#> 
#> [[10]]
#> Image. Width: 957 pix Height: 782 pix Depth: 1 Colour channels: 3 
#> 
#> [[11]]
#> Image. Width: 2388 pix Height: 2252 pix Depth: 1 Colour channels: 3 
#> 
#> [[12]]
#> Image. Width: 1793 pix Height: 1058 pix Depth: 1 Colour channels: 3 
#> 
#> [[13]]
#> Image. Width: 1732 pix Height: 1761 pix Depth: 1 Colour channels: 3 
#> 
#> [[14]]
#> Image. Width: 2811 pix Height: 2544 pix Depth: 1 Colour channels: 3 
#> 
#> [[15]]
#> Image. Width: 1326 pix Height: 711 pix Depth: 1 Colour channels: 3

9.1 Check Image Dimension

Salah satu aspek penting dalam klasifikasi gambar adalah memahami dimensi input gambar. Kita perlu mengetahui distribusi dimensi gambar untuk membuat input dimensi yang tepat untuk membangun model deep learning. Mari kita periksa properti gambar pertama.

# Full Image Description
img <- load.image(file_name[1])
img
#> Image. Width: 3421 pix Height: 3046 pix Depth: 1 Colour channels: 3

Kita bisa mendapatkan informasi tentang dimensi gambar. Tinggi dan lebar mewakili tinggi dan lebar gambar dalam pixel. Colour channels mewakili apakah warna dalam format skala grayscale (colour channels = 1) atau dalam format RGB (colour channels = 3). Untuk mendapatkan nilai setiap dimensi, kita bisa menggunakan fungsi dim(). Ini akan mengeluarkan nilai tinggi, lebar, depth pixel, dan colour channels.

# Image Dimension
dim(img)
#> [1] 3421 3046    1    3

Jadi kita sudah berhasil memasukkan gambar dan mendapatkan dimensi gambarnya. Pada kode berikut, kita akan membuat fungsi yang akan langsung mendapatkan tinggi dan lebar gambar dan mengubahnya menjadi data.frame.

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

Sekarang kita akan mengambil sampel 100 gambar dari nama file dan mendapatkan tinggi dan lebar gambar. Kami menggunakan sampling di sini karena akan memakan waktu yang cukup lama untuk memuat semua gambar.

# Randomly get 100 sample images
set.seed(123)
sample_file <- sample(file_name, 100)

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

file_dim 

Sekarang mari kita dapatkan statistik untuk dimensi gambar.

summary(file_dim)
#>      height         width        filename        
#>  Min.   : 711   Min.   :1119   Length:100        
#>  1st Qu.:1200   1st Qu.:1600   Class :character  
#>  Median :1687   Median :2021   Mode  :character  
#>  Mean   :1686   Mean   :2003                     
#>  3rd Qu.:2204   3rd Qu.:2242                     
#>  Max.   :3172   Max.   :3414

Data gambar memiliki variasi dimensi yang besar. Beberapa gambar memiliki tinggi dengan minimal 711 pixel dan lebar dengan minimal 902 pixel, sementara gambar lainnya memiliki tinggi dengan maksimal hingga 3214 pixel dan lebar dengan maksimal hingga 3414 pixel. Memahami dimensi gambar akan membantu kita pada bagian proses selanjutnya yaitu data pre-processing.

10 Product Design

  • Menggunakan algoritma Neural Network untuk melakukan klasifikasi gambar.

  • Fitur yang akan ada di dashboard adalah:

10.1 Dashboard klasifikasi

Dashboard klasifikasi gambar untuk mengenali jenis makanan dengan langkah seperti berikut:

  • User input gambar makanan dengan cara klik tombol upload
  • Dashboard mengeluarkan output berupa nama makanan Indonesia beserta informasinya

10.2 Dashboard voice

Dashboard yang menampilkan suara pelafalan dari nama makanan yang keluar sebagai hasil klasifikasi. Skenarionya adalah ketika user memilih jenis makanan melalui dropdown menu misalnya rendang, kemudian ada tombol yang mampu mengeluarkan suara “rendang” (contohnya seperti google translate, ketika kita mengetikkan sebuah kata ada tombol icon sound dan ketika klik mengeluarkan suara pelafalan dari kata tersebut). Tujuannya adalah memudahkan wisatawan asing yang ingin membeli makanan tradisional tersebut tetapi tidak mengetahui bagaimana cara mengucapkan/melafalkan kata atau nama dari makanan tersebut.

10.3 Dashboard aksara

Dashboard yang menampilkan fitur aksara bahasa lain atas hasil klasifikasi jenis makanan. Datanya akan saya ambil dengan menggunakan google translate atas 34 jenis makanan tersebut. Hasil google translatenya saya simpan dalam bentuk gambar. Jadi skenarionya, user memilih jenis makanan melalui dropdown menu, misalnya soto, kemudian akan muncul aksara bahasa lain yg ditampilkan, misalnya dalam aksara mandarin, jepang, dll. Tujuannya adalah untuk wisatawan asing yang baru berkunjung ke Indonesia yang membutuhkan nama makanan dalam tulisan asli mereka seperti jepang, mandarin, thailand, dll.

10.4 Dashboard maps

Dashboard yang menampilkan maps Indonesia. Terdapat menu untuk memilih jenis makanan, misalnya saat user memilih jenis makanan rawon, akan keluar informasi rawon berasal dari kota surabaya. Kemudian ada visualisasi yang menunjukkan titik kota surabaya pada sebuah maps. Tujuannya memberikan edukasi kepada user dimana letak kota asal dari makanan tradisional tersebut sehingga memungkinkan user yang ingin mengunjungi daerah tersebut sehingga juga mendorong pariwisata Indonesia.

11 Data Pre-processing

Data pre-processing untuk klasifikasi gambar cukup sederhana dan dapat dilakukan dalam satu langkah di bagian berikut.

11.1 Data Augmentation

Berdasarkan rangkuman dimensi gambar sebelumnya, kita dapat menentukan dimensi input untuk model deep learning. Semua input gambar harus memiliki dimensi yang sama. Disini kita dapat menentukan ukuran input gambar, misalnya mengubah seluruh gambar menjadi 64 x 64 pixel. Proses ini akan serupa dengan kita mengubah ukuran gambar. Kita bisa menggunakan pilihan dimensi gambar lainnya, seperti 125 x 125 pixel atau bahkan 200 x 200 pixel. Dimensi yang lebih besar akan memiliki lebih banyak fitur tetapi juga membutuhkan waktu lebih lama untuk dilatih. Namun jika ukuran gambar terlalu kecil, kita akan kehilangan banyak informasi dari data tersebut. Jadi menyeimbangkan nilai trade-off ini adalah seni dalam tahapan data pre-processing dalam klasifikasi gambar.

Kita juga menetapkan ukuran batch untuk data sehingga model akan diperbarui setiap kali menyelesaikan pelatihan pada satu batch. Di sini, kami mengatur ukuran batch menjadi 47.

# Desired height and width of images
target_size <- c(500, 500)

# Batch size for training the model
batch_size <- 47

Karena jumlah training set yang saya miliki sedikit, saya akan membuat data buatan menggunakan metode yang disebut Augmentasi Gambar. Augmentasi gambar adalah salah satu teknik yang berguna dalam membangun model yang dapat meningkatkan ukuran training set tanpa mencari gambar baru. Tujuannya adalah untuk mengajarkan model tidak hanya dengan gambar aslinya tetapi juga modifikasi gambar, seperti membalik gambar, memutarnya, memperbesar, memotong gambar, dll. Hal ini akan menghasilkan model yang lebih robust. Kita bisa melakukan augmentasi data dengan menggunakan image generator dari keras.

Untuk melakukan augmentasi gambar, kita dapat memasukkan data ke dalam generator. Disini, kita akan membuat image generator dengan menggunakan keras dengan properti berikut:

  • Penskalaan nilai pixel dengan membagi nilai pixel dengan 255
  • Balikkan gambar secara horizontal
  • Balikkan gambar secara vertikal
  • Putar gambar dari 0 hingga 45 derajat
  • Memperbesar atau memperkecil sebesar 25% (memperbesar 75% atau 125%)
  • Gunakan 20% data sebagai kumpulan data validasi
# Image Generator
train_data_gen <- image_data_generator(rescale = 1/255, # Scaling pixel value
                                       horizontal_flip = T, # Flip image horizontally
                                       vertical_flip = T, # Flip image vertically 
                                       rotation_range = 45, # Rotate image from 0 to 45 degrees
                                       zoom_range = 0.25, # Zoom in or zoom out range
                                       validation_split = 0.2 # 20% data as validation data
                                       )
# Generator for data validation
test_data_gen <- image_data_generator(rescale = 1/255) # Rescale

Sekarang kita perlu mengambil data gambar dari tiap folder baik itu data train, data val maupun data test. Data train dan data validation diambil datanya pada pada path data/train/. Sedangkan data test kita ambil datanya pada path data/test/. Untuk mengambil data pada direktori tersebut kita menggunakan fungsi flow_images_from_directory().

Dari proses ini akan diperoleh gambar augmented, baik untuk data train maupun data validasi. Sedangkan pada data test, hanya dilakukan rescale.

# Training Dataset
train_image_array_gen <- flow_images_from_directory(directory = "data/train/", # Folder of the data
                                                    target_size = target_size, # target of the image dimension (64 x 64)  
                                                    color_mode = "rgb", # use RGB color
                                                    batch_size = batch_size, 
                                                    seed = 123,  # set random seed
                                                    subset = "training", # declare that this is for training data
                                                    generator = train_data_gen
                                                    )
#> Found 458 images belonging to 17 classes.
# Validation Dataset
val_image_array_gen <- flow_images_from_directory(directory = "data/train/",
                                                  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
                                                  )
#> Found 106 images belonging to 17 classes.
test_image_array_gen <- flow_images_from_directory(directory = "data/test/",
                                                  target_size = target_size, 
                                                  color_mode = "rgb", 
                                                  batch_size = batch_size,
                                                  seed = 123,
                                                  generator = test_data_gen
                                                  )
#> Found 152 images belonging to 17 classes.

Disini kita akan mengumpulkan beberapa informasi dari generator dan memeriksa proporsi kelas dari kumpulan data train. Indeks sesuai dengan setiap label variabel target dan diurutkan berdasarkan abjad (asinan jakarta, ayam betutu, bika ambon).

# 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.07641921 0.06113537 0.05021834 0.05676856 0.06986900 0.04366812 0.06331878 
#>          7          8          9         10         11         12         13 
#> 0.06113537 0.06550218 0.05895197 0.05676856 0.04585153 0.06768559 0.05676856 
#>         14         15         16 
#> 0.06550218 0.05240175 0.04803493

12 Convolutional Neural Network

Convolutional Neural Network atau Layer Konvolusional adalah layer yang populer untuk klasifikasi gambar. Jika kita ingat, gambar hanyalah sebuah array 2 dimensi dengan tinggi dan lebar tertentu. Misalnya, gambar dengan ukuran 64 x 64 pixel berarti gambar tersebut memiliki 4096 pixel yang didistribusikan dalam array 64 x 64, bukan vektor berdimensi tunggal. Manfaat menggunakan gambar sebagai array 2D adalah kita dapat mengekstrak fitur-fitur tertentu dari gambar seperti bentuk hidung, bentuk mata, tangan, dll.

Ambil contoh berikut dari setosa.io. Kita memiliki gambar dan representasi array 2D-nya. Nilai pada array adalah nilai pixel dari gambar, nilai yang lebih tinggi berarti pixel yang lebih terang.

Untuk mengekstrak fitur dari gambar, kita membuat sesuatu yang disebut kernel filter. Filter adalah array dengan ukuran tertentu, misalnya array 3 x 3 untuk menangkap fitur dari suatu gambar. Pada gambar berikut, segi empat mengilustrasikan kernel filter tunggal.

Misalnya sisi kiri gambar berikut adalah gambar berukuran 5 x 5. Kernel mempunyai bobot yang akan menangkap fitur-fitur tertentu, yang dalam contoh ini adalah fitur X yang ditandai dengan nilai 1 sehingga menghasilkan bentuk X. Fitur baru yang disebut convoluted feature merupakan produk bagian image dengan fitur kernel. Semakin mirip bagian image dengan kernel, semakin tinggi nilai convoluted feature.

Kernel akan bergerak ke samping ke kanan untuk menangkap setiap bagian gambar untuk membuat convoluted feature baru. Jika kernel sudah sampai di pinggir, turunkan 1 baris ke bawah dan lanjutkan prosesnya. Prosesnya diilustrasikan sebagai berikut.

Untuk menyorot fitur yang paling penting dan juga memperkecil dimensi convoluted feature, kita dapat menggunakan metode yang disebut Max pooling yang hanya mengambil nilai maksimum dari window / jendela tertentu. Misalnya pada array kiri atas yang berisi nilai 1, 1, 5, dan 6 hanya mengambil nilai maksimal yaitu 6.

Di bawah ini adalah ilustrasi melakukan max pooling 2 x 2 area pooling di sepanjang bagian. Max pooling hanya mengambil nilai maksimum dari setiap area pooling.

Untuk membuat array 2D yang diekstraksi menjadi array 1D, kita menggunakan flattening layer sehingga kita dapat terus menggunakan dense layer yang terhubung penuh dan ke output layer.

Gambar berikut mengilustrasikan model deep learning lengkap dengan CNN, max pooling, dan dense layer yang terhubung sepenuhnya.

13 Model Architecture

Kita bisa mulai membangun arsitektur model untuk deep learning. Kita akan membangun model sederhana terlebih dahulu dengan lapisan berikut:

  • Convolutional layer untuk mengekstrak fitur dari gambar 2D dengan fungsi aktivasi relu
  • Max Pooling layer untuk menurunkan sampel fitur gambar
  • Flattening layer untuk meratakan data dari array 2D ke array 1D
  • Dense layer untuk menangkap lebih banyak informasi
  • Dense layer untuk output dengan fungsi aktivasi softmax

Jangan lupa untuk mengatur ukuran input pada layer pertama. Jika gambar input dalam RGB, atur angka akhir menjadi 3, yaitu jumlah color channels. Jika gambar input dalam grayscale, atur angka akhir menjadi 1.

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

model <- keras_model_sequential(name = "simple_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 = "softmax",
              name = "Output")
  
model
#> Model: "simple_model"
#> ________________________________________________________________________________
#>  Layer (type)                       Output Shape                    Param #     
#> ================================================================================
#>  conv2d (Conv2D)                    (None, 500, 500, 16)            448         
#>  max_pooling2d (MaxPooling2D)       (None, 250, 250, 16)            0           
#>  flatten (Flatten)                  (None, 1000000)                 0           
#>  dense (Dense)                      (None, 16)                      16000016    
#>  Output (Dense)                     (None, 17)                      289         
#> ================================================================================
#> Total params: 16,000,753
#> Trainable params: 16,000,753
#> Non-trainable params: 0
#> ________________________________________________________________________________

Seperti yang kita lihat, kita mulai dengan memasukkan data gambar berukuran 500 x 500 pixel ke dalam convolusional layer, yang memiliki 16 filter untuk mengekstrak fitur dari gambar. Argumen padding = same digunakan untuk menjaga dimensi fitur menjadi 500 x 500 pixel setelah diekstraksi. Kita kemudian melakukan downsample atau hanya mengambil nilai maksimal untuk setiap pooling area 2x2 sehingga datanya kini hanya berukuran 250 x 250 pixel dengan 16 filter. Setelah itu, dari 250 x 250 pixel kita ratakan atau flatten array 2D menjadi array 1D dalam bentuk 250 x 250 x 16 = 1000000 node. Selanjutnya kita dapat mengekstrak informasi menggunakan dense layer sederhana dan menyelesaikannya dengan mengalirkan informasi ke output layer, yang akan ditransformasikan menggunakan fungsi aktivasi softmax untuk mendapatkan probabilitas setiap kelas sebagai output.

14 Model Fitting

Kita dapat mulai memasukkan data ke dalam model. Jangan lupa untuk mengkompilasi model dengan menentukan loss function dan optimizer. Sebagai permulaan, kami akan menggunakan 30 epoch untuk melatih data. Untuk klasifikasi multilabel, kita akan menggunakan categorical cross-entropy sebagai loss function. Untuk contoh ini, kita menggunakan adam optimizer dengan learning rate 0,01. Kita juga akan mengevaluasi model dengan data validasi dari generator.

model %>% 
  compile(
    loss = "categorical_crossentropy",
    optimizer = optimizer_adam(learning_rate = 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 = 20, 
  
  # validation data
  validation_data = val_image_array_gen,
  validation_steps = as.integer(valid_samples / batch_size)
)
#> Epoch 1/20
#> 9/9 - 67s - loss: 145.3683 - accuracy: 0.0535 - val_loss: 2.8337 - val_accuracy: 0.0745 - 67s/epoch - 7s/step
#> Epoch 2/20
#> 9/9 - 53s - loss: 3.0490 - accuracy: 0.0608 - val_loss: 2.8337 - val_accuracy: 0.0532 - 53s/epoch - 6s/step
#> Epoch 3/20
#> 9/9 - 52s - loss: 2.8972 - accuracy: 0.0706 - val_loss: 2.8310 - val_accuracy: 0.0638 - 52s/epoch - 6s/step
#> Epoch 4/20
#> 9/9 - 51s - loss: 2.8310 - accuracy: 0.0730 - val_loss: 2.8318 - val_accuracy: 0.0745 - 51s/epoch - 6s/step
#> Epoch 5/20
#> 9/9 - 50s - loss: 2.8523 - accuracy: 0.0560 - val_loss: 2.8276 - val_accuracy: 0.0745 - 50s/epoch - 6s/step
#> Epoch 6/20
#> 9/9 - 51s - loss: 2.8286 - accuracy: 0.0706 - val_loss: 2.8271 - val_accuracy: 0.0745 - 51s/epoch - 6s/step
#> Epoch 7/20
#> 9/9 - 51s - loss: 2.8278 - accuracy: 0.0730 - val_loss: 2.8262 - val_accuracy: 0.0745 - 51s/epoch - 6s/step
#> Epoch 8/20
#> 9/9 - 50s - loss: 2.8282 - accuracy: 0.0730 - val_loss: 2.8241 - val_accuracy: 0.0638 - 50s/epoch - 6s/step
#> Epoch 9/20
#> 9/9 - 50s - loss: 2.8278 - accuracy: 0.0730 - val_loss: 2.8288 - val_accuracy: 0.0638 - 50s/epoch - 6s/step
#> Epoch 10/20
#> 9/9 - 51s - loss: 2.8244 - accuracy: 0.0779 - val_loss: 2.8243 - val_accuracy: 0.0638 - 51s/epoch - 6s/step
#> Epoch 11/20
#> 9/9 - 50s - loss: 2.8250 - accuracy: 0.0657 - val_loss: 2.8238 - val_accuracy: 0.0745 - 50s/epoch - 6s/step
#> Epoch 12/20
#> 9/9 - 50s - loss: 2.8278 - accuracy: 0.0706 - val_loss: 2.8235 - val_accuracy: 0.0745 - 50s/epoch - 6s/step
#> Epoch 13/20
#> 9/9 - 50s - loss: 2.8253 - accuracy: 0.0681 - val_loss: 2.8293 - val_accuracy: 0.0638 - 50s/epoch - 6s/step
#> Epoch 14/20
#> 9/9 - 51s - loss: 2.8225 - accuracy: 0.0730 - val_loss: 2.8215 - val_accuracy: 0.0745 - 51s/epoch - 6s/step
#> Epoch 15/20
#> 9/9 - 50s - loss: 2.8250 - accuracy: 0.0730 - val_loss: 2.8141 - val_accuracy: 0.0638 - 50s/epoch - 6s/step
#> Epoch 16/20
#> 9/9 - 50s - loss: 2.8212 - accuracy: 0.0706 - val_loss: 2.8243 - val_accuracy: 0.0638 - 50s/epoch - 6s/step
#> Epoch 17/20
#> 9/9 - 50s - loss: 2.8236 - accuracy: 0.0730 - val_loss: 2.8315 - val_accuracy: 0.0745 - 50s/epoch - 6s/step
#> Epoch 18/20
#> 9/9 - 50s - loss: 2.8263 - accuracy: 0.0681 - val_loss: 2.8288 - val_accuracy: 0.0745 - 50s/epoch - 6s/step
#> Epoch 19/20
#> 9/9 - 51s - loss: 2.8252 - accuracy: 0.0730 - val_loss: 2.8263 - val_accuracy: 0.0745 - 51s/epoch - 6s/step
#> Epoch 20/20
#> 9/9 - 50s - loss: 2.8278 - accuracy: 0.0608 - val_loss: 2.8122 - val_accuracy: 0.0851 - 50s/epoch - 6s/step
plot(history)

15 Model Evaluation

Sekarang kita akan mengevaluasi lebih lanjut dan memperoleh confusion matrix menggunakan data test dari generator. Pertama, kita perlu mendapatkan nama file gambar yang digunakan sebagai data test / data uji. Dari nama file, kita akan mengekstrak label kategoris sebagai nilai sebenarnya dari variabel target.

test_data <- data.frame(file_name = paste0("data/test/", test_image_array_gen$filenames)) %>% 
  mutate(class = str_extract(file_name, "asinan-jakarta|ayam-betutu|bika-ambon|cendol|gado-gado|gudeg|keladi|klappertart|kolak|kue-lumpur|nasi-goreng-kampung|papeda|rawon-surabaya|rendang|sate-ayam-madura|soerabi|soto-ayam-lamongan"))

test_data

Apa yang harus dilakukan selanjutnya? Kita perlu memasukkan gambar ke dalam R dengan mengubah gambar menjadi array. Karena dimensi input kita untuk model CNN adalah gambar berukuran 500 x 500 pixel dengan 3 saluran warna (RGB), kita akan melakukan hal yang sama pada gambar data testing. Alasan menggunakan array adalah kita ingin memprediksi gambar asli yang baru dari folder sehingga kita tidak akan menggunakan generator gambar karena akan mengubah gambar dan tidak mencerminkan gambar sebenarnya.

# 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(test_data$file_name)

# Check dimension of testing data set
dim(test_x)
#> [1] 152 500 500   3

Data test terdiri dari 152 gambar dengan dimensi 500 x 500 pixel dan 3 saluran warna (RGB). Setelah kita menyiapkan data test, sekarang kita dapat melanjutkan untuk memprediksi label setiap gambar menggunakan model CNN kita.

pred_test <- model %>% predict(test_x) %>% k_argmax()
#> 5/5 - 3s - 3s/epoch - 628ms/step
head(pred_test, 10)
#> tf.Tensor([0 0 0 0 0 0 0 0 0 0], shape=(10), dtype=int64)

Untuk memudahkan interpretasi prediksi, kita akan melakukan encoding menjadi label kelas yang tepat.

# Convert encoding to label
decode <- function(x){
  case_when(x == 0 ~ "asinan-jakarta",
            x == 1 ~ "ayam-betutu",
            x == 2 ~ "bika-ambon",
            x == 3 ~ "cendol",
            x == 4 ~ "gado-gado",
            x == 5 ~ "gudeg",
            x == 6 ~ "keladi",
            x == 7 ~ "klappertart",
            x == 8 ~ "kolak",
            x == 9 ~ "kue-lumpur",
            x == 10 ~ "nasi-goreng-kampung",
            x == 11 ~ "papeda",
            x == 12 ~ "rawon-surabaya",
            x == 13 ~ "rendang",
            x == 14 ~ "sate-ayam-madura",
            x == 15 ~ "soerabi",
            x == 16 ~ "soto-ayam-lamongan"
            )
}

pred_test <- sapply(pred_test, decode) 

pred_test
#>   [1] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>   [5] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>   [9] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [13] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [17] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [21] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [25] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [29] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [33] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [37] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [41] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [45] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [49] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [53] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [57] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [61] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [65] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [69] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [73] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [77] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [81] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [85] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [89] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [93] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#>  [97] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [101] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [105] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [109] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [113] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [117] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [121] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [125] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [129] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [133] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [137] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [141] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [145] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"
#> [149] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "asinan-jakarta"

Terakhir, kita mengevaluasi model menggunakan confusion matrix.

confusionMatrix(as.factor(pred_test), 
                as.factor(test_data$class)
                )
#> Confusion Matrix and Statistics
#> 
#>                      Reference
#> Prediction            asinan-jakarta ayam-betutu bika-ambon cendol gado-gado
#>   asinan-jakarta                  10          11          7     14         6
#>   ayam-betutu                      0           0          0      0         0
#>   bika-ambon                       0           0          0      0         0
#>   cendol                           0           0          0      0         0
#>   gado-gado                        0           0          0      0         0
#>   gudeg                            0           0          0      0         0
#>   keladi                           0           0          0      0         0
#>   klappertart                      0           0          0      0         0
#>   kolak                            0           0          0      0         0
#>   kue-lumpur                       0           0          0      0         0
#>   nasi-goreng-kampung              0           0          0      0         0
#>   papeda                           0           0          0      0         0
#>   rawon-surabaya                   0           0          0      0         0
#>   rendang                          0           0          0      0         0
#>   sate-ayam-madura                 0           0          0      0         0
#>   soerabi                          0           0          0      0         0
#>   soto-ayam-lamongan               0           0          0      0         0
#>                      Reference
#> Prediction            gudeg keladi klappertart kolak kue-lumpur
#>   asinan-jakarta          4      9           8     6          4
#>   ayam-betutu             0      0           0     0          0
#>   bika-ambon              0      0           0     0          0
#>   cendol                  0      0           0     0          0
#>   gado-gado               0      0           0     0          0
#>   gudeg                   0      0           0     0          0
#>   keladi                  0      0           0     0          0
#>   klappertart             0      0           0     0          0
#>   kolak                   0      0           0     0          0
#>   kue-lumpur              0      0           0     0          0
#>   nasi-goreng-kampung     0      0           0     0          0
#>   papeda                  0      0           0     0          0
#>   rawon-surabaya          0      0           0     0          0
#>   rendang                 0      0           0     0          0
#>   sate-ayam-madura        0      0           0     0          0
#>   soerabi                 0      0           0     0          0
#>   soto-ayam-lamongan      0      0           0     0          0
#>                      Reference
#> Prediction            nasi-goreng-kampung papeda rawon-surabaya rendang
#>   asinan-jakarta                       10     10             12      13
#>   ayam-betutu                           0      0              0       0
#>   bika-ambon                            0      0              0       0
#>   cendol                                0      0              0       0
#>   gado-gado                             0      0              0       0
#>   gudeg                                 0      0              0       0
#>   keladi                                0      0              0       0
#>   klappertart                           0      0              0       0
#>   kolak                                 0      0              0       0
#>   kue-lumpur                            0      0              0       0
#>   nasi-goreng-kampung                   0      0              0       0
#>   papeda                                0      0              0       0
#>   rawon-surabaya                        0      0              0       0
#>   rendang                               0      0              0       0
#>   sate-ayam-madura                      0      0              0       0
#>   soerabi                               0      0              0       0
#>   soto-ayam-lamongan                    0      0              0       0
#>                      Reference
#> Prediction            sate-ayam-madura soerabi soto-ayam-lamongan
#>   asinan-jakarta                     6      10                 12
#>   ayam-betutu                        0       0                  0
#>   bika-ambon                         0       0                  0
#>   cendol                             0       0                  0
#>   gado-gado                          0       0                  0
#>   gudeg                              0       0                  0
#>   keladi                             0       0                  0
#>   klappertart                        0       0                  0
#>   kolak                              0       0                  0
#>   kue-lumpur                         0       0                  0
#>   nasi-goreng-kampung                0       0                  0
#>   papeda                             0       0                  0
#>   rawon-surabaya                     0       0                  0
#>   rendang                            0       0                  0
#>   sate-ayam-madura                   0       0                  0
#>   soerabi                            0       0                  0
#>   soto-ayam-lamongan                 0       0                  0
#> 
#> Overall Statistics
#>                                          
#>                Accuracy : 0.0658         
#>                  95% CI : (0.032, 0.1177)
#>     No Information Rate : 0.0921         
#>     P-Value [Acc > NIR] : 0.9018         
#>                                          
#>                   Kappa : 0              
#>                                          
#>  Mcnemar's Test P-Value : NA             
#> 
#> Statistics by Class:
#> 
#>                      Class: asinan-jakarta Class: ayam-betutu Class: bika-ambon
#> Sensitivity                        1.00000            0.00000           0.00000
#> Specificity                        0.00000            1.00000           1.00000
#> Pos Pred Value                     0.06579                NaN               NaN
#> Neg Pred Value                         NaN            0.92763           0.95395
#> Prevalence                         0.06579            0.07237           0.04605
#> Detection Rate                     0.06579            0.00000           0.00000
#> Detection Prevalence               1.00000            0.00000           0.00000
#> Balanced Accuracy                  0.50000            0.50000           0.50000
#>                      Class: cendol Class: gado-gado Class: gudeg Class: keladi
#> Sensitivity                0.00000          0.00000      0.00000       0.00000
#> Specificity                1.00000          1.00000      1.00000       1.00000
#> Pos Pred Value                 NaN              NaN          NaN           NaN
#> Neg Pred Value             0.90789          0.96053      0.97368       0.94079
#> Prevalence                 0.09211          0.03947      0.02632       0.05921
#> Detection Rate             0.00000          0.00000      0.00000       0.00000
#> Detection Prevalence       0.00000          0.00000      0.00000       0.00000
#> Balanced Accuracy          0.50000          0.50000      0.50000       0.50000
#>                      Class: klappertart Class: kolak Class: kue-lumpur
#> Sensitivity                     0.00000      0.00000           0.00000
#> Specificity                     1.00000      1.00000           1.00000
#> Pos Pred Value                      NaN          NaN               NaN
#> Neg Pred Value                  0.94737      0.96053           0.97368
#> Prevalence                      0.05263      0.03947           0.02632
#> Detection Rate                  0.00000      0.00000           0.00000
#> Detection Prevalence            0.00000      0.00000           0.00000
#> Balanced Accuracy               0.50000      0.50000           0.50000
#>                      Class: nasi-goreng-kampung Class: papeda
#> Sensitivity                             0.00000       0.00000
#> Specificity                             1.00000       1.00000
#> Pos Pred Value                              NaN           NaN
#> Neg Pred Value                          0.93421       0.93421
#> Prevalence                              0.06579       0.06579
#> Detection Rate                          0.00000       0.00000
#> Detection Prevalence                    0.00000       0.00000
#> Balanced Accuracy                       0.50000       0.50000
#>                      Class: rawon-surabaya Class: rendang
#> Sensitivity                        0.00000        0.00000
#> Specificity                        1.00000        1.00000
#> Pos Pred Value                         NaN            NaN
#> Neg Pred Value                     0.92105        0.91447
#> Prevalence                         0.07895        0.08553
#> Detection Rate                     0.00000        0.00000
#> Detection Prevalence               0.00000        0.00000
#> Balanced Accuracy                  0.50000        0.50000
#>                      Class: sate-ayam-madura Class: soerabi
#> Sensitivity                          0.00000        0.00000
#> Specificity                          1.00000        1.00000
#> Pos Pred Value                           NaN            NaN
#> Neg Pred Value                       0.96053        0.93421
#> Prevalence                           0.03947        0.06579
#> Detection Rate                       0.00000        0.00000
#> Detection Prevalence                 0.00000        0.00000
#> Balanced Accuracy                    0.50000        0.50000
#>                      Class: soto-ayam-lamongan
#> Sensitivity                            0.00000
#> Specificity                            1.00000
#> Pos Pred Value                             NaN
#> Neg Pred Value                         0.92105
#> Prevalence                             0.07895
#> Detection Rate                         0.00000
#> Detection Prevalence                   0.00000
#> Balanced Accuracy                      0.50000

16 Tuning the Model

16.1 Model Architecture

Mari kita lihat kembali arsitektur model kita. Jika Anda perhatikan, kami sebenarnya dapat mengekstrak lebih banyak informasi saat data masih dalam susunan gambar 2D. CNN pertama hanya mengekstrak fitur umum gambar kita dan kemudian melakukan downsampling menggunakan max pooling layer. Bahkan setelah pengumpulan, kami masih memiliki array 250 x 250 yang memiliki banyak informasi untuk diekstraksi sebelum meratakan datanya. Oleh karena itu, kita dapat menumpuk lebih banyak lapisan CNN ke dalam model sehingga lebih banyak informasi yang dapat ditangkap. Kita juga bisa meletakkan 2 layer CNN secara berurutan sebelum melakukan max pooling.

model
#> Model: "simple_model"
#> ________________________________________________________________________________
#>  Layer (type)                       Output Shape                    Param #     
#> ================================================================================
#>  conv2d (Conv2D)                    (None, 500, 500, 16)            448         
#>  max_pooling2d (MaxPooling2D)       (None, 250, 250, 16)            0           
#>  flatten (Flatten)                  (None, 1000000)                 0           
#>  dense (Dense)                      (None, 16)                      16000016    
#>  Output (Dense)                     (None, 17)                      289         
#> ================================================================================
#> Total params: 16,000,753
#> Trainable params: 16,000,753
#> Non-trainable params: 0
#> ________________________________________________________________________________

Berikut ini adalah arsitektur model yang telah ditingkatkan:

  • Layer konvolusional pertama untuk mengekstrak fitur dari gambar 2D dengan fungsi aktivasi relu
  • Layer konvolusional ke-2 untuk mengekstrak fitur dari gambar 2D dengan fungsi aktivasi relu Lapisan pengumpulan maksimal
  • Layer konvolusional ke-3 untuk mengekstrak fitur dari gambar 2D dengan fungsi aktivasi relu Lapisan pengumpulan maksimal
  • Layer konvolusional ke-4 untuk mengekstrak fitur dari gambar 2D dengan fungsi aktivasi relu Lapisan pengumpulan maksimal
  • Layer konvolusional ke-5 untuk mengekstrak fitur dari gambar 2D dengan fungsi aktivasi relu Lapisan pengumpulan maksimal
  • Meratakan layer (flatten layer) dari larik 2D ke larik 1D
  • Dense layer untuk menangkap lebih banyak informasi
  • Dense layer untuk output layer

Anda dapat bermain dan berkreasi dengan merancang arsitektur model Anda sendiri.

# tensorflow::tf$random$set_seed(123)

model_big <- keras_model_sequential() %>% 
  
  # First convolutional layer
  layer_conv_2d(filters = 16,
                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 = 16,
                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 = 32,
                kernel_size = c(3,3),
                padding = "same",
                activation = "relu"
                ) %>% 

  # Max pooling layer
  layer_max_pooling_2d(pool_size = c(2,2)) %>% 
  
  # Forth convolutional layer
  layer_conv_2d(filters = 32,
                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 = 32,
              activation = "relu") %>% 
  
  # Dropout Layer
  layer_dropout(rate = 0.5) %>%
  
  # Output layer
  layer_dense(name = "Output",
              units = output_n, 
              activation = "softmax")

model_big
#> Model: "sequential"
#> ________________________________________________________________________________
#>  Layer (type)                       Output Shape                    Param #     
#> ================================================================================
#>  conv2d_4 (Conv2D)                  (None, 500, 500, 16)            1216        
#>  conv2d_3 (Conv2D)                  (None, 500, 500, 16)            2320        
#>  max_pooling2d_3 (MaxPooling2D)     (None, 250, 250, 16)            0           
#>  conv2d_2 (Conv2D)                  (None, 250, 250, 32)            4640        
#>  max_pooling2d_2 (MaxPooling2D)     (None, 125, 125, 32)            0           
#>  conv2d_1 (Conv2D)                  (None, 125, 125, 32)            9248        
#>  max_pooling2d_1 (MaxPooling2D)     (None, 62, 62, 32)              0           
#>  flatten_1 (Flatten)                (None, 123008)                  0           
#>  dense_1 (Dense)                    (None, 32)                      3936288     
#>  dropout (Dropout)                  (None, 32)                      0           
#>  Output (Dense)                     (None, 17)                      561         
#> ================================================================================
#> Total params: 3,954,273
#> Trainable params: 3,954,273
#> Non-trainable params: 0
#> ________________________________________________________________________________

16.2 Model Fitting

Kita dapat sekali lagi memasukkan model ke dalam data. Kami akan membiarkan data dilatih dengan lebih banyak periode karena kami memiliki jumlah data yang sedikit. Misalnya, kita akan melatih data dengan 30 epoch. Kami juga akan menurunkan kecepatan pemelajaran dari 0,01 menjadi 0,001.

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

history <- model %>% 
  fit(
  # training data
  train_image_array_gen,
  
  # epochs
  steps_per_epoch = as.integer(train_samples / batch_size), 
  epochs = 30,
  
  # 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
)
#> Epoch 1/30
#> 
#> 1/9 [==>...........................] - ETA: 1:01 - loss: 2.7952 - accuracy: 0.1489
#> 2/9 [=====>........................] - ETA: 34s - loss: 2.8010 - accuracy: 0.1170 
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8147 - accuracy: 0.0993
#> 4/9 [============>.................] - ETA: 24s - loss: 2.8204 - accuracy: 0.0851
#> 5/9 [===============>..............] - ETA: 19s - loss: 2.8180 - accuracy: 0.0851
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8191 - accuracy: 0.0778
#> 7/9 [======================>.......] - ETA: 9s - loss: 2.8227 - accuracy: 0.0726 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8231 - accuracy: 0.0742
#> 9/9 [==============================] - ETA: 0s - loss: 2.8237 - accuracy: 0.0779
#> 9/9 [==============================] - 60s 7s/step - loss: 2.8237 - accuracy: 0.0779 - val_loss: 2.8272 - val_accuracy: 0.0745
#> Epoch 2/30
#> 
#> 1/9 [==>...........................] - ETA: 47s - loss: 2.8170 - accuracy: 0.1064
#> 2/9 [=====>........................] - ETA: 33s - loss: 2.8273 - accuracy: 0.0638
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8201 - accuracy: 0.0638
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8282 - accuracy: 0.0745
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8301 - accuracy: 0.0681
#> 6/9 [===================>..........] - ETA: 14s - loss: 2.8270 - accuracy: 0.0745
#> 7/9 [======================>.......] - ETA: 9s - loss: 2.8263 - accuracy: 0.0699 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8257 - accuracy: 0.0714
#> 9/9 [==============================] - ETA: 0s - loss: 2.8256 - accuracy: 0.0754
#> 9/9 [==============================] - 52s 6s/step - loss: 2.8256 - accuracy: 0.0754 - val_loss: 2.8253 - val_accuracy: 0.0851
#> Epoch 3/30
#> 
#> 1/9 [==>...........................] - ETA: 48s - loss: 2.8167 - accuracy: 0.0851
#> 2/9 [=====>........................] - ETA: 32s - loss: 2.8247 - accuracy: 0.0532
#> 3/9 [=========>....................] - ETA: 27s - loss: 2.8198 - accuracy: 0.0496
#> 4/9 [============>.................] - ETA: 22s - loss: 2.8184 - accuracy: 0.0745
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8218 - accuracy: 0.0766
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8221 - accuracy: 0.0709
#> 7/9 [======================>.......] - ETA: 9s - loss: 2.8246 - accuracy: 0.0790 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8225 - accuracy: 0.0798
#> 9/9 [==============================] - ETA: 0s - loss: 2.8216 - accuracy: 0.0779
#> 9/9 [==============================] - 51s 6s/step - loss: 2.8216 - accuracy: 0.0779 - val_loss: 2.8214 - val_accuracy: 0.0745
#> Epoch 4/30
#> 
#> 1/9 [==>...........................] - ETA: 46s - loss: 2.8308 - accuracy: 0.0638
#> 2/9 [=====>........................] - ETA: 32s - loss: 2.8298 - accuracy: 0.0532
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8225 - accuracy: 0.0638
#> 4/9 [============>.................] - ETA: 20s - loss: 2.8249 - accuracy: 0.0682
#> 5/9 [===============>..............] - ETA: 17s - loss: 2.8201 - accuracy: 0.0762
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8187 - accuracy: 0.0741
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8243 - accuracy: 0.0694 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8237 - accuracy: 0.0742
#> 9/9 [==============================] - ETA: 0s - loss: 2.8234 - accuracy: 0.0730
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8234 - accuracy: 0.0730 - val_loss: 2.8265 - val_accuracy: 0.0532
#> Epoch 5/30
#> 
#> 1/9 [==>...........................] - ETA: 46s - loss: 2.8371 - accuracy: 0.0426
#> 2/9 [=====>........................] - ETA: 22s - loss: 2.8133 - accuracy: 0.0610
#> 3/9 [=========>....................] - ETA: 24s - loss: 2.8205 - accuracy: 0.0620
#> 4/9 [============>.................] - ETA: 21s - loss: 2.8239 - accuracy: 0.0568
#> 5/9 [===============>..............] - ETA: 17s - loss: 2.8124 - accuracy: 0.0673
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8150 - accuracy: 0.0704
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8111 - accuracy: 0.0915 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8174 - accuracy: 0.0907
#> 9/9 [==============================] - ETA: 0s - loss: 2.8216 - accuracy: 0.0827
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8216 - accuracy: 0.0827 - val_loss: 2.8239 - val_accuracy: 0.0851
#> Epoch 6/30
#> 
#> 1/9 [==>...........................] - ETA: 45s - loss: 2.8234 - accuracy: 0.1064
#> 2/9 [=====>........................] - ETA: 34s - loss: 2.8135 - accuracy: 0.1064
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8057 - accuracy: 0.0993
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8139 - accuracy: 0.0798
#> 5/9 [===============>..............] - ETA: 17s - loss: 2.8180 - accuracy: 0.0762
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8170 - accuracy: 0.0778
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8197 - accuracy: 0.0726 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8163 - accuracy: 0.0797
#> 9/9 [==============================] - ETA: 0s - loss: 2.8216 - accuracy: 0.0779
#> 9/9 [==============================] - 51s 6s/step - loss: 2.8216 - accuracy: 0.0779 - val_loss: 2.8242 - val_accuracy: 0.0745
#> Epoch 7/30
#> 
#> 1/9 [==>...........................] - ETA: 45s - loss: 2.8502 - accuracy: 0.0426
#> 2/9 [=====>........................] - ETA: 32s - loss: 2.8436 - accuracy: 0.0638
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8403 - accuracy: 0.0780
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8232 - accuracy: 0.0957
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8183 - accuracy: 0.0936
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8203 - accuracy: 0.0889
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8190 - accuracy: 0.0883 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8204 - accuracy: 0.0879
#> 9/9 [==============================] - ETA: 0s - loss: 2.8220 - accuracy: 0.0827
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8220 - accuracy: 0.0827 - val_loss: 2.8189 - val_accuracy: 0.0745
#> Epoch 8/30
#> 
#> 1/9 [==>...........................] - ETA: 45s - loss: 2.8086 - accuracy: 0.0426
#> 2/9 [=====>........................] - ETA: 33s - loss: 2.8111 - accuracy: 0.0638
#> 3/9 [=========>....................] - ETA: 24s - loss: 2.8074 - accuracy: 0.0620
#> 4/9 [============>.................] - ETA: 21s - loss: 2.8124 - accuracy: 0.0511
#> 5/9 [===============>..............] - ETA: 17s - loss: 2.8108 - accuracy: 0.0673
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8112 - accuracy: 0.0667
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8173 - accuracy: 0.0694 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8216 - accuracy: 0.0824
#> 9/9 [==============================] - ETA: 0s - loss: 2.8242 - accuracy: 0.0754
#> 9/9 [==============================] - 51s 6s/step - loss: 2.8242 - accuracy: 0.0754 - val_loss: 2.8249 - val_accuracy: 0.0638
#> Epoch 9/30
#> 
#> 1/9 [==>...........................] - ETA: 47s - loss: 2.8299 - accuracy: 0.1064
#> 2/9 [=====>........................] - ETA: 31s - loss: 2.8444 - accuracy: 0.0851
#> 3/9 [=========>....................] - ETA: 27s - loss: 2.8300 - accuracy: 0.0709
#> 4/9 [============>.................] - ETA: 22s - loss: 2.8302 - accuracy: 0.0638
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8249 - accuracy: 0.0766
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8259 - accuracy: 0.0741
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8307 - accuracy: 0.0662 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8209 - accuracy: 0.0797
#> 9/9 [==============================] - ETA: 0s - loss: 2.8199 - accuracy: 0.0803
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8199 - accuracy: 0.0803 - val_loss: 2.8261 - val_accuracy: 0.0745
#> Epoch 10/30
#> 
#> 1/9 [==>...........................] - ETA: 46s - loss: 2.7970 - accuracy: 0.0426
#> 2/9 [=====>........................] - ETA: 33s - loss: 2.8033 - accuracy: 0.1170
#> 3/9 [=========>....................] - ETA: 23s - loss: 2.8118 - accuracy: 0.1008
#> 4/9 [============>.................] - ETA: 21s - loss: 2.8179 - accuracy: 0.1023
#> 5/9 [===============>..............] - ETA: 17s - loss: 2.8222 - accuracy: 0.0852
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8201 - accuracy: 0.0889
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8228 - accuracy: 0.0820 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8217 - accuracy: 0.0824
#> 9/9 [==============================] - ETA: 0s - loss: 2.8228 - accuracy: 0.0852
#> 9/9 [==============================] - 51s 6s/step - loss: 2.8228 - accuracy: 0.0852 - val_loss: 2.8228 - val_accuracy: 0.0851
#> Epoch 11/30
#> 
#> 1/9 [==>...........................] - ETA: 46s - loss: 2.8745 - accuracy: 0.0851
#> 2/9 [=====>........................] - ETA: 34s - loss: 2.8293 - accuracy: 0.0851
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8321 - accuracy: 0.0780
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8323 - accuracy: 0.0745
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8309 - accuracy: 0.0851
#> 6/9 [===================>..........] - ETA: 14s - loss: 2.8255 - accuracy: 0.0851
#> 7/9 [======================>.......] - ETA: 9s - loss: 2.8208 - accuracy: 0.0851 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8215 - accuracy: 0.0824
#> 9/9 [==============================] - ETA: 0s - loss: 2.8187 - accuracy: 0.0803
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8187 - accuracy: 0.0803 - val_loss: 2.8310 - val_accuracy: 0.0532
#> Epoch 12/30
#> 
#> 1/9 [==>...........................] - ETA: 35s - loss: 2.8075 - accuracy: 0.0571
#> 2/9 [=====>........................] - ETA: 32s - loss: 2.8206 - accuracy: 0.0732
#> 3/9 [=========>....................] - ETA: 27s - loss: 2.8243 - accuracy: 0.0543
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8190 - accuracy: 0.0568
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8174 - accuracy: 0.0628
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8184 - accuracy: 0.0667
#> 7/9 [======================>.......] - ETA: 9s - loss: 2.8209 - accuracy: 0.0757 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8214 - accuracy: 0.0769
#> 9/9 [==============================] - ETA: 0s - loss: 2.8244 - accuracy: 0.0730
#> 9/9 [==============================] - 51s 6s/step - loss: 2.8244 - accuracy: 0.0730 - val_loss: 2.8219 - val_accuracy: 0.0851
#> Epoch 13/30
#> 
#> 1/9 [==>...........................] - ETA: 47s - loss: 2.8097 - accuracy: 0.0851
#> 2/9 [=====>........................] - ETA: 33s - loss: 2.8102 - accuracy: 0.0957
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8137 - accuracy: 0.0709
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8159 - accuracy: 0.0798
#> 5/9 [===============>..............] - ETA: 17s - loss: 2.8224 - accuracy: 0.0807
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8283 - accuracy: 0.0778
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8284 - accuracy: 0.0726 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8280 - accuracy: 0.0769
#> 9/9 [==============================] - ETA: 0s - loss: 2.8276 - accuracy: 0.0730
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8276 - accuracy: 0.0730 - val_loss: 2.8196 - val_accuracy: 0.0851
#> Epoch 14/30
#> 
#> 1/9 [==>...........................] - ETA: 45s - loss: 2.8242 - accuracy: 0.0638
#> 2/9 [=====>........................] - ETA: 31s - loss: 2.8364 - accuracy: 0.0532
#> 3/9 [=========>....................] - ETA: 27s - loss: 2.8308 - accuracy: 0.0567
#> 4/9 [============>.................] - ETA: 22s - loss: 2.8262 - accuracy: 0.0638
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8264 - accuracy: 0.0681
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8173 - accuracy: 0.0816
#> 7/9 [======================>.......] - ETA: 9s - loss: 2.8220 - accuracy: 0.0729 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8249 - accuracy: 0.0745
#> 9/9 [==============================] - ETA: 0s - loss: 2.8232 - accuracy: 0.0757
#> 9/9 [==============================] - 52s 6s/step - loss: 2.8232 - accuracy: 0.0757 - val_loss: 2.8365 - val_accuracy: 0.0638
#> Epoch 15/30
#> 
#> 1/9 [==>...........................] - ETA: 47s - loss: 2.8271 - accuracy: 0.0851
#> 2/9 [=====>........................] - ETA: 32s - loss: 2.8241 - accuracy: 0.0957
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8319 - accuracy: 0.0993
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8226 - accuracy: 0.1011
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8253 - accuracy: 0.0851
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8263 - accuracy: 0.0816
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8261 - accuracy: 0.0852 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8220 - accuracy: 0.0797
#> 9/9 [==============================] - ETA: 0s - loss: 2.8240 - accuracy: 0.0754
#> 9/9 [==============================] - 51s 6s/step - loss: 2.8240 - accuracy: 0.0754 - val_loss: 2.8200 - val_accuracy: 0.0745
#> Epoch 16/30
#> 
#> 1/9 [==>...........................] - ETA: 46s - loss: 2.8829 - accuracy: 0.0638
#> 2/9 [=====>........................] - ETA: 32s - loss: 2.8482 - accuracy: 0.0638
#> 3/9 [=========>....................] - ETA: 27s - loss: 2.8289 - accuracy: 0.0638
#> 4/9 [============>.................] - ETA: 22s - loss: 2.8227 - accuracy: 0.0691
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8161 - accuracy: 0.0809
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8165 - accuracy: 0.0815
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8203 - accuracy: 0.0726 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8188 - accuracy: 0.0769
#> 9/9 [==============================] - ETA: 0s - loss: 2.8233 - accuracy: 0.0779
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8233 - accuracy: 0.0779 - val_loss: 2.8151 - val_accuracy: 0.0745
#> Epoch 17/30
#> 
#> 1/9 [==>...........................] - ETA: 35s - loss: 2.8636 - accuracy: 0.1429
#> 2/9 [=====>........................] - ETA: 33s - loss: 2.8580 - accuracy: 0.0732
#> 3/9 [=========>....................] - ETA: 29s - loss: 2.8471 - accuracy: 0.0543
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8354 - accuracy: 0.0625
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8258 - accuracy: 0.0762
#> 6/9 [===================>..........] - ETA: 14s - loss: 2.8299 - accuracy: 0.0778
#> 7/9 [======================>.......] - ETA: 9s - loss: 2.8247 - accuracy: 0.0757 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8250 - accuracy: 0.0769
#> 9/9 [==============================] - ETA: 0s - loss: 2.8238 - accuracy: 0.0730
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8238 - accuracy: 0.0730 - val_loss: 2.8275 - val_accuracy: 0.0851
#> Epoch 18/30
#> 
#> 1/9 [==>...........................] - ETA: 47s - loss: 2.8280 - accuracy: 0.1064
#> 2/9 [=====>........................] - ETA: 31s - loss: 2.8146 - accuracy: 0.0957
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8270 - accuracy: 0.0993
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8199 - accuracy: 0.0957
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8243 - accuracy: 0.0851
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8237 - accuracy: 0.0780
#> 7/9 [======================>.......] - ETA: 9s - loss: 2.8250 - accuracy: 0.0760 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8274 - accuracy: 0.0742
#> 9/9 [==============================] - ETA: 0s - loss: 2.8265 - accuracy: 0.0754
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8265 - accuracy: 0.0754 - val_loss: 2.8148 - val_accuracy: 0.0851
#> Epoch 19/30
#> 
#> 1/9 [==>...........................] - ETA: 35s - loss: 2.8171 - accuracy: 0.0857
#> 2/9 [=====>........................] - ETA: 35s - loss: 2.8033 - accuracy: 0.0854
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8201 - accuracy: 0.0698
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8206 - accuracy: 0.0852
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8278 - accuracy: 0.0673
#> 6/9 [===================>..........] - ETA: 14s - loss: 2.8227 - accuracy: 0.0741
#> 7/9 [======================>.......] - ETA: 9s - loss: 2.8215 - accuracy: 0.0726 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8228 - accuracy: 0.0742
#> 9/9 [==============================] - ETA: 0s - loss: 2.8232 - accuracy: 0.0730
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8232 - accuracy: 0.0730 - val_loss: 2.8317 - val_accuracy: 0.0638
#> Epoch 20/30
#> 
#> 1/9 [==>...........................] - ETA: 47s - loss: 2.8248 - accuracy: 0.0213
#> 2/9 [=====>........................] - ETA: 32s - loss: 2.8210 - accuracy: 0.0638
#> 3/9 [=========>....................] - ETA: 27s - loss: 2.8235 - accuracy: 0.0638
#> 4/9 [============>.................] - ETA: 22s - loss: 2.8176 - accuracy: 0.0798
#> 5/9 [===============>..............] - ETA: 16s - loss: 2.8146 - accuracy: 0.0807
#> 6/9 [===================>..........] - ETA: 12s - loss: 2.8196 - accuracy: 0.0704
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8249 - accuracy: 0.0757 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8210 - accuracy: 0.0742
#> 9/9 [==============================] - ETA: 0s - loss: 2.8205 - accuracy: 0.0779
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8205 - accuracy: 0.0779 - val_loss: 2.8223 - val_accuracy: 0.0851
#> Epoch 21/30
#> 
#> 1/9 [==>...........................] - ETA: 47s - loss: 2.8659 - accuracy: 0.0426
#> 2/9 [=====>........................] - ETA: 33s - loss: 2.8364 - accuracy: 0.0745
#> 3/9 [=========>....................] - ETA: 29s - loss: 2.8257 - accuracy: 0.0780
#> 4/9 [============>.................] - ETA: 21s - loss: 2.8197 - accuracy: 0.0852
#> 5/9 [===============>..............] - ETA: 17s - loss: 2.8263 - accuracy: 0.0717
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8235 - accuracy: 0.0704
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8207 - accuracy: 0.0789 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8229 - accuracy: 0.0797
#> 9/9 [==============================] - ETA: 0s - loss: 2.8222 - accuracy: 0.0779
#> 9/9 [==============================] - 51s 6s/step - loss: 2.8222 - accuracy: 0.0779 - val_loss: 2.8311 - val_accuracy: 0.0532
#> Epoch 22/30
#> 
#> 1/9 [==>...........................] - ETA: 47s - loss: 2.8480 - accuracy: 0.0426
#> 2/9 [=====>........................] - ETA: 32s - loss: 2.8248 - accuracy: 0.0851
#> 3/9 [=========>....................] - ETA: 27s - loss: 2.8298 - accuracy: 0.0780
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8280 - accuracy: 0.0798
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8306 - accuracy: 0.0681
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8301 - accuracy: 0.0674
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8295 - accuracy: 0.0694 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8258 - accuracy: 0.0742
#> 9/9 [==============================] - ETA: 0s - loss: 2.8266 - accuracy: 0.0681
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8266 - accuracy: 0.0681 - val_loss: 2.8256 - val_accuracy: 0.0851
#> Epoch 23/30
#> 
#> 1/9 [==>...........................] - ETA: 46s - loss: 2.8194 - accuracy: 0.0426
#> 2/9 [=====>........................] - ETA: 30s - loss: 2.8131 - accuracy: 0.0532
#> 3/9 [=========>....................] - ETA: 26s - loss: 2.8206 - accuracy: 0.0567
#> 4/9 [============>.................] - ETA: 22s - loss: 2.8251 - accuracy: 0.0691
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8260 - accuracy: 0.0681
#> 6/9 [===================>..........] - ETA: 12s - loss: 2.8225 - accuracy: 0.0704
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8200 - accuracy: 0.0789 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8236 - accuracy: 0.0742
#> 9/9 [==============================] - ETA: 0s - loss: 2.8247 - accuracy: 0.0754
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8247 - accuracy: 0.0754 - val_loss: 2.8241 - val_accuracy: 0.0638
#> Epoch 24/30
#> 
#> 1/9 [==>...........................] - ETA: 49s - loss: 2.8411 - accuracy: 0.0851
#> 2/9 [=====>........................] - ETA: 32s - loss: 2.8442 - accuracy: 0.0638
#> 3/9 [=========>....................] - ETA: 27s - loss: 2.8411 - accuracy: 0.0567
#> 4/9 [============>.................] - ETA: 20s - loss: 2.8391 - accuracy: 0.0682
#> 5/9 [===============>..............] - ETA: 17s - loss: 2.8369 - accuracy: 0.0628
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8292 - accuracy: 0.0704
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8297 - accuracy: 0.0662 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8307 - accuracy: 0.0632
#> 9/9 [==============================] - ETA: 0s - loss: 2.8296 - accuracy: 0.0681
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8296 - accuracy: 0.0681 - val_loss: 2.8214 - val_accuracy: 0.0851
#> Epoch 25/30
#> 
#> 1/9 [==>...........................] - ETA: 47s - loss: 2.8098 - accuracy: 0.0638
#> 2/9 [=====>........................] - ETA: 33s - loss: 2.8177 - accuracy: 0.0638
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8158 - accuracy: 0.0709
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8192 - accuracy: 0.0585
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8137 - accuracy: 0.0766
#> 6/9 [===================>..........] - ETA: 14s - loss: 2.8195 - accuracy: 0.0709
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8174 - accuracy: 0.0789 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8192 - accuracy: 0.0769
#> 9/9 [==============================] - ETA: 0s - loss: 2.8228 - accuracy: 0.0754
#> 9/9 [==============================] - 51s 6s/step - loss: 2.8228 - accuracy: 0.0754 - val_loss: 2.8312 - val_accuracy: 0.0851
#> Epoch 26/30
#> 
#> 1/9 [==>...........................] - ETA: 48s - loss: 2.8443 - accuracy: 0.0213
#> 2/9 [=====>........................] - ETA: 22s - loss: 2.8238 - accuracy: 0.0732
#> 3/9 [=========>....................] - ETA: 23s - loss: 2.8239 - accuracy: 0.0698
#> 4/9 [============>.................] - ETA: 21s - loss: 2.8161 - accuracy: 0.0852
#> 5/9 [===============>..............] - ETA: 17s - loss: 2.8182 - accuracy: 0.0897
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8187 - accuracy: 0.0889
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8251 - accuracy: 0.0852 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8209 - accuracy: 0.0824
#> 9/9 [==============================] - ETA: 0s - loss: 2.8241 - accuracy: 0.0779
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8241 - accuracy: 0.0779 - val_loss: 2.8201 - val_accuracy: 0.0745
#> Epoch 27/30
#> 
#> 1/9 [==>...........................] - ETA: 45s - loss: 2.8240 - accuracy: 0.0213
#> 2/9 [=====>........................] - ETA: 31s - loss: 2.8245 - accuracy: 0.0106
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8286 - accuracy: 0.0426
#> 4/9 [============>.................] - ETA: 20s - loss: 2.8369 - accuracy: 0.0455
#> 5/9 [===============>..............] - ETA: 17s - loss: 2.8358 - accuracy: 0.0538
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8342 - accuracy: 0.0593
#> 7/9 [======================>.......] - ETA: 9s - loss: 2.8259 - accuracy: 0.0662 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8280 - accuracy: 0.0687
#> 9/9 [==============================] - ETA: 0s - loss: 2.8254 - accuracy: 0.0730
#> 9/9 [==============================] - 51s 6s/step - loss: 2.8254 - accuracy: 0.0730 - val_loss: 2.8288 - val_accuracy: 0.0638
#> Epoch 28/30
#> 
#> 1/9 [==>...........................] - ETA: 46s - loss: 2.8289 - accuracy: 0.0638
#> 2/9 [=====>........................] - ETA: 22s - loss: 2.8287 - accuracy: 0.0854
#> 3/9 [=========>....................] - ETA: 24s - loss: 2.8243 - accuracy: 0.0853
#> 4/9 [============>.................] - ETA: 21s - loss: 2.8243 - accuracy: 0.0682
#> 5/9 [===============>..............] - ETA: 17s - loss: 2.8200 - accuracy: 0.0673
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8247 - accuracy: 0.0741
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8274 - accuracy: 0.0757 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8271 - accuracy: 0.0742
#> 9/9 [==============================] - ETA: 0s - loss: 2.8229 - accuracy: 0.0779
#> 9/9 [==============================] - 51s 6s/step - loss: 2.8229 - accuracy: 0.0779 - val_loss: 2.8183 - val_accuracy: 0.0745
#> Epoch 29/30
#> 
#> 1/9 [==>...........................] - ETA: 46s - loss: 2.7897 - accuracy: 0.0638
#> 2/9 [=====>........................] - ETA: 35s - loss: 2.7978 - accuracy: 0.1170
#> 3/9 [=========>....................] - ETA: 28s - loss: 2.8111 - accuracy: 0.1064
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8208 - accuracy: 0.0798
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8201 - accuracy: 0.0723
#> 6/9 [===================>..........] - ETA: 14s - loss: 2.8239 - accuracy: 0.0816
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8174 - accuracy: 0.0820 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8178 - accuracy: 0.0797
#> 9/9 [==============================] - ETA: 0s - loss: 2.8184 - accuracy: 0.0803
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8184 - accuracy: 0.0803 - val_loss: 2.8263 - val_accuracy: 0.0745
#> Epoch 30/30
#> 
#> 1/9 [==>...........................] - ETA: 46s - loss: 2.8554 - accuracy: 0.0638
#> 2/9 [=====>........................] - ETA: 33s - loss: 2.8372 - accuracy: 0.0638
#> 3/9 [=========>....................] - ETA: 27s - loss: 2.8369 - accuracy: 0.0567
#> 4/9 [============>.................] - ETA: 23s - loss: 2.8326 - accuracy: 0.0532
#> 5/9 [===============>..............] - ETA: 18s - loss: 2.8251 - accuracy: 0.0723
#> 6/9 [===================>..........] - ETA: 13s - loss: 2.8261 - accuracy: 0.0745
#> 7/9 [======================>.......] - ETA: 8s - loss: 2.8248 - accuracy: 0.0757 
#> 8/9 [=========================>....] - ETA: 4s - loss: 2.8234 - accuracy: 0.0742
#> 9/9 [==============================] - ETA: 0s - loss: 2.8220 - accuracy: 0.0779
#> 9/9 [==============================] - 50s 6s/step - loss: 2.8220 - accuracy: 0.0779 - val_loss: 2.8221 - val_accuracy: 0.0745
plot(history)

16.3 Model Evaluation

Sekarang kita akan mengevaluasi data lebih lanjut dan memperoleh confusion matrix untuk data validasi.

pred_test <- model_big %>% predict(test_x) %>% k_argmax()
#> 5/5 - 6s - 6s/epoch - 1s/step
head(pred_test, 10)
#> tf.Tensor([5 5 5 5 8 5 5 8 8 5], shape=(10), dtype=int64)
pred_test <- sapply(pred_test, decode) 

head(pred_test, 10)
#>  [1] "gudeg" "gudeg" "gudeg" "gudeg" "kolak" "gudeg" "gudeg" "kolak" "kolak"
#> [10] "gudeg"
confusionMatrix(as.factor(pred_test), 
                as.factor(test_data$class)
                )
#> Confusion Matrix and Statistics
#> 
#>                      Reference
#> Prediction            asinan-jakarta ayam-betutu bika-ambon cendol gado-gado
#>   asinan-jakarta                   0           0          0      0         0
#>   ayam-betutu                      0           0          0      1         0
#>   bika-ambon                       0           1          0      0         0
#>   cendol                           0           0          0      0         0
#>   gado-gado                        0           0          0      0         0
#>   gudeg                            7           8          7     12         5
#>   keladi                           0           0          0      0         0
#>   klappertart                      0           0          0      0         0
#>   kolak                            3           2          0      1         1
#>   kue-lumpur                       0           0          0      0         0
#>   nasi-goreng-kampung              0           0          0      0         0
#>   papeda                           0           0          0      0         0
#>   rawon-surabaya                   0           0          0      0         0
#>   rendang                          0           0          0      0         0
#>   sate-ayam-madura                 0           0          0      0         0
#>   soerabi                          0           0          0      0         0
#>   soto-ayam-lamongan               0           0          0      0         0
#>                      Reference
#> Prediction            gudeg keladi klappertart kolak kue-lumpur
#>   asinan-jakarta          0      0           0     0          0
#>   ayam-betutu             0      7           0     0          0
#>   bika-ambon              1      0           0     0          0
#>   cendol                  0      0           0     0          0
#>   gado-gado               0      0           0     0          0
#>   gudeg                   1      0           7     6          3
#>   keladi                  0      0           0     0          0
#>   klappertart             0      0           0     0          0
#>   kolak                   2      2           1     0          1
#>   kue-lumpur              0      0           0     0          0
#>   nasi-goreng-kampung     0      0           0     0          0
#>   papeda                  0      0           0     0          0
#>   rawon-surabaya          0      0           0     0          0
#>   rendang                 0      0           0     0          0
#>   sate-ayam-madura        0      0           0     0          0
#>   soerabi                 0      0           0     0          0
#>   soto-ayam-lamongan      0      0           0     0          0
#>                      Reference
#> Prediction            nasi-goreng-kampung papeda rawon-surabaya rendang
#>   asinan-jakarta                        0      0              0       0
#>   ayam-betutu                           0      7              2       0
#>   bika-ambon                            0      0              1       1
#>   cendol                                0      0              0       0
#>   gado-gado                             0      0              0       0
#>   gudeg                                10      0              5       8
#>   keladi                                0      0              0       0
#>   klappertart                           0      0              0       1
#>   kolak                                 0      3              1       2
#>   kue-lumpur                            0      0              0       0
#>   nasi-goreng-kampung                   0      0              0       0
#>   papeda                                0      0              0       0
#>   rawon-surabaya                        0      0              0       0
#>   rendang                               0      0              0       0
#>   sate-ayam-madura                      0      0              3       0
#>   soerabi                               0      0              0       0
#>   soto-ayam-lamongan                    0      0              0       1
#>                      Reference
#> Prediction            sate-ayam-madura soerabi soto-ayam-lamongan
#>   asinan-jakarta                     0       0                  0
#>   ayam-betutu                        0       0                  0
#>   bika-ambon                         0       6                  0
#>   cendol                             0       0                  0
#>   gado-gado                          1       0                  0
#>   gudeg                              1       3                 10
#>   keladi                             0       0                  0
#>   klappertart                        0       0                  0
#>   kolak                              4       1                  2
#>   kue-lumpur                         0       0                  0
#>   nasi-goreng-kampung                0       0                  0
#>   papeda                             0       0                  0
#>   rawon-surabaya                     0       0                  0
#>   rendang                            0       0                  0
#>   sate-ayam-madura                   0       0                  0
#>   soerabi                            0       0                  0
#>   soto-ayam-lamongan                 0       0                  0
#> 
#> Overall Statistics
#>                                           
#>                Accuracy : 0.0066          
#>                  95% CI : (0.0002, 0.0361)
#>     No Information Rate : 0.0921          
#>     P-Value [Acc > NIR] : 1               
#>                                           
#>                   Kappa : -0.0304         
#>                                           
#>  Mcnemar's Test P-Value : NA              
#> 
#> Statistics by Class:
#> 
#>                      Class: asinan-jakarta Class: ayam-betutu Class: bika-ambon
#> Sensitivity                        0.00000            0.00000           0.00000
#> Specificity                        1.00000            0.87943           0.93103
#> Pos Pred Value                         NaN            0.00000           0.00000
#> Neg Pred Value                     0.93421            0.91852           0.95070
#> Prevalence                         0.06579            0.07237           0.04605
#> Detection Rate                     0.00000            0.00000           0.00000
#> Detection Prevalence               0.00000            0.11184           0.06579
#> Balanced Accuracy                  0.50000            0.43972           0.46552
#>                      Class: cendol Class: gado-gado Class: gudeg Class: keladi
#> Sensitivity                0.00000         0.000000     0.250000       0.00000
#> Specificity                1.00000         0.993151     0.378378       1.00000
#> Pos Pred Value                 NaN         0.000000     0.010753           NaN
#> Neg Pred Value             0.90789         0.960265     0.949153       0.94079
#> Prevalence                 0.09211         0.039474     0.026316       0.05921
#> Detection Rate             0.00000         0.000000     0.006579       0.00000
#> Detection Prevalence       0.00000         0.006579     0.611842       0.00000
#> Balanced Accuracy          0.50000         0.496575     0.314189       0.50000
#>                      Class: klappertart Class: kolak Class: kue-lumpur
#> Sensitivity                    0.000000      0.00000           0.00000
#> Specificity                    0.993056      0.82192           1.00000
#> Pos Pred Value                 0.000000      0.00000               NaN
#> Neg Pred Value                 0.947020      0.95238           0.97368
#> Prevalence                     0.052632      0.03947           0.02632
#> Detection Rate                 0.000000      0.00000           0.00000
#> Detection Prevalence           0.006579      0.17105           0.00000
#> Balanced Accuracy              0.496528      0.41096           0.50000
#>                      Class: nasi-goreng-kampung Class: papeda
#> Sensitivity                             0.00000       0.00000
#> Specificity                             1.00000       1.00000
#> Pos Pred Value                              NaN           NaN
#> Neg Pred Value                          0.93421       0.93421
#> Prevalence                              0.06579       0.06579
#> Detection Rate                          0.00000       0.00000
#> Detection Prevalence                    0.00000       0.00000
#> Balanced Accuracy                       0.50000       0.50000
#>                      Class: rawon-surabaya Class: rendang
#> Sensitivity                        0.00000        0.00000
#> Specificity                        1.00000        1.00000
#> Pos Pred Value                         NaN            NaN
#> Neg Pred Value                     0.92105        0.91447
#> Prevalence                         0.07895        0.08553
#> Detection Rate                     0.00000        0.00000
#> Detection Prevalence               0.00000        0.00000
#> Balanced Accuracy                  0.50000        0.50000
#>                      Class: sate-ayam-madura Class: soerabi
#> Sensitivity                          0.00000        0.00000
#> Specificity                          0.97945        1.00000
#> Pos Pred Value                       0.00000            NaN
#> Neg Pred Value                       0.95973        0.93421
#> Prevalence                           0.03947        0.06579
#> Detection Rate                       0.00000        0.00000
#> Detection Prevalence                 0.01974        0.00000
#> Balanced Accuracy                    0.48973        0.50000
#>                      Class: soto-ayam-lamongan
#> Sensitivity                           0.000000
#> Specificity                           0.992857
#> Pos Pred Value                        0.000000
#> Neg Pred Value                        0.920530
#> Prevalence                            0.078947
#> Detection Rate                        0.000000
#> Detection Prevalence                  0.006579
#> Balanced Accuracy                     0.496429

17 Fine-Tuning dengan Transfer Learning

17.1 Architecture Model Transfer Learning

Disini saya membuat arsitektur model menggunakan metode transfer learning yang saya gunakan:

conv_base <- application_mobilenet(
  weights = "imagenet",
  include_top = FALSE, #including or not the densly connected classifier on top of the network.
  input_shape = c(target_size, 3)
)

conv_base
#> Model: "mobilenet_1.00_224"
#> ________________________________________________________________________________
#>  Layer (type)                  Output Shape               Param #    Trainable  
#> ================================================================================
#>  input_1 (InputLayer)          [(None, 500, 500, 3)]      0          Y          
#>  conv1 (Conv2D)                (None, 250, 250, 32)       864        Y          
#>  conv1_bn (BatchNormalization)  (None, 250, 250, 32)      128        Y          
#>  conv1_relu (ReLU)             (None, 250, 250, 32)       0          Y          
#>  conv_dw_1 (DepthwiseConv2D)   (None, 250, 250, 32)       288        Y          
#>  conv_dw_1_bn (BatchNormalizat  (None, 250, 250, 32)      128        Y          
#>  ion)                                                                           
#>  conv_dw_1_relu (ReLU)         (None, 250, 250, 32)       0          Y          
#>  conv_pw_1 (Conv2D)            (None, 250, 250, 64)       2048       Y          
#>  conv_pw_1_bn (BatchNormalizat  (None, 250, 250, 64)      256        Y          
#>  ion)                                                                           
#>  conv_pw_1_relu (ReLU)         (None, 250, 250, 64)       0          Y          
#>  conv_pad_2 (ZeroPadding2D)    (None, 251, 251, 64)       0          Y          
#>  conv_dw_2 (DepthwiseConv2D)   (None, 125, 125, 64)       576        Y          
#>  conv_dw_2_bn (BatchNormalizat  (None, 125, 125, 64)      256        Y          
#>  ion)                                                                           
#>  conv_dw_2_relu (ReLU)         (None, 125, 125, 64)       0          Y          
#>  conv_pw_2 (Conv2D)            (None, 125, 125, 128)      8192       Y          
#>  conv_pw_2_bn (BatchNormalizat  (None, 125, 125, 128)     512        Y          
#>  ion)                                                                           
#>  conv_pw_2_relu (ReLU)         (None, 125, 125, 128)      0          Y          
#>  conv_dw_3 (DepthwiseConv2D)   (None, 125, 125, 128)      1152       Y          
#>  conv_dw_3_bn (BatchNormalizat  (None, 125, 125, 128)     512        Y          
#>  ion)                                                                           
#>  conv_dw_3_relu (ReLU)         (None, 125, 125, 128)      0          Y          
#>  conv_pw_3 (Conv2D)            (None, 125, 125, 128)      16384      Y          
#>  conv_pw_3_bn (BatchNormalizat  (None, 125, 125, 128)     512        Y          
#>  ion)                                                                           
#>  conv_pw_3_relu (ReLU)         (None, 125, 125, 128)      0          Y          
#>  conv_pad_4 (ZeroPadding2D)    (None, 126, 126, 128)      0          Y          
#>  conv_dw_4 (DepthwiseConv2D)   (None, 62, 62, 128)        1152       Y          
#>  conv_dw_4_bn (BatchNormalizat  (None, 62, 62, 128)       512        Y          
#>  ion)                                                                           
#>  conv_dw_4_relu (ReLU)         (None, 62, 62, 128)        0          Y          
#>  conv_pw_4 (Conv2D)            (None, 62, 62, 256)        32768      Y          
#>  conv_pw_4_bn (BatchNormalizat  (None, 62, 62, 256)       1024       Y          
#>  ion)                                                                           
#>  conv_pw_4_relu (ReLU)         (None, 62, 62, 256)        0          Y          
#>  conv_dw_5 (DepthwiseConv2D)   (None, 62, 62, 256)        2304       Y          
#>  conv_dw_5_bn (BatchNormalizat  (None, 62, 62, 256)       1024       Y          
#>  ion)                                                                           
#>  conv_dw_5_relu (ReLU)         (None, 62, 62, 256)        0          Y          
#>  conv_pw_5 (Conv2D)            (None, 62, 62, 256)        65536      Y          
#>  conv_pw_5_bn (BatchNormalizat  (None, 62, 62, 256)       1024       Y          
#>  ion)                                                                           
#>  conv_pw_5_relu (ReLU)         (None, 62, 62, 256)        0          Y          
#>  conv_pad_6 (ZeroPadding2D)    (None, 63, 63, 256)        0          Y          
#>  conv_dw_6 (DepthwiseConv2D)   (None, 31, 31, 256)        2304       Y          
#>  conv_dw_6_bn (BatchNormalizat  (None, 31, 31, 256)       1024       Y          
#>  ion)                                                                           
#>  conv_dw_6_relu (ReLU)         (None, 31, 31, 256)        0          Y          
#>  conv_pw_6 (Conv2D)            (None, 31, 31, 512)        131072     Y          
#>  conv_pw_6_bn (BatchNormalizat  (None, 31, 31, 512)       2048       Y          
#>  ion)                                                                           
#>  conv_pw_6_relu (ReLU)         (None, 31, 31, 512)        0          Y          
#>  conv_dw_7 (DepthwiseConv2D)   (None, 31, 31, 512)        4608       Y          
#>  conv_dw_7_bn (BatchNormalizat  (None, 31, 31, 512)       2048       Y          
#>  ion)                                                                           
#>  conv_dw_7_relu (ReLU)         (None, 31, 31, 512)        0          Y          
#>  conv_pw_7 (Conv2D)            (None, 31, 31, 512)        262144     Y          
#>  conv_pw_7_bn (BatchNormalizat  (None, 31, 31, 512)       2048       Y          
#>  ion)                                                                           
#>  conv_pw_7_relu (ReLU)         (None, 31, 31, 512)        0          Y          
#>  conv_dw_8 (DepthwiseConv2D)   (None, 31, 31, 512)        4608       Y          
#>  conv_dw_8_bn (BatchNormalizat  (None, 31, 31, 512)       2048       Y          
#>  ion)                                                                           
#>  conv_dw_8_relu (ReLU)         (None, 31, 31, 512)        0          Y          
#>  conv_pw_8 (Conv2D)            (None, 31, 31, 512)        262144     Y          
#>  conv_pw_8_bn (BatchNormalizat  (None, 31, 31, 512)       2048       Y          
#>  ion)                                                                           
#>  conv_pw_8_relu (ReLU)         (None, 31, 31, 512)        0          Y          
#>  conv_dw_9 (DepthwiseConv2D)   (None, 31, 31, 512)        4608       Y          
#>  conv_dw_9_bn (BatchNormalizat  (None, 31, 31, 512)       2048       Y          
#>  ion)                                                                           
#>  conv_dw_9_relu (ReLU)         (None, 31, 31, 512)        0          Y          
#>  conv_pw_9 (Conv2D)            (None, 31, 31, 512)        262144     Y          
#>  conv_pw_9_bn (BatchNormalizat  (None, 31, 31, 512)       2048       Y          
#>  ion)                                                                           
#>  conv_pw_9_relu (ReLU)         (None, 31, 31, 512)        0          Y          
#>  conv_dw_10 (DepthwiseConv2D)  (None, 31, 31, 512)        4608       Y          
#>  conv_dw_10_bn (BatchNormaliza  (None, 31, 31, 512)       2048       Y          
#>  tion)                                                                          
#>  conv_dw_10_relu (ReLU)        (None, 31, 31, 512)        0          Y          
#>  conv_pw_10 (Conv2D)           (None, 31, 31, 512)        262144     Y          
#>  conv_pw_10_bn (BatchNormaliza  (None, 31, 31, 512)       2048       Y          
#>  tion)                                                                          
#>  conv_pw_10_relu (ReLU)        (None, 31, 31, 512)        0          Y          
#>  conv_dw_11 (DepthwiseConv2D)  (None, 31, 31, 512)        4608       Y          
#>  conv_dw_11_bn (BatchNormaliza  (None, 31, 31, 512)       2048       Y          
#>  tion)                                                                          
#>  conv_dw_11_relu (ReLU)        (None, 31, 31, 512)        0          Y          
#>  conv_pw_11 (Conv2D)           (None, 31, 31, 512)        262144     Y          
#>  conv_pw_11_bn (BatchNormaliza  (None, 31, 31, 512)       2048       Y          
#>  tion)                                                                          
#>  conv_pw_11_relu (ReLU)        (None, 31, 31, 512)        0          Y          
#>  conv_pad_12 (ZeroPadding2D)   (None, 32, 32, 512)        0          Y          
#>  conv_dw_12 (DepthwiseConv2D)  (None, 15, 15, 512)        4608       Y          
#>  conv_dw_12_bn (BatchNormaliza  (None, 15, 15, 512)       2048       Y          
#>  tion)                                                                          
#>  conv_dw_12_relu (ReLU)        (None, 15, 15, 512)        0          Y          
#>  conv_pw_12 (Conv2D)           (None, 15, 15, 1024)       524288     Y          
#>  conv_pw_12_bn (BatchNormaliza  (None, 15, 15, 1024)      4096       Y          
#>  tion)                                                                          
#>  conv_pw_12_relu (ReLU)        (None, 15, 15, 1024)       0          Y          
#>  conv_dw_13 (DepthwiseConv2D)  (None, 15, 15, 1024)       9216       Y          
#>  conv_dw_13_bn (BatchNormaliza  (None, 15, 15, 1024)      4096       Y          
#>  tion)                                                                          
#>  conv_dw_13_relu (ReLU)        (None, 15, 15, 1024)       0          Y          
#>  conv_pw_13 (Conv2D)           (None, 15, 15, 1024)       1048576    Y          
#>  conv_pw_13_bn (BatchNormaliza  (None, 15, 15, 1024)      4096       Y          
#>  tion)                                                                          
#>  conv_pw_13_relu (ReLU)        (None, 15, 15, 1024)       0          Y          
#> ================================================================================
#> Total params: 3,228,864
#> Trainable params: 3,206,976
#> Non-trainable params: 21,888
#> ________________________________________________________________________________

Berdasarkan arsitektur model tersebut, saya membuat model prediksi untuk klasifikasi gambar makanan dengan melakukan setting parameter sebagai berikut:

model_tf <- keras_model_sequential() %>% 
  conv_base %>% 
  layer_flatten() %>% 
  layer_dense(units = 64, activation = "relu") %>% 
  layer_dense(name = "Output", units = output_n, activation = "softmax")

freeze_weights(conv_base)

17.2 Model Fitting

Setelah model untuk klasifikasi makanan telah dibuat dengan nama model_tf disini saya melatih model tersebut dengan data_train dan sekaligus melakukan validasi atas hasil training-nya.

model_tf %>% 
  compile(
    loss = "categorical_crossentropy",
    optimizer = optimizer_adam(learning_rate = 0.01),
    metrics = "accuracy"
  )

history_tf <- model_tf %>% 
  fit(
  # training data
  train_image_array_gen,
  
  # epochs
  steps_per_epoch = as.integer(train_samples / batch_size), 
  epochs = 3,
  
  # validation data
  validation_data = val_image_array_gen,
  validation_steps = as.integer(valid_samples / batch_size),
  
  # print progress but don't create graphic
  verbose = 2
)
#> Epoch 1/3
#> 9/9 - 71s - loss: 250.6254 - accuracy: 0.2968 - val_loss: 39.8011 - val_accuracy: 0.7340 - 71s/epoch - 8s/step
#> Epoch 2/3
#> 9/9 - 66s - loss: 20.7341 - accuracy: 0.8061 - val_loss: 17.5259 - val_accuracy: 0.8511 - 66s/epoch - 7s/step
#> Epoch 3/3
#> 9/9 - 65s - loss: 3.5118 - accuracy: 0.9173 - val_loss: 3.1387 - val_accuracy: 0.8617 - 65s/epoch - 7s/step
plot(history_tf)

Didapatkan hasil evaluasi berdasarkan parameter di atas dengan hasil akurasi 96%.

17.3 Model Evaluation

Sekarang kita akan mengevaluasi data lebih lanjut dan memperoleh confusion matrix untuk data test.

pred_tf_test <- model_tf %>% predict(test_x) %>% k_argmax()
#> 5/5 - 12s - 12s/epoch - 2s/step
head(pred_tf_test, 10)
#> tf.Tensor([0 0 0 4 4 0 0 4 0 0], shape=(10), dtype=int64)
# Convert encoding to label
pred_tf_test <- sapply(pred_tf_test, decode) 

head(pred_tf_test, 10)
#>  [1] "asinan-jakarta" "asinan-jakarta" "asinan-jakarta" "gado-gado"     
#>  [5] "gado-gado"      "asinan-jakarta" "asinan-jakarta" "gado-gado"     
#>  [9] "asinan-jakarta" "asinan-jakarta"
confusionMatrix(as.factor(pred_tf_test), 
                as.factor(test_data$class)
                )
#> Confusion Matrix and Statistics
#> 
#>                      Reference
#> Prediction            asinan-jakarta ayam-betutu bika-ambon cendol gado-gado
#>   asinan-jakarta                   7           0          0      0         0
#>   ayam-betutu                      0          11          0      0         0
#>   bika-ambon                       0           0          6      0         0
#>   cendol                           0           0          0     14         0
#>   gado-gado                        3           0          0      0         6
#>   gudeg                            0           0          0      0         0
#>   keladi                           0           0          0      0         0
#>   klappertart                      0           0          0      0         0
#>   kolak                            0           0          0      0         0
#>   kue-lumpur                       0           0          0      0         0
#>   nasi-goreng-kampung              0           0          0      0         0
#>   papeda                           0           0          0      0         0
#>   rawon-surabaya                   0           0          0      0         0
#>   rendang                          0           0          1      0         0
#>   sate-ayam-madura                 0           0          0      0         0
#>   soerabi                          0           0          0      0         0
#>   soto-ayam-lamongan               0           0          0      0         0
#>                      Reference
#> Prediction            gudeg keladi klappertart kolak kue-lumpur
#>   asinan-jakarta          0      0           0     0          0
#>   ayam-betutu             0      0           0     0          0
#>   bika-ambon              0      0           0     0          0
#>   cendol                  0      0           0     0          0
#>   gado-gado               0      0           0     0          0
#>   gudeg                   4      0           0     0          0
#>   keladi                  0      9           0     0          0
#>   klappertart             0      0           8     0          0
#>   kolak                   0      0           0     6          0
#>   kue-lumpur              0      0           0     0          4
#>   nasi-goreng-kampung     0      0           0     0          0
#>   papeda                  0      0           0     0          0
#>   rawon-surabaya          0      0           0     0          0
#>   rendang                 0      0           0     0          0
#>   sate-ayam-madura        0      0           0     0          0
#>   soerabi                 0      0           0     0          0
#>   soto-ayam-lamongan      0      0           0     0          0
#>                      Reference
#> Prediction            nasi-goreng-kampung papeda rawon-surabaya rendang
#>   asinan-jakarta                        0      0              0       0
#>   ayam-betutu                           0      0              0       0
#>   bika-ambon                            0      0              0       0
#>   cendol                                0      0              0       0
#>   gado-gado                             0      0              0       0
#>   gudeg                                 0      0              0       0
#>   keladi                                0      0              0       0
#>   klappertart                           0      0              0       0
#>   kolak                                 0      0              0       0
#>   kue-lumpur                            0      0              0       0
#>   nasi-goreng-kampung                  10      0              0       0
#>   papeda                                0      7              0       0
#>   rawon-surabaya                        0      0             12       0
#>   rendang                               0      3              0      13
#>   sate-ayam-madura                      0      0              0       0
#>   soerabi                               0      0              0       0
#>   soto-ayam-lamongan                    0      0              0       0
#>                      Reference
#> Prediction            sate-ayam-madura soerabi soto-ayam-lamongan
#>   asinan-jakarta                     0       0                  0
#>   ayam-betutu                        0       0                  0
#>   bika-ambon                         0       0                  0
#>   cendol                             0       0                  3
#>   gado-gado                          0       0                  0
#>   gudeg                              0       0                  0
#>   keladi                             0       0                  0
#>   klappertart                        0       0                  0
#>   kolak                              0       0                  0
#>   kue-lumpur                         0       0                  0
#>   nasi-goreng-kampung                0       0                  0
#>   papeda                             0       0                  0
#>   rawon-surabaya                     0       0                  0
#>   rendang                            0       0                  0
#>   sate-ayam-madura                   6       0                  0
#>   soerabi                            0       6                  0
#>   soto-ayam-lamongan                 0       4                  9
#> 
#> Overall Statistics
#>                                                
#>                Accuracy : 0.9079               
#>                  95% CI : (0.8503, 0.9487)     
#>     No Information Rate : 0.0921               
#>     P-Value [Acc > NIR] : < 0.00000000000000022
#>                                                
#>                   Kappa : 0.9014               
#>                                                
#>  Mcnemar's Test P-Value : NA                   
#> 
#> Statistics by Class:
#> 
#>                      Class: asinan-jakarta Class: ayam-betutu Class: bika-ambon
#> Sensitivity                        0.70000            1.00000           0.85714
#> Specificity                        1.00000            1.00000           1.00000
#> Pos Pred Value                     1.00000            1.00000           1.00000
#> Neg Pred Value                     0.97931            1.00000           0.99315
#> Prevalence                         0.06579            0.07237           0.04605
#> Detection Rate                     0.04605            0.07237           0.03947
#> Detection Prevalence               0.04605            0.07237           0.03947
#> Balanced Accuracy                  0.85000            1.00000           0.92857
#>                      Class: cendol Class: gado-gado Class: gudeg Class: keladi
#> Sensitivity                1.00000          1.00000      1.00000       1.00000
#> Specificity                0.97826          0.97945      1.00000       1.00000
#> Pos Pred Value             0.82353          0.66667      1.00000       1.00000
#> Neg Pred Value             1.00000          1.00000      1.00000       1.00000
#> Prevalence                 0.09211          0.03947      0.02632       0.05921
#> Detection Rate             0.09211          0.03947      0.02632       0.05921
#> Detection Prevalence       0.11184          0.05921      0.02632       0.05921
#> Balanced Accuracy          0.98913          0.98973      1.00000       1.00000
#>                      Class: klappertart Class: kolak Class: kue-lumpur
#> Sensitivity                     1.00000      1.00000           1.00000
#> Specificity                     1.00000      1.00000           1.00000
#> Pos Pred Value                  1.00000      1.00000           1.00000
#> Neg Pred Value                  1.00000      1.00000           1.00000
#> Prevalence                      0.05263      0.03947           0.02632
#> Detection Rate                  0.05263      0.03947           0.02632
#> Detection Prevalence            0.05263      0.03947           0.02632
#> Balanced Accuracy               1.00000      1.00000           1.00000
#>                      Class: nasi-goreng-kampung Class: papeda
#> Sensitivity                             1.00000       0.70000
#> Specificity                             1.00000       1.00000
#> Pos Pred Value                          1.00000       1.00000
#> Neg Pred Value                          1.00000       0.97931
#> Prevalence                              0.06579       0.06579
#> Detection Rate                          0.06579       0.04605
#> Detection Prevalence                    0.06579       0.04605
#> Balanced Accuracy                       1.00000       0.85000
#>                      Class: rawon-surabaya Class: rendang
#> Sensitivity                        1.00000        1.00000
#> Specificity                        1.00000        0.97122
#> Pos Pred Value                     1.00000        0.76471
#> Neg Pred Value                     1.00000        1.00000
#> Prevalence                         0.07895        0.08553
#> Detection Rate                     0.07895        0.08553
#> Detection Prevalence               0.07895        0.11184
#> Balanced Accuracy                  1.00000        0.98561
#>                      Class: sate-ayam-madura Class: soerabi
#> Sensitivity                          1.00000        0.60000
#> Specificity                          1.00000        1.00000
#> Pos Pred Value                       1.00000        1.00000
#> Neg Pred Value                       1.00000        0.97260
#> Prevalence                           0.03947        0.06579
#> Detection Rate                       0.03947        0.03947
#> Detection Prevalence                 0.03947        0.03947
#> Balanced Accuracy                    1.00000        0.80000
#>                      Class: soto-ayam-lamongan
#> Sensitivity                            0.75000
#> Specificity                            0.97143
#> Pos Pred Value                         0.69231
#> Neg Pred Value                         0.97842
#> Prevalence                             0.07895
#> Detection Rate                         0.05921
#> Detection Prevalence                   0.08553
#> Balanced Accuracy                      0.86071

18 Save the Model

Menyimpan model untuk keperluan deploy model ke Shinydashboard.

save_model_tf(model_tf, "finalmodel")