Pneumonia X-Ray detection, Keras | Python

In this article, a simple convolutional neural network is demonstrated without any pre-trained network or any data augmentation. The aim of this article is to explain the basic concepts of the neural network using the example of Pneumonia X-Ray detection using a network from scratch using Keras TensorFlow API in Python language.

A study shows 15% of children under the age of 5 die every year due to pneumonia. Air pollution, crowded homes, or parental smoking are the major factors. Early detection will be a major benefit for the helpful treatment. Thus, we are proposing a simple yet robust network with a high true positive count. It can clearly be observed in the Confusion matrix in the coming sections.

Splitting of the article for the ease of understanding is as follows:

  • Importing libraries
  • Building the model
  • Data Generation
  • Compilation and training
  • Confusion Matrix
  • Prediction

Happy Readings!!!

LIBRARIES

Importing of necessary Python libraries required for the project

import numpy as np 
import pandas as pd 
import os
print(os.listdir("../input"))
import keras
import h5py
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Conv2D,MaxPool2D,Dense,Dropout,Softmax,Input,Flatten
from keras.optimizers import Adam,RMSprop,SGD
from keras.layers.merge import add
from keras.layers import Dense, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D, BatchNormalization
from keras.layers import BatchNormalization
from keras.callbacks import ModelCheckpoint, Callback, EarlyStopping
from tensorflow import set_random_seed
from sklearn.metrics import roc_auc_score,roc_curve,accuracy_score,recall_score
from keras.metrics import categorical_accuracy
%matplotlib inline
from keras.preprocessing.image import ImageDataGenerator
os.environ['PYTHONHASHSEED'] = "0"
np.random.seed(1)
set_random_seed(1)
['chest-xray-pneumonia', 'vgg16']
/opt/conda/lib/python3.6/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
from ._conv import register_converters as _register_converters
Using TensorFlow backend.

BUILDING MODEL

The building of the model using simple convolutional layers, max-pooling, flattening layer, dropout, dense, and batch normalization.

  • Convolutional Layer: Capturing of low-level and high-level features from the image and understanding just the ways humans have when they visualize.
  • Max-Pooling: Dropping off the parameters of the network thus reducing the size of the network.
  • Flatten: Flattens the layer into a 1×1 vector for the output.
  • Dropout: Used to avoid overfitting of the network by dropping off some layers from the built network
  • Dense: Changes the dimension of the vectors which connect deeply to the neural network. Thus, applying operations like rotation, translation, and scaling.
  • Batch Normalization: Standardization of the input to have computationally efficient and faster processing of the images.
model = Sequential()
model.add(Conv2D(32, (3,3), strides=(1,1), padding='same', input_shape=(64,64,1), activation='relu'))
model.add(Conv2D(32, (3,3), strides=(1,1), padding='same', activation='relu'))
model.add(MaxPooling2D((2,2)))

model.add(Conv2D(64, (3,3), strides=(1,1), padding='same', activation='relu'))
model.add(BatchNormalization())
model.add(Conv2D(64, (3,3), strides=(1,1), padding='same', activation='relu'))
model.add(MaxPooling2D((2,2)))

model.add(Conv2D(128, (3,3), strides=(1,1), padding='same', activation='relu'))
model.add(BatchNormalization())
model.add(Conv2D(128, (3,3), strides=(1,1), padding='same', activation='relu'))
model.add(MaxPooling2D((2,2)))

model.add(Flatten())
model.add(Dense(1024, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.60))
model.add(Dense(512, activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.4))
model.add(Dense(2, activation='softmax'))

SUMMARY

Printing the summary of the network we built in the previous section using various types of layers mentioned and explained in detail.

