Beginner’s guide to building Convolutional Neural Networks using TensorFlow’s Keras API in Python
By: Varshita Sher
Tags: Keras, Image Classification, Deep Learning, Neural Networks, Python

DEEP LEARNING FOR BEGINNERS

Beginner’s guide to building Convolutional Neural Networks using TensorFlow’s Keras API in Python

Image by Pete Linforth from Pixabay

Welcome to Part 2 of the Neural Network series! In Part 1, we worked our way through an Artificial Neural Network (ANNs) using the Keras API. We talked about Sequential network architecture, activation functions, hidden layers, neurons, etc. and finally wrapped it all up in an end-to-end example that predicted whether loan application would be approved or rejected.

In this tutorial, we will be learning how to create a Convolutional Neural Network (CNN) using the Keras API. To make it more intuitive, I will be explaining what each layer of this network does and provide tips and tricks to ease your deep learning journey. Our aim in this tutorial is to build a basic CNN that can classify images of chest Xrays and establish if it is normal or has pneumonia. Given the Covid-19 pandemic, I think this would make for an interesting project even for your data science interviews!

Let’s get started!

When should I use a Convolutional Neural Network instead of an Artificial Neural Network?

CNNs work best when the data can be represented in a spatial manner, say an image in MxN pixels. If you data is just as useful after shuffling any of your columns with each other then you cannot use CNN.

For instance, if you recall the loan application dataset from Part 1, it had two columns (or features), namely age and area , and if I were to swap the two columns (before feeding it to my network) it would make no difference whatsoever to my dataset. Hence, ANNs are preferred for such datasets. On the contrary, if I were to swap the columns (which are essentially pixel arrays) in my image, I am surely going to mess up my actual image. Hence, using ANNs is a big no-no and you must use CNNs.

Let’s dive right into the coding...

We begin by installing Keras onto our machine. As I discussed in Part 1, Keras is integrated within TensorFlow, so all you have to do is pip install tensorflow in your terminal (for Mac OS) to access Keras in your Jupyter notebook. To check the version of Tensorflow, use tf.__version__ .

Importing libraries

import tensorflow as tf

from tensorflow import keras

from tensorflow.keras.models import Sequential

from tensorflow.keras.layers import Activation, Dense, Flatten, BatchNormalization, Conv2D, MaxPool2D

from tensorflow.keras.optimizers import Adam

from tensorflow.keras.metrics import categorical_crossentropy

from sklearn.metrics import confusion_matrix

from tensorflow.keras.preprocessing.image import ImageDataGenerator import numpy as np

import itertools

import os

import random import matplotlib.pyplot as plt

%matplotlib inline

Dataset

We will be working with the Chest X-Ray Images Kaggle dataset. Once you download and unzip it, you will see the chest_xray folder that contains images organized neatly into train, valid, and test subfolders. Each of these subfolders contains two sub-sub folders, one for normal chest Xrays and another for Xrays indicating pneumonia .

Note: Make sure the chest_xray folder is sitting in the same directory as your Python Jupyter notebook.

Setting the paths to the train, validate, and test folders:

train_path = "chest_xray/train"

test_path = "chest_xray/test"

valid_path = "chest_xray/val"

Note: If you collected your own dataset & it is not neatly organized into the train, valid and test folders, worry not! Just make sure (a) you have saved all the images (normal and pneumonia) in a common folder called chest_xrays AND (b) the name of the image contains either of the two words — normal or pneumonia. From thereon, to organize images into train, valid, and test set, use the following code snippet.

# Organize data into train, valid, test directories

os.chdir('data/chest_xrays')

if os.path.isdir('train/normal') is False:

os.makedirs('train/normal')

os.makedirs('train/pneumonia')

os.makedirs('valid/normal')

os.makedirs('valid/pneumonia')

os.makedirs('test/normal')

os.makedirs('test/pneumonia')



for i in random.sample(glob.glob('pneumonia*'), 500):

shutil.move(i, 'train/pneumonia')

for i in random.sample(glob.glob('normal*'), 500):

shutil.move(i, 'train/normal')

for i in random.sample(glob.glob('pneumonia*'), 100):

shutil.move(i, 'valid/pneumonia')

for i in random.sample(glob.glob('normal*'), 100):

shutil.move(i, 'valid/normal')

for i in random.sample(glob.glob('pneumonia*'), 50):

shutil.move(i, 'test/pneumonia')

for i in random.sample(glob.glob('normal*'), 50):

shutil.move(i, 'test/normal')



os.chdir('../../')

Preprocessing the Image data

We need to put the data into a format that the model expects. So we must put our images into a format of a Keras ImageGenerator . In short, we are going to create batches from train, test, and valid directories using the flow_from_directory() function in the ImageGenerator class. And it is precisely these batches of data that will be passed to the sequential model using the fit function. (Previously, we were passing NumPy arrays to the fit function when building ANNs in Part 1, but now we need to pass the batches to the fit function).

As part of the preprocessing (before the batches are created), we will be applying preprocessing_function = tf.keras.applications.vgg16.preprocess_input . This will process our images in the same manner that images get processed when they are passed to the vgg16 model (the CNN model that won the ImageNet competition in 2014).

# Creating train, test and valid batches from the respective directories train_batches = ImageDataGenerator(preprocessing_function=tf.keras.applications.vgg16.preprocess_input).flow_from_directory(directory=train_path, target_size=(224,224), classes=['pneumonia', 'normal'], batch_size=10) valid_batches = ImageDataGenerator(preprocessing_function=tf.keras.applications.vgg16.preprocess_input).flow_from_directory(directory=valid_path, target_size=(224,224), classes=['pneumonia', 'normal'], batch_size=10) test_batches = ImageDataGenerator(preprocessing_function=tf.keras.applications.vgg16.preprocess_input).flow_from_directory(directory=test_path, target_size=(224,224), classes=['pneumonia', 'normal'], batch_size=10, shuffle=False)

The target_size specifies what height and width we want the images to be resized to. This is important since we might have images of different sizes in our dataset.

The batch_size is set to 10 and the choice of 10 is arbitrary. Another common batch size value used in 32 but ideally, the most optimal value should be found by hyperparameter tuning (we will learn how to do so in the next tutorial).

Batch size specifies how many training samples should be passed to the neural network in one go before the weights of the neural network are updated.

We also specify shuffle = False only for the test set because later on when we test our model, we want to have access to unshuffled labels to plot our confusion matrix. By default, shuffle is set to True.

Note: If you see Found 0 images beloning to 2 classes when you run the code above, chances are you are pointing to the wrong directory! Fix that and it should work fine!

Visualize the image data:

Using the plotting helper function from TensorFlow’s documentation.

# plot images in the form of a 1 by 10 grid and resize img to 20x20

def plotImages(images_arr):

fig, axes = plt.subplots(1, 10, figsize=(20,20))

axes = axes.flatten()

for img, ax in zip( images_arr, axes):

ax.imshow(img.astype(np.uint8))

ax.axis('off')

plt.tight_layout()

plt.show()

Newt, we will use the Python function next() to grab a single batch of images and corresponding labels from the trainset. Since we set batch_size = 10 , we will see we have 10 images in imgs and 10 corresponding labels in labels .

imgs, labels = next(train_batches) plotImages(imgs)

print(labels)