Spot the mask
Implementation

Lets Jump In

First we need to understand the given dataset and what problem classification problem are we trying to solve. As in the introdction we are dealing with pictures so this jumps in to computer vision classification problem. As for image classification problems neural networks (opens in a new tab) have been proven to be the right solution, without wasting anytime let start coding 🔥.

Data Preparation

In any machine learning problem data plays a big part. So first with need to prepare the data to be in the right order. We have being given image zipfile images.zip, a train label file train_label.csv and sample submission file SampleSubmission.csv. So we need to extract the data from image.zip file and filter the data from train and for submission.

1. Import Libraries

import cv2
import numpy as np
import os
import pandas as pd
from PIL import Image
import tensorflow as tf
 
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.utils import img_to_array
from tensorflow.keras.optimizers import RMSprop
import matplotlib.pyplot as plt
 
from tensorflow.keras import layers
from tensorflow.keras import Model
from tensorflow.keras.preprocessing.image import ImageDataGenerator
 
import shutil

With a google search you can learn what each libray does. I strong recommend to read the documentation from more information. After importing the libraries we start preparing the data.

2. Import the csv datasets By using the csv dataset train_labels and SampleSubmission it will enable to filter out the train set for the images.

df_train_labels = pd.read_csv("./csv_dataset/train_labels.csv")
df_submission = pd.read_csv("./csv_dataset/SampleSubmission.csv")

It adivise to view the dataset and to take a look that all the dataset are correct.

By using the train label it will help us to split the training and testing dataset. By the following the function that reading all the images from the directory then if it is not in the train_label file then it is a test data the rest are the training dataset. For the SampleSubmission file will be used later on filtering the images that will be used for inference and submission.

def train_test(dataset):
    # From the dir of the total images or after unziping the folder
 
    nameImage = os.listdir('./dataset/')
 
    # Creating image file name and its target (0,1)
    train_list = []
    test_list =[]
 
    for i in range(len(dataset)):
        name = dataset.loc[i, "image"]
        label = dataset.loc[i, "target"]
 
        if (nameImage[i].replace(" ", "") in np.array(dataset["image"].replace(" ", ""))):
            train_list.append([name, label])
 
        else:
            test_list.append([name, label])
 
    return train_list , test_list
 

This function will return two list train and test that have the file name and its label.

# Get the train data and test data
 
train , test = train_test(df_train_labels)
 
print(f"The training dataset are {len(train)} and the test dataset are len(test) in totally")

The training dataset are 950 and the test dataset are 358 in totally

After filtering the data it important to storage the images in the respective label as it will help in dataloader function when its need to train the model. The file folders are consist of training and validation for both withmask and nomask. The test list from above will be store on the validation folder. And the sub folder will be used to store the submission images for inferences.

# Create the directory
os.makedirs("image", exist_ok=True)
os.makedirs("./image/train", exist_ok=True)
os.makedirs("./image/valid", exist_ok=True)
 
os.makedirs("./image/train/withMask", exist_ok=True)
os.makedirs("./image/train/noMask", exist_ok=True)
 
os.makedirs("./image/valid/withMask", exist_ok=True)
os.makedirs("./image/valid/noMask", exist_ok=True)
 
os.makedirs("./image/sub", exist_ok=True)
os.makedirs("./image/sub/sub", exist_ok=True)
 

After making directories images are needed to be stored in their corresponding labels

# Inserting the images to their respetive folders.
for i in train:
 
    if i[1] == 1:
 
        image = "./dataset/" + i[0]
        target = "./image/train/withMask"
        shutil.copy(image, target)
 
    else:
        image = "./dataset/" + i[0]
        target = "./image/train/noMask"
        shutil.copy(image, target)
 
for i in test:
 
    if i[1] == 1:
 
        image = "./dataset/" + i[0]
        target = "./image/valid/withMask"
        shutil.copy(image, target)
 
    else:
        image = "./dataset/" + i[0]
        target = "./image/valid/noMask"
        shutil.copy(image, target) # shutil.copy is used to copy file from one dir to another.
 

Training and Validation Generators

Generators will enable the fetching of images in their files and a batch and also a place to perform data augmentation (opens in a new tab). Data Augmentation is a techniques to make tranformation on the dataset to create more data and reduce overfiting.

 
TRAINING_DIR = "./image/train/"
VALIDATION_DIR = "./image/valid/"
 