model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 64, 64, 32)        320       
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 64, 64, 32)        9248      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 32, 32, 32)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 32, 32, 64)        18496     
_________________________________________________________________
batch_normalization_1 (Batch (None, 32, 32, 64)        256       
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 32, 32, 64)        36928     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 16, 16, 64)        0         
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 16, 16, 128)       73856     
_________________________________________________________________
batch_normalization_2 (Batch (None, 16, 16, 128)       512       
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 16, 16, 128)       147584    
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 8, 8, 128)         0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 8192)              0         
_________________________________________________________________
dense_1 (Dense)              (None, 1024)              8389632   
_________________________________________________________________
batch_normalization_3 (Batch (None, 1024)              4096      
_________________________________________________________________
dropout_1 (Dropout)          (None, 1024)              0         
_________________________________________________________________
dense_2 (Dense)              (None, 512)               524800    
_________________________________________________________________
batch_normalization_4 (Batch (None, 512)               2048      
_________________________________________________________________
dropout_2 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_3 (Dense)              (None, 2)                 1026      
=================================================================
Total params: 9,208,802
Trainable params: 9,205,346
Non-trainable params: 3,456
_________________________________________________________________

DATA GENERATION

Data generation and normalizing(size and channel) of the images. Also, declaring appropriate batch size and folder directories of the dataset for the training of the model.

gen = ImageDataGenerator()
train_batches = gen.flow_from_directory("../input/chest-xray-pneumonia/chest_xray/chest_xray/train",model.input_shape[1:3],color_mode="grayscale",shuffle=True,seed=1,
                                        batch_size=16)
valid_batches = gen.flow_from_directory("../input/chest-xray-pneumonia/chest_xray/chest_xray/val", model.input_shape[1:3],color_mode="grayscale", shuffle=True,seed=1,
                                        batch_size=16)
test_batches = gen.flow_from_directory("../input/chest-xray-pneumonia/chest_xray/chest_xray/test", model.input_shape[1:3], shuffle=False,
                                       color_mode="grayscale", batch_size=8)
Found 5216 images belonging to 2 classes.
Found 16 images belonging to 2 classes.
Found 624 images belonging to 2 classes.

CALL BACK

Declaring of the Callback functions. Terminates the training of the model. At the end of each epoch, the fit will check the callback functions. Also, the callback functions used here are:

  • EarlyStopping: When the ‘val_loss’ stops improving, the training will be stopped.
  • ModelCheckpoint: The weights and model are saved in intervals so that even if training is halted in some unexpected cases, we can load the saved model and continue training.
es = EarlyStopping(monitor='val_loss',patience=5)
chkpt = ModelCheckpoint(filepath='best_model_todate', save_best_only=True, save_weights_only=True)

TRAINING

Compilation and training of the network. The compilation is done using ADAM optimizer and categorical cross-entropy loss function. 50 epochs are used to train the network but terminated at the end of the 13th epochs by the callback function.

model.compile(Adam(lr=0.0001, decay=1e-5),loss="categorical_crossentropy", metrics=["accuracy"])
model.fit_generator(train_batches,validation_data=valid_batches,epochs=50, steps_per_epoch=16, validation_steps=16)
WARNING:tensorflow:Variable *= will be deprecated. Use variable.assign_mul if you want assignment to the variable value or 'x = x * y' if you want a new python Tensor object.
Epoch 1/20
16/16 [==============================] - 9s 567ms/step - loss: 0.8233 - acc: 0.6875 - val_loss: 1.7335 - val_acc: 0.5000
Epoch 2/20
16/16 [==============================] - 6s 345ms/step - loss: 0.6202 - acc: 0.7695 - val_loss: 0.9575 - val_acc: 0.5625
Epoch 3/20
16/16 [==============================] - 5s 330ms/step - loss: 0.5424 - acc: 0.8008 - val_loss: 0.4026 - val_acc: 0.7500
Epoch 4/20
16/16 [==============================] - 5s 342ms/step - loss: 0.4494 - acc: 0.8359 - val_loss: 0.5479 - val_acc: 0.7500
Epoch 5/20
16/16 [==============================] - 5s 343ms/step - loss: 0.4504 - acc: 0.8633 - val_loss: 0.1668 - val_acc: 0.8750
Epoch 6/20
16/16 [==============================] - 5s 333ms/step - loss: 0.4303 - acc: 0.8438 - val_loss: 0.1286 - val_acc: 1.0000
Epoch 7/20
16/16 [==============================] - 5s 324ms/step - loss: 0.4691 - acc: 0.8555 - val_loss: 0.2109 - val_acc: 0.8750
Epoch 8/20
16/16 [==============================] - 6s 356ms/step - loss: 0.2969 - acc: 0.8945 - val_loss: 0.1121 - val_acc: 1.0000
Epoch 9/20
16/16 [==============================] - 5s 325ms/step - loss: 0.4194 - acc: 0.8633 - val_loss: 0.1600 - val_acc: 1.0000
Epoch 10/20
16/16 [==============================] - 5s 336ms/step - loss: 0.5360 - acc: 0.8008 - val_loss: 0.2148 - val_acc: 0.9375
Epoch 11/20
16/16 [==============================] - 5s 335ms/step - loss: 0.3992 - acc: 0.8438 - val_loss: 0.3841 - val_acc: 0.7500
Epoch 12/20
16/16 [==============================] - 5s 339ms/step - loss: 0.3500 - acc: 0.8633 - val_loss: 0.2278 - val_acc: 0.9375
Epoch 13/20
16/16 [==============================] - 5s 342ms/step - loss: 0.4122 - acc: 0.8672 - val_loss: 0.1987 - val_acc: 0.9375
Out[9]: <keras.callbacks.History at 0x7fc1f8ff23c8>

CONFUSION MATRIX

The confusion matrix tests the images from the dataset. Then, the prediction of the image with respect to the labels. Prediction of 521 images (380 pneumonia and 141 not pneumonia) accurately and the recognition of 103 false positives (93 no pneumonia and 10 pneumonia).

from sklearn.metrics import confusion_matrix
CM = confusion_matrix(pre["label"], pre["pre"])
from mlxtend.plotting import plot_confusion_matrix
fig, ax = plot_confusion_matrix(conf_mat=CM ,  figsize=(5, 5))
plt.show()

CONFUSION MATRIX

PREDICTION

Estimation of the prediction rate and recall drawn from the confusion matrix for understanding the accuracy of the model.

tn, fp, fn, tp = CM.ravel()
precision = tp/(tp+fp)
recall = tp/(tp+fn)
print("Recall of the model is {:.2f}".format(recall))
print("Precision of the model is {:.2f}".format(precision))
Recall of the model is 0.97
Precision of the model is 0.80

FINAL THOUGHTS

In this article, we discussed the basic deep learning framework without transfer learning or augmentation is done resulting in a prediction accuracy of 80% with just 13 epochs. The building, compiling, and training of the network. Then, explaining all these layers along with the callback functions used in this article in detail for increasing the scope of understanding of the readers. Thus, the flow is as follows: first, the importing of the dependencies for the project. Then building the model and followed by compilation and training of the model. Then, evaluation of the model to get the accuracy of the model.

The source code for the pneumonia x-ray can be found and downloaded from here.

To learn transfer learning approaches, you can refer to my blogs:

[1]. Image classification of Bird species (Used UNets fused with a Residual net)

[2]. Salt Identification (Used a VGG16 net)

To learn from my other machine learning blogs, refer here.

REFERENCES:

[1]. A Novel Transfer Learning-Based Approach for Pneumonia Detection in Chest X-ray Images (Used Transfer Learning)

[2]. Predict pneumonia with chest X-ray images based on convolutional deep neural learning networks

Thank you. Hope this article was helpful for all!

Leave a Reply

Your email address will not be published. Required fields are marked *