import os from typing import List import gradio as gr import numpy as np import torch import torch.nn.functional as F from model import DavidPageNet from PIL import Image from pytorch_grad_cam import GradCAM from pytorch_grad_cam.utils.image import show_cam_on_image from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget from torchvision import transforms # imagenet mean and std mean = [0.485, 0.456, 0.406] std = [0.229, 0.224, 0.225] inv_mean = [-mean / std for mean, std in zip(mean, std)] inv_std = [1 / s for s in std] # transforms transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize(mean=mean, std=std), ] ) inv_normalize = transforms.Normalize(mean=inv_mean, std=inv_std) classes = [ 'airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck' ] class Gradio: def __init__(self, model_path: str): use_cuda = torch.cuda.is_available() self.device = torch.device("cuda" if use_cuda else "cpu") self.model = self.load_model(model_path) self.temperature = 2 def load_model(self, model_path: str): model = DavidPageNet().to(self.device) if os.path.isfile(model_path): model.load_state_dict( torch.load(model_path, map_location=self.device), strict=False ) return model def cam( self, input_tensor: torch.Tensor, target_class_id: int, layer_nums: List, transparency: float = 0.7, ): targets = [ClassifierOutputTarget(target_class_id)] target_layers = [getattr(self.model, f"block{layer-1}") for layer in layer_nums] with GradCAM( model=self.model, target_layers=target_layers, use_cuda=self.device == torch.device("cuda"), ) as cam: grayscale_cam = cam(input_tensor=input_tensor, targets=targets) grayscale_cam = grayscale_cam[0, :] img = inv_normalize(input_tensor) rgb_img = img[0].permute(1, 2, 0).cpu().numpy() visualization = show_cam_on_image( rgb_img, grayscale_cam, use_rgb=True, image_weight=transparency ) return visualization def inference( self, input_img: np.array, transparency: float=0.7, ntop_classes: int=2, layer_nums: List=[3, 4], cam_for_class: str="default", ): self.model.eval() input_img = transform(input_img) input_img = input_img.to(self.device) input_img = input_img.unsqueeze(0) with torch.no_grad(): outputs = self.model(input_img).squeeze(0) outputs = F.softmax(outputs / self.temperature, dim=-1) probability, prediction = torch.sort(outputs, descending=True) prediction = list(zip(prediction.tolist(), probability.tolist())) class_id = ( prediction[0][0] if cam_for_class in ["default", ""] else classes.index(cam_for_class) ) visualization = self.cam( input_tensor=input_img, target_class_id=class_id, layer_nums=layer_nums, transparency=transparency, ) top_nclass_result = [ (classes[class_id], round(score, 2)) for class_id, score in prediction[:ntop_classes] ] return visualization, dict(top_nclass_result) method = Gradio(model_path="./checkpoint/model.pt") demo = gr.Interface( fn=method.inference, inputs=[ gr.Image(shape=(32, 32), label="Input Image", value="./samples/dog_cat.jpeg"), gr.Slider( minimum=0, maximum=1, value=0.5, label="Transparency", info="Transparency of the CAM-Attention Output", ), gr.Slider( minimum=1, maximum=10, step=1, value=2, label="Top Classes", info="Number of Top Predicted Classes", ), gr.CheckboxGroup( choices=[1, 2, 3, 4], value=[3, 4], label="Network Layers", info="Network Layers for CAM-Attention Extraction", ), gr.Dropdown( choices=["default"] + classes, multiselect=False, value="default", label="Class Activation Map (CAM) Focus Visualization", info="This section showcases the specific region of interest within the input image that the Class Activation Map (CAM) algorithm emphasizes to make predictions based on the selected class from the dropdown menu. The 'default' value serves as the default choice, representing the top class predicted by the model.", ), ], outputs=[ gr.Image(shape=(32, 32), label="CAM Output for the Chosen Class").style(width=256, height=256), gr.Label(label="Top Classes"), ], examples=[[os.path.join("./samples/", f)] for f in os.listdir("./samples/")] ) demo.launch()