|
|
|
""" |
|
Gradio Application for model trained on CIFAR10 dataset |
|
Author: Shilpaj Bhalerao |
|
Date: Aug 06, 2023 |
|
""" |
|
|
|
import os |
|
from collections import OrderedDict |
|
|
|
|
|
import gradio as gr |
|
import numpy as np |
|
import torch |
|
from torchvision import transforms |
|
from pytorch_grad_cam import GradCAM |
|
from pytorch_grad_cam.utils.image import show_cam_on_image |
|
from PIL import Image |
|
|
|
|
|
from resnet import LITResNet |
|
from visualize import FeatureMapVisualizer |
|
|
|
|
|
example_directory = 'examples/' |
|
model_path = 'epoch=23-step=2112.ckpt' |
|
|
|
classes = ('plane', 'car', 'bird', 'cat', 'deer', |
|
'dog', 'frog', 'horse', 'ship', 'truck') |
|
|
|
model = LITResNet.load_from_checkpoint(model_path, map_location=torch.device('cpu'), strict=False, class_names=classes) |
|
model.eval() |
|
|
|
|
|
viz = FeatureMapVisualizer(model) |
|
|
|
|
|
def inference(input_img, |
|
transparency=0.5, |
|
number_of_top_classes=3, |
|
target_layer_number=4): |
|
""" |
|
Function to run inference on the input image |
|
:param input_img: Image provided by the user |
|
:parma transparency: Percentage of cam overlap over the input image |
|
:param number_of_top_classes: Number of top predictions for the input image |
|
:param target_layer_number: Layer for which GradCam to be shown |
|
""" |
|
|
|
input_img = Image.fromarray(input_img).resize((32, 32)) |
|
input_img = np.array(input_img) |
|
|
|
|
|
mean_r, mean_g, mean_b = np.mean(input_img[:, :, 0]/255.), np.mean(input_img[:, :, 1]/255.), np.mean(input_img[:, :, 2]/255.) |
|
|
|
|
|
std_r, std_g, std_b = np.std(input_img[:, :, 0]/255.), np.std(input_img[:, :, 1]/255.), np.std(input_img[:, :, 2]/255.) |
|
|
|
|
|
_transform = transforms.Compose([ |
|
transforms.ToTensor(), |
|
transforms.Normalize((mean_r, mean_g, mean_b), (std_r, std_g, std_b)) |
|
]) |
|
|
|
|
|
org_img = input_img |
|
|
|
|
|
input_img = _transform(input_img) |
|
|
|
|
|
input_img = input_img.unsqueeze(0) |
|
|
|
|
|
with torch.no_grad(): |
|
outputs = model(input_img) |
|
o = torch.exp(outputs)[0] |
|
confidences = {classes[i]: float(o[i]) for i in range(10)} |
|
|
|
|
|
sorted_confidences = sorted(confidences.items(), key=lambda val: val[1], reverse=True) |
|
show_confidences = OrderedDict(sorted_confidences[:number_of_top_classes]) |
|
|
|
|
|
_layers = ['prep_layer', 'custom_block1', 'resnet_block1', |
|
'custom_block2', 'custom_block3', 'resnet_block3'] |
|
target_layers = [eval(f'model.{_layers[target_layer_number-1]}[0]')] |
|
|
|
|
|
cam = GradCAM(model=model, target_layers=target_layers) |
|
grayscale_cam = cam(input_tensor=input_img, targets=None) |
|
grayscale_cam = grayscale_cam[0, :] |
|
|
|
|
|
visualization = show_cam_on_image(org_img/255, grayscale_cam, use_rgb=True, image_weight=transparency) |
|
return show_confidences, visualization |
|
|
|
|
|
def display_misclassified_images(number: int = 1): |
|
""" |
|
Display the misclassified images saved during training |
|
:param number: Number of images to display |
|
""" |
|
|
|
data = [] |
|
|
|
|
|
file_names = os.listdir('misclassified/') |
|
|
|
|
|
for file in file_names: |
|
file_name, extension = file.split('.') |
|
correct_label, misclassified = file_name.split('_') |
|
data.append((correct_label, misclassified)) |
|
|
|
|
|
file_path = ['misclassified/' + file for file in file_names] |
|
|
|
|
|
return file_path[:number], data[:number] |
|
|
|
|
|
def feature_maps(input_img, kernel_number=32): |
|
""" |
|
Function to return feature maps for the selected image |
|
:param input_img: User input image |
|
:param kernel_number: Number of kernel in all 6 layers |
|
""" |
|
|
|
input_img = Image.fromarray(input_img).resize((32, 32)) |
|
input_img = np.array(input_img) |
|
|
|
|
|
mean_r, mean_g, mean_b = np.mean(input_img[:, :, 0]/255.), np.mean(input_img[:, :, 1]/255.), np.mean(input_img[:, :, 2]/255.) |
|
|
|
|
|
std_r, std_g, std_b = np.std(input_img[:, :, 0]/255.), np.std(input_img[:, :, 1]/255.), np.std(input_img[:, :, 2]/255.) |
|
|
|
|
|
_transform = transforms.Compose([ |
|
transforms.ToTensor(), |
|
transforms.Normalize((mean_r, mean_g, mean_b), (std_r, std_g, std_b)) |
|
]) |
|
|
|
|
|
input_img = _transform(input_img) |
|
|
|
|
|
plt = viz.visualize_feature_map_of_kernel(image=input_img, kernel_number=kernel_number) |
|
return plt |
|
|
|
|
|
def get_kernels(layer_number): |
|
""" |
|
Function to get the kernels from the layer |
|
:param layer_number: Number of layer from which kernels to be visualized |
|
""" |
|
|
|
plt = viz.visualize_kernels_from_layer(layer_number=layer_number) |
|
return plt |
|
|
|
|
|
if __name__ == '__main__': |
|
with gr.Blocks() as demo: |
|
gr.Markdown( |
|
""" |
|
# CIFAR10 trained on ResNet18 Model |
|
A model architecture by [David C](https://github.com/davidcpage) which is trained on CIFAR10 for 24 Epochs to achieve accuracy of 90+% |
|
The model works for following classes: `plane`, `car`, `bird`, `cat`, `deer`, `dog`, `frog`, `horse`, `ship`, `truck` |
|
""" |
|
) |
|
|
|
|
|
|
|
|
|
with gr.Tab("GradCam"): |
|
gr.Markdown( |
|
""" |
|
Visualize Class Activations Maps generated by the model's layer for the predicted class |
|
This is used to see what the model is actually looking at in the image |
|
""" |
|
) |
|
with gr.Row(): |
|
img_input = gr.Image(label="Input Image") |
|
gradcam_outputs = [gr.Label(), |
|
gr.Image(label="Output")] |
|
|
|
with gr.Row(): |
|
gradcam_inputs = [gr.Slider(0, 1, value=0.5, |
|
label="How much percentage overlap of the input image on the activation maps?"), |
|
gr.Slider(1, 10, value=3, step=1, |
|
label="How many top class predictions you want to see?"), |
|
gr.Slider(1, 6, value=4, step=1, |
|
label="From 6 blocks of the model, which block's first convolutional layer's class activation you want to see?")] |
|
|
|
gradcam_button = gr.Button("Submit") |
|
gradcam_button.click(inference, inputs=[img_input] + gradcam_inputs, outputs=gradcam_outputs) |
|
|
|
gr.Markdown("## Examples") |
|
gr.Examples([example_directory + 'dog.jpg', example_directory + 'cat.jpg', example_directory + 'frog.jpg', |
|
example_directory + 'bird.jpg', example_directory + 'shark-plane.jpg', |
|
example_directory + 'car.jpg', example_directory + 'truck.jpg', |
|
example_directory + 'horse.jpg', example_directory + 'plane.jpg', |
|
example_directory + 'ship.png'], |
|
inputs=img_input, fn=inference) |
|
|
|
|
|
|
|
|
|
with gr.Tab("Misclassified Images"): |
|
gr.Markdown( |
|
""" |
|
10% of test images were misclassified by the model at the end of the training |
|
You can visualize those images with their correct label and misclassified label |
|
""" |
|
) |
|
with gr.Row(): |
|
mis_inputs = gr.Slider(1, 10, value=1, step=1, |
|
label="Number of misclassified images to display") |
|
mis_outputs = [ |
|
gr.Gallery(label="Misclassified Images", show_label=False, elem_id="gallery"), |
|
gr.Dataframe(headers=["Correct Label", "Misclassified Label"], type="array", datatype="str", |
|
row_count=10, col_count=2)] |
|
mis_button = gr.Button("Display Misclassified Images") |
|
mis_button.click(display_misclassified_images, inputs=mis_inputs, outputs=mis_outputs) |
|
|
|
|
|
|
|
|
|
with gr.Tab("Feature Map Visualization"): |
|
gr.Markdown( |
|
""" |
|
The model has 6 convolutional blocks. Each block has two or three convolutional layers |
|
From each block's first convolutional layer, output of specific kernel number is visualized |
|
In the below images `l1` represents first block and `kx` represents the number of kerenel from the first convolutional layer of that block |
|
""" |
|
) |
|
with gr.Column(): |
|
feature_map_input = gr.Image(label="Feature Map Input Image") |
|
feature_map_slider = gr.Slider(1, 32, value=16, step=1, |
|
label="Select a Kernel number whose Features Maps from all 6 block's to be shown") |
|
feature_map_output = gr.Plot() |
|
feature_map_button = gr.Button("Visualize FeatureMaps") |
|
feature_map_button.click(feature_maps, inputs=[feature_map_input, feature_map_slider], outputs=feature_map_output) |
|
|
|
|
|
|
|
|
|
with gr.Tab("Kernel Visualization"): |
|
gr.Markdown( |
|
""" |
|
The model has 6 convolutional blocks. Each block has two or three convolutional layers |
|
Some of the Kernels from the first convolutional layer of selected block number are visualized below |
|
""" |
|
) |
|
with gr.Column(): |
|
kernel_input = gr.Slider(1, 4, value=2, step=1, |
|
label="Select a block number whose first convolutional layer's Kernels to be shown") |
|
kernel_output = gr.Plot() |
|
kernel_button = gr.Button("Visualize Kernels") |
|
kernel_button.click(get_kernels, inputs=kernel_input, outputs=kernel_output) |
|
|
|
gr.close_all() |
|
demo.launch() |
|
|