Dementia Classification Web App— Developing And Deploying A Deep Learning Algorithm To Predict Dementia Severity

Kamen Damov
7 min readJul 13, 2022

--

Introduction

Through medical and technological advances, life expectancy increases. This is great, granted, but it’s a double edged sword. As life expectancy increases, more people will live to there 80s and 90s, which will inevitably increase the prevalence of dementia.

Here’s a statistic from the CDC regarding dementia:

Of those at least 65 years of age, there is an estimated 5.0 million adults with dementia in 2014 and projected to be nearly 14 million by 2060.

Source: https://www.cdc.gov/aging/dementia/index.html

This increase will be a challenge for medical systems and professionals working in this field. Advances in technology and medicine will be crucial to detect dementia cases earlier, and treating them.

In light of these facts, the present article will go through the development process of a tool to predict dementia in a patients’ horizontal MRI brain scan.

Data

Here is a link to the kaggle page: https://www.kaggle.com/datasets/sachinkumar413/alzheimer-mri-dataset. Each MRI image has a size of 128x128 pixels. The data-set was labelled in 4 classes: mild dementia, moderate dementia, no dementia, and very mild dementia.

Here is a sample of one image per category:

#Visualize images
dem_types = ['Mild_Demented',
'Moderate_Demented',
'Non_Demented',
'Very_Mild_Demented']
examples = ['Mild_Demented/mild_2.jpg', 'Moderate_Demented/moderate_2.jpg',
'Non_Demented/non_2.jpg',
'Very_Mild_Demented/verymild_2.jpg']
for i in range(len(dem_types)):
plt.plot(128,128)
plt.figure(i+1)
plt.title(dem_types[i])
plt.xlabel("X pixel scaling")
plt.ylabel("Y pixels scaling")
plt.imshow(image.imread('Dataset/'+ examples[i]))
plt.show()

Ouput:

Data preparation

The data is divided in 4 folders, each folder includes MRI images of a class. To create our train, test, and validation splits, we will use the python splitfolders module.

import splitfolderssplitfolders.ratio('../Alzheimer_classification/Dataset', output="output", seed=1345, ratio=(.8, 0.1,0.1))

Note that the train, test, and validation split is passed in the ratio argument. In this case, our split will be 80% of the total data for training, 10% for testing and 10% for validation.

Now let’s initialize the train, test, and validation data:

#Height and width in pixels
img_height = 128
img_width = 128
data_splits = []
direct = ['output/train', 'output/test', 'output/val']
for d in direct:
var_name = tf.keras.utils.image_dataset_from_directory(
d,
batch_size=64,
image_size=(img_height, img_width),
seed=123,
)
data_splits.append(var_name)

Great! The data is ready to be fed to the model. Let’s build it!

Model

Given that we are dealing with a image classification problem, we will use a Convolutional Neural Network (CNN). I used the popular tensorflow library, Keras, to build the model. After testing out different architectures, I decided to use a modified version of the LeNet-5 Archicture, conceptualized by Yan LeCun and Yoshua Bengio.

What I kept from their architecture (n.b.: I won’t go in detail for the technical terms, but will reference some helpful resources):

What I changed:

Here is a graph of the original LeNet-5:

Here is my version (note: a rescaling layer has been added to produce an “edible” input) :

#LeNet-5 CNN Architecture Modified (MaxPool and Dropout 25%)model = keras.Sequential()
model.add(Rescaling(1./255, input_shape=(128, 128, 3)))
#First convolutional layer
model.add(layers.Conv2D(filters=16, kernel_size=(3, 3), padding='same', activation = 'relu', input_shape = (128, 128, 3), kernel_initializer="he_normal"))
#First pooling layer
model.add(layers.MaxPooling2D())
#Second convolutional layer
model.add(layers.Conv2D(filters=32, kernel_size=(3, 3), padding='same', activation = 'relu', input_shape = (128, 128, 3), kernel_initializer="he_normal"))
#Second pooling layer
model.add(layers.MaxPooling2D())
#Dropout 1/4 of nodes
model.add(Dropout(0.25))
model.add(layers.Flatten())
model.add(layers.Dense(units = 128, activation = 'relu',kernel_initializer="he_normal"))
model.add(layers.Dense(units = 64, activation = 'relu'))
#Softmax for multiclass classifying
model.add(layers.Dense(units = 4, activation = 'softmax'))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy',
metrics = ['accuracy'])

Let’s train the model:

