This is a simple REST
api that is served to classify pneumonia
given an X-ray image of a chest of a human beings. The following are expected results when the model does it's classification.
- pneumonia bacteria
- pneumonia virus
- normal
You can download and install our app on android at here.. Alternatively you can scan the following QR Code
using your android phone. This will take you to the download page of our app.
After you have downloaded the app you can follow the following steps to install it on your android phone.
-
On devices running
Android 8.0
(API level 26) and higher, you must navigate to theInstall unknown apps system settings
screen to enable app installations from a particular location (i.e. the web browser you are downloading the app from). -
On devices running Android
7.1.1
(API level 25) and lower, you should enable theUnknown sources system setting
, found inSettings
>Security
on your device.
The following demo video was tested on
iOS V.16
using an iphoneX
demo.mp4
The deployed version of the API can be found at PC-API where you can make classification requests to the server and get response.
To run this server and make prediction on your own images follow the following steps
- clone this repository by running the following command:
git clone https://github.com/CrispenGari/pneumonia-infection.git
- Navigate to the folder
pneumonia-infection
by running the following command:
cd pneumonia-infection/server
- create a virtual environment and activate it, you can create a virtual environment in may ways for example on windows you can create a virtual environment by running the following command:
virtualenv venv && .\venv\Scripts\activate
- run the following command to install packages
pip install -r requirements.txt
- navigate to the folder where the
app.py
file is located and run
python app.py
We have 2 models which are specified by versions and have different model architecture.
- Multi Layer Perceptron (MLP) -
v0
- LeNET -
v1
Our simple Multi Layer Perceptron (MLP) architecture to do the categorical image classification on chest-x-ray
looks simply as follows:
class MLP(nn.Module):
def __init__(self, input_dim, output_dim, dropout=.5):
super(MLP, self).__init__()
self.classifier = nn.Sequential(
nn.Linear(input_dim, 250),
nn.ReLU(),
nn.Dropout(dropout),
nn.Linear(250, 100),
nn.ReLU(),
nn.Dropout(dropout),
nn.Linear(100, output_dim)
)
def forward(self, x):
# x = [batch size, height, width]
batch_size = x.shape[0]
x = x.view(batch_size, -1)
# x = [batch size, height * width]
x = self.classifier(x) # x = [batch_size, output_dim]
return x
All images are transformed to
grayscale
.
The LeNet
architecture to do the categorical image classification on chest-x-ray
looks simply as follows:
class LeNet(nn.Module):
def __init__(self, output_dim):
super(LeNet, self).__init__()
self.maxpool2d = nn.MaxPool2d(2)
self.relu = nn.ReLU()
self.convs = nn.Sequential(
nn.Conv2d(
in_channels=1,
out_channels=6,
kernel_size =5
),
nn.MaxPool2d(2),
nn.ReLU(),
nn.Conv2d(
in_channels=6,
out_channels=16,
kernel_size = 5
),
nn.MaxPool2d(2),
nn.ReLU()
)
self.classifier = nn.Sequential(
nn.Linear(16 * 5 * 5, 120),
nn.ReLU(),
nn.Linear(120, 84),
nn.ReLU(),
nn.Linear(84, output_dim)
)
def forward(self, x):
# x = [batch size, 1, 32, 32]
x = self.convs(x)
# x = [batch_size, 16, 5, 5]
x = x.view(x.shape[0], -1) # x = [batch size, 16*5*5]
x = self.classifier(x)
return x
First let's have a look on how many examples our dataset was having in each set. We had 3
sets which are train
, validation
and test
. In the following table we will see how many examples for each set was used to train these models.
ARCHITECTURE | TRAIN EXAMPLES | VALIDATION EXAMPLES | TEST EXAMPLES | TOTAL EXAMPLES |
---|---|---|---|---|
MLP | 5,442 | 1,135 | 1,135 | 7,712 |
LeNet | 5,442 | 1,135 | 1,135 | 7,712 |
All models were trained for 20
epochs and the training the following table shows the training summary for each model architecture.
ARCHITECTURE | TOTAL EPOCHS | LAST SAVED EPOCH | TOTAL TRAINING TIME |
---|---|---|---|
MLP | 20 | 14 | 1:39:17.87 |
LeNet | 20 | 20 | 0:55:03.84 |
We can see that the
mlp
model architecture took a lot of time to train for20
epochs as compared to thelenet
architecture. This is because of the total number of trainable parameters it has which are more that the ones thatlenet
has. We can further visualize the training time for each model using line graphs:
MLP
(v0)
LeNet
(v1)
These models have different model parameters, in the following table we are going to show the model parameters for each architecture.
ARCHITECTURE | TOTAL PARAMETERS | TRAINABLE PARAMETERS |
---|---|---|
MLP | 2,329,653 | 2,329,653 |
LeNet | 61,111 | 61,111 |
In the following table we are going to show the best model's train
, evaluation
and test
accuracy
for each model version.
MODEL NAME | MODEL ARCHITECTURE | MODEL DESCRIPTION | MODEL VERSION | TEST ACCURACY | VALIDATION ACCURACY | TRAIN ACCURACY | TEST LOSS | VALIDATION LOSS | TRAIN LOSS |
---|---|---|---|---|---|---|---|---|---|
pneumonia_mlp.pt | MLP | pneumonia classification using Multi Layer Perceprton (MLP) | v0 | 75.46% | 75.46% | 73.89% | 0.602 | 0.602 | 0.606 |
pneunomia_lenet.pt | LeNET | pneumonia classification model using the modified LeNet architecture. | v1 | 76.51% | 76.51% | 78.49% | 0.551 | 0.551 | 0.505 |
We can further display visualize the model training history using line graphs for each model architecture in terms of train loss
, validation loss
, train accuracy
and validation accuracy
and see how each architecture manage to reduce loss and increase accuracy over each training epoch.
MLP
(v0)
LeNet
(v1)
Next, we are going to show the model evaluation metrics using the whole test
data which contains 1,135
examples of images mapped to their labels.
MLP
(v0)
The following visualization is of a confusion matrix based on the MLP
model architecture which was tested using 1,135
images on the test
dataset.
LeNet
(v1) The following visualization is of a confusion matrix based on theLeNet
model architecture which was tested using1,135
images on thetest
dataset.
In this section we are going to show the summary of the classification report
based on the best saved model of the best model.
MLP
(v0)
This is the mlp
model's cr
.
# | precision | recall | f1-score | support |
---|---|---|---|---|
0 (NORMAL) | 0.81 | 0.84 | 0.82 | 337 |
1 (BACTERIA) | 0.73 | 0.90 | 0.81 | 442 |
2 (VIRUS) | 0.76 | 0.50 | 0.60 | 356 |
accuracy | 0.76 | 1135 | ||
micro avg | 0.76 | 0.75 | 0.74 | 1135 |
weighted avg | 0.76 | 0.76 | 0.75 | 1135 |
LeNet
(v1)
This is the LeNet
model's cr
.
# | precision | recall | f1-score | support |
---|---|---|---|---|
0 (NORMAL) | 0.88 | 0.82 | 0.85 | 337 |
1 (BACTERIA) | 0.70 | 0.95 | 0.81 | 442 |
2 (VIRUS) | 0.79 | 0.49 | 0.61 | 356 |
accuracy | 0.77 | 1135 | ||
micro avg | 0.79 | 0.75 | 0.75 | 1135 |
weighted avg | 0.78 | 0.77 | 0.76 | 1135 |
During model evaluation both models were tested to see if they were classifying images correctly. A sample of 24
images was taken from the first batch of test data and here are the visual results of the classification for each model.
MLP
(v0)
LeNet
(v1)
The images that were marked their labels in color
RED
are the ones the model if misclassifying.
This project exposes a REST
api that is running on port 3001
which can be configured by changing the AppConfig
in the app/app.py
file that looks as follows:
class AppConfig:
PORT = 3001
DEBUG = False
The server exposes two model versions the v0
which is the mlp
model and v1
which is the lenet
architecture. When making a request to the server you need to specify the model version that you want to use to make predictions. The URL
looks as follows:
# remote
https://pc-djhy.onrender.com/api/<MODEL_VERSION>/pneumonia
# locally
http://localhost:3001/api/<MODEL_VERSION>/pneumonia
The MODEL_VERSION
an be either v0
or v1
. Here are the example of url's that can be used to make request to the server using these model versions.
# remote
https://pc-djhy.onrender.com/api/v0/pneumonia - mlp-model
https://pc-djhy.onrender.com/api/v1/pneumonia - lenet-model
# locally
http://localhost:3001/api/v0/pneumonia - mlp-model
http://localhost:3001/api/v1/pneumonia - lenet-model
Note that all the request should be sent to the server using the
POST
method.
The expected response at http://localhost:3001/api/v0/pneumonia
or at https://pc-djhy.onrender.com/api/v0/pneumonia
with a file image
of the right format will yield the following json
response to the client.
{
"meta": {
"description": "given a medical chest-x-ray image of a human being we are going to classify weather a person have pneumonia virus, pneumonia bacteria or none of those(normal).",
"language": "python",
"library": "pytorch",
"main": "computer vision (cv)",
"programmer": "@crispengari"
},
"modelVersion": "v0",
"predictions": {
"all_predictions": [
{ "class_label": "NORMAL", "label": 0, "probability": 1.0 },
{ "class_label": "PNEUMONIA BACTERIA", "label": 1, "probability": 0.0 },
{ "class_label": "PNEUMONIA VIRAL", "label": 2, "probability": 0.0 }
],
"top_prediction": {
"class_label": "NORMAL",
"label": 0,
"probability": 1.0
}
},
"success": true
}
Make sure that you have the image named normal.jpeg
in the current folder that you are running your cmd
otherwise you have to provide an absolute or relative path to the image.
To make a
curl
POST
request athttp://localhost:3001/api/v0/pneumonia
or athttps://pc-djhy.onrender.com/api/v0/pneumonia
with the filenormal.jpeg
we run the following command.
# remote
cURL -X POST -F image=@normal.jpeg https://pc-djhy.onrender.com/api/v0/pneumonia
# locally
cURL -X POST -F image=@normal.jpeg http://127.0.0.1:3001/api/v0/pneumonia
To make this request with postman we do it as follows:
- Change the request method to
POST
- Click on
form-data
- Select type to be
file
on theKEY
attribute - For the
KEY
typeimage
and select the image you want to predict undervalue
- Click send
If everything went well you will get the following response depending on the face you have selected:
{
"meta": {
"description": "given a medical chest-x-ray image of a human being we are going to classify weather a person have pneumonia virus, pneumonia bacteria or none of those(normal).",
"language": "python",
"library": "pytorch",
"main": "computer vision (cv)",
"programmer": "@crispengari"
},
"modelVersion": "v0",
"predictions": {
"all_predictions": [
{ "class_label": "NORMAL", "label": 0, "probability": 1.0 },
{ "class_label": "PNEUMONIA BACTERIA", "label": 1, "probability": 0.0 },
{ "class_label": "PNEUMONIA VIRAL", "label": 2, "probability": 0.0 }
],
"top_prediction": {
"class_label": "NORMAL",
"label": 0,
"probability": 1.0
}
},
"success": true
}
- First you need to get the input from
html
- Create a
formData
object - make a POST requests
const online = false;
const url = online
? `https://pc-djhy.onrender.com/api/v0/pneumonia`
: "http://127.0.0.1:3001/api/v0/pneumonia";
const input = document.getElementById("input").files[0];
let formData = new FormData();
formData.append("image", input);
fetch(url, {
method: "POST",
body: formData,
})
.then((res) => res.json())
.then((data) => console.log(data));
If everything went well you will be able to get expected response.
{
"meta": {
"description": "given a medical chest-x-ray image of a human being we are going to classify weather a person have pneumonia virus, pneumonia bacteria or none of those(normal).",
"language": "python",
"library": "pytorch",
"main": "computer vision (cv)",
"programmer": "@crispengari"
},
"modelVersion": "v0",
"predictions": {
"all_predictions": [
{ "class_label": "NORMAL", "label": 0, "probability": 1.0 },
{ "class_label": "PNEUMONIA BACTERIA", "label": 1, "probability": 0.0 },
{ "class_label": "PNEUMONIA VIRAL", "label": 2, "probability": 0.0 }
],
"top_prediction": {
"class_label": "NORMAL",
"label": 0,
"probability": 1.0
}
},
"success": true
}
The ipynb
notebook that i used for training the models and saving an .pt
file was can be found in respective links bellow: