# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# + [markdown] _uuid="38d00b67-b054-466a-9ede-4dab43b928ad" _cell_guid="b067ff91-14f3-4948-b784-5e6b41670a96"
# # Detecting COVID-19 with Chest X-Rays

# + [markdown] _uuid="077b0bf0-aaeb-4609-adf7-8ca07e4b8527" _cell_guid="0ce864e4-75ec-41c3-9862-4936ae3299ae"
# The data set used may be found at
# https://www.kaggle.com/tawsifurrahman/covid19-radiography-database

# + [markdown] _uuid="0f5e59e7-61bc-4e63-8053-d8175a3d5865" _cell_guid="3b1e4f23-111b-4804-86cc-b8c8e88c1372"
# # Importing Libraries

# + _uuid="86d26b14-76b2-44fa-90d3-0d7682b0537b" _cell_guid="73e39aba-0e6d-46de-9798-57ef369e28f2"
import os
import random
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import cv2
import tensorflow as tf
from tensorflow.keras.applications import InceptionV3
from tensorflow.keras import Sequential
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.layers import Input, Flatten, Dense, Conv2D, MaxPooling2D, GlobalAveragePooling2D, Dropout
from tensorflow.keras.models import Model
from tensorflow.keras.metrics import Recall, Precision
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
from sklearn.metrics import confusion_matrix, classification_report

# + _uuid="25c44bc3-0617-477d-8e64-e20153b9df00" _cell_guid="5a150a55-4c12-482f-9e61-2655a34accca"


# + _uuid="a1d65f88-85b0-49e4-85a0-e7fe2fb6f768" _cell_guid="b492805e-518d-4def-99dd-257d78b02b3f"
path = "../input/covid19-radiography-database/COVID-19 Radiography Database"
labels = ["COVID-19", "NORMAL", "Viral Pneumonia"]

# + [markdown] _uuid="b6b8ced3-5068-428b-ad23-77a465863cba" _cell_guid="7e64a6e1-3014-4dff-ac5c-316accf39753"
# # Visualizing Classes

# + _uuid="7eadb89d-87a8-44a9-8ad0-5c7d7fcedace" _cell_guid="e75aa87f-b4ac-4eea-86bf-0a4362440785"
# Randomly view 5 images in each category

fig, axs = plt.subplots(len(labels), 5, figsize = (15, 15))

class_len = {}
for i, c in enumerate(labels):
    class_path = os.path.join(path, c)
    all_images = os.listdir(class_path)
    sample_images = random.sample(all_images, 5)
    class_len[c] = len(all_images)
    
    for j, image in enumerate(sample_images):
        img_path = os.path.join(class_path, image)
        img = cv2.imread(img_path)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        
        axs[i, j].imshow(img)
        axs[i, j].set(xlabel = c, xticks = [], yticks = [])

fig.tight_layout()

# + _uuid="1ba42e0a-b387-496f-9625-07f9866f8d08" _cell_guid="cc2d5b3d-feb1-4bf6-b812-b07619df51d2"
# Make a pie-chart to visualize the percentage contribution of each category.
fig, ax = plt.subplots()
ax.pie(
    class_len.values(),
    labels = class_len.keys(),
    autopct = "%1.1f%%"
)
fig.show()
# The dataset is imbalance so we will have to take care of that later.

# + [markdown] _uuid="eeef1565-8752-45eb-9a2f-963914213777" _cell_guid="0fec5155-21d2-4ce0-9178-8367b9e0d5b7"
# # Creating image generators

# + _uuid="3af5ded7-eaca-4c17-92f0-3c9d4b77219a" _cell_guid="aa42de1c-0107-4fdb-961d-6b567f781c67"
# We do not have separate folders for training and validation. 
# We need to read training and validation images from the same folder such that:
# 1. There is no data leak i.e. Training images should not appear as validation images.                 
# 2. We must be able to apply augmentation to training images but not validation images.  
# We shall adopt the following strategy:
# 1. Use the same validation_split in ImageDataGenerator for training and validation.
# 2. Use the same seed when using flow_from_directory for training and validation. 
# To veify the correctness of this approach, you can print filenames from each generator and check for overlap.

# Another problem is that along with the 3 image data folders, there are files we are not making use of.
# To be sure that images are read from the correct folders, we can specify the directory and the labels.

# Note that we use simple augmentation to avoid producing unsuitable images.

datagen_train = ImageDataGenerator(
    rescale = 1./255, 
    validation_split = 0.2,
    rotation_range = 5,
    width_shift_range = 0.05,
    height_shift_range = 0.05,
    zoom_range = 0.01
)

datagen_val = ImageDataGenerator(
    rescale = 1./255, 
    validation_split = 0.2 
)    

train_generator = datagen_train.flow_from_directory(
    directory = path,
    classes = labels,
    seed = 42,
    batch_size = 32, 
    shuffle = True,
    subset = 'training'
)

val_generator = datagen_val.flow_from_directory(
    directory = path,
    classes = labels,
    seed = 42,
    batch_size = 32, 
    shuffle = True,
    subset = 'validation'
)

# + _uuid="f32b70f4-70cf-45b1-a6ab-f5b909861cb2" _cell_guid="13b361e2-bfb3-41f7-a55b-1023fc0a4b52"
# To veify the correctness of this approach (empty set is expected)
set(val_generator.filenames).intersection(set(train_generator.filenames))

# + _uuid="69d23a68-ed53-4cef-86f6-e5a2b8e6fb5d" _cell_guid="6f5c3041-cc7b-40f8-ae06-3308aa7bbc80"
# Check out labeling
val_generator.class_indices

# + [markdown] _uuid="ff14f07c-d2f6-48fa-a577-f826f59b5f10" _cell_guid="f9bc7a32-7d7b-4eae-bdff-cdc979dc7c05"
# # Model building