#Reference the initialization, data_splits[0] is the training data, #and data_splits[2] is the validation data
hist=model.fit(data_splits[0],
epochs=50,
validation_data=data_splits[2])

Now let’s see if the model is over-fitted by plotting the accuracy and loss functions.

For accuracy :

For loss :

As the validation and train functions seem to be converging for 50 epochs, we can conclude that the model is not over-fitted. It should perform well on unseen data.

Let’s evaluate or model:

#data_splits[1] being our test data
model.evaluate(data_splits[1])

Output:

Our model has an accuracy of 0.994. Pretty good.

Let’s compute the classification report and the confusion matrix, respectively:

preds = np.array([])
labels = np.array([])
for x, y in data_splits[1]:
preds = np.concatenate([preds, np.argmax(model.predict(x), axis = -1)])
labels = np.concatenate([labels, np.argmax(y.numpy(), axis=-1)])
print(classification_report(labels,preds))
print(confusion_matrix(labels,preds))

Let’s test it out with an MRI image:

LeNet5 = load_model('LeNet-5e50Mod.h5')
img = load_img("output/test/Very_Mild_Demented/verymild_1737.jpg")
img = img.resize((128, 128))
img = img_to_array(img)
img = img.reshape(-1,128, 128,3)
#print out the predicted class
print(np.argmax(LeNet5.predict(img)))

Output:

It has classified a very mild dementia MRI image as 3 (out of the 0, 1, 2, 3 possible categories), which is a correct classification if we look at the order of the initial folders:

Great! Now let’s develop a web application to deploy it.

Web Application

We will use gradio, a low-code web application python library, to deploy the model.

Here are the imports:

import gradio as gr
import numpy as np
import tensorflow as tf
from tensorflow import keras
from keras.models import load_model

First off, we need to create a dictionnary to index the category name:

#Dictionary to index a category
res_dict = {
0: "Mild dementia detected",
1: "Moderate dementia detected",
2: "No dementia detected",
3: "Very mild dementia detected"
}

Now let’s define the function that will take an MRI brain image as input, preprocess the image so it’s properly fitted to the model, and return the prediction:

def classify_image(img):
#Preprocessing image
scan = tf.keras.preprocessing.image.img_to_array(img)
scan = scan.reshape(-1,128, 128,3)
LeNet5 = load_model('LeNet-5e50Mod.h5')
return res_dict[np.argmax(LeNet5.predict(scan))]

Now let’s create the interface:

app = gr.Interface(title="Input your horizontal section MRI scan",
#Calling the function to predict MRI input
fn=classify_image,
#Creating the input slot for the image
inputs=gr.inputs.Image(shape=(128, 128)),
#Creating the ouput slot
outputs=gr.outputs.Label(num_top_classes=4),
#Setting out a few examples to "test drive" the app
#Mild dementia MRI
examples=["output/test/Mild_Demented/mild_2.jpg",
#Moderate dementia MRI
"output/test/Moderate_Demented/moderate_7.jpg",
#No dementia MRI
"output/test/Non_Demented/non_10.jpg",
#Very mild dementia MRI
"output/test/Very_Mild_Demented/verymild_3.jpg"
]
)
app.launch()

And there we go! We have our web application.

Deployement

This app was deployed on Heroku. To deploy a gradio app on Heroku you need to add 3 files to your Github repository to deploy it properly.

  • setup.sh
  • Procfile (commands executed on startup)
  • requirements.txt (list of all your dependencies

Here’s my setup.sh file:

export GRADIO_SERVER_NAME=0.0.0.0                              export GRADIO_SERVER_PORT="$PORT"

Here’s my Procfile file (make sure that the ‘p’ is capital!):

web: source setup.sh && python WebApp.py

And finally, here’s my requirements.txt file:

numpy                             
pandas
gunicorn
rq
tensorflow-cpu
keras
gradio

Make sure you have tensorflow-cpu and not tensorflow as it is too large and will bust the 500mb limit.

Testing

Here’s a demo of the app:

As we can see, the model predicted no dementia for the new MRI image (found on Google Images). Dementia is directly correlated to white and grey matter volume in the brain. The classification is correct as the grey and white matter seems to be intact in this case (and also the fact that I specifically searched for a healthy brain MRI)!

Test out the app for yourself! Link to the app

Thank you for reading!

Here’s a link to the github repository (which includes the app, the data preprocessing, and the model development: https://github.com/KamenDamov/Alzheimer_classification

--

--

Kamen Damov

Mathematics and Computer Science student at University of Montreal.