{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":14774,"databundleVersionId":875431},{"sourceType":"datasetVersion","sourceId":1512919,"datasetId":611716,"databundleVersionId":1547166}],"dockerImageVersionId":31287,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# GPU is working or not","metadata":{}},{"cell_type":"code","source":"import torch\ntorch.cuda.is_available()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:17:41.866678Z","iopub.execute_input":"2026-03-13T20:17:41.867050Z","iopub.status.idle":"2026-03-13T20:17:46.311930Z","shell.execute_reply.started":"2026-03-13T20:17:41.867008Z","shell.execute_reply":"2026-03-13T20:17:46.311304Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Check Which GPU","metadata":{}},{"cell_type":"code","source":"!nvidia-smi","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:17:50.140916Z","iopub.execute_input":"2026-03-13T20:17:50.141803Z","iopub.status.idle":"2026-03-13T20:17:50.616614Z","shell.execute_reply.started":"2026-03-13T20:17:50.141771Z","shell.execute_reply":"2026-03-13T20:17:50.615776Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Load the Labels","metadata":{}},{"cell_type":"code","source":"import pandas as pd\n\nlabels = pd.read_csv(\"/kaggle/input/competitions/aptos2019-blindness-detection/train.csv\")\nlabels.head()\n# Load ODIR dataset\nodir = pd.read_csv(\"/kaggle/input/datasets/andrewmvd/ocular-disease-recognition-odir5k/full_df.csv\")\n\nodir.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:17:54.711114Z","iopub.execute_input":"2026-03-13T20:17:54.711901Z","iopub.status.idle":"2026-03-13T20:17:55.106466Z","shell.execute_reply.started":"2026-03-13T20:17:54.711868Z","shell.execute_reply":"2026-03-13T20:17:55.105717Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Display a Sample Retina Image","metadata":{}},{"cell_type":"code","source":"import cv2\nimport matplotlib.pyplot as plt\n\nimg_path = \"/kaggle/input/competitions/aptos2019-blindness-detection/train_images/\" + labels.iloc[0,0] + \".png\"\n\nimg = cv2.imread(img_path)\nimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\nplt.imshow(img)\nplt.title(\"Sample Retina Image\")\nplt.axis(\"off\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:18:13.159147Z","iopub.execute_input":"2026-03-13T20:18:13.160154Z","iopub.status.idle":"2026-03-13T20:18:14.095506Z","shell.execute_reply.started":"2026-03-13T20:18:13.160112Z","shell.execute_reply":"2026-03-13T20:18:14.094635Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nimport matplotlib.pyplot as plt\n\n# ODIR image path\nimg_path = \"/kaggle/input/datasets/andrewmvd/ocular-disease-recognition-odir5k/preprocessed_images/\"\n\n# Example image from dataframe\nimg_name = odir.iloc[0]['Left-Fundus']\n\nimg = cv2.imread(img_path + img_name)\nimg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\nplt.imshow(img)\nplt.title(\"Sample ODIR Retina Image\")\nplt.axis(\"off\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:18:17.713011Z","iopub.execute_input":"2026-03-13T20:18:17.713369Z","iopub.status.idle":"2026-03-13T20:18:17.892704Z","shell.execute_reply.started":"2026-03-13T20:18:17.713341Z","shell.execute_reply":"2026-03-13T20:18:17.891917Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Install Required Libraries","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport torch\nimport torchvision\nfrom torchvision import transforms\nfrom torch.utils.data import Dataset, DataLoader","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:18:22.002415Z","iopub.execute_input":"2026-03-13T20:18:22.003002Z","iopub.status.idle":"2026-03-13T20:18:25.771795Z","shell.execute_reply.started":"2026-03-13T20:18:22.002976Z","shell.execute_reply":"2026-03-13T20:18:25.770986Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Image Preprocessing","metadata":{}},{"cell_type":"code","source":"transform = transforms.Compose([\n    transforms.ToPILImage(),\n    transforms.Resize((224,224)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(10),\n    transforms.ToTensor()\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:18:29.386542Z","iopub.execute_input":"2026-03-13T20:18:29.386998Z","iopub.status.idle":"2026-03-13T20:18:29.391745Z","shell.execute_reply.started":"2026-03-13T20:18:29.386971Z","shell.execute_reply":"2026-03-13T20:18:29.391055Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#  Load CNN Model","metadata":{}},{"cell_type":"code","source":"import torchvision.models as models\nimport torch.nn as nn\nfrom torchvision.models import resnet50, ResNet50_Weights\n\nmodel = resnet50(weights=ResNet50_Weights.DEFAULT)\n\n# 3 classes: Normal, DR, AMD\nmodel.fc = nn.Linear(2048,3)\n\ndevice = torch.device(\"cuda\")\nmodel = model.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:18:33.242193Z","iopub.execute_input":"2026-03-13T20:18:33.242610Z","iopub.status.idle":"2026-03-13T20:18:34.860682Z","shell.execute_reply.started":"2026-03-13T20:18:33.242569Z","shell.execute_reply":"2026-03-13T20:18:34.860031Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training Setup","metadata":{}},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=0.0001)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:18:40.340603Z","iopub.execute_input":"2026-03-13T20:18:40.340917Z","iopub.status.idle":"2026-03-13T20:18:40.346089Z","shell.execute_reply.started":"2026-03-13T20:18:40.340890Z","shell.execute_reply":"2026-03-13T20:18:40.345420Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Create Dataset Loader","metadata":{}},{"cell_type":"code","source":"import torchvision.models as models\n\nmodel = models.resnet18(pretrained=True)\nmodel.fc = torch.nn.Linear(model.fc.in_features, 5)\nmodel = model.to(device)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:20:46.046111Z","iopub.execute_input":"2026-03-13T20:20:46.046536Z","iopub.status.idle":"2026-03-13T20:20:46.741825Z","shell.execute_reply.started":"2026-03-13T20:20:46.046506Z","shell.execute_reply":"2026-03-13T20:20:46.740740Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import Dataset\nimport cv2\nimport torch\nimport os\n\nclass RetinaDataset(Dataset):\n    num_classes = 5\n\n\n    def __init__(self, df, img_dir, transform=None):\n        self.df = df\n        self.img_dir = img_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n        \n    def __getitem__(self, idx):\n\n        img_name = str(self.df.iloc[idx, 0])\n        label = int(self.df.iloc[idx, 1])\n    \n        # Add extension if missing\n        if not img_name.endswith((\".png\", \".jpg\", \".jpeg\")):\n            img_name = img_name + \".png\"\n    \n        img_path = os.path.join(self.img_dir, img_name)\n    \n        image = cv2.imread(img_path)\n    \n        if image is None:\n            print(\"Skipping:\", img_path)\n            return self.__getitem__((idx + 1) % len(self.df))\n    \n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    \n        if self.transform:\n            image = self.transform(image)\n    \n        label = int(self.df.iloc[idx, 1])\n\n        # Debug check\n        if label < 0 or label >= self.num_classes:\n            print(\"Invalid label:\", label)\n            label = label % self.num_classes\n\n\n    \n        return image, torch.tensor(label).long()\n\n    \n\n    \n        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:18:43.311176Z","iopub.execute_input":"2026-03-13T20:18:43.311808Z","iopub.status.idle":"2026-03-13T20:18:43.320115Z","shell.execute_reply.started":"2026-03-13T20:18:43.311780Z","shell.execute_reply":"2026-03-13T20:18:43.319231Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Create Dataset","metadata":{}},{"cell_type":"code","source":"# APTOS dataset (DR)\naptos_dir = \"/kaggle/input/competitions/aptos2019-blindness-detection/train_images\"\n\n\naptos_df = labels[['id_code','diagnosis']]\naptos_df.columns = ['image','label']\n\naptos_dataset = RetinaDataset(aptos_df, aptos_dir, transform)\n\n\n# ODIR dataset (AMD)\nodir_dir = \"/kaggle/input/datasets/andrewmvd/ocular-disease-recognition-odir5k/preprocessed_images\"\n\n\n# Select AMD images only\namd_df = odir[odir['A'] == 1][['Left-Fundus']]\namd_df.columns = ['image']\namd_df['label'] = 2   # AMD class\n\nodir_dataset = RetinaDataset(amd_df, odir_dir, transform)\n\n\n# Combine datasets\nfrom torch.utils.data import ConcatDataset\n\ndataset = ConcatDataset([aptos_dataset, odir_dataset])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:18:52.836225Z","iopub.execute_input":"2026-03-13T20:18:52.836806Z","iopub.status.idle":"2026-03-13T20:18:52.848952Z","shell.execute_reply.started":"2026-03-13T20:18:52.836778Z","shell.execute_reply":"2026-03-13T20:18:52.848184Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Create DataLoader","metadata":{}},{"cell_type":"code","source":"\n\ntrain_df = aptos_df\n\ntrain_dir = aptos_dir   # or odir_dir depending on which images you load\n\nfrom torch.utils.data import DataLoader\ntrain_dataset = RetinaDataset(train_df, train_dir, transform)\n\n\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=0)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:18:56.951564Z","iopub.execute_input":"2026-03-13T20:18:56.951889Z","iopub.status.idle":"2026-03-13T20:18:56.956540Z","shell.execute_reply.started":"2026-03-13T20:18:56.951865Z","shell.execute_reply":"2026-03-13T20:18:56.955815Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Training Loop","metadata":{}},{"cell_type":"code","source":"model.train()\n\nfor epoch in range(3):\n\n    total_loss = 0\n    correct = 0\n    total = 0\n\n    for images, labels_batch in train_loader:\n\n        print(labels_batch.min(), labels_batch.max())  # add here\n\n        images = images.to(device)\n        labels_batch = labels_batch.to(device)\n\n        optimizer.zero_grad()\n\n        outputs = model(images)\n        loss = criterion(outputs, labels_batch)\n\n        loss.backward()\n        optimizer.step()\n\n        total_loss += loss.item()\n\n        _, predicted = torch.max(outputs, 1)\n        total += labels_batch.size(0)\n        correct += (predicted == labels_batch).sum().item()\n\n    accuracy = 100 * correct / total\n\n    print(\"Epoch:\", epoch+1, \"Loss:\", total_loss, \"Accuracy:\", accuracy)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-13T20:19:00.108540Z","iopub.execute_input":"2026-03-13T20:19:00.109123Z","iopub.status.idle":"2026-03-13T20:19:06.023209Z","shell.execute_reply.started":"2026-03-13T20:19:00.109095Z","shell.execute_reply":"2026-03-13T20:19:06.022113Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Accuracy Graph (Training Performance)","metadata":{}},{"cell_type":"code","source":"loss_history = []\nfor epoch in range(5):\n\n    total_loss = 0\n\n    for images, labels_batch in train_loader:\n\n        images = images.to(device)\n        labels_batch = labels_batch.to(device)\n\n        outputs = model(images)\n        loss = criterion(outputs, labels_batch)\n\n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n\n        total_loss += loss.item()\n\n    loss_history.append(total_loss)\n    print(\"Epoch:\", epoch+1, \"Loss:\", total_loss)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T19:19:27.806949Z","iopub.execute_input":"2026-03-05T19:19:27.807546Z","iopub.status.idle":"2026-03-05T19:56:52.404664Z","shell.execute_reply.started":"2026-03-05T19:19:27.807515Z","shell.execute_reply":"2026-03-05T19:56:52.403987Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nplt.plot(loss_history)\nplt.title(\"Training Loss Curve\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T19:58:36.957845Z","iopub.execute_input":"2026-03-05T19:58:36.958115Z","iopub.status.idle":"2026-03-05T19:58:37.081881Z","shell.execute_reply.started":"2026-03-05T19:58:36.958093Z","shell.execute_reply":"2026-03-05T19:58:37.081300Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Confusion Matrix","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix\nimport numpy as np\n\nmodel.eval()\n\ntrue_labels = []\npred_labels = []\n\nwith torch.no_grad():\n\n    for images, labels_batch in train_loader:\n\n        images = images.to(device)\n\n        outputs = model(images)\n        _, predicted = torch.max(outputs, 1)\n\n        true_labels.extend(labels_batch.numpy())\n        pred_labels.extend(predicted.cpu().numpy())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T19:59:28.352063Z","iopub.execute_input":"2026-03-05T19:59:28.352818Z","iopub.status.idle":"2026-03-05T20:06:32.957337Z","shell.execute_reply.started":"2026-03-05T19:59:28.352788Z","shell.execute_reply":"2026-03-05T20:06:32.956723Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cm = confusion_matrix(true_labels, pred_labels)\n\nimport seaborn as sns\n\nplt.figure(figsize=(6,6))\nsns.heatmap(cm, annot=True, fmt=\"d\", cmap=\"Blues\")\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"Actual\")\nplt.title(\"Confusion Matrix\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T20:06:53.047392Z","iopub.execute_input":"2026-03-05T20:06:53.047863Z","iopub.status.idle":"2026-03-05T20:06:53.760803Z","shell.execute_reply.started":"2026-03-05T20:06:53.047835Z","shell.execute_reply":"2026-03-05T20:06:53.760119Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Prediction Examples","metadata":{}},{"cell_type":"code","source":"model.eval()\n\nimages, labels_batch = next(iter(train_loader))\n\nimages = images.to(device)\n\noutputs = model(images)\n\n_, preds = torch.max(outputs,1)\n\nimages = images.cpu()\n\nplt.figure(figsize=(12,6))\n\nfor i in range(6):\n\n    plt.subplot(2,3,i+1)\n    img = images[i].permute(1,2,0)\n    plt.imshow(img)\n    plt.title(f\"Pred: {preds[i].item()}\")\n    plt.axis(\"off\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T20:06:59.584104Z","iopub.execute_input":"2026-03-05T20:06:59.584389Z","iopub.status.idle":"2026-03-05T20:07:01.292224Z","shell.execute_reply.started":"2026-03-05T20:06:59.584363Z","shell.execute_reply":"2026-03-05T20:07:01.291548Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Grad-CAM Visualization (Explainable AI)","metadata":{}},{"cell_type":"code","source":"!pip install grad-cam","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T20:07:11.190618Z","iopub.execute_input":"2026-03-05T20:07:11.190894Z","iopub.status.idle":"2026-03-05T20:07:21.654662Z","shell.execute_reply.started":"2026-03-05T20:07:11.190873Z","shell.execute_reply":"2026-03-05T20:07:21.653895Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pytorch_grad_cam import GradCAM\nfrom pytorch_grad_cam.utils.image import show_cam_on_image\n\ntarget_layer = model.layer4[-1]\n\ncam = GradCAM(model=model, target_layers=[target_layer])\n\ninput_tensor = images[0].unsqueeze(0).to(device)\n\ngrayscale_cam = cam(input_tensor=input_tensor)\n\nheatmap = grayscale_cam[0]\n\nimg = images[0].permute(1,2,0).numpy()\n\nvisualization = show_cam_on_image(img, heatmap, use_rgb=True)\n\nplt.imshow(visualization)\nplt.title(\"Grad-CAM Visualization\")\nplt.axis(\"off\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T20:07:28.029834Z","iopub.execute_input":"2026-03-05T20:07:28.030461Z","iopub.status.idle":"2026-03-05T20:07:28.612407Z","shell.execute_reply.started":"2026-03-05T20:07:28.030430Z","shell.execute_reply":"2026-03-05T20:07:28.611881Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Testing from the user  \n\ntelling how percentage is good and categorise","metadata":{}},{"cell_type":"markdown","source":"# Define Disease Labels","metadata":{}},{"cell_type":"code","source":"classes = {\n    0: \"No DR\",\n    1: \"Mild\",\n    2: \"Moderate\",\n    3: \"Severe\",\n    4: \"Proliferative DR\"\n}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T20:23:32.702799Z","iopub.execute_input":"2026-03-05T20:23:32.703082Z","iopub.status.idle":"2026-03-05T20:23:32.706338Z","shell.execute_reply.started":"2026-03-05T20:23:32.703058Z","shell.execute_reply":"2026-03-05T20:23:32.705722Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Get Prediction With Percentage","metadata":{}},{"cell_type":"code","source":"import torch.nn.functional as F\n\nmodel.eval()\n\nimages, labels_batch = next(iter(train_loader))\n\nimages = images.to(device)\n\noutputs = model(images)\n\nprobabilities = F.softmax(outputs, dim=1)\n\nconfidence, preds = torch.max(probabilities,1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T20:24:09.114070Z","iopub.execute_input":"2026-03-05T20:24:09.114387Z","iopub.status.idle":"2026-03-05T20:24:11.173764Z","shell.execute_reply.started":"2026-03-05T20:24:09.114362Z","shell.execute_reply":"2026-03-05T20:24:11.173195Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Display Prediction Results","metadata":{}},{"cell_type":"code","source":"images = images.cpu()\n\nplt.figure(figsize=(12,6))\n\nfor i in range(6):\n\n    plt.subplot(2,3,i+1)\n\n    img = images[i].permute(1,2,0)\n\n    plt.imshow(img)\n\n    pred_class = preds[i].item()\n    conf = confidence[i].item()*100\n\n    plt.title(f\"{classes[pred_class]}\\nConfidence: {conf:.2f}%\")\n\n    plt.axis(\"off\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T20:25:16.013431Z","iopub.execute_input":"2026-03-05T20:25:16.013775Z","iopub.status.idle":"2026-03-05T20:25:16.345105Z","shell.execute_reply.started":"2026-03-05T20:25:16.013749Z","shell.execute_reply":"2026-03-05T20:25:16.344219Z"}},"outputs":[],"execution_count":null}]}