A first attempt at a deep learning model. Most of the code is from: https://shirinsplayground.netlify.com/2018/06/keras_fruits/
The dataset is a fruit images dataset from Kaggle: https://www.kaggle.com/moltean/fruits/data
This code will build a neural network to classify fruit from images
# list of fruits to model
fruit_list <- c("Kiwi", "Banana", "Apricot", "Avocado", "Cocos", "Clementine", "Mandarine", "Orange",
"Limes", "Lemon", "Peach", "Plum", "Raspberry", "Strawberry", "Pineapple", "Pomegranate")
# number of output classes (i.e. fruits)
output_n <- length(fruit_list)
# image size to scale down to (original images are 100 x 100 px)
img_width <- 20
img_height <- 20
target_size <- c(img_width, img_height)
# RGB = 3 channels
channels <- 3
# path to image folders
train_image_files_path <- "datasets/fruits-360_dataset/fruits-360/Training/"
valid_image_files_path <- "datasets/fruits-360_dataset/fruits-360/Test/"
“Augmentation” creates variations of the training data.
# optional data augmentation
train_data_gen = image_data_generator(
rescale = 1/255 #,
#rotation_range = 40,
#width_shift_range = 0.2,
#height_shift_range = 0.2,
#shear_range = 0.2,
#zoom_range = 0.2,
#horizontal_flip = TRUE,
#fill_mode = "nearest"
)
# Never use augmentation on test data, but do rescale!
test_data_gen = image_data_generator(
rescale = 1/255 #,
)
Load into memory and resize.
# training images
train_image_array_gen <- flow_images_from_directory(train_image_files_path,
train_data_gen,
target_size = target_size,
class_mode = "categorical",
classes = fruit_list,
seed = 42)
# validation images
valid_image_array_gen <- flow_images_from_directory(valid_image_files_path,
test_data_gen,
target_size = target_size,
class_mode = "categorical",
classes = fruit_list,
seed = 42)
typeof(train_data_gen)
## [1] "environment"
names(train_image_array_gen)
## [1] "allowed_class_modes" "batch_index" "batch_size"
## [4] "class_indices" "class_mode" "classes"
## [7] "color_mode" "data_format" "directory"
## [10] "dtype" "filenames" "filepaths"
## [13] "image_data_generator" "image_shape" "index_array"
## [16] "index_generator" "interpolation" "labels"
## [19] "lock" "n" "next"
## [22] "num_classes" "on_epoch_end" "reset"
## [25] "sample_weight" "samples" "save_format"
## [28] "save_prefix" "save_to_dir" "seed"
## [31] "set_processing_attrs" "shuffle" "split"
## [34] "subset" "target_size" "total_batches_seen"
## [37] "white_list_formats"
table(factor(train_image_array_gen$classes))
##
## 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
## 466 490 492 427 490 490 490 479 490 492 492 447 490 492 490 492
Store the fruit name to index mapping:
fruits_classes_indices <- train_image_array_gen$class_indices
# number of training samples
train_samples <- train_image_array_gen$n
# number of validation samples
valid_samples <- valid_image_array_gen$n
# define batch size and number of epochs
batch_size <- 32
epochs <- 10
# initialise model
model <- keras_model_sequential()
# add layers
model %>%
layer_conv_2d(filter = 32, kernel_size = c(3,3), padding = "same", input_shape = c(img_width, img_height, channels)) %>%
layer_activation("relu") %>%
# Second hidden layer
layer_conv_2d(filter = 16, kernel_size = c(3,3), padding = "same") %>%
layer_activation_leaky_relu(0.5) %>%
layer_batch_normalization() %>%
# Use max pooling
layer_max_pooling_2d(pool_size = c(2,2)) %>%
layer_dropout(0.25) %>%
# Flatten max filtered output into feature vector
# and feed into dense layer
layer_flatten() %>%
layer_dense(100) %>%
layer_activation("relu") %>%
layer_dropout(0.5) %>%
# Outputs from dense layer are projected onto output layer
layer_dense(output_n) %>%
layer_activation("softmax")
Show the structure of the neural network:
model
## Model
## Model: "sequential"
## ___________________________________________________________________________
## Layer (type) Output Shape Param #
## ===========================================================================
## conv2d (Conv2D) (None, 20, 20, 32) 896
## ___________________________________________________________________________
## activation (Activation) (None, 20, 20, 32) 0
## ___________________________________________________________________________
## conv2d_1 (Conv2D) (None, 20, 20, 16) 4624
## ___________________________________________________________________________
## leaky_re_lu (LeakyReLU) (None, 20, 20, 16) 0
## ___________________________________________________________________________
## batch_normalization (BatchNormal (None, 20, 20, 16) 64
## ___________________________________________________________________________
## max_pooling2d (MaxPooling2D) (None, 10, 10, 16) 0
## ___________________________________________________________________________
## dropout (Dropout) (None, 10, 10, 16) 0
## ___________________________________________________________________________
## flatten (Flatten) (None, 1600) 0
## ___________________________________________________________________________
## dense (Dense) (None, 100) 160100
## ___________________________________________________________________________
## activation_1 (Activation) (None, 100) 0
## ___________________________________________________________________________
## dropout_1 (Dropout) (None, 100) 0
## ___________________________________________________________________________
## dense_1 (Dense) (None, 16) 1616
## ___________________________________________________________________________
## activation_2 (Activation) (None, 16) 0
## ===========================================================================
## Total params: 167,300
## Trainable params: 167,268
## Non-trainable params: 32
## ___________________________________________________________________________
Output layer has 16 neurons, for 16 fruit types.
# compile
model %>% compile(
loss = "categorical_crossentropy",
optimizer = optimizer_rmsprop(lr = 0.0001, decay = 1e-6),
metrics = "accuracy"
)
Note: there is a warning because I don’t have the exact right specifications for TensorFlow, because it was not built from source.
Fit the model:
# fit
hist <- model %>% fit_generator(
# training data
train_image_array_gen,
# epochs
steps_per_epoch = as.integer(train_samples / batch_size),
epochs = epochs,
# validation data
validation_data = valid_image_array_gen,
validation_steps = as.integer(valid_samples / batch_size),
# print progress
verbose = 2,
callbacks = list(
# save best model after every epoch
callback_model_checkpoint("tmp/fruits", save_best_only = TRUE),
# only needed for visualising with TensorBoard
callback_tensorboard(log_dir = "tmp/fruits")
)
)
plot(hist)
tensorboard("tmp/fruits")
## Started TensorBoard at http://127.0.0.1:5073