def train_val_generators(TRAINING_DIR, VALIDATION_DIR):
  """
  Creates the training and validation data generators
 
  Args:
    TRAINING_DIR (string): directory path containing the training images
    VALIDATION_DIR (string): directory path containing the testing/validation images
 
  Returns:
    train_generator, validation_generator: tuple containing the generators
  """
 
  # Instantiate the ImageDataGenerator class
  train_datagen = ImageDataGenerator(rescale=1./255,
                                     rotation_range=30,
                                     width_shift_range=0.2,
                                     height_shift_range=0.2,
                                     shear_range=0.2,
                                     zoom_range=0.2,
                                     horizontal_flip=True,
                                     fill_mode='nearest')
 
  train_generator = train_datagen.flow_from_directory(directory=TRAINING_DIR,
                                                      batch_size=16,
                                                      class_mode="binary",
                                                      target_size=(256, 256))
 
  validation_datagen = ImageDataGenerator(rescale=1./255)
 
  validation_generator = validation_datagen.flow_from_directory(directory=VALIDATION_DIR,
                                                                batch_size=16,
                                                                class_mode="binary",
                                                                target_size=(256, 256))
 
  return train_generator, validation_generator
 

The train_val_generators function helps in transforming the data and create a dataloader or data generator for the directory with a batch size of 16 and target size of 256. After this our dataset is read now lets jump on creating a model.

Creating a model

This is what you where waching for, on creating neural networks its important to understand different techniques that are used to create a more accuracy model. On this part we will take a look on tranfer learning (opens in a new tab) / tensorflow fine tunning (opens in a new tab) a technique that used other computer vision models to train on other classification problems. On this problem inception v3 model (opens in a new tab) will be used.

1. Download the model

# Get the Invception_V3 model
 
!wget --no-check-certificate \
    https://storage.googleapis.com/mledu-datasets/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5 \
    -O /tmp/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5
 

2. Import the InceptionV3

# Import the inception model
from tensorflow.keras.applications.inception_v3 import InceptionV3
 
# Create an instance of the inception model from the local pre-trained weights
local_weights_file = '/tmp/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5'
 

3. Create a pre-trained model

This is done by using the InceptionV3 weightss

# Create the pretrain model from the imported model
 
def create_pre_trained_model(local_weights_file):
 
    """
    Initializes an InceptionV3 model.
 
    Args:
        local_weights_file (string): path pointing to a pretrained weights H5 file
 
    Returns:
        pre_trained_model: the initialized InceptionV3 model
 
    """
 
    pre_trained_model = InceptionV3(input_shape = (256, 256, 3),
                                    include_top = False,
                                    weights = None
                                    )
 
    pre_trained_model.load_weights(local_weights_file)
 
    # Make all the layers in the pre-trained model non-trainable
    for layer in pre_trained_model.layers:
        layer.trainable = False
 
    return pre_trained_model
 

4. Create an ouput layer

This is the layer that will be used to feed its output to the next neural networks.

def output_of_last_layer(pre_trained_model):
  """
  Gets the last layer output of a model
 
  Args:
    pre_trained_model (tf.keras Model): model to get the last layer output from
 
  Returns:
    last_output: output of the model's last layer
    
  """
 
  last_desired_layer = pre_trained_model.get_layer('mixed7')
  print('last layer output shape: ', last_desired_layer.output_shape)
  last_output = last_desired_layer.output
  print('last layer output: ', last_output)
 
 
  return last_output
 

5. Creating the final model

This combines the output layer, pre-train weights and the classification layer.

def create_final_model(pre_trained_model, last_output):
  """
  Appends a custom model to a pre-trained model
 
  Args:
    pre_trained_model (tf.keras Model): model that will accept the train/test inputs
    last_output (tensor): last layer output of the pre-trained model
 
  Returns:
    model: the combined model
  """
  # Flatten the output layer to 1 dimension
  x = layers.Flatten()(last_output)
 
  # Add a fully connected layer with 1024 hidden units and ReLU activation
  x = layers.Dense(512, activation='relu')(x)
  x = layers.Dropout(0.4)(x)
  x = layers.Dense(1024, activation='relu')(x)
  # Add a dropout rate of 0.2
  x = layers.Dropout(0.4)(x)
  # Add a final sigmoid layer for classification
  x = layers.Dense(1, activation='sigmoid')(x)
 
  # Create the complete model by using the Model class
  model = Model(inputs=pre_trained_model.input, outputs= x)
 
  # Compile the model
  model.compile(optimizer = RMSprop(learning_rate=0.001),
                loss = 'binary_crossentropy',
                metrics = [['accuracy', 'AUC', 'binary_accuracy']])
 
 
 
  return model
 

6. Viewing the total parameter and the trainable parameters

model = create_final_model(pre_trained_model, last_output)
 
# Inspect parameters
total_params = model.count_params()
num_trainable_params = sum([w.shape.num_elements() for w in model.trainable_weights])
 
print(f"There are {total_params:,} total parameters in this model.")
print(f"There are {num_trainable_params:,} trainable parameters in this model.")
 

7. Training the model

This is the part that will be used to fit the dataset

history = model.fit(train_generator,
                    validation_data = validation_generator,
                    epochs = 100,
                    verbose = 2,
                    batch_size=10,)