In the expansive realm of computer vision and pattern recognition, image classification serves as a pivotal task with applications spanning diverse fields. Among the intriguing challenges is the recognition of handwritten digits, a foundational issue for which the revered MNIST dataset is a cornerstone. Esteemed for its transformative role in shaping machine learning, the MNIST dataset comprises an extensive array of meticulously annotated handwritten digits, curated meticulously to facilitate training and rigorous evaluation. This study embarks on an odyssey to amplify digit recognition prowess through strategic Convolutional Neural Network (CNN) deployment. By delving into the transformative capacity of CNNs, our research aims to harness their innate ability to discern intricate patterns and interdependencies encoded within images, catalyzing a paradigm shift in accurate digit classification. This exploration amalgamates the richness of the MNIST dataset with the architectural sophistication of CNNs, utilizing the primary data source from the Digit Recognizer Kaggle competition, accessible at: Link to the MNIST Dataset. Through meticulous analysis of this fusion, our objective is to unveil untapped potential in image classification, charting new trajectories in the evolution of pattern recognition technology and potentially redefining the horizons at the captivating crossroads of computer vision and machine learning.
In order to design and execute an efficient and structured project, the use of appropriate libraries is crucial. In this context, we import a number of libraries that provide specific tools and functions for each stage of our project. These libraries not only facilitate our work, but also ensure that we can make the most of their advanced features. Here are some of the libraries we import:
# Data wrangling
library(tidyverse)
# Image manipulation
library(imager)
# Deep learning
library(keras)
# Model Evaluation
library(caret)
# use conda env
#use_condaenv("r-tensorflow")
options(scipen = 999)tidyverse: This library forms the core foundation for
data cleaning and transformation. With powerful tools like dplyr and
ggplot2, tidyverse allows us to perform data analysis tasks more
systematically and efficiently.
imager: For image manipulation, the imager library is
our go-to choice. With a range of functions that facilitate image
manipulation, processing, and visualization, this library is very useful
in preparing image data before the deep learning process.
keras: This library is our top pick for implementing
deep learning. With a user-friendly interface and support for advanced
neural network architectures, keras allows us to easily design, train,
and evaluate neural network models.
caret: In the model evaluation stage, the caret library
is our choice. With a set of tools for model evaluation,
cross-validation, and feature selection, this library helps us
understand and enhance the performance of our models.
The combination of these libraries not only provides a strong foundation for our project, but also enables us to execute each stage with the necessary expertise and skills. By leveraging the strengths of each library, we are confident that we can produce quality outcomes and effectively address the challenges at hand.
Data Preparation in the context of image processing is a crucial step before embarking on analysis or machine learning tasks. In this stage, raw images need to be processed to make them suitable for use in the desired model or application. This process involves steps such as image preprocessing, resizing to uniform dimensions, normalizing pixel intensities, and addressing images that may be corrupted or of low quality. Additionally, tasks such as image augmentation can be performed to enhance data variability and diversity. The entire sequence of Data Preparation steps aims to ensure that the image data to be used is clean, structured, and ready for further processing in the subsequent analysis or machine learning models.
Reading data images involves the process of loading and interpreting visual information from image files into a format that can be utilized for analysis, manipulation, or machine learning tasks.
#> [1] "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"
#> [1] "data/trainingSample/trainingSample/0/"
#> [2] "data/trainingSample/trainingSample/1/"
#> [3] "data/trainingSample/trainingSample/2/"
#> [4] "data/trainingSample/trainingSample/3/"
#> [5] "data/trainingSample/trainingSample/4/"
#> [6] "data/trainingSample/trainingSample/5/"
#> [7] "data/trainingSample/trainingSample/6/"
#> [8] "data/trainingSample/trainingSample/7/"
#> [9] "data/trainingSample/trainingSample/8/"
#> [10] "data/trainingSample/trainingSample/9/"
The displayed output showcases the names of subdirectories or files present within the “data/trainingSample/trainingSample/” directory. Each entry in the list corresponds to a specific category, such as “0” “1” “2” “3” “4” “5” “6” “7” “8” “9”, This output effectively provides an overview of the contents within the directory and serves as a valuable starting point for further data processing or analysis tasks. By displaying the output in a new window, the code chunk ensures that the results are easily viewable and interpretable.
Reading filenames involves extracting and interpreting the names of files within a specified directory. This process is a fundamental step in understanding the composition and structure of a dataset.
# Get file name
file_name <- map(folder_path,
function(x) paste0(x, list.files(x))
) %>%
unlist()
# first 6 file name
head(file_name)#> [1] "data/trainingSample/trainingSample/0/img_1.jpg"
#> [2] "data/trainingSample/trainingSample/0/img_108.jpg"
#> [3] "data/trainingSample/trainingSample/0/img_110.jpg"
#> [4] "data/trainingSample/trainingSample/0/img_111.jpg"
#> [5] "data/trainingSample/trainingSample/0/img_114.jpg"
#> [6] "data/trainingSample/trainingSample/0/img_129.jpg"
#> [1] "data/trainingSample/trainingSample/9/img_71.jpg"
#> [2] "data/trainingSample/trainingSample/9/img_83.jpg"
#> [3] "data/trainingSample/trainingSample/9/img_85.jpg"
#> [4] "data/trainingSample/trainingSample/9/img_86.jpg"
#> [5] "data/trainingSample/trainingSample/9/img_88.jpg"
#> [6] "data/trainingSample/trainingSample/9/img_95.jpg"
#> [1] 600
The image dataset consists of 600 files that have been successfully extracted and stored from the designated directory. This information provides a comprehensive overview of the accessible data volume, representing the diversity and complexity inherent in the dataset. By being aware of the presence of 600 image files, we can gain a deeper understanding of the resources available for analysis, machine learning, or model development purposes. Furthermore, comprehending the dataset size also aids in planning data cleaning strategies, transformations, and the selection of appropriate features. By utilizing this information, we can ensure that the subsequent steps in dataset exploration and utilization are carried out efficiently and effectively. Let’s take a look at 6 sample examples that will be selected for further analysis.
# Randomly select image
set.seed(99)
sample_image <- sample(file_name, 6)
# Load image into R
img <- map(sample_image, load.image)
# Plot image
par(mfrow = c(2, 3)) # Create 2 x 3 image grid
map(img, plot)#> [[1]]
#> Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1
#>
#> [[2]]
#> Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1
#>
#> [[3]]
#> Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1
#>
#> [[4]]
#> Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1
#>
#> [[5]]
#> Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1
#>
#> [[6]]
#> Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1
In the first step, I randomly select 6 image files from the dataset using the ‘sample’ function and set the random seed to 99 for reproducibility. Next, I load these image files into the R environment using the ‘load.image’ function. Afterward, I generate visual displays of each image in a 2x3 grid format using ‘par(mfrow = c(2, 3))’ and execute the ‘plot’ function for each image.
Checking image dimensions is an essential task in image data analysis. By examining the dimensions of images, we gain insights into their width, height, and color channels. This information is crucial for understanding the structure and characteristics of the image data. Image dimensions can influence various aspects of analysis and modeling, such as data preprocessing, resizing, and model architecture design.
#> Image. Width: 28 pix Height: 28 pix Depth: 1 Colour channels: 1
#> [1] 28 28 1 1
That the loaded image is a grayscale image with dimensions of 28x28 pixels. Each pixel is represented by a single value (grayscale intensity) since there’s only one color channel.
# 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])The get_dim function loads an image using the load.image function and then creates a data frame df_img containing three columns: height, width, and filename. The dimensions of the loaded image are stored in the height and width columns, and the corresponding filename is stored in the filename column.
“The Distribution of Image Dimensions” refers to the analysis and understanding of how the sizes of images within a dataset are spread or distributed. it helps us determine the range of sizes encompassed by the dataset, from the smallest to the largest dimensions.
# Randomly get 1000 sample images
set.seed(123)
sample_file <- sample(file_name, 600)
# Run the get_dim() function for each image
file_dim <- map_df(sample_file, get_dim)
head(file_dim, 10)#> height width filename
#> Min. :28 Min. :28 Length:600
#> 1st Qu.:28 1st Qu.:28 Class :character
#> Median :28 Median :28 Mode :character
#> Mean :28 Mean :28
#> 3rd Qu.:28 3rd Qu.:28
#> Max. :28 Max. :28
All images in the sample have the same dimensions of 28x28 pixels. This indicates that these images have a uniform size.
Data augmentation is a transformative technique that enriches the diversity and volume of a dataset by creating modified versions of existing data samples. This process involves applying a variety of operations, such as rotations, flips, translations, and changes in lighting, to generate new instances that are conceptually similar but exhibit slight variations. Data augmentation is particularly valuable in image analysis and machine learning, where a larger and more diverse dataset often leads to improved model generalization, performance, and robustness.
# Desired height and width of images
target_size <- c(28, 28)
# Batch size for training the model
batch_size <- 75The target_size variable is defined as a vector [28, 28], indicating the desired dimensions for resizing the images. This preprocessing step ensures that all images are uniformly resized to a height and width of 28 pixels, facilitating consistent input dimensions for the model.
Additionally, the batch_size variable is set to 75. During model training, the dataset is divided into batches, and each batch contains 75 samples. This configuration helps optimize memory usage and computational efficiency during the training process
set.seed(100)
# Image Generator
train_data_gen <- image_data_generator(rescale = 1/255,
zoom_range = 0.25, # Zoom in or zoom out range
validation_split = 0.2, # 20% data as validation data
fill_mode = "nearest"
)The following settings are configured for the train_data_gen image data generator:
rescale: Pixel values are scaled to the range [0, 1] by dividing by 255, which standardizes the input data.
zoom_range: Images can be zoomed in or out by up to 25%, further diversifying the dataset.
validation_split: A portion of the data (20%) is set aside as validation data to monitor model performance during training.
fill_mode = “nearest”: This specifies how pixel values are filled in when applying transformations like rotation or zooming. “nearest” fills missing values with the nearest pixel.
These data augmentation techniques, combined with rescaling and validation splitting, contribute to creating a more robust and diverse dataset for training. The train_data_gen generator prepares the data with these transformations, helping the model learn to recognize patterns and features under various conditions and orientations.
The “Training Dataset” serves as the foundation for teaching machine learning models, containing a diverse array of labeled examples that enable algorithms to learn patterns and relationships. Through exposure to this dataset, models fine-tune their parameters to make accurate predictions on new, unseen data. Conversely, the “Validation Dataset” plays a crucial role in assessing model performance. By evaluating the model on separate validation examples, practitioners gain insights into its ability to generalize and can make necessary adjustments to optimize its predictive power. Together, these datasets form a symbiotic relationship, guiding the iterative process of model development and refinement.
set.seed(100)
# Training Dataset
train_image_array_gen <- flow_images_from_directory(directory = "data/trainingSample/trainingSample/", # Folder of the data
target_size = target_size, # target of the image dimension (28 x 28)
color_mode = "grayscale", # use grayscale color
batch_size = batch_size ,
seed = 100, # set random seed
subset = "training", # declare that this is for training data
generator = train_data_gen
)
# Validation Dataset
val_image_array_gen <- flow_images_from_directory(directory = "data/trainingSample/trainingSample/",
target_size = target_size,
color_mode = "grayscale",
batch_size = batch_size ,
seed = 100,
subset = "validation", # declare that this is the validation data
generator = train_data_gen
)The dataset is split into two subsets: the “Training Dataset” (train_image_array_gen) and the “Validation Dataset” (val_image_array_gen). Each subset is prepared using the flow_images_from_directory function.
For the training dataset:
directory parameter specifies the folder containing the
data.target_size defines the desired image dimensions (64 x
64 pixels).color_mode indicates the use of grayscale color.batch_size sets the batch size for training.seed establishes the random seed for
reproducibility.subset is set to “training” to designate this subset
for training data.generator specifies the train_data_gen generator for
data augmentation.Balanced data proportions across subsets help ensure that the model is exposed to a representative variety of examples during training, enabling it to learn effectively across different classes or categories. A proper data proportion in the validation set is equally important, as it ensures a fair assessment of the model’s performance on unseen data.
set.seed(100)
# 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 7 8 9
#> 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1
Each label has the same number of samples and there are a total of 10 labels, so each label has a proportion of 10% of the total samples. Therefore, this dataset can be considered as a balanced dataset.
A “Convolutional Neural Network” (CNN) is a deep learning architecture specifically designed for processing and analyzing visual data, such as images and videos. Inspired by the human visual system, CNNs excel at capturing intricate patterns, features, and hierarchies present in visual inputs. This is achieved through a series of specialized layers, including convolutional, pooling, and fully connected layers, which collectively enable the network to learn and extract meaningful information from images.
A Model Architecture that utilizes Convolutional Neural Network (CNN) is a structural representation of how the CNN is organized to process and analyze visual data, such as images. This model consists of a series of layers that have unique functions in feature extraction and transformation. With the right structure and configuration, a CNN architecture is capable of addressing the challenges of image analysis. The careful utilization of these layers allows the model to learn and represent essential features in visual data efficiently and accurately.
This architecture typically begins with an input layer that receives image data. The convolutional layer is at the heart of the CNN, utilizing filters or kernels to identify features like edges, textures, and patterns in the images. Subsequently, pooling layers reduce spatial dimensions, decreasing complexity while retaining vital information. These layers are repeated multiple times to capture increasingly abstract and complex features.
In conclusion, a CNN-based Model Architecture is a structural blueprint that enables effective processing and analysis of visual data. It leverages a series of specialized layers to extract and transform features, addressing image analysis challenges. By skillfully arranging these layers, the architecture allows the model to efficiently and accurately learn and represent essential features in visual data.
#> [1] 28 28 1
In this Model Architecture, a sequential model named “simple_model” is constructed step by step:
The architecture follows a sequential model structure, where each layer flows into the next. Convolutional layers capture local patterns, batch normalization aids in stable training, max pooling reduces spatial dimensions, and fully connected layers extract high-level features. The output layer uses the softmax activation to provide probabilities for each class.
# Set Initial Random Weight
tensorflow::tf$random$set_seed(100)
model <- keras_model_sequential(name = "simple_model") %>%
# Convolution Layer
layer_conv_2d(filters = 32,
kernel_size = c(3,3),
padding = "same",
activation = "relu",
input_shape = c(target_size, 1)
) %>%
layer_batch_normalization() %>%
# Max Pooling Layer
layer_max_pooling_2d(pool_size = c(2,2)) %>%
# Flattening Layer
layer_flatten() %>%
# Dense Layer
layer_dense(units = 128,
activation = "relu") %>%
layer_dropout(rate = 0.5) %>% # Add dropout layer
# Dense Layer
layer_dense(units = 64,
activation = "relu") %>%
layer_dropout(rate = 0.5) %>% # Add dropout layer
# Output Layer
layer_dense(units = output_n,
activation = "softmax",
name = "Output")
model#> Model: "simple_model"
#> ________________________________________________________________________________
#> Layer (type) Output Shape Param # Trainable
#> ================================================================================
#> conv2d (Conv2D) (None, 28, 28, 32) 320 Y
#> batch_normalization (BatchNor (None, 28, 28, 32) 128 Y
#> malization)
#> max_pooling2d (MaxPooling2D) (None, 14, 14, 32) 0 Y
#> flatten (Flatten) (None, 6272) 0 Y
#> dense_1 (Dense) (None, 128) 802944 Y
#> dropout_1 (Dropout) (None, 128) 0 Y
#> dense (Dense) (None, 64) 8256 Y
#> dropout (Dropout) (None, 64) 0 Y
#> Output (Dense) (None, 10) 650 Y
#> ================================================================================
#> Total params: 812,298
#> Trainable params: 812,234
#> Non-trainable params: 64
#> ________________________________________________________________________________
The main function of the Flatten layer is to prepare the data for the subsequent fully connected (dense) layers. Convolutional and pooling layers capture hierarchical features in the input data, but fully connected layers require a one-dimensional input. The Flatten layer reshapes the multi-dimensional feature maps into a flat vector by concatenating all the values together, ensuring that they can be fed into the dense layers for further processing.
In the context of image classification using CNNs:
Convolutional and Pooling Layers: These layers extract local features and reduce spatial dimensions while preserving important information.
Flatten Layer: After the convolutional and pooling layers, the Flatten layer transforms the 2D or 3D feature maps into a 1D vector.
Dense (Fully Connected) Layers: These layers process the flattened vector to learn high-level patterns and relationships in the data, ultimately making predictions.
Output Layer (The activation function) : The softmax activation function is commonly used for multi-class classification tasks. It converts the raw output scores (logits) of the model into a probability distribution over multiple classes. Each class is assigned a probability value between 0 and 1, and the sum of probabilities across all classes adds up to 1.
The model architecture is presented in a sequential fashion, with each layer building upon the previous one. This code outlines the foundational components of a CNN model, which is a common choice for image classification tasks due to its ability to capture hierarchical features within visual data.
During model fitting, the algorithm adjusts its parameters through an iterative optimization process, usually using a technique called gradient descent. The model’s performance is evaluated using a loss function that quantifies the discrepancy between predicted and actual values. By iteratively updating the parameters to minimize this loss, the model gradually converges to a state where its predictions align more closely with the ground truth.
tensorflow::tf$random$set_seed(123)
model %>%
compile(
loss = "categorical_crossentropy",
optimizer = optimizer_adam(learning_rate = 0.001, beta_1 = 0.9),
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 = 30,
# validation data
validation_data = val_image_array_gen,
validation_steps = as.integer(valid_samples / batch_size)
)
plot(history)The model is compiled and fitted using the Keras library:
Loss Function: The loss function used to fit the model is “categorical_crossentropy”. This loss function is commonly used for multi-class classification tasks, where the goal is to minimize the difference between predicted class probabilities and the actual class labels. It calculates the cross-entropy between the true distribution of the classes and the predicted distribution.
Optimizer Adjustment: The Adam optimizer is used with specific settings:
Learning Rate: 0.001 Beta_1: 0.9 The optimizer adjusts the model’s weights during training to minimize the chosen loss function. The learning rate determines the step size for updating the weights, and the beta_1 parameter controls the exponential decay rate for the moving averages of the gradient.
It’s important to note that the choice of loss function, optimizer, and number of epochs can impact the model’s convergence, accuracy, and training speed. These choices are often made through experimentation and fine-tuning based on the specific characteristics of the dataset and the desired performance of the model.
By executing these steps, the model undergoes training, optimizing its parameters to improve its ability to make accurate predictions on new, unseen data.
The primary goal of model evaluation is to understand how well the model generalizes to new, unseen data and to gain insights into its strengths, weaknesses, and potential areas for improvement.
Cross-validation is a common technique in model evaluation, where the dataset is split into multiple subsets (folds) for training and validation. This helps assess the model’s stability and robustness by testing it on different subsets of the data.
set.seed(100)
val_data <- data.frame(file_name = paste0("data/trainingSample/trainingSample/", val_image_array_gen$filenames)) %>%
mutate(class = str_extract(val_image_array_gen$labels, "0|1|2|3|4|5|6|7|8|9"))
val_dataNext, let’s create a function to read image files, perform preprocessing, and transform them into a suitable format for the model’s input. This function will play a crucial role in preparing image data before the training and model evaluation processes.
# Function to convert image to array
image_prep <- function(x) {
arrays <- lapply(x, function(path) {
img <- image_load(path, target_size = target_size, color_mode = "grayscale")
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)))
}This image_prep function is a crucial preprocessing step to convert raw image files into a format suitable for feeding into the CNN model for training or prediction. It ensures that the data is properly shaped and scaled before being used in machine learning processes.
Dividing a dataset into training and validation subsets is a fundamental practice in machine learning. The validation subset serves as a means to assess the performance and generalization ability of a trained model. In the context of image classification. the validation subset plays a crucial role in ensuring that the model can accurately classify unseen images beyond those it was trained on.
Let’s create a variable test_x that will contain array representations of the image data within the Validation Dataset. We will use the image_prep function we defined earlier and fetch image files from val_data$file_name. This step will prepare the image data in a suitable format for evaluating our model on the validation dataset.
#> [1] 120 28 28 1
The resulting dimensions of the testing dataset are 120 samples, each with a height and width of 64 pixels, and 1 color channels (grayscale). This format aligns with the model’s input requirements and is suitable for further evaluation or prediction processes.
set.seed(100)
# Get the class proportion
table("\nFrequency" = factor(val_image_array_gen$classes)
) %>%
prop.table()#>
#> Frequency
#> 0 1 2 3 4 5 6 7 8 9
#> 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1
During prediction data evaluation, each image in the dataset is passed through the trained CNN, which produces a prediction score or class probability distribution for each image. These predictions are then compared to the true labels of the images to measure the model’s accuracy and effectiveness.
set.seed(100)
pred_test <- predict(model, test_x)%>%
k_argmax() %>% # untuk mengambil nilai probability paling besar
as.array() %>%
as.factor()
pred_test#> [1] 0 0 0 0 0 0 0 0 8 0 0 0 1 1 1 1 1 1 1 1 1 1 9 1 2 2 2 8 2 2 1 2 7 2 2 6 3
#> [38] 3 3 3 3 8 3 8 3 7 8 3 4 4 4 4 4 4 9 4 4 4 4 9 8 5 8 8 5 8 5 5 5 5 5 8 6 6
#> [75] 6 6 6 6 1 6 6 6 6 6 7 7 7 7 7 7 2 7 7 7 7 9 8 8 8 8 1 8 8 8 8 8 8 8 9 9 9
#> [112] 9 7 9 9 9 9 9 9 9
#> Levels: 0 1 2 3 4 5 6 7 8 9
This line of code predicts the classes for the images in the test_x dataset using the trained model. The predictions are obtained as probability values for each class. The subsequent operations are applied to process these predictions:
The resulting pred_test variable contains the predicted classes for the images in the validation dataset, obtained through the trained model’s predictions and post-processing steps
# Convert encoding to label
decode <- function(x){
case_when(x == 0 ~ "0",
x == 1 ~ "1",
x == 2 ~ "2",
x == 3 ~ "3",
x == 4 ~ "4",
x == 5 ~ "5",
x == 6 ~ "6",
x == 7 ~ "7",
x == 8 ~ "8",
x == 9 ~ "9"
)
}
pred_test <- sapply(pred_test, decode)
head(pred_test)#> [1] "0" "0" "0" "0" "0" "0"
In the realm of machine learning, accurately assessing the performance of a classification model is paramount to understanding its effectiveness in making predictions. One commonly employed method for such evaluation is through the use of a confusion matrix. This matrix provides an insightful breakdown of the model’s predictions and reveals how well it differentiates between different classes.
The Confusion Matrix and accompanying statistics provide a comprehensive insight into the performance of our image classification model, particularly in the task of distinguishing between “0,1,2,3,4,5,6,7,8,9” classes. This evaluation facilitates a detailed breakdown of the model’s predictions, revealing both its strengths and areas for improvement.
#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction 0 1 2 3 4 5 6 7 8 9
#> 0 11 0 0 0 0 0 0 0 0 0
#> 1 0 11 1 0 0 0 1 0 1 0
#> 2 0 0 8 0 0 0 0 1 0 0
#> 3 0 0 0 8 0 0 0 0 0 0
#> 4 0 0 0 0 10 0 0 0 0 0
#> 5 0 0 0 0 0 7 0 0 0 0
#> 6 0 0 1 0 0 0 11 0 0 0
#> 7 0 0 1 1 0 0 0 10 0 1
#> 8 1 0 1 3 0 5 0 0 11 0
#> 9 0 1 0 0 2 0 0 1 0 11
#>
#> Overall Statistics
#>
#> Accuracy : 0.8167
#> 95% CI : (0.7357, 0.8814)
#> No Information Rate : 0.1
#> P-Value [Acc > NIR] : < 0.00000000000000022
#>
#> Kappa : 0.7963
#>
#> Mcnemar's Test P-Value : NA
#>
#> Statistics by Class:
#>
#> Class: 0 Class: 1 Class: 2 Class: 3 Class: 4 Class: 5
#> Sensitivity 0.91667 0.91667 0.66667 0.66667 0.83333 0.58333
#> Specificity 1.00000 0.97222 0.99074 1.00000 1.00000 1.00000
#> Pos Pred Value 1.00000 0.78571 0.88889 1.00000 1.00000 1.00000
#> Neg Pred Value 0.99083 0.99057 0.96396 0.96429 0.98182 0.95575
#> Prevalence 0.10000 0.10000 0.10000 0.10000 0.10000 0.10000
#> Detection Rate 0.09167 0.09167 0.06667 0.06667 0.08333 0.05833
#> Detection Prevalence 0.09167 0.11667 0.07500 0.06667 0.08333 0.05833
#> Balanced Accuracy 0.95833 0.94444 0.82870 0.83333 0.91667 0.79167
#> Class: 6 Class: 7 Class: 8 Class: 9
#> Sensitivity 0.91667 0.83333 0.91667 0.91667
#> Specificity 0.99074 0.97222 0.90741 0.96296
#> Pos Pred Value 0.91667 0.76923 0.52381 0.73333
#> Neg Pred Value 0.99074 0.98131 0.98990 0.99048
#> Prevalence 0.10000 0.10000 0.10000 0.10000
#> Detection Rate 0.09167 0.08333 0.09167 0.09167
#> Detection Prevalence 0.10000 0.10833 0.17500 0.12500
#> Balanced Accuracy 0.95370 0.90278 0.91204 0.93981
The accuracy metric, which stands at approximately 80%, provides a significant assessment of how well our image classification model is performing. This gives us a clear picture of the model’s ability to make correct predictions across different classes, serving as a fundamental measure to gauge its effectiveness. Importantly, the fact that our model achieves around 80% accuracy also suggests that there are still opportunities for improvement, allowing us to focus on refining and optimizing its performance further.
Tuning a machine learning model involves making careful adjustments to its various hyperparameters and architecture to enhance its performance on the given task. In the context of the provided code snippet, tuning the Convolutional Neural Network (CNN) model involves iteratively experimenting with different hyperparameters to achieve better accuracy and generalization.
Here’s a breakdown of the layers and their functions in our improved model:
Random Seed Setting:
tensorflow::tf$random$set_seed(123) ensures that the random
initialization of the network’s weights and other random processes are
reproducible by setting the random seed to 123.
Convolution Layer: The first layer is a 2D convolutional layer:
filters = 32: 32 filters are applied.kernel_size = c(3,3): A 3x3 kernel is used.padding = “same”: The padding is set to “same”.activation = “relu”: Rectified Linear Unit (ReLU)
activation is applied.input_shape = c(target_size, 1): The input shape is
specified as (target_size, 1) where target_size represents the image
size and 1 is for a single channel (grayscale image).Batch Normalization: layer_batch_normalization() applies batch normalization after the convolutional layer.
Max Pooling Layer: layer_max_pooling_2d(pool_size = c(2,2)) performs max pooling with a 2x2 window size.
Flattening Layer: layer_flatten() flattens the output of the previous layer into a 1D array.
Dense Layer 1: layer_dense(units = 256, activation = “relu”) creates a dense (fully connected) layer with 256 units and ReLU activation.
Dropout Layer 1: layer_dropout(rate = 0.5) adds dropout regularization to the network with a dropout rate of 0.5.
Dense Layer 2: layer_dense(units = 128, activation = “relu”) adds another dense layer with 128 units and ReLU activation.
Dropout Layer 2: Another dropout layer is added with a rate of 0.5.
Output Layer: layer_dense(units = output_n, activation = “softmax”, name = “Output”) defines the output layer with output_n units (representing the number of classes) and a softmax activation function for multi-class classification.
tensorflow::tf$random$set_seed(123)
model_big <- keras_model_sequential(name = "tuning_model") %>%
# Convolution Layer
layer_conv_2d(filters = 32,
kernel_size = c(3,3),
padding = "same",
activation = "relu",
input_shape = c(target_size, 1)
) %>%
layer_batch_normalization() %>%
# Max Pooling Layer
layer_max_pooling_2d(pool_size = c(2,2)) %>%
# Flattening Layer
layer_flatten() %>%
# Dense Layer
layer_dense(units = 256,
activation = "relu") %>%
layer_dropout(rate = 0.5) %>% # Add dropout layer
# Dense Layer
layer_dense(units = 128,
activation = "relu") %>%
layer_dropout(rate = 0.5) %>% # Add dropout layer
# Output Layer
layer_dense(units = output_n,
activation = "softmax",
name = "Output")
model_big#> Model: "tuning_model"
#> ________________________________________________________________________________
#> Layer (type) Output Shape Param # Trainable
#> ================================================================================
#> conv2d_1 (Conv2D) (None, 28, 28, 32) 320 Y
#> batch_normalization_1 (BatchN (None, 28, 28, 32) 128 Y
#> ormalization)
#> max_pooling2d_1 (MaxPooling2D (None, 14, 14, 32) 0 Y
#> )
#> flatten_1 (Flatten) (None, 6272) 0 Y
#> dense_3 (Dense) (None, 256) 1605888 Y
#> dropout_3 (Dropout) (None, 256) 0 Y
#> dense_2 (Dense) (None, 128) 32896 Y
#> dropout_2 (Dropout) (None, 128) 0 Y
#> Output (Dense) (None, 10) 1290 Y
#> ================================================================================
#> Total params: 1,640,522
#> Trainable params: 1,640,458
#> Non-trainable params: 64
#> ________________________________________________________________________________
The model fitting process involves training the improved architecture using the provided hyperparameters and data augmentation settings. We use the Adam optimizer with a learning rate of 0.001 and a batch size of 75. The training data is fed in batches, and each epoch is run for 105 iterations. The validation data is also used to assess the model’s performance and ensure it generalizes well to unseen examples. This approach aims to fine-tune the model’s parameters and optimize its ability to accurately classify images into the ten specified categories: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
tensorflow::set_random_seed(100)
# Create Early Stopping Callback
early_stopping <- callback_early_stopping(monitor = "val_loss", patience = 10)
model_big %>%
compile(
loss = loss_categorical_crossentropy(),
optimizer_adam(learning_rate = 0.001, beta_1 = 0.9),
metrics = "accuracy"
)
history <- model_big %>%
fit(
# training data
train_image_array_gen,
# epochs
steps_per_epoch = as.integer(train_samples / batch_size),
epochs = 105, #21 #35
# validation data
validation_data = val_image_array_gen,
validation_steps = as.integer(valid_samples / batch_size),
# Use Early Stopping Callback
callbacks = list(early_stopping)
)
plot(history)After tuning the model architecture and fitting it with the adjusted hyperparameters, the model’s performance is evaluated to gauge its effectiveness in classifying images. The confusion matrix and relevant metrics such as accuracy, sensitivity, specificity, positive predictive value, and negative predictive value are computed to provide a comprehensive understanding of how well the model performs across different classes. This evaluation process aims to validate the improvements made to the model and determine its ability to make accurate predictions on new and unseen data.
In this case, “accuracy” is considered the most important metric. Accuracy measures the proportion of correctly classified images out of the total images in the dataset. It provides a holistic view of the model’s overall performance in terms of correctly predicting the classes.
set.seed(100)
val_data <- data.frame(file_name = paste0("data/trainingSample/trainingSample/", val_image_array_gen$filenames)) %>%
mutate(class = str_extract(val_image_array_gen$labels, "0|1|2|3|4|5|6|7|8|9"))
# Function to convert image to array
image_prep <- function(x) {
arrays <- lapply(x, function(path) {
img <- image_load(path, target_size = target_size, color_mode = "grayscale")
x <- image_to_array(img)
x <- array_reshape(x, c(1, dim(x)))
x <- x/255 # rescale image pixel
})
do.call(abind::abind, c(arrays, list(along = 1)))
}
test_x <- image_prep(val_data$file_name)
pred_test <- predict(model_big, test_x)%>%
k_argmax() %>% # untuk mengambil nilai probability paling besar
as.array() %>%
as.factor()
# Convert encoding to label
decode <- function(x){
case_when(x == 0 ~ "0",
x == 1 ~ "1",
x == 2 ~ "2",
x == 3 ~ "3",
x == 4 ~ "4",
x == 5 ~ "5",
x == 6 ~ "6",
x == 7 ~ "7",
x == 8 ~ "8",
x == 9 ~ "9"
)
}
pred_test <- sapply(pred_test, decode)
head(pred_test, 10)#> [1] "0" "0" "0" "0" "0" "0" "0" "0" "0" "0"
#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction 0 1 2 3 4 5 6 7 8 9
#> 0 12 0 0 0 0 0 1 0 0 0
#> 1 0 11 0 0 0 0 0 0 1 0
#> 2 0 1 11 0 0 0 0 1 0 0
#> 3 0 0 0 12 0 1 0 1 0 0
#> 4 0 0 1 0 12 0 0 0 4 2
#> 5 0 0 0 0 0 11 0 0 0 0
#> 6 0 0 0 0 0 0 11 0 0 0
#> 7 0 0 0 0 0 0 0 10 0 0
#> 8 0 0 0 0 0 0 0 0 7 0
#> 9 0 0 0 0 0 0 0 0 0 10
#>
#> Overall Statistics
#>
#> Accuracy : 0.8917
#> 95% CI : (0.8219, 0.941)
#> No Information Rate : 0.1
#> P-Value [Acc > NIR] : < 0.00000000000000022
#>
#> Kappa : 0.8796
#>
#> Mcnemar's Test P-Value : NA
#>
#> Statistics by Class:
#>
#> Class: 0 Class: 1 Class: 2 Class: 3 Class: 4 Class: 5
#> Sensitivity 1.0000 0.91667 0.91667 1.0000 1.0000 0.91667
#> Specificity 0.9907 0.99074 0.98148 0.9815 0.9352 1.00000
#> Pos Pred Value 0.9231 0.91667 0.84615 0.8571 0.6316 1.00000
#> Neg Pred Value 1.0000 0.99074 0.99065 1.0000 1.0000 0.99083
#> Prevalence 0.1000 0.10000 0.10000 0.1000 0.1000 0.10000
#> Detection Rate 0.1000 0.09167 0.09167 0.1000 0.1000 0.09167
#> Detection Prevalence 0.1083 0.10000 0.10833 0.1167 0.1583 0.09167
#> Balanced Accuracy 0.9954 0.95370 0.94907 0.9907 0.9676 0.95833
#> Class: 6 Class: 7 Class: 8 Class: 9
#> Sensitivity 0.91667 0.83333 0.58333 0.83333
#> Specificity 1.00000 1.00000 1.00000 1.00000
#> Pos Pred Value 1.00000 1.00000 1.00000 1.00000
#> Neg Pred Value 0.99083 0.98182 0.95575 0.98182
#> Prevalence 0.10000 0.10000 0.10000 0.10000
#> Detection Rate 0.09167 0.08333 0.05833 0.08333
#> Detection Prevalence 0.09167 0.08333 0.05833 0.08333
#> Balanced Accuracy 0.95833 0.91667 0.79167 0.91667
To assess and compare the prediction performance between the “simple_model” and the “tuning_model”, we can delve into various metrics that highlight their strengths and weaknesses. By analyzing these metrics, we can gain valuable insights into how each model performs in classifying images accurately. Let’s take a closer look at the comparison:
# Calculate other metrics for the simple model
compare_simple <- data_frame(Model = "simple_model",
Accuracy = round((s_conf$overall[1] * 100), 2),
Sensitivity = round((s_conf$byClass[1] * 100), 2),
Specificity = round((s_conf$byClass[2] * 100), 2), # True Negative Rate
Precision = round((s_conf$byClass[3] * 100), 2))
# Calculate other metrics for the tuning model
compare_tuning <- data_frame(Model = "tuning_model",
Accuracy = round((t_conf$overall[1] * 100), 2),
Sensitivity = round((t_conf$byClass[1] * 100), 2),
Specificity = round((t_conf$byClass[2] * 100), 2), # True Negative Rate
Precision = round((t_conf$byClass[3] * 100), 2))
rbind(compare_simple, compare_tuning)The table presents a comparison of two models, namely “simple_model” and “tuning_model”, based on their performance metrics. The “Accuracy” metric indicates the overall correctness of the model’s predictions. The “model_tuning” exhibits improved predictive capabilities across all evaluated metrics, making it a more effective and accurate model compared to the “simple_model”.
Predicting data in the testing dataset involves applying the trained model to make predictions on previously unseen images. This step serves as the final test of the model’s ability to generalize its learned patterns to new instances. By inputting the testing images into the model, the algorithm generates predictions for each image’s class. These predictions are compared to the actual ground truth labels to evaluate the model’s accuracy and effectiveness in correctly classifying the images.
Let’s take a look at the testing dataset
folder_list <- list.files("data/testSet/testSet/")
folder_path <- paste0("data/testSet/testSet/", folder_list, "/")
# 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)
}
sample_file <- sample(file_name, 600)
summary(file_dim)#> height width filename
#> Min. :28 Min. :28 Length:600
#> 1st Qu.:28 1st Qu.:28 Class :character
#> Median :28 Median :28 Mode :character
#> Mean :28 Mean :28
#> 3rd Qu.:28 3rd Qu.:28
#> Max. :28 Max. :28
set.seed(100)
data_test1 <- data.frame(ImageId = paste0(folder_path))
data_test1$ImageId <- gsub("/$", "", data_test1$ImageId)
data_test1#> [1] 28000 28 28 1
we have a total of 28,000 test samples, each comprising images with dimensions of 28x28 pixels and a single color channel.
Next, we proceed to generate predictions and create labels for each of the predicted outcomes. This allows us to associate the model’s predictions with their corresponding class labels, enabling us to further analyze and evaluate the accuracy and effectiveness of the model’s classifications.
set.seed(100)
pred_test_big <- predict(model_big, test_x)%>%
k_argmax() %>% # untuk mengambil nilai probability paling besar
as.array() %>%
as.factor()
# Convert encoding to label
decode <- function(x){
case_when(x == 0 ~ "0",
x == 1 ~ "1",
x == 2 ~ "2",
x == 3 ~ "3",
x == 4 ~ "4",
x == 5 ~ "5",
x == 6 ~ "6",
x == 7 ~ "7",
x == 8 ~ "8",
x == 9 ~ "9"
)
}
pred_test_big <- sapply(pred_test_big, decode)
head(pred_test_big, 10)#> [1] "2" "3" "4" "9" "7" "0" "1" "5" "1" "6"
After making the predictions, the results will be saved into a submission file named “submission-rusdi-big.csv.” This file will contain the predicted labels for the corresponding images in the testing dataset. To ensure a clean format, we also remove the file paths, leaving only the file names associated with each prediction. The first three rows of the submission file can be checked below for a preview of the saved data:
# Create data submission
submission <- data.frame(ImageId = data_test1$ImageId,
label = pred_test_big
) %>%
mutate(ImageId = str_remove(ImageId, "data/testSet/testSet/img_")) %>%
mutate(ImageId = as.integer(str_remove(ImageId, "\\.jpg")))
# Write submission
write.csv(submission, "submission-rusdi-big.csv", row.names = FALSE)
# check first 3 data
head(submission, 3)Finally, here are the results of the evaluation metrics from the submission file that has been uploaded to the leaderboard. These metrics provide valuable insights into the model’s performance on the unseen testing data and its ability to generalize to new examples. The evaluation metrics is accuracy.
In this project, I successfully built a Convolutional Neural Network (CNN) model from scratch to perform image classification tasks. The steps I took involved various stages, from data preprocessing to model evaluation, which collectively allowed me to develop an efficient and accurate model.
Firstly, I prepared the data by reading and processing images from directories. I divided the dataset into training and validation sets, and applied data augmentation to enhance the variety of the training data. This is crucial to ensure that the model can recognize different image variations and become more robust in classifying objects within images.
Next, I designed the architecture of the CNN model using convolutional layers, batch normalization, and other related layers. The model was designed to gradually extract important features from images while increasing complexity. I carried out the process of fitting the model to the training data, adjusting hyperparameters such as learning rate and batch size, and utilized the Adam optimization algorithm to train the model.
Model evaluation was conducted using various metrics such as accuracy, sensitivity, specificity, and others. I also performed model tuning by modifying the architecture and hyperparameters, as well as employing more diverse augmentation techniques. This helped me optimize the model to perform better in classifying images with higher accuracy.
Through this project, I gained valuable insights into the process of building and evaluating CNN models for image classification tasks. I understood the importance of proper data preparation, designing an appropriate architecture, conducting tuning, and optimizing the model to achieve better results. With these skills, I am able to apply CNN models to various image classification problems and continuously improve their performance over time.
Looking forward, the potential business implementations of this capstone project are noteworthy. The developed model can be leveraged for a variety of real-world applications, such as automating image classification tasks in industries ranging from e-commerce to environmental monitoring. By harnessing the power of machine learning, businesses can streamline decision-making processes, enhance operational efficiency, and gain deeper insights from visual data. This project thus exemplifies the tangible benefits that machine learning can bring to the realm of image analysis and classification, opening doors to innovation and optimization across various sectors.