{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Cell Classification Captum Interpretation \n\n\nHere, we will interpret the resnet model used while predicting the cell type using Integrated Gradients technique implemented in captum.\n\nIntegrated Gradient is an interpretability or explainability technique for deep neural networks which visualizes its input feature importance that contributes to the model's prediction. In this, we compute the integral of the gradients of the model outputs for the predicted output with respect to the input image pixels along the path from the black image to our input image.\n\nThis paper (which can be found [here](https://arxiv.org/pdf/1703.01365.pdf)) discusses Integrated gradients in much more detail along with introduing some axioms which every integrated gradients should follow. ","metadata":{"execution":{"iopub.status.busy":"2021-10-23T04:12:16.649005Z","iopub.execute_input":"2021-10-23T04:12:16.6494Z","iopub.status.idle":"2021-10-23T04:12:16.726081Z","shell.execute_reply.started":"2021-10-23T04:12:16.649296Z","shell.execute_reply":"2021-10-23T04:12:16.723107Z"}}},{"cell_type":"code","source":"%%capture\n!pip install captum","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:24:22.310124Z","iopub.execute_input":"2021-12-16T12:24:22.310583Z","iopub.status.idle":"2021-12-16T12:24:34.418997Z","shell.execute_reply.started":"2021-12-16T12:24:22.310536Z","shell.execute_reply":"2021-12-16T12:24:34.417065Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Imports","metadata":{}},{"cell_type":"code","source":"import os\nimport time\nimport json\nimport random\nimport collections\nimport cv2\n\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nfrom matplotlib.colors import LinearSegmentedColormap\n\nimport torch\nimport torchvision\nfrom torchvision import transforms\nfrom torchvision import models\nfrom torchvision.transforms import ToPILImage\nfrom torchvision.transforms import functional as F\nfrom torch.utils.data import Dataset, DataLoader\n\nfrom torchvision.models import resnet34\n\nimport torch.nn as nn\n\nimport torch\nimport torch.nn.functional as F\n\nfrom PIL import Image\n\nfrom captum.attr import IntegratedGradients\nfrom captum.attr import GradientShap\nfrom captum.attr import Occlusion\nfrom captum.attr import NoiseTunnel\nfrom captum.attr import visualization as viz","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-12-16T12:24:34.423547Z","iopub.execute_input":"2021-12-16T12:24:34.423917Z","iopub.status.idle":"2021-12-16T12:24:34.493782Z","shell.execute_reply.started":"2021-12-16T12:24:34.423877Z","shell.execute_reply":"2021-12-16T12:24:34.493012Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"SEED = 3011\n\ndef fix_seeds(seed):\n    np.random.seed(seed)\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    \nfix_seeds(SEED)","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:24:34.495627Z","iopub.execute_input":"2021-12-16T12:24:34.496294Z","iopub.status.idle":"2021-12-16T12:24:34.509595Z","shell.execute_reply.started":"2021-12-16T12:24:34.496247Z","shell.execute_reply":"2021-12-16T12:24:34.508638Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Configuration","metadata":{}},{"cell_type":"code","source":"TRAIN_CSV = \"../input/sartorius-cell-instance-segmentation/train.csv\"\nTRAIN_PATH = \"../input/sartorius-cell-instance-segmentation/train\"\nTEST_PATH = \"../input/sartorius-cell-instance-segmentation/test\"","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:24:34.512169Z","iopub.execute_input":"2021-12-16T12:24:34.512831Z","iopub.status.idle":"2021-12-16T12:24:34.519480Z","shell.execute_reply.started":"2021-12-16T12:24:34.512783Z","shell.execute_reply":"2021-12-16T12:24:34.518575Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DEVICE = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:24:35.275830Z","iopub.execute_input":"2021-12-16T12:24:35.276046Z","iopub.status.idle":"2021-12-16T12:24:35.283791Z","shell.execute_reply.started":"2021-12-16T12:24:35.276020Z","shell.execute_reply":"2021-12-16T12:24:35.282797Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = resnet34(True)\nmodel.fc = nn.Linear(512, 3)","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:23:26.581649Z","iopub.execute_input":"2021-12-16T12:23:26.581965Z","iopub.status.idle":"2021-12-16T12:23:29.432325Z","shell.execute_reply.started":"2021-12-16T12:23:26.581933Z","shell.execute_reply":"2021-12-16T12:23:29.431439Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = torch.load(\"../input/cell-classification-vanilla-torch/resnet34-finetuned.bin\", map_location=DEVICE)","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:23:53.409907Z","iopub.execute_input":"2021-12-16T12:23:53.410499Z","iopub.status.idle":"2021-12-16T12:23:55.656672Z","shell.execute_reply.started":"2021-12-16T12:23:53.410422Z","shell.execute_reply":"2021-12-16T12:23:55.655757Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:23:59.709989Z","iopub.execute_input":"2021-12-16T12:23:59.710453Z","iopub.status.idle":"2021-12-16T12:23:59.721221Z","shell.execute_reply.started":"2021-12-16T12:23:59.710400Z","shell.execute_reply":"2021-12-16T12:23:59.720255Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ToTensorNew:\n    def __call__(self, image, target):\n        image = torchvision.transforms.functional.to_tensor(image)\n        return image, target\n\nclass ClassificationInterpDataset(Dataset):\n    def __init__(self, image_dir, df):\n        self.image_dir = image_dir\n        self.df = df\n        self.transforms=transforms\n        \n        self.height = 520\n        self.width = 704\n        \n        self.image_info = collections.defaultdict(dict)\n        temp_df = self.df.groupby('id')['annotation'].agg(lambda x: list(x)).reset_index()\n        for index, row in temp_df.iterrows():\n            self.image_info[index] = {\n                'image_id': row['id'],\n                'image_path': os.path.join(self.image_dir, row['id'] + '.png'),\n                }\n\n    def __getitem__(self, idx):\n        \n        img_path = self.image_info[idx][\"image_path\"]\n        img = Image.open(img_path).convert(\"RGB\")\n        img, _ = ToTensorNew()(image=img, target=None)\n        info = self.image_info[idx]\n        image_id = torch.tensor([idx])\n        target = {\n            'image_id': image_id,\n        }\n        return img, target\n\n    def __len__(self):\n        return len(self.image_info)","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:31:06.452538Z","iopub.execute_input":"2021-12-16T12:31:06.453500Z","iopub.status.idle":"2021-12-16T12:31:06.466438Z","shell.execute_reply.started":"2021-12-16T12:31:06.453440Z","shell.execute_reply":"2021-12-16T12:31:06.465277Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train = pd.read_csv(TRAIN_CSV)\nds_train = ClassificationInterpDataset(TRAIN_PATH, df_train)","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:31:33.286178Z","iopub.execute_input":"2021-12-16T12:31:33.286714Z","iopub.status.idle":"2021-12-16T12:31:33.798497Z","shell.execute_reply.started":"2021-12-16T12:31:33.286658Z","shell.execute_reply":"2021-12-16T12:31:33.797577Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"idx=20\nimage = ds_train[idx][0]\ninput = torch.unsqueeze(image, dim=0).to(DEVICE)\n\noutput = model(input)\n\nprediction_score, pred_label_idx = torch.topk(output, 1)\npred_label_idx.squeeze_()\npredicted_label = df_train[df_train.id == ds_train.image_info[idx]['image_id']].iloc[0].cell_type\nprint('Predicted:', predicted_label, '(', prediction_score.squeeze().item(), ')')","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:31:35.532156Z","iopub.execute_input":"2021-12-16T12:31:35.532478Z","iopub.status.idle":"2021-12-16T12:31:36.390758Z","shell.execute_reply.started":"2021-12-16T12:31:35.532447Z","shell.execute_reply":"2021-12-16T12:31:36.389775Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"integrated_gradients = IntegratedGradients(model)\nattributions_ig = integrated_gradients.attribute(input, target=pred_label_idx, n_steps=20)","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:31:39.498637Z","iopub.execute_input":"2021-12-16T12:31:39.499086Z","iopub.status.idle":"2021-12-16T12:32:14.660955Z","shell.execute_reply.started":"2021-12-16T12:31:39.499045Z","shell.execute_reply":"2021-12-16T12:32:14.659978Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.imshow(np.transpose(image.squeeze().cpu().detach().numpy(), (1,2,0)))\nplt.axis('off')","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:32:45.437917Z","iopub.execute_input":"2021-12-16T12:32:45.438255Z","iopub.status.idle":"2021-12-16T12:32:45.728122Z","shell.execute_reply.started":"2021-12-16T12:32:45.438220Z","shell.execute_reply":"2021-12-16T12:32:45.727157Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"default_cmap = LinearSegmentedColormap.from_list('custom blue', \n                                                 [(0, '#ffffff'),\n                                                  (0.25, '#000000'),\n                                                  (1, '#000000')], N=256)\n\n_ = viz.visualize_image_attr(np.transpose(attributions_ig.squeeze().cpu().detach().numpy(), (1,2,0)),\n                             np.transpose(image.squeeze().cpu().detach().numpy(), (1,2,0)),\n                             method='heat_map',\n                             cmap=default_cmap,\n                             show_colorbar=True,\n                             sign='positive',\n                             outlier_perc=1)\nplt.axis('off')","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:32:48.263191Z","iopub.execute_input":"2021-12-16T12:32:48.263856Z","iopub.status.idle":"2021-12-16T12:32:48.754904Z","shell.execute_reply.started":"2021-12-16T12:32:48.263814Z","shell.execute_reply":"2021-12-16T12:32:48.753946Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gradient_shap = GradientShap(model)\n\n# Defining baseline distribution of images\nrand_img_dist = torch.cat([input * 0, input * 1])\n\nattributions_gs = gradient_shap.attribute(input,\n                                          n_samples=5,\n                                          stdevs=0.0001,\n                                          baselines=rand_img_dist,\n                                          target=pred_label_idx)\n_ = viz.visualize_image_attr_multiple(np.transpose(attributions_gs.squeeze().cpu().detach().numpy(), (1,2,0)),\n                                      np.transpose(image.squeeze().cpu().detach().numpy(), (1,2,0)),\n                                      [\"original_image\", \"heat_map\"],\n                                      [\"all\", \"absolute_value\"],\n                                      cmap=default_cmap,\n                                      show_colorbar=True)","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:33:02.809000Z","iopub.execute_input":"2021-12-16T12:33:02.809453Z","iopub.status.idle":"2021-12-16T12:33:11.760725Z","shell.execute_reply.started":"2021-12-16T12:33:02.809420Z","shell.execute_reply":"2021-12-16T12:33:11.759791Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"noise_tunnel = NoiseTunnel(integrated_gradients)\n\nattributions_ig_nt = noise_tunnel.attribute(input, nt_samples=2, nt_type='smoothgrad_sq', target=pred_label_idx)\n_ = viz.visualize_image_attr_multiple(np.transpose(attributions_ig_nt.squeeze().cpu().detach().numpy(), (1,2,0)),\n                                      np.transpose(transformed_img.squeeze().cpu().detach().numpy(), (1,2,0)),\n                                      [\"original_image\", \"heat_map\"],\n                                      [\"all\", \"positive\"],\n                                      cmap=default_cmap,\n                                      show_colorbar=True)","metadata":{"execution":{"iopub.status.busy":"2021-12-16T12:33:17.481322Z","iopub.execute_input":"2021-12-16T12:33:17.482195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## References\n\nFor calculating the attributions, the [captum library](https://captum.ai/)  was used, which is a package for model interpretability in Pytorch.","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:34:42.182831Z","iopub.execute_input":"2021-12-13T08:34:42.183562Z","iopub.status.idle":"2021-12-13T08:34:42.209676Z","shell.execute_reply.started":"2021-12-13T08:34:42.183456Z","shell.execute_reply":"2021-12-13T08:34:42.208785Z"}}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}