{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\n\nprint(\"PyTorch version:\", torch.__version__)\nprint(\"CUDA available:\", torch.cuda.is_available())\n\nif torch.cuda.is_available():\n    print(\"GPU:\", torch.cuda.get_device_name(0))\nelse:\n    print(\"No GPU available\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T18:52:47.916199Z","iopub.execute_input":"2026-09-20T18:52:47.91647Z","iopub.status.idle":"2026-09-20T18:52:52.849103Z","shell.execute_reply.started":"2026-09-20T18:52:47.916437Z","shell.execute_reply":"2026-09-20T18:52:52.84814Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nprint(os.listdir(\"/kaggle/input\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T18:54:30.559002Z","iopub.execute_input":"2026-09-20T18:54:30.559628Z","iopub.status.idle":"2026-09-20T18:54:30.564297Z","shell.execute_reply.started":"2026-09-20T18:54:30.559599Z","shell.execute_reply":"2026-09-20T18:54:30.563409Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nfor root, dirs, files in os.walk(\"/kaggle/input\"):\n    print(root)\n    for file in files[:5]:\n        print(\"   \", file)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T18:56:32.539525Z","iopub.execute_input":"2026-09-20T18:56:32.540377Z","iopub.status.idle":"2026-09-20T18:56:42.53369Z","shell.execute_reply.started":"2026-09-20T18:56:32.540344Z","shell.execute_reply":"2026-09-20T18:56:42.533118Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ncsv_path = \"/kaggle/input/competitions/aptos2019-blindness-detection/train.csv\"\n\ndf = pd.read_csv(csv_path)\n\nprint(df.head())\nprint(\"\\nDataset size:\", len(df))\nprint(\"\\nDiagnosis counts:\")\nprint(df[\"diagnosis\"].value_counts().sort_index())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T18:58:09.228465Z","iopub.execute_input":"2026-09-20T18:58:09.229088Z","iopub.status.idle":"2026-09-20T18:58:09.537564Z","shell.execute_reply.started":"2026-09-20T18:58:09.229058Z","shell.execute_reply":"2026-09-20T18:58:09.536915Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from PIL import Image\nimport os\n\nimage_path = \"/kaggle/input/competitions/aptos2019-blindness-detection/train_images/\" + df.iloc[0][\"id_code\"] + \".png\"\n\nimg = Image.open(image_path)\n\nprint(\"Image size:\", img.size)\nprint(\"Image mode:\", img.mode)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:00:22.452501Z","iopub.execute_input":"2026-09-20T19:00:22.452981Z","iopub.status.idle":"2026-09-20T19:00:22.551649Z","shell.execute_reply.started":"2026-09-20T19:00:22.452951Z","shell.execute_reply":"2026-09-20T19:00:22.550873Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torchvision\nimport pandas as pd\nimport numpy as np\nfrom PIL import Image\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models\n\nprint(\"Torch:\", torch.__version__)\nprint(\"Torchvision:\", torchvision.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:01:57.57095Z","iopub.execute_input":"2026-09-20T19:01:57.571369Z","iopub.status.idle":"2026-09-20T19:02:01.394233Z","shell.execute_reply.started":"2026-09-20T19:01:57.57134Z","shell.execute_reply":"2026-09-20T19:02:01.393574Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DRDataset(Dataset):\n    def __init__(self, dataframe, image_dir, transform=None):\n        self.dataframe = dataframe.reset_index(drop=True)\n        self.image_dir = image_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, index):\n        row = self.dataframe.iloc[index]\n\n        image_path = os.path.join(\n            self.image_dir,\n            row[\"id_code\"] + \".png\"\n        )\n\n        image = Image.open(image_path).convert(\"RGB\")\n        label = int(row[\"diagnosis\"])\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label\n\n\nIMAGE_DIR = \"/kaggle/input/competitions/aptos2019-blindness-detection/train_images\"\n\nprint(\"Dataset loader created successfully.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:03:27.533598Z","iopub.execute_input":"2026-09-20T19:03:27.534165Z","iopub.status.idle":"2026-09-20T19:03:27.541219Z","shell.execute_reply.started":"2026-09-20T19:03:27.534134Z","shell.execute_reply":"2026-09-20T19:03:27.540355Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ntrain_df, val_df = train_test_split(\n    df,\n    test_size=0.20,\n    random_state=42,\n    stratify=df[\"diagnosis\"]\n)\n\nprint(\"Training images:\", len(train_df))\nprint(\"Validation images:\", len(val_df))\n\nprint(\"\\nTraining class distribution:\")\nprint(train_df[\"diagnosis\"].value_counts().sort_index())\n\nprint(\"\\nValidation class distribution:\")\nprint(val_df[\"diagnosis\"].value_counts().sort_index())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:24:05.264417Z","iopub.execute_input":"2026-09-20T19:24:05.265129Z","iopub.status.idle":"2026-09-20T19:24:06.080132Z","shell.execute_reply.started":"2026-09-20T19:24:05.265098Z","shell.execute_reply":"2026-09-20T19:24:06.079384Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transform = transforms.Compose([\n    transforms.Resize((300, 300)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(10),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],\n        std=[0.229, 0.224, 0.225]\n    )\n])\n\nval_transform = transforms.Compose([\n    transforms.Resize((300, 300)),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],\n        std=[0.229, 0.224, 0.225]\n    )\n])\n\nprint(\"Image preprocessing ready.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:32:41.448232Z","iopub.execute_input":"2026-09-20T19:32:41.449167Z","iopub.status.idle":"2026-09-20T19:32:41.455116Z","shell.execute_reply.started":"2026-09-20T19:32:41.449135Z","shell.execute_reply":"2026-09-20T19:32:41.454354Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = DRDataset(\n    train_df,\n    IMAGE_DIR,\n    transform=train_transform\n)\n\nval_dataset = DRDataset(\n    val_df,\n    IMAGE_DIR,\n    transform=val_transform\n)\n\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=16,\n    shuffle=True,\n    num_workers=2\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=16,\n    shuffle=False,\n    num_workers=2\n)\n\nprint(\"Training batches:\", len(train_loader))\nprint(\"Validation batches:\", len(val_loader))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:33:11.641696Z","iopub.execute_input":"2026-09-20T19:33:11.642263Z","iopub.status.idle":"2026-09-20T19:33:11.648836Z","shell.execute_reply.started":"2026-09-20T19:33:11.642234Z","shell.execute_reply":"2026-09-20T19:33:11.648177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nimages, labels = next(iter(train_loader))\n\nimages = images.to(device)\nlabels = labels.to(device)\n\nprint(\"Device:\", device)\nprint(\"Image batch shape:\", images.shape)\nprint(\"Label batch shape:\", labels.shape)\nprint(\"GPU:\", torch.cuda.get_device_name(0))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:33:51.639669Z","iopub.execute_input":"2026-09-20T19:33:51.640098Z","iopub.status.idle":"2026-09-20T19:33:56.127297Z","shell.execute_reply.started":"2026-09-20T19:33:51.640064Z","shell.execute_reply":"2026-09-20T19:33:56.126539Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = models.efficientnet_b3(\n    weights=models.EfficientNet_B3_Weights.DEFAULT\n)\n\n# Replace the final classifier for 5 DR classes\nnum_features = model.classifier[1].in_features\n\nmodel.classifier[1] = torch.nn.Linear(\n    num_features,\n    5\n)\n\nmodel = model.to(device)\n\nprint(\"EfficientNet-B3 loaded successfully.\")\nprint(\"Output classes:\", 5)\nprint(\"Device:\", device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:36:04.0804Z","iopub.execute_input":"2026-09-20T19:36:04.081127Z","iopub.status.idle":"2026-09-20T19:36:36.300949Z","shell.execute_reply.started":"2026-09-20T19:36:04.081098Z","shell.execute_reply":"2026-09-20T19:36:36.299607Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\ncache_dir = \"/root/.cache/torch/hub/checkpoints\"\n\nprint(\"Cached files:\")\nprint(os.listdir(cache_dir) if os.path.exists(cache_dir) else \"Cache folder does not exist\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:38:59.990188Z","iopub.execute_input":"2026-09-20T19:38:59.990591Z","iopub.status.idle":"2026-09-20T19:38:59.995882Z","shell.execute_reply.started":"2026-09-20T19:38:59.990564Z","shell.execute_reply":"2026-09-20T19:38:59.995006Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = models.efficientnet_b3(weights=None)\n\n# Replace final classifier for 5 DR classes\nnum_features = model.classifier[1].in_features\n\nmodel.classifier[1] = torch.nn.Linear(\n    num_features,\n    5\n)\n\nmodel = model.to(device)\n\nprint(\"EfficientNet-B3 created successfully.\")\nprint(\"Output classes:\", 5)\nprint(\"Device:\", device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:39:53.837756Z","iopub.execute_input":"2026-09-20T19:39:53.83851Z","iopub.status.idle":"2026-09-20T19:39:54.072314Z","shell.execute_reply.started":"2026-09-20T19:39:53.838481Z","shell.execute_reply":"2026-09-20T19:39:54.071477Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.utils.class_weight import compute_class_weight\n\n# Calculate weights for the 5 DR classes\nclass_weights = compute_class_weight(\n    class_weight=\"balanced\",\n    classes=np.array([0, 1, 2, 3, 4]),\n    y=train_df[\"diagnosis\"].values\n)\n\nclass_weights = torch.tensor(\n    class_weights,\n    dtype=torch.float32\n).to(device)\n\nprint(\"Class weights:\", class_weights)\n\n# Loss function\ncriterion = torch.nn.CrossEntropyLoss(\n    weight=class_weights\n)\n\n# Optimizer\noptimizer = torch.optim.AdamW(\n    model.parameters(),\n    lr=0.0001,\n    weight_decay=0.0001\n)\n\nprint(\"Loss function and optimizer ready.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:42:09.368327Z","iopub.execute_input":"2026-09-20T19:42:09.368735Z","iopub.status.idle":"2026-09-20T19:42:09.696785Z","shell.execute_reply.started":"2026-09-20T19:42:09.368695Z","shell.execute_reply":"2026-09-20T19:42:09.696103Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"num_epochs = 1\n\nfor epoch in range(num_epochs):\n    model.train()\n\n    running_loss = 0.0\n    correct = 0\n    total = 0\n\n    for batch_idx, (images, labels) in enumerate(train_loader):\n        images = images.to(device)\n        labels = labels.to(device)\n\n        # Clear previous gradients\n        optimizer.zero_grad()\n\n        # Forward pass\n        outputs = model(images)\n\n        # Calculate loss\n        loss = criterion(outputs, labels)\n\n        # Backpropagation\n        loss.backward()\n\n        # Update model weights\n        optimizer.step()\n\n        # Statistics\n        running_loss += loss.item()\n\n        _, predicted = torch.max(outputs, 1)\n\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n\n        if (batch_idx + 1) % 20 == 0:\n            print(\n                f\"Batch {batch_idx + 1}/{len(train_loader)} \"\n                f\"- Loss: {loss.item():.4f}\"\n            )\n\n    train_accuracy = 100 * correct / total\n\n    print(\"\\nEpoch complete\")\n    print(f\"Training Loss: {running_loss / len(train_loader):.4f}\")\n    print(f\"Training Accuracy: {train_accuracy:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:44:57.729648Z","iopub.execute_input":"2026-09-20T19:44:57.730464Z","iopub.status.idle":"2026-09-20T19:48:32.487697Z","shell.execute_reply.started":"2026-09-20T19:44:57.730433Z","shell.execute_reply":"2026-09-20T19:48:32.486834Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()\n\ncorrect = 0\ntotal = 0\nval_loss = 0.0\n\nwith torch.no_grad():\n    for images, labels in val_loader:\n        images = images.to(device)\n        labels = labels.to(device)\n\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        val_loss += loss.item()\n\n        _, predicted = torch.max(outputs, 1)\n\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n\nval_accuracy = 100 * correct / total\n\nprint(f\"Validation Loss: {val_loss / len(val_loader):.4f}\")\nprint(f\"Validation Accuracy: {val_accuracy:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:49:28.72913Z","iopub.execute_input":"2026-09-20T19:49:28.730007Z","iopub.status.idle":"2026-09-20T19:50:21.826434Z","shell.execute_reply.started":"2026-09-20T19:49:28.729977Z","shell.execute_reply":"2026-09-20T19:50:21.825501Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"num_epochs = 2\n\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n\n    for batch_idx, (images, labels) in enumerate(train_loader):\n        images = images.to(device)\n        labels = labels.to(device)\n\n        optimizer.zero_grad()\n\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item()\n\n        _, predicted = torch.max(outputs, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n\n        if (batch_idx + 1) % 20 == 0:\n            print(\n                f\"Epoch {epoch+2}/3 | \"\n                f\"Batch {batch_idx+1}/{len(train_loader)} | \"\n                f\"Loss: {loss.item():.4f}\"\n            )\n\n    train_accuracy = 100 * correct / total\n\n    print(\"\\nEpoch complete\")\n    print(f\"Training Loss: {running_loss / len(train_loader):.4f}\")\n    print(f\"Training Accuracy: {train_accuracy:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T19:51:35.494811Z","iopub.execute_input":"2026-09-20T19:51:35.495338Z","iopub.status.idle":"2026-09-20T19:58:10.533076Z","shell.execute_reply.started":"2026-09-20T19:51:35.495306Z","shell.execute_reply":"2026-09-20T19:58:10.532114Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()\n\ncorrect = 0\ntotal = 0\nval_loss = 0.0\n\nwith torch.no_grad():\n    for images, labels in val_loader:\n        images = images.to(device)\n        labels = labels.to(device)\n\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        val_loss += loss.item()\n\n        _, predicted = torch.max(outputs, 1)\n\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n\nval_accuracy = 100 * correct / total\n\nprint(f\"Validation Loss: {val_loss / len(val_loader):.4f}\")\nprint(f\"Validation Accuracy: {val_accuracy:.2f}%\") ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T20:25:52.105275Z","iopub.execute_input":"2026-09-20T20:25:52.105791Z","iopub.status.idle":"2026-09-20T20:26:40.602747Z","shell.execute_reply.started":"2026-09-20T20:25:52.105755Z","shell.execute_reply":"2026-09-20T20:26:40.601713Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_path = \"/kaggle/working/efficientnet_dr.pth\"\n\ntorch.save(model.state_dict(), model_path)\n\nprint(\"Model saved successfully!\")\nprint(model_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T20:28:30.87299Z","iopub.execute_input":"2026-09-20T20:28:30.873794Z","iopub.status.idle":"2026-09-20T20:28:30.995222Z","shell.execute_reply.started":"2026-09-20T20:28:30.873754Z","shell.execute_reply":"2026-09-20T20:28:30.994459Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Pick one validation image\nsample_row = val_df.iloc[0]\n\nsample_path = os.path.join(\n    IMAGE_DIR,\n    sample_row[\"id_code\"] + \".png\"\n)\n\nprint(\"Image:\", sample_row[\"id_code\"])\nprint(\"Actual DR stage:\", sample_row[\"diagnosis\"])\nprint(\"Path:\", sample_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T20:29:24.744795Z","iopub.execute_input":"2026-09-20T20:29:24.745634Z","iopub.status.idle":"2026-09-20T20:29:24.75128Z","shell.execute_reply.started":"2026-09-20T20:29:24.745604Z","shell.execute_reply":"2026-09-20T20:29:24.750335Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"id2stage = {\n    0: \"No DR\",\n    1: \"Mild DR\",\n    2: \"Moderate DR\",\n    3: \"Severe DR\",\n    4: \"Proliferative DR\"\n}\n\nmodel.eval()\n\nimage = Image.open(sample_path).convert(\"RGB\")\ninput_tensor = val_transform(image).unsqueeze(0).to(device)\n\nwith torch.no_grad():\n    output = model(input_tensor)\n    probabilities = torch.softmax(output, dim=1)\n    predicted_class = torch.argmax(probabilities, dim=1).item()\n    confidence = probabilities[0][predicted_class].item()\n\nprint(\"Actual:\", id2stage[sample_row[\"diagnosis\"]])\nprint(\"Predicted:\", id2stage[predicted_class])\nprint(f\"Confidence: {confidence * 100:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T20:30:49.919361Z","iopub.execute_input":"2026-09-20T20:30:49.919835Z","iopub.status.idle":"2026-09-20T20:30:50.017592Z","shell.execute_reply.started":"2026-09-20T20:30:49.919805Z","shell.execute_reply":"2026-09-20T20:30:50.016777Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Prediction probabilities:\")\n\nfor i, probability in enumerate(probabilities[0]):\n    print(f\"{id2stage[i]}: {probability.item() * 100:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T20:31:27.511521Z","iopub.execute_input":"2026-09-20T20:31:27.512053Z","iopub.status.idle":"2026-09-20T20:31:27.517658Z","shell.execute_reply.started":"2026-09-20T20:31:27.512023Z","shell.execute_reply":"2026-09-20T20:31:27.51678Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for name, module in model.named_modules():\n    if isinstance(module, torch.nn.Conv2d):\n        last_conv_name = name\n\nprint(\"Last convolutional layer:\", last_conv_name)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T20:32:24.054202Z","iopub.execute_input":"2026-09-20T20:32:24.054649Z","iopub.status.idle":"2026-09-20T20:32:24.060439Z","shell.execute_reply.started":"2026-09-20T20:32:24.054618Z","shell.execute_reply":"2026-09-20T20:32:24.059506Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"activations = None\ngradients = None\n\ndef forward_hook(module, input, output):\n    global activations\n    activations = output\n\ndef backward_hook(module, grad_input, grad_output):\n    global gradients\n    gradients = grad_output[0]\n\ntarget_layer = model.features[8][0]\n\nforward_handle = target_layer.register_forward_hook(forward_hook)\nbackward_handle = target_layer.register_full_backward_hook(backward_hook)\n\nprint(\"Grad-CAM hooks registered successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T20:33:52.81328Z","iopub.execute_input":"2026-09-20T20:33:52.814156Z","iopub.status.idle":"2026-09-20T20:33:52.819503Z","shell.execute_reply.started":"2026-09-20T20:33:52.814124Z","shell.execute_reply":"2026-09-20T20:33:52.818806Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Generate Grad-CAM for the predicted class\n\nmodel.eval()\n\n# Clear previous gradients\nmodel.zero_grad()\n\n# Forward pass\noutput = model(input_tensor)\n\n# Get the predicted class\npredicted_class = output.argmax(dim=1).item()\n\n# Backward pass for the predicted class\noutput[0, predicted_class].backward()\n\n# Get saved activations and gradients\nactivation = activations[0]\ngradient = gradients[0]\n\n# Calculate importance of each feature map\nweights = gradient.mean(dim=(1, 2))\n\n# Create heatmap\ncam = torch.zeros(activation.shape[1:], device=device)\n\nfor i, weight in enumerate(weights):\n    cam += weight * activation[i]\n\n# Keep positive influence only\ncam = torch.relu(cam)\n\n# Normalize between 0 and 1\ncam -= cam.min()\ncam /= cam.max() + 1e-8\n\nprint(\"Grad-CAM heatmap generated!\")\nprint(\"Predicted:\", id2stage[predicted_class])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T20:34:55.941283Z","iopub.execute_input":"2026-09-20T20:34:55.941732Z","iopub.status.idle":"2026-09-20T20:34:56.119819Z","shell.execute_reply.started":"2026-09-20T20:34:55.941702Z","shell.execute_reply":"2026-09-20T20:34:56.118912Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\n\n# Convert Grad-CAM tensor to NumPy\ncam_np = cam.detach().cpu().numpy()\n\n# Resize heatmap to original image size\nheatmap = cv2.resize(\n    cam_np,\n    (image.width, image.height)\n)\n\n# Convert to 0–255\nheatmap = np.uint8(255 * heatmap)\n\n# Apply color map\nheatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)\n\n# Convert original image to NumPy\noriginal = np.array(image)\n\n# Convert RGB → BGR for OpenCV\noriginal_bgr = cv2.cvtColor(original, cv2.COLOR_RGB2BGR)\n\n# Blend original image + heatmap\noverlay = cv2.addWeighted(\n    original_bgr,\n    0.6,\n    heatmap,\n    0.4,\n    0\n)\n\n# Convert back to RGB for displaying\noverlay_rgb = cv2.cvtColor(overlay, cv2.COLOR_BGR2RGB)\n\nplt.figure(figsize=(10, 6))\nplt.imshow(overlay_rgb)\nplt.axis(\"off\")\nplt.title(f\"Grad-CAM — {id2stage[predicted_class]}\")\nplt.show()\n\nprint(\"Grad-CAM overlay created!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T20:35:21.787118Z","iopub.execute_input":"2026-09-20T20:35:21.787722Z","iopub.status.idle":"2026-09-20T20:35:22.428542Z","shell.execute_reply.started":"2026-09-20T20:35:21.787692Z","shell.execute_reply":"2026-09-20T20:35:22.427719Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"output_path = \"/kaggle/working/gradcam_result.png\"\n\ncv2.imwrite(\n    output_path,\n    cv2.cvtColor(overlay_rgb, cv2.COLOR_RGB2BGR)\n)\n\nprint(\"Grad-CAM image saved!\")\nprint(output_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T20:36:28.40926Z","iopub.execute_input":"2026-09-20T20:36:28.40974Z","iopub.status.idle":"2026-09-20T20:36:28.482701Z","shell.execute_reply.started":"2026-09-20T20:36:28.409709Z","shell.execute_reply":"2026-09-20T20:36:28.481892Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"result = {\n    \"image\": sample_row[\"id_code\"],\n    \"actual_stage\": id2stage[sample_row[\"diagnosis\"]],\n    \"predicted_stage\": id2stage[predicted_class],\n    \"confidence\": round(confidence * 100, 2),\n    \"gradcam_image\": output_path\n}\n\nprint(\"DrishtiAI ML result\")\nprint(\"-------------------\")\n\nfor key, value in result.items():\n    print(f\"{key}: {value}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-20T20:37:06.321109Z","iopub.execute_input":"2026-09-20T20:37:06.321415Z","iopub.status.idle":"2026-09-20T20:37:06.327446Z","shell.execute_reply.started":"2026-09-20T20:37:06.32139Z","shell.execute_reply":"2026-09-20T20:37:06.32655Z"}},"outputs":[],"execution_count":null}]}