{"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\n# import os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:34.477800Z","iopub.execute_input":"2026-06-29T14:56:34.478358Z","iopub.status.idle":"2026-06-29T14:56:34.482854Z","shell.execute_reply.started":"2026-06-29T14:56:34.478324Z","shell.execute_reply":"2026-06-29T14:56:34.482139Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!python --version","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T06:26:21.822491Z","iopub.execute_input":"2026-06-29T06:26:21.822922Z","iopub.status.idle":"2026-06-29T06:26:21.963988Z","shell.execute_reply.started":"2026-06-29T06:26:21.822893Z","shell.execute_reply":"2026-06-29T06:26:21.963179Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch \nprint(torch.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T06:28:26.106972Z","iopub.execute_input":"2026-06-29T06:28:26.108145Z","iopub.status.idle":"2026-06-29T06:28:26.117795Z","shell.execute_reply.started":"2026-06-29T06:28:26.108098Z","shell.execute_reply":"2026-06-29T06:28:26.117209Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.__version__","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T06:30:38.098000Z","iopub.execute_input":"2026-06-29T06:30:38.098486Z","iopub.status.idle":"2026-06-29T06:30:38.103513Z","shell.execute_reply.started":"2026-06-29T06:30:38.098459Z","shell.execute_reply":"2026-06-29T06:30:38.102641Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sklearn \nsklearn.__version__","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T06:30:51.097344Z","iopub.execute_input":"2026-06-29T06:30:51.097597Z","iopub.status.idle":"2026-06-29T06:30:51.102914Z","shell.execute_reply.started":"2026-06-29T06:30:51.097577Z","shell.execute_reply":"2026-06-29T06:30:51.102194Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torchvision\ntorchvision.__version__","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T06:31:14.678462Z","iopub.execute_input":"2026-06-29T06:31:14.679146Z","iopub.status.idle":"2026-06-29T06:31:14.683868Z","shell.execute_reply.started":"2026-06-29T06:31:14.679117Z","shell.execute_reply":"2026-06-29T06:31:14.683111Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ddr_path = kagglehub.dataset_download(\n    \"mariaherrerot/ddrdataset\"\n)\n\nmessidor_path = kagglehub.dataset_download(\n    \"mariaherrerot/messidor2preprocess\"\n)\n\nprint(ddr_path)\nprint(messidor_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:39.228110Z","iopub.execute_input":"2026-06-29T14:56:39.228847Z","iopub.status.idle":"2026-06-29T14:56:39.363849Z","shell.execute_reply.started":"2026-06-29T14:56:39.228814Z","shell.execute_reply":"2026-06-29T14:56:39.363243Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DDR_CSV = \"/kaggle/input/datasets/mariaherrerot/ddrdataset/DR_grading.csv\"\nDDR_IMG_DIR = \"/kaggle/input/datasets/mariaherrerot/ddrdataset/DR_grading/DR_grading\"\n\nMESSIDOR_CSV = \"/kaggle/input/datasets/mariaherrerot/messidor2preprocess/messidor_data.csv\"\nMESSIDOR_IMG_DIR = \"/kaggle/input/datasets/mariaherrerot/messidor2preprocess/messidor-2/messidor-2/preprocess\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:39.432859Z","iopub.execute_input":"2026-06-29T14:56:39.433088Z","iopub.status.idle":"2026-06-29T14:56:39.436983Z","shell.execute_reply.started":"2026-06-29T14:56:39.433049Z","shell.execute_reply":"2026-06-29T14:56:39.436197Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ddr_df = pd.read_csv(DDR_CSV)\nmessidor_df = pd.read_csv(MESSIDOR_CSV)\n\nprint(ddr_df.head())\nprint(ddr_df.columns)\n\nprint(messidor_df.head())\nprint(messidor_df.columns)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:39.597470Z","iopub.execute_input":"2026-06-29T14:56:39.597665Z","iopub.status.idle":"2026-06-29T14:56:39.619992Z","shell.execute_reply.started":"2026-06-29T14:56:39.597645Z","shell.execute_reply":"2026-06-29T14:56:39.619402Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import kagglehub\n\n# Download latest version\npath = kagglehub.competition_download('aptos2019-blindness-detection')\n\nprint(\"Path to competition files:\", path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:39.761494Z","iopub.execute_input":"2026-06-29T14:56:39.761684Z","iopub.status.idle":"2026-06-29T14:56:41.380452Z","shell.execute_reply.started":"2026-06-29T14:56:39.761666Z","shell.execute_reply":"2026-06-29T14:56:41.379737Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"APTOS_TRAIN_CSV='/kaggle/input/competitions/aptos2019-blindness-detection/train.csv'\nAPTOS_TEST_CSV='/kaggle/input/competitions/aptos2019-blindness-detection/test.csv'\nAPTOS_TRAIN_IMAGES='/kaggle/input/competitions/aptos2019-blindness-detection/train_images'\nAPTOS_TEST_IMAGES='/kaggle/input/competitions/aptos2019-blindness-detection/test_images'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:41.381684Z","iopub.execute_input":"2026-06-29T14:56:41.381929Z","iopub.status.idle":"2026-06-29T14:56:41.385688Z","shell.execute_reply.started":"2026-06-29T14:56:41.381907Z","shell.execute_reply":"2026-06-29T14:56:41.384876Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"aptos_train_df = pd.read_csv(APTOS_TRAIN_CSV)\naptos_test_df = pd.read_csv(APTOS_TEST_CSV)\n\nprint(aptos_train_df.head())\nprint(aptos_train_df.shape)\n\nprint(aptos_test_df.head())\nprint(aptos_test_df.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:43.776834Z","iopub.execute_input":"2026-06-29T14:56:43.777695Z","iopub.status.idle":"2026-06-29T14:56:43.794397Z","shell.execute_reply.started":"2026-06-29T14:56:43.777660Z","shell.execute_reply":"2026-06-29T14:56:43.793572Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ddr_df['diagnosis'].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:43.950528Z","iopub.execute_input":"2026-06-29T14:56:43.951399Z","iopub.status.idle":"2026-06-29T14:56:43.957397Z","shell.execute_reply.started":"2026-06-29T14:56:43.951372Z","shell.execute_reply":"2026-06-29T14:56:43.956678Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ddr_df['diagnosis'].value_counts().plot(kind='bar')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:44.102113Z","iopub.execute_input":"2026-06-29T14:56:44.102348Z","iopub.status.idle":"2026-06-29T14:56:44.211498Z","shell.execute_reply.started":"2026-06-29T14:56:44.102327Z","shell.execute_reply":"2026-06-29T14:56:44.210863Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"messidor_df['diagnosis'].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:44.230983Z","iopub.execute_input":"2026-06-29T14:56:44.231553Z","iopub.status.idle":"2026-06-29T14:56:44.237821Z","shell.execute_reply.started":"2026-06-29T14:56:44.231505Z","shell.execute_reply":"2026-06-29T14:56:44.236916Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"messidor_df['diagnosis'].value_counts().plot(kind='bar')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:44.507562Z","iopub.execute_input":"2026-06-29T14:56:44.508292Z","iopub.status.idle":"2026-06-29T14:56:44.609812Z","shell.execute_reply.started":"2026-06-29T14:56:44.508266Z","shell.execute_reply":"2026-06-29T14:56:44.609003Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"aptos_train_df['diagnosis'].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:44.697015Z","iopub.execute_input":"2026-06-29T14:56:44.697580Z","iopub.status.idle":"2026-06-29T14:56:44.702802Z","shell.execute_reply.started":"2026-06-29T14:56:44.697558Z","shell.execute_reply":"2026-06-29T14:56:44.702162Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"aptos_train_df['diagnosis'].value_counts().plot(kind='bar')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:47.781676Z","iopub.execute_input":"2026-06-29T14:56:47.782561Z","iopub.status.idle":"2026-06-29T14:56:47.889527Z","shell.execute_reply.started":"2026-06-29T14:56:47.782528Z","shell.execute_reply":"2026-06-29T14:56:47.888979Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ddr_df[\"source\"] = \"ddr\"\naptos_train_df[\"source\"] = \"aptos\"\n\ntrain_df = pd.concat(\n    [ddr_df, aptos_train_df],\n    ignore_index=True\n)\n\nprint(train_df.shape)\nprint(train_df[\"diagnosis\"].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:47.946347Z","iopub.execute_input":"2026-06-29T14:56:47.946868Z","iopub.status.idle":"2026-06-29T14:56:47.954404Z","shell.execute_reply.started":"2026-06-29T14:56:47.946844Z","shell.execute_reply":"2026-06-29T14:56:47.953515Z"}},"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    train_df,\n    test_size=0.1,\n    stratify=train_df[\"diagnosis\"],\n    random_state=42\n)\n\nprint(train_df.shape)\nprint(val_df.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:48.085486Z","iopub.execute_input":"2026-06-29T14:56:48.085721Z","iopub.status.idle":"2026-06-29T14:56:48.100625Z","shell.execute_reply.started":"2026-06-29T14:56:48.085700Z","shell.execute_reply":"2026-06-29T14:56:48.100091Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torchvision import transforms\n\nIMG_SIZE = 384","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:48.238559Z","iopub.execute_input":"2026-06-29T14:56:48.238747Z","iopub.status.idle":"2026-06-29T14:56:48.242186Z","shell.execute_reply.started":"2026-06-29T14:56:48.238729Z","shell.execute_reply":"2026-06-29T14:56:48.241479Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transform = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(15),\n    transforms.ColorJitter(\n        brightness=0.2,\n        contrast=0.2,\n        saturation=0.2\n    ),\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((IMG_SIZE, IMG_SIZE)),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.485,0.456,0.406],\n        std=[0.229,0.224,0.225]\n    )\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:50.934628Z","iopub.execute_input":"2026-06-29T14:56:50.935422Z","iopub.status.idle":"2026-06-29T14:56:50.941921Z","shell.execute_reply.started":"2026-06-29T14:56:50.935389Z","shell.execute_reply":"2026-06-29T14:56:50.941144Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import Dataset\nfrom PIL import Image\nimport os\n\nclass DRDataset(Dataset):\n\n    def __init__(self, df, transform=None):\n        self.df = df.reset_index(drop=True)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n\n        row = self.df.iloc[idx]\n\n        if row[\"source\"] == \"ddr\":\n\n            img_path = os.path.join(\n                DDR_IMG_DIR,\n                row[\"id_code\"]\n            )\n\n        else:\n\n            img_path = os.path.join(\n                APTOS_TRAIN_IMAGES,\n                row[\"id_code\"] + \".png\"\n            )\n\n        image = Image.open(img_path).convert(\"RGB\")\n\n        label = int(row[\"diagnosis\"])\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:51.088230Z","iopub.execute_input":"2026-06-29T14:56:51.088506Z","iopub.status.idle":"2026-06-29T14:56:51.094799Z","shell.execute_reply.started":"2026-06-29T14:56:51.088483Z","shell.execute_reply":"2026-06-29T14:56:51.093870Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MessidorDataset(Dataset):\n\n    def __init__(self, df, transform=None):\n        self.df = df.reset_index(drop=True)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n\n        row = self.df.iloc[idx]\n\n        img_path = os.path.join(\n            MESSIDOR_IMG_DIR,\n            row[\"id_code\"]\n        )\n\n        image = Image.open(img_path).convert(\"RGB\")\n\n        label = int(row[\"diagnosis\"])\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:54.600345Z","iopub.execute_input":"2026-06-29T14:56:54.601092Z","iopub.status.idle":"2026-06-29T14:56:54.606812Z","shell.execute_reply.started":"2026-06-29T14:56:54.601044Z","shell.execute_reply":"2026-06-29T14:56:54.606263Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import DataLoader\n\ntrain_dataset = DRDataset(\n    train_df,\n    train_transform\n)\n\nval_dataset = DRDataset(\n    val_df,\n    val_transform\n)\n\nmessidor_dataset = MessidorDataset(\n    messidor_df,\n    val_transform\n)\n\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=32,\n    shuffle=True,\n    num_workers=4,\n    pin_memory=True\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=32,\n    shuffle=False,\n    num_workers=4\n)\n\nmessidor_loader = DataLoader(\n    messidor_dataset,\n    batch_size=32,\n    shuffle=False,\n    num_workers=4\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:54.763219Z","iopub.execute_input":"2026-06-29T14:56:54.763739Z","iopub.status.idle":"2026-06-29T14:56:54.772498Z","shell.execute_reply.started":"2026-06-29T14:56:54.763715Z","shell.execute_reply":"2026-06-29T14:56:54.771534Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df[\"diagnosis\"].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:57.747368Z","iopub.execute_input":"2026-06-29T14:56:57.748118Z","iopub.status.idle":"2026-06-29T14:56:57.756920Z","shell.execute_reply.started":"2026-06-29T14:56:57.748076Z","shell.execute_reply":"2026-06-29T14:56:57.756089Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\n\nclass_counts = train_df[\"diagnosis\"].value_counts().sort_index()\n\nweights = 1 / class_counts\n\nweights = torch.tensor(\n    weights.values,\n    dtype=torch.float32\n)\n\nweights = weights / weights.sum() * len(weights)\n\nprint(weights)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:57.925319Z","iopub.execute_input":"2026-06-29T14:56:57.925864Z","iopub.status.idle":"2026-06-29T14:56:57.935161Z","shell.execute_reply.started":"2026-06-29T14:56:57.925830Z","shell.execute_reply":"2026-06-29T14:56:57.934232Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch.nn as nn\n\ncriterion = nn.CrossEntropyLoss(\n    weight=weights.cuda()\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:56:58.309792Z","iopub.execute_input":"2026-06-29T14:56:58.310439Z","iopub.status.idle":"2026-06-29T14:56:58.315079Z","shell.execute_reply.started":"2026-06-29T14:56:58.310407Z","shell.execute_reply":"2026-06-29T14:56:58.314410Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\ndevice","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:57:00.641751Z","iopub.execute_input":"2026-06-29T14:57:00.642155Z","iopub.status.idle":"2026-06-29T14:57:00.648900Z","shell.execute_reply.started":"2026-06-29T14:57:00.642113Z","shell.execute_reply":"2026-06-29T14:57:00.648109Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#raise SystemExit()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T05:13:15.079923Z","iopub.execute_input":"2026-06-29T05:13:15.080194Z","iopub.status.idle":"2026-06-29T05:13:15.090571Z","shell.execute_reply.started":"2026-06-29T05:13:15.080174Z","shell.execute_reply":"2026-06-29T05:13:15.090023Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import torchvision.models as models\n# import torch.nn as nn\n\n# model = models.resnet50(\n#     weights=models.ResNet50_Weights.IMAGENET1K_V2\n# )\n\n# model.fc = nn.Linear(\n#     model.fc.in_features,\n#     5\n# )\n\n# model = model.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T05:13:15.091439Z","iopub.execute_input":"2026-06-29T05:13:15.091899Z","iopub.status.idle":"2026-06-29T05:13:15.104451Z","shell.execute_reply.started":"2026-06-29T05:13:15.091877Z","shell.execute_reply":"2026-06-29T05:13:15.103693Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# optimizer = torch.optim.AdamW(\n#     model.parameters(),\n#     lr=1e-4,\n#     weight_decay=1e-4\n# )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T05:13:15.105355Z","iopub.execute_input":"2026-06-29T05:13:15.105682Z","iopub.status.idle":"2026-06-29T05:13:15.117681Z","shell.execute_reply.started":"2026-06-29T05:13:15.105649Z","shell.execute_reply":"2026-06-29T05:13:15.117156Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n#     optimizer,\n#     mode='max',\n#     factor=0.5,\n#     patience=2\n# )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T05:13:15.118541Z","iopub.execute_input":"2026-06-29T05:13:15.118879Z","iopub.status.idle":"2026-06-29T05:13:15.130202Z","shell.execute_reply.started":"2026-06-29T05:13:15.118850Z","shell.execute_reply":"2026-06-29T05:13:15.129437Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm import tqdm\n\ndef train_one_epoch(model, loader, criterion, optimizer):\n\n    model.train()\n\n    running_loss = 0\n\n    for images, labels in tqdm(loader):\n\n        images = images.to(device)\n        labels = labels.to(device)\n\n        optimizer.zero_grad()\n\n        outputs = model(images)\n\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n\n        optimizer.step()\n\n        running_loss += loss.item()\n\n    return running_loss / len(loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:57:03.690403Z","iopub.execute_input":"2026-06-29T14:57:03.691220Z","iopub.status.idle":"2026-06-29T14:57:03.696306Z","shell.execute_reply.started":"2026-06-29T14:57:03.691188Z","shell.execute_reply":"2026-06-29T14:57:03.695409Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:57:03.904713Z","iopub.execute_input":"2026-06-29T14:57:03.905395Z","iopub.status.idle":"2026-06-29T14:57:03.909005Z","shell.execute_reply.started":"2026-06-29T14:57:03.905362Z","shell.execute_reply":"2026-06-29T14:57:03.908103Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import (\n    accuracy_score,\n    precision_score,\n    recall_score,\n    f1_score,\n    cohen_kappa_score,\n    confusion_matrix\n)\n\n@torch.no_grad()\ndef evaluate(model, loader):\n\n    model.eval()\n\n    preds = []\n    targets = []\n\n    for images, labels in loader:\n\n        images = images.to(device)\n\n        outputs = model(images)\n\n        pred = outputs.argmax(1)\n\n        preds.extend(pred.cpu().numpy())\n        targets.extend(labels.numpy())\n\n    accuracy = accuracy_score(targets, preds)\n\n    precision = precision_score(\n        targets,\n        preds,\n        average='macro',\n        zero_division=0\n    )\n\n    recall = recall_score(\n        targets,\n        preds,\n        average='macro',\n        zero_division=0\n    )\n\n    f1 = f1_score(\n        targets,\n        preds,\n        average='macro',\n        zero_division=0\n    )\n\n    qwk = cohen_kappa_score(\n        targets,\n        preds,\n        weights='quadratic'\n    )\n\n    cm = confusion_matrix(\n        targets,\n        preds\n    )\n\n    return (\n        accuracy,\n        precision,\n        recall,\n        f1,\n        qwk,\n        cm,\n        targets,\n        preds\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:57:08.136492Z","iopub.execute_input":"2026-06-29T14:57:08.137314Z","iopub.status.idle":"2026-06-29T14:57:08.143771Z","shell.execute_reply.started":"2026-06-29T14:57:08.137279Z","shell.execute_reply":"2026-06-29T14:57:08.142630Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# NUM_EPOCHS = 20\n\n# best_qwk = 0\n\n# for epoch in range(NUM_EPOCHS):\n\n#     train_loss = train_one_epoch(\n#         model,\n#         train_loader,\n#         criterion,\n#         optimizer\n#     )\n\n#     val_acc, val_f1, val_qwk = evaluate(\n#         model,\n#         val_loader\n#     )\n\n#     scheduler.step(val_qwk)\n\n#     print(\n#         f\"Epoch {epoch+1}/{NUM_EPOCHS}\"\n#         f\" | Loss={train_loss:.4f}\"\n#         f\" | Val Acc={val_acc:.4f}\"\n#         f\" | Val F1={val_f1:.4f}\"\n#         f\" | Val QWK={val_qwk:.4f}\"\n#     )\n\n#     if val_qwk > best_qwk:\n\n#         best_qwk = val_qwk\n\n#         torch.save(\n#             model.state_dict(),\n#             \"best_resnet50.pth\"\n#         )\n\n#         print(\"Saved Best Model\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T05:13:15.160528Z","iopub.execute_input":"2026-06-29T05:13:15.160837Z","iopub.status.idle":"2026-06-29T05:13:15.174985Z","shell.execute_reply.started":"2026-06-29T05:13:15.160803Z","shell.execute_reply":"2026-06-29T05:13:15.174447Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import torchvision.models as models\n# import torch.nn as nn\n\n# model = models.densenet121(\n#     weights=models.DenseNet121_Weights.IMAGENET1K_V1\n# )\n\n# model.classifier = nn.Linear(\n#     model.classifier.in_features,\n#     5\n# )\n\n# model = model.to(device)\n\n# print(model.classifier)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T05:13:15.175746Z","iopub.execute_input":"2026-06-29T05:13:15.176026Z","iopub.status.idle":"2026-06-29T05:13:15.189689Z","shell.execute_reply.started":"2026-06-29T05:13:15.175997Z","shell.execute_reply":"2026-06-29T05:13:15.189090Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# optimizer = torch.optim.AdamW(\n#     model.parameters(),\n#     lr=1e-4,\n#     weight_decay=1e-4\n# )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T05:13:15.190635Z","iopub.execute_input":"2026-06-29T05:13:15.190913Z","iopub.status.idle":"2026-06-29T05:13:15.206133Z","shell.execute_reply.started":"2026-06-29T05:13:15.190880Z","shell.execute_reply":"2026-06-29T05:13:15.205539Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(\n#     optimizer,\n#     mode='max',\n#     factor=0.5,\n#     patience=2\n# )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T05:13:15.206911Z","iopub.execute_input":"2026-06-29T05:13:15.207237Z","iopub.status.idle":"2026-06-29T05:13:15.220589Z","shell.execute_reply.started":"2026-06-29T05:13:15.207217Z","shell.execute_reply":"2026-06-29T05:13:15.220032Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# NUM_EPOCHS = 20\n\n# best_qwk = 0\n\n# for epoch in range(NUM_EPOCHS):\n\n#     train_loss = train_one_epoch(\n#         model,\n#         train_loader,\n#         criterion,\n#         optimizer\n#     )\n\n#     val_acc, val_f1, val_qwk = evaluate(\n#         model,\n#         val_loader\n#     )\n\n#     scheduler.step(val_qwk)\n\n#     print(\n#         f\"Epoch {epoch+1}/{NUM_EPOCHS}\"\n#         f\" | Loss={train_loss:.4f}\"\n#         f\" | Val Acc={val_acc:.4f}\"\n#         f\" | Val F1={val_f1:.4f}\"\n#         f\" | Val QWK={val_qwk:.4f}\"\n#     )\n\n#     if val_qwk > best_qwk:\n\n#         best_qwk = val_qwk\n\n#         torch.save(\n#             model.state_dict(),\n#             \"/kaggle/working/best_densenet121.pth\"\n#         )\n\n#         print(\n#             f\"Saved DenseNet121 | Best QWK = {best_qwk:.4f}\"\n#         )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T05:13:15.221593Z","iopub.execute_input":"2026-06-29T05:13:15.221861Z","iopub.status.idle":"2026-06-29T05:13:15.234393Z","shell.execute_reply.started":"2026-06-29T05:13:15.221830Z","shell.execute_reply":"2026-06-29T05:13:15.233822Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nfrom torchvision.models import swin_t, Swin_T_Weights\n\nmodel = swin_t(\n    weights=Swin_T_Weights.IMAGENET1K_V1\n)\n\nmodel.head = nn.Linear(\n    model.head.in_features,\n    5\n)\n\nmodel = model.to(device)\n\nprint(\"Model loaded successfully\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:57:13.380897Z","iopub.execute_input":"2026-06-29T14:57:13.381628Z","iopub.status.idle":"2026-06-29T14:57:13.946686Z","shell.execute_reply.started":"2026-06-29T14:57:13.381587Z","shell.execute_reply":"2026-06-29T14:57:13.945868Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"optimizer = torch.optim.AdamW(\n    model.parameters(),\n    lr=1e-4,\n    weight_decay=0.05\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:57:15.460779Z","iopub.execute_input":"2026-06-29T14:57:15.461780Z","iopub.status.idle":"2026-06-29T14:57:15.467040Z","shell.execute_reply.started":"2026-06-29T14:57:15.461738Z","shell.execute_reply":"2026-06-29T14:57:15.466312Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(\n    optimizer,\n    T_max=40\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:57:18.659280Z","iopub.execute_input":"2026-06-29T14:57:18.659564Z","iopub.status.idle":"2026-06-29T14:57:18.663686Z","shell.execute_reply.started":"2026-06-29T14:57:18.659538Z","shell.execute_reply":"2026-06-29T14:57:18.662809Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.amp import autocast, GradScaler\nfrom tqdm import tqdm\n\nscaler = GradScaler(\"cuda\")\n\ndef train_one_epoch_swin(\n    model,\n    loader,\n    criterion,\n    optimizer\n):\n\n    model.train()\n\n    running_loss = 0\n\n    for images, labels in tqdm(loader):\n\n        images = images.to(device)\n        labels = labels.to(device)\n\n        optimizer.zero_grad(set_to_none=True)\n\n        with autocast(\"cuda\"):\n\n            outputs = model(images)\n\n            loss = criterion(\n                outputs,\n                labels\n            )\n\n        scaler.scale(loss).backward()\n\n        scaler.unscale_(optimizer)\n\n        torch.nn.utils.clip_grad_norm_(\n            model.parameters(),\n            max_norm=1.0\n            )\n        \n        scaler.step(optimizer)\n\n        scaler.update()\n\n        running_loss += loss.item()\n\n    return running_loss / len(loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:57:20.973661Z","iopub.execute_input":"2026-06-29T14:57:20.974393Z","iopub.status.idle":"2026-06-29T14:57:20.980228Z","shell.execute_reply.started":"2026-06-29T14:57:20.974361Z","shell.execute_reply":"2026-06-29T14:57:20.979366Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"NUM_EPOCHS = 60\n\nbest_qwk = 0\n\npatience = 10\ncounter = 0\n\nfor epoch in range(NUM_EPOCHS):\n\n    train_loss = train_one_epoch_swin(\n        model,\n        train_loader,\n        criterion,\n        optimizer\n    )\n\n    val_acc, p, r, val_f1, val_qwk, c, t, preds = evaluate(\n        model,\n        val_loader\n    )\n    \n    scheduler.step()\n\n    print(\n        f\"Epoch {epoch+1}/{NUM_EPOCHS}\"\n        f\" | Loss={train_loss:.4f}\"\n        f\" | Val Acc={val_acc:.4f}\"\n        f\" | Val F1={val_f1:.4f}\"\n        f\" | Val QWK={val_qwk:.4f}\"\n    )\n\n    if val_qwk > best_qwk:\n\n        best_qwk = val_qwk\n        counter = 0\n\n        torch.save(\n            model.state_dict(),\n            \"/kaggle/working/best_swin_t.pth\"\n        )\n\n        print(\n            f\"Saved Swin-T | Best QWK = {best_qwk:.4f}\"\n        )\n\n    else:\n\n        counter += 1\n\n        print(\n            f\"No improvement for {counter} epoch(s)\"\n        )\n\n    if counter >= patience:\n\n        print(\n            f\"\\nEarly stopping triggered after {epoch+1} epochs\"\n        )\n\n        print(\n            f\"Best QWK = {best_qwk:.4f}\"\n        )\n\n        break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T12:27:04.035841Z","iopub.execute_input":"2026-06-29T12:27:04.036321Z","iopub.status.idle":"2026-06-29T13:39:46.252717Z","shell.execute_reply.started":"2026-06-29T12:27:04.036290Z","shell.execute_reply":"2026-06-29T13:39:46.251935Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**External validation**","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.models as models\n\nresnet50 = models.resnet50(\n    weights=None\n)\n\nresnet50.fc = nn.Linear(\n    resnet50.fc.in_features,\n    5\n)\n\nresnet50.load_state_dict(\n    torch.load(\n        \"/kaggle/input/models/pratibimbswain/resnet-v2/pytorch/default/1/best_resnet50_v2.pth\",\n        map_location=device\n    )\n)\n\nresnet50 = resnet50.to(device)\nresnet50.eval();","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:57:46.864712Z","iopub.execute_input":"2026-06-29T14:57:46.865255Z","iopub.status.idle":"2026-06-29T14:57:47.318664Z","shell.execute_reply.started":"2026-06-29T14:57:46.865223Z","shell.execute_reply":"2026-06-29T14:57:47.317768Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"(\n    acc,\n    precision,\n    recall,\n    f1,\n    qwk,\n    cm,\n    targets,\n    preds\n) = evaluate(resnet50, messidor_loader)\n\nprint(\"ResNet\")\nprint(f\"Accuracy  : {acc:.4f}\")\nprint(f\"Precision : {precision:.4f}\")\nprint(f\"Recall    : {recall:.4f}\")\nprint(f\"Macro F1  : {f1:.4f}\")\nprint(f\"QWK       : {qwk:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:57:50.407949Z","iopub.execute_input":"2026-06-29T14:57:50.408738Z","iopub.status.idle":"2026-06-29T14:58:10.274783Z","shell.execute_reply.started":"2026-06-29T14:57:50.408698Z","shell.execute_reply":"2026-06-29T14:58:10.273944Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\n\nplt.figure(figsize=(8,6))\n\nsns.heatmap(\n    cm,\n    annot=True,\n    fmt='d',\n    cmap='Blues',\n    xticklabels=[0,1,2,3,4],\n    yticklabels=[0,1,2,3,4]\n)\n\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"Actual\")\nplt.title(\"Confusion Matrix for ResNet\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:58:26.043765Z","iopub.execute_input":"2026-06-29T14:58:26.044045Z","iopub.status.idle":"2026-06-29T14:58:26.231592Z","shell.execute_reply.started":"2026-06-29T14:58:26.044014Z","shell.execute_reply":"2026-06-29T14:58:26.230968Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"densenet121 = models.densenet121(\n    weights=None\n)\n\ndensenet121.classifier = nn.Linear(\n    densenet121.classifier.in_features,\n    5\n)\n\ndensenet121.load_state_dict(\n    torch.load(\n        \"/kaggle/input/models/pratibimbswain/densenet-v2/pytorch/default/1/best_densenet121_v2.pth\",\n        map_location=device\n    )\n)\n\ndensenet121 = densenet121.to(device)\ndensenet121.eval();","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:58:29.333358Z","iopub.execute_input":"2026-06-29T14:58:29.334037Z","iopub.status.idle":"2026-06-29T14:58:29.694741Z","shell.execute_reply.started":"2026-06-29T14:58:29.334005Z","shell.execute_reply":"2026-06-29T14:58:29.694184Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"(\n    acc,\n    precision,\n    recall,\n    f1,\n    qwk,\n    cm,\n    targets,\n    preds\n) = evaluate(densenet121, messidor_loader)\n\nprint(\"DenseNet\")\nprint(f\"Accuracy  : {acc:.4f}\")\nprint(f\"Precision : {precision:.4f}\")\nprint(f\"Recall    : {recall:.4f}\")\nprint(f\"Macro F1  : {f1:.4f}\")\nprint(f\"QWK       : {qwk:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:58:29.856833Z","iopub.execute_input":"2026-06-29T14:58:29.857067Z","iopub.status.idle":"2026-06-29T14:58:46.988156Z","shell.execute_reply.started":"2026-06-29T14:58:29.857033Z","shell.execute_reply":"2026-06-29T14:58:46.987325Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\n\nplt.figure(figsize=(8,6))\n\nsns.heatmap(\n    cm,\n    annot=True,\n    fmt='d',\n    cmap='Blues',\n    xticklabels=[0,1,2,3,4],\n    yticklabels=[0,1,2,3,4]\n)\n\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"Actual\")\nplt.title(\"Confusion Matrix for DenseNet\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:58:50.926820Z","iopub.execute_input":"2026-06-29T14:58:50.927513Z","iopub.status.idle":"2026-06-29T14:58:51.108251Z","shell.execute_reply.started":"2026-06-29T14:58:50.927478Z","shell.execute_reply":"2026-06-29T14:58:51.107391Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.models as models\nswin_trans = models.swin_t(weights=None)\n\nswin_trans.head = nn.Linear(\n    swin_trans.head.in_features,\n    5\n)\n\nswin_trans.load_state_dict(\n    torch.load(\n        \"/kaggle/working/best_swin_t.pth\",\n        map_location=device\n    )\n)\n\nswin_trans = swin_trans.to(device)\nswin_trans.eval();","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:58:54.697656Z","iopub.execute_input":"2026-06-29T14:58:54.698208Z","iopub.status.idle":"2026-06-29T14:58:55.390835Z","shell.execute_reply.started":"2026-06-29T14:58:54.698178Z","shell.execute_reply":"2026-06-29T14:58:55.390009Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"(\n    acc,\n    precision,\n    recall,\n    f1,\n    qwk,\n    cm,\n    targets,\n    preds\n) = evaluate(swin_trans, messidor_loader)\n\nprint(\"Swin Transformer\")\nprint(f\"Accuracy  : {acc:.4f}\")\nprint(f\"Precision : {precision:.4f}\")\nprint(f\"Recall    : {recall:.4f}\")\nprint(f\"Macro F1  : {f1:.4f}\")\nprint(f\"QWK       : {qwk:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:58:58.687388Z","iopub.execute_input":"2026-06-29T14:58:58.688169Z","iopub.status.idle":"2026-06-29T14:59:31.637605Z","shell.execute_reply.started":"2026-06-29T14:58:58.688137Z","shell.execute_reply":"2026-06-29T14:59:31.636758Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\n\nplt.figure(figsize=(8,6))\n\nsns.heatmap(\n    cm,\n    annot=True,\n    fmt='d',\n    cmap='Blues',\n    xticklabels=[0,1,2,3,4],\n    yticklabels=[0,1,2,3,4]\n)\n\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"Actual\")\nplt.title(\"Confusion Matrix for swin tarnsformer\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:59:40.878945Z","iopub.execute_input":"2026-06-29T14:59:40.879733Z","iopub.status.idle":"2026-06-29T14:59:41.071334Z","shell.execute_reply.started":"2026-06-29T14:59:40.879698Z","shell.execute_reply":"2026-06-29T14:59:41.070438Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**ROC-AUC**","metadata":{}},{"cell_type":"code","source":"from sklearn.preprocessing import label_binarize\nfrom sklearn.metrics import roc_auc_score\n\n@torch.no_grad()\ndef evaluate_auc(model, loader):\n\n    model.eval()\n\n    all_probs = []\n    all_targets = []\n\n    for images, labels in loader:\n\n        images = images.to(device)\n\n        outputs = model(images)\n\n        probs = torch.softmax(outputs, dim=1)\n\n        all_probs.append(probs.cpu().numpy())\n        all_targets.append(labels.numpy())\n\n    all_probs = np.concatenate(all_probs)\n    all_targets = np.concatenate(all_targets)\n\n    return all_targets, all_probs","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:59:46.918277Z","iopub.execute_input":"2026-06-29T14:59:46.919079Z","iopub.status.idle":"2026-06-29T14:59:46.925078Z","shell.execute_reply.started":"2026-06-29T14:59:46.919034Z","shell.execute_reply":"2026-06-29T14:59:46.924311Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"models_for_auc=[resnet50, densenet121, swin_trans]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:59:47.993711Z","iopub.execute_input":"2026-06-29T14:59:47.993954Z","iopub.status.idle":"2026-06-29T14:59:47.997709Z","shell.execute_reply.started":"2026-06-29T14:59:47.993933Z","shell.execute_reply":"2026-06-29T14:59:47.996977Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cnt=0\nfor model in models_for_auc:\n    targets, probs = evaluate_auc(\n        model,\n        messidor_loader\n    )\n    \n    targets_bin = label_binarize(\n        targets,\n        classes=[0,1,2,3,4]\n    )\n    \n    auc_per_class = {}\n\n    if cnt==0:\n        print(\"ResNet50\")\n    if cnt==1:\n        print(\"DenseNet121\")\n    elif cnt==2:\n        print(\"Swin Transformer\")\n        \n    cnt+=1\n    for i in range(5):\n    \n        auc = roc_auc_score(\n            targets_bin[:, i],\n            probs[:, i]\n        )\n    \n        auc_per_class[i] = auc\n\n        print(f\"Class {i} AUC = {auc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:59:50.814840Z","iopub.execute_input":"2026-06-29T14:59:50.815104Z","iopub.status.idle":"2026-06-29T15:00:56.116196Z","shell.execute_reply.started":"2026-06-29T14:59:50.815080Z","shell.execute_reply":"2026-06-29T15:00:56.115169Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"classes = [\"Class 0\", \"Class 1\", \"Class 2\", \"Class 3\", \"Class 4\"]\n\nresnet_auc = [\n    0.8491,\n    0.6915,\n    0.8483,\n    0.9614,\n    0.8109\n]\n\ndensenet_auc = [\n    0.8548,\n    0.6787,\n    0.8553,\n    0.9262,\n    0.8898\n]\n\nswin_auc = [\n    0.8462,\n    0.6689,\n    0.8451,\n    0.9823,\n    0.9770\n]\n\nx = np.arange(len(classes))\nwidth = 0.25\n\nplt.figure(figsize=(10, 6))\n\nplt.bar(\n    x - width,\n    resnet_auc,\n    width,\n    label=\"ResNet50\"\n)\n\nplt.bar(\n    x,\n    densenet_auc,\n    width,\n    label=\"DenseNet121\"\n)\n\nplt.bar(\n    x + width,\n    swin_auc,\n    width,\n    label=\"Swin Transformer\"\n)\n\nplt.xticks(x, classes)\nplt.ylabel(\"ROC-AUC\")\nplt.xlabel(\"DR Grade\")\nplt.title(\"One-vs-Rest ROC-AUC Comparison\")\n\nplt.ylim(0.5, 1.0)\n\nplt.legend()\n\nplt.grid(axis=\"y\", linestyle=\"--\", alpha=0.5)\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T15:01:13.797547Z","iopub.execute_input":"2026-06-29T15:01:13.798216Z","iopub.status.idle":"2026-06-29T15:01:13.958948Z","shell.execute_reply.started":"2026-06-29T15:01:13.798171Z","shell.execute_reply":"2026-06-29T15:01:13.958171Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\n\nlabels = ['0', '1', '2', '3', '4']\n\nresnet = [0.8491, 0.6915, 0.8483, 0.9614, 0.8109]\ndensenet = [0.8548, 0.6787, 0.8553, 0.9262, 0.8898]\nswin = [0.8462, 0.6689, 0.8451, 0.9823, 0.9770]\n\nangles = np.linspace(\n    0,\n    2*np.pi,\n    len(labels),\n    endpoint=False\n).tolist()\n\nangles += angles[:1]\n\nresnet += resnet[:1]\ndensenet += densenet[:1]\nswin += swin[:1]\n\nplt.figure(figsize=(8,8))\n\nax = plt.subplot(111, polar=True)\n\nax.plot(angles, resnet, linewidth=2, label='ResNet50')\nax.fill(angles, resnet, alpha=0.1)\n\nax.plot(angles, densenet, linewidth=2, label='DenseNet121')\nax.fill(angles, densenet, alpha=0.1)\n\nax.plot(angles, swin, linewidth=2, label='Swin Transformer')\nax.fill(angles, swin, alpha=0.1)\n\nax.set_xticks(angles[:-1])\nax.set_xticklabels(labels)\n\nax.set_title(\n    \"Per-Class ROC-AUC Comparison\"\n)\n\nax.legend(\n    loc=\"upper right\",\n    bbox_to_anchor=(1.3, 1.1)\n)\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T15:01:32.067874Z","iopub.execute_input":"2026-06-29T15:01:32.068658Z","iopub.status.idle":"2026-06-29T15:01:32.267426Z","shell.execute_reply.started":"2026-06-29T15:01:32.068628Z","shell.execute_reply":"2026-06-29T15:01:32.266602Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"models_to_test = {\n    \"ResNet50\": resnet50,\n    \"DenseNet121\": densenet121,\n    \"Swin-T\": swin_trans\n}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T15:01:35.686701Z","iopub.execute_input":"2026-06-29T15:01:35.687395Z","iopub.status.idle":"2026-06-29T15:01:35.692252Z","shell.execute_reply.started":"2026-06-29T15:01:35.687362Z","shell.execute_reply":"2026-06-29T15:01:35.691393Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for name, model in models_to_test.items():\n\n    targets, probs = evaluate_auc(\n        model,\n        messidor_loader\n    )\n\n    targets_bin = label_binarize(\n        targets,\n        classes=[0,1,2,3,4]\n    )\n\n    macro_auc = roc_auc_score(\n        targets_bin,\n        probs,\n        average=\"macro\",\n        multi_class=\"ovr\"\n    )\n\n    print(\n        f\"{name}: Macro ROC-AUC = {macro_auc:.4f}\"\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T15:01:36.019269Z","iopub.execute_input":"2026-06-29T15:01:36.019978Z","iopub.status.idle":"2026-06-29T15:02:41.413589Z","shell.execute_reply.started":"2026-06-29T15:01:36.019934Z","shell.execute_reply":"2026-06-29T15:02:41.412728Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**GradCam**","metadata":{}},{"cell_type":"code","source":"resnet50.layer4[-1].conv3","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:01:45.132512Z","iopub.execute_input":"2026-06-29T14:01:45.133316Z","iopub.status.idle":"2026-06-29T14:01:45.138920Z","shell.execute_reply.started":"2026-06-29T14:01:45.133276Z","shell.execute_reply":"2026-06-29T14:01:45.138312Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install grad-cam -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:01:46.482391Z","iopub.execute_input":"2026-06-29T14:01:46.483118Z","iopub.status.idle":"2026-06-29T14:01:57.891228Z","shell.execute_reply.started":"2026-06-29T14:01:46.483085Z","shell.execute_reply":"2026-06-29T14:01:57.890225Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\n\nfrom pytorch_grad_cam import GradCAM\nfrom pytorch_grad_cam.utils.image import show_cam_on_image\nfrom pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:01:57.893252Z","iopub.execute_input":"2026-06-29T14:01:57.893495Z","iopub.status.idle":"2026-06-29T14:01:58.496249Z","shell.execute_reply.started":"2026-06-29T14:01:57.893467Z","shell.execute_reply":"2026-06-29T14:01:58.495297Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"target_layers = [resnet50.layer4[-1].conv3]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:02:05.013891Z","iopub.execute_input":"2026-06-29T14:02:05.015168Z","iopub.status.idle":"2026-06-29T14:02:05.019405Z","shell.execute_reply.started":"2026-06-29T14:02:05.015133Z","shell.execute_reply":"2026-06-29T14:02:05.018484Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cam = GradCAM(\n    model=resnet50,\n    target_layers=target_layers\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:02:06.196971Z","iopub.execute_input":"2026-06-29T14:02:06.197713Z","iopub.status.idle":"2026-06-29T14:02:06.202633Z","shell.execute_reply.started":"2026-06-29T14:02:06.197680Z","shell.execute_reply":"2026-06-29T14:02:06.201982Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torchvision.transforms as transforms\n\ntransform = transforms.Compose([\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],\n        std=[0.229, 0.224, 0.225]\n    )\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:02:12.930148Z","iopub.execute_input":"2026-06-29T14:02:12.930833Z","iopub.status.idle":"2026-06-29T14:02:12.935204Z","shell.execute_reply.started":"2026-06-29T14:02:12.930800Z","shell.execute_reply":"2026-06-29T14:02:12.934338Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for idx in range(5):\n\n    img_name = messidor_df.iloc[idx][\"id_code\"]\n\n    img_path = (\n        MESSIDOR_IMG_DIR + \"/\"\n        + img_name\n    )\n\n    # Load image\n    image = cv2.imread(img_path)\n\n    image = cv2.cvtColor(\n        image,\n        cv2.COLOR_BGR2RGB\n    )\n\n    image = cv2.resize(\n        image,\n        (224, 224)\n    )\n\n    rgb_img = image.astype(np.float32) / 255.0\n\n    # Preprocess\n    input_tensor = transform(image).unsqueeze(0).to(device)\n\n    # Prediction\n    with torch.no_grad():\n\n        output = resnet50(input_tensor)\n\n        probs = torch.softmax(\n            output,\n            dim=1\n        )\n\n        pred_class = output.argmax(1).item()\n\n        confidence = probs[0, pred_class].item()\n\n    # Grad-CAM\n    targets = [\n        ClassifierOutputTarget(pred_class)\n    ]\n\n    grayscale_cam = cam(\n        input_tensor=input_tensor,\n        targets=targets\n    )[0]\n\n    visualization = show_cam_on_image(\n        rgb_img,\n        grayscale_cam,\n        use_rgb=True\n    )\n\n    true_label = messidor_df.iloc[idx][\"diagnosis\"]\n\n    # Plot\n    plt.figure(figsize=(10, 4))\n\n    plt.subplot(1, 2, 1)\n    plt.imshow(rgb_img)\n    print(\"ResNet\")\n    plt.title(\n        f\"Original\\nTrue={true_label}\"\n    )\n    plt.axis(\"off\")\n\n    plt.subplot(1, 2, 2)\n    plt.imshow(visualization)\n    plt.title(\n        f\"GradCAM\\nPred={pred_class} ({confidence:.2%})\"\n    )\n    plt.axis(\"off\")\n\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:03:13.682512Z","iopub.execute_input":"2026-06-29T14:03:13.682804Z","iopub.status.idle":"2026-06-29T14:03:15.011653Z","shell.execute_reply.started":"2026-06-29T14:03:13.682779Z","shell.execute_reply":"2026-06-29T14:03:15.010974Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"target_layers = [swin_trans.features[-1]]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:03:20.313878Z","iopub.execute_input":"2026-06-29T14:03:20.314238Z","iopub.status.idle":"2026-06-29T14:03:20.318345Z","shell.execute_reply.started":"2026-06-29T14:03:20.314207Z","shell.execute_reply":"2026-06-29T14:03:20.317669Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def reshape_transform(tensor):\n\n    # tensor shape:\n    # [B, H, W, C]\n\n    result = tensor.permute(\n        0,\n        3,\n        1,\n        2\n    )\n\n    return result","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:03:21.671672Z","iopub.execute_input":"2026-06-29T14:03:21.671937Z","iopub.status.idle":"2026-06-29T14:03:21.675881Z","shell.execute_reply.started":"2026-06-29T14:03:21.671914Z","shell.execute_reply":"2026-06-29T14:03:21.675310Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cam = GradCAM(\n    model=swin_trans,\n    target_layers=target_layers,\n    reshape_transform=reshape_transform\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:03:25.456709Z","iopub.execute_input":"2026-06-29T14:03:25.456974Z","iopub.status.idle":"2026-06-29T14:03:25.462842Z","shell.execute_reply.started":"2026-06-29T14:03:25.456952Z","shell.execute_reply":"2026-06-29T14:03:25.462099Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"img_name = messidor_df.iloc[0][\"id_code\"]\n\nimg_path = (\n    MESSIDOR_IMG_DIR +\n    \"/\" +\n    img_name\n)\n\nimage = cv2.imread(img_path)\n\nimage = cv2.cvtColor(\n    image,\n    cv2.COLOR_BGR2RGB\n)\n\nimage = cv2.resize(\n    image,\n    (224,224)\n)\n\nrgb_img = image.astype(np.float32) / 255.0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:03:28.222954Z","iopub.execute_input":"2026-06-29T14:03:28.223686Z","iopub.status.idle":"2026-06-29T14:03:28.239895Z","shell.execute_reply.started":"2026-06-29T14:03:28.223652Z","shell.execute_reply":"2026-06-29T14:03:28.239046Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"input_tensor = (\n    transform(image)\n    .unsqueeze(0)\n    .to(device)\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:03:30.637147Z","iopub.execute_input":"2026-06-29T14:03:30.637880Z","iopub.status.idle":"2026-06-29T14:03:30.643262Z","shell.execute_reply.started":"2026-06-29T14:03:30.637845Z","shell.execute_reply":"2026-06-29T14:03:30.642517Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with torch.no_grad():\n\n    output = swin_trans(input_tensor)\n\n    probs = torch.softmax(\n        output,\n        dim=1\n    )\n\n    pred_class = output.argmax(1).item()\n\n    confidence = probs[\n        0,\n        pred_class\n    ].item()\n\nprint(\n    pred_class,\n    confidence\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:03:31.593113Z","iopub.execute_input":"2026-06-29T14:03:31.593420Z","iopub.status.idle":"2026-06-29T14:03:31.620312Z","shell.execute_reply.started":"2026-06-29T14:03:31.593392Z","shell.execute_reply":"2026-06-29T14:03:31.619443Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"targets = [\n    ClassifierOutputTarget(\n        pred_class\n    )\n]\n\ngrayscale_cam = cam(\n    input_tensor=input_tensor,\n    targets=targets\n)\n\ngrayscale_cam = grayscale_cam[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:03:34.791093Z","iopub.execute_input":"2026-06-29T14:03:34.791888Z","iopub.status.idle":"2026-06-29T14:03:34.841822Z","shell.execute_reply.started":"2026-06-29T14:03:34.791853Z","shell.execute_reply":"2026-06-29T14:03:34.840984Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualization = show_cam_on_image(\n    rgb_img,\n    grayscale_cam,\n    use_rgb=True\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:03:36.392235Z","iopub.execute_input":"2026-06-29T14:03:36.392868Z","iopub.status.idle":"2026-06-29T14:03:36.401023Z","shell.execute_reply.started":"2026-06-29T14:03:36.392837Z","shell.execute_reply":"2026-06-29T14:03:36.400311Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(12,6))\n\nplt.subplot(1,2,1)\nplt.imshow(rgb_img)\nplt.title(\"Original\")\nplt.axis(\"off\")\n\nplt.subplot(1,2,2)\nplt.imshow(visualization)\nplt.title(\n    f\"Swin-T GradCAM\\nPred={pred_class} ({confidence:.2%})\"\n)\nplt.axis(\"off\")\n\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:03:40.202371Z","iopub.execute_input":"2026-06-29T14:03:40.203238Z","iopub.status.idle":"2026-06-29T14:03:40.481049Z","shell.execute_reply.started":"2026-06-29T14:03:40.203201Z","shell.execute_reply":"2026-06-29T14:03:40.480588Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"target_layers = [swin_trans.features[-1]]\n\ncam = GradCAM(\n    model=swin_trans,\n    target_layers=target_layers,\n    reshape_transform=reshape_transform\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:04:04.997076Z","iopub.execute_input":"2026-06-29T14:04:04.997984Z","iopub.status.idle":"2026-06-29T14:04:05.004977Z","shell.execute_reply.started":"2026-06-29T14:04:04.997947Z","shell.execute_reply":"2026-06-29T14:04:05.004326Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for idx in range(50):\n\n    # -----------------------------\n    # Load image\n    # -----------------------------\n    img_name = messidor_df.iloc[idx][\"id_code\"]\n\n    img_path = (\n        MESSIDOR_IMG_DIR +\n        \"/\" +\n        img_name\n    )\n\n    image = cv2.imread(img_path)\n\n    image = cv2.cvtColor(\n        image,\n        cv2.COLOR_BGR2RGB\n    )\n\n    image = cv2.resize(\n        image,\n        (224, 224)\n    )\n\n    rgb_img = image.astype(np.float32) / 255.0\n\n    true_label = int(\n        messidor_df.iloc[idx][\"diagnosis\"]\n    )\n\n    # -----------------------------\n    # Preprocess\n    # -----------------------------\n    input_tensor = (\n        transform(image)\n        .unsqueeze(0)\n        .to(device)\n    )\n\n    # -----------------------------\n    # Predict\n    # -----------------------------\n    with torch.no_grad():\n\n        output = swin_trans(\n            input_tensor\n        )\n\n        probs = torch.softmax(\n            output,\n            dim=1\n        )\n\n        pred_class = output.argmax(\n            dim=1\n        ).item()\n\n        confidence = probs[\n            0,\n            pred_class\n        ].item()\n\n    # -----------------------------\n    # Grad-CAM\n    # -----------------------------\n    targets = [\n        ClassifierOutputTarget(\n            pred_class\n        )\n    ]\n\n    grayscale_cam = cam(\n        input_tensor=input_tensor,\n        targets=targets\n    )\n\n    grayscale_cam = grayscale_cam[0]\n\n    # -----------------------------\n    # Overlay heatmap\n    # -----------------------------\n    visualization = show_cam_on_image(\n        rgb_img,\n        grayscale_cam,\n        use_rgb=True\n    )\n\n    # -----------------------------\n    # Plot\n    # -----------------------------\n    plt.figure(figsize=(12, 5))\n\n    plt.subplot(1, 2, 1)\n\n    plt.imshow(rgb_img)\n\n    plt.title(\n        f\"Original\\nTrue={true_label}\"\n    )\n\n    plt.axis(\"off\")\n\n    plt.subplot(1, 2, 2)\n\n    plt.imshow(visualization)\n\n    plt.title(\n        f\"Swin-T GradCAM\\nPred={pred_class} ({confidence:.2%})\"\n    )\n\n    plt.axis(\"off\")\n\n    plt.tight_layout()\n\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-06-29T14:05:14.961530Z","iopub.execute_input":"2026-06-29T14:05:14.962461Z","iopub.status.idle":"2026-06-29T14:05:33.566115Z","shell.execute_reply.started":"2026-06-29T14:05:14.962425Z","shell.execute_reply":"2026-06-29T14:05:33.565252Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}