# + [markdown] _uuid="b374cbc0-c61a-4cb1-8369-3cccf896ef0a" _cell_guid="5ba2b9fe-b3f8-4015-ae80-fddbef10de79"
# We shall start with InceptionV3 architecture trained on imagenet and fine tune.

# + _uuid="7a940e78-88d3-4ac2-97f5-1a18313bb4ae" _cell_guid="5e5e8a02-a612-46c7-acd0-65cce22f2b3b"
basemodel = InceptionV3(
    include_top = False, 
    weights = 'imagenet', 
    input_tensor = Input((256, 256, 3)),
)

# + _uuid="47321fc7-8b87-4446-8aa6-47092f6adfe1" _cell_guid="82b10647-c8f2-40ff-bbcc-254c2e332eee"
basemodel.trainable = True

# + _uuid="64625cf1-2a6c-4b5d-a5e9-366a96142d4b" _cell_guid="5e4ea749-6e5c-43fd-993d-9a9bdc04bba9"
basemodel.summary()

# + _uuid="ba88355a-e35c-414e-bfde-78e20a18e38d" _cell_guid="2295b719-7fdb-47bb-8a1b-56b4e51414f5"
# Add classification head to the model
headmodel = basemodel.output
headmodel = GlobalAveragePooling2D()(headmodel)
headmodel = Flatten()(headmodel) 
headmodel = Dense(256, activation = "relu")(headmodel)
headmodel = Dropout(0.3)(headmodel)
headmodel = Dense(128, activation = "relu")(headmodel)
headmodel = Dropout(0.3)(headmodel)
headmodel = Dense(3, activation = "softmax")(headmodel) # 3 classes

model = Model(inputs = basemodel.input, outputs = headmodel)

# + _uuid="a73794c4-0619-49ce-b0b7-cbac836c5f50" _cell_guid="6f8e6a95-4ac0-47dd-8684-a02902f4fb2b"
model.summary()

# + _uuid="0f4c7a8b-b473-4305-b43c-9a95a59f57a3" _cell_guid="5e316fef-42bf-49cc-8cae-5dc941b2ff51"
# Compile the model

# Given that COVID-19 spreads very quickly, it is important that we identify as many cases as possible.
# We do not care a lot about Flase Positives (precision), because it may be okay to declare normal people as being COVID-19 positive.
# However, we really really care about False Negatives (recall), because it is NOT okay to declare COVID-19 positive people as being normal!

MyList = ["accuracy"]
MyList += [Recall(class_id = i) for i in range(len(labels))] 
MyList += [Precision(class_id = i) for i in range(len(labels))]

model.compile(
    loss = "categorical_crossentropy",
    optimizer = "adam",
    metrics = MyList
)

# + _uuid="c23ddb6f-9316-4da0-88d0-2988f3fb7ffe" _cell_guid="74ee18b5-6cb0-44fa-a149-0b6f4b699d9e"
# Using early stopping to exit training if validation loss is not decreasing even after certain epochs (patience)
earlystopping = EarlyStopping(
    monitor = "recall",
    patience = 20
)

# save the best model with lower loss
checkpointer = ModelCheckpoint(
    filepath = "weights.hdf5", 
    save_best_only = True
)

# + [markdown] _uuid="2a7eb75e-5e48-49fc-ad9f-6aa7648daf47" _cell_guid="68b54505-5bbe-4d48-bc0a-05e1f41551e8"
# # Model training

# + _uuid="9ac7deb1-dd47-4e52-8d19-eef41217f6bd" _cell_guid="70edacda-b4eb-484a-b363-6be5711e2c9c"
# Previously we found that there was class imbalance. 
# We shall use class weights to tackle this before moving to training.

total_wt = sum(class_len.values())

weights = {
    0: 0.5 * (1 - class_len[labels[0]]/total_wt),
    1: 0.5 * (1 - class_len[labels[1]]/total_wt),
    2: 0.5 * (1 - class_len[labels[2]]/total_wt)
}
weights

# + _uuid="f331daeb-8aaa-4532-9edf-eda383940909" _cell_guid="c0a9a56c-b1b3-41c2-8715-19810a891763"
# Finally, fit the neural network model to the data.

history = model.fit_generator(
    train_generator,
    steps_per_epoch = 64,
    epochs = 10
)

# + _uuid="bb7cdb99-276a-40db-89bd-428d3a6562bf" _cell_guid="f43d4896-73dc-4d20-b07f-fd8f8fa616ec"
val_generator = datagen_val.flow_from_directory(
    directory = path,
    classes = labels,
    seed = 42,
    batch_size = 32, 
    shuffle = False,
    subset = 'validation'
)

# Obtain predictions
pred = model.predict_generator(val_generator) # Gives class probabilities
pred = np.round(pred) # Gives one-hot encoded classes
pred = np.argmax(pred, axis = 1) # Gives class labels

# Obtain actual labels
actual = val_generator.classes
    
# Now plot matrix
cm = confusion_matrix(actual, pred, labels = [0, 1, 2])
sns.heatmap(
    cm, 
    cmap="Blues",
    annot = True, 
    fmt = "d"
)
plt.show()

# + _uuid="8ed7c319-d8f9-4408-ad95-e4bbb352363a" _cell_guid="98050f20-9df4-4733-8d6a-2f2f3ffdc244"
# Classification Report
print(classification_report(actual, pred))

# + _uuid="2e36e034-2209-4f74-9834-66a4af608582" _cell_guid="29f02ce4-5661-4f2d-892b-894d329433f5"
tf.saved_model.save(model, 'keras_model/modelo')
# -



StarcoderdataJupyterScriptsDedupFiltered