{"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":"gpu","dataSources":[{"sourceId":14774,"databundleVersionId":875431,"sourceType":"competition"}],"dockerImageVersionId":31260,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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\nimport os\nfor 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","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install -q timm\n\nimport os\nimport numpy as np\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport timm\nfrom PIL import Image\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import accuracy_score, classification_report\nfrom tqdm import tqdm\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-30T13:58:24.313519Z","iopub.execute_input":"2026-01-30T13:58:24.313802Z","iopub.status.idle":"2026-01-30T13:58:39.820762Z","shell.execute_reply.started":"2026-01-30T13:58:24.313776Z","shell.execute_reply":"2026-01-30T13:58:39.820179Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"CSV_PATH = \"/kaggle/input/aptos2019-blindness-detection/train.csv\"\nIMG_DIR  = \"/kaggle/input/aptos2019-blindness-detection/train_images\"\n\nprint(\"CSV exists:\", os.path.exists(CSV_PATH))\nprint(\"IMG DIR exists:\", os.path.exists(IMG_DIR))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-30T13:58:39.822078Z","iopub.execute_input":"2026-01-30T13:58:39.822606Z","iopub.status.idle":"2026-01-30T13:58:39.832678Z","shell.execute_reply.started":"2026-01-30T13:58:39.822568Z","shell.execute_reply":"2026-01-30T13:58:39.832155Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(CSV_PATH)\nprint(df.head())\nprint(\"\\nClass distribution:\")\nprint(df[\"diagnosis\"].value_counts())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-30T13:58:39.833460Z","iopub.execute_input":"2026-01-30T13:58:39.833711Z","iopub.status.idle":"2026-01-30T13:58:39.902076Z","shell.execute_reply.started":"2026-01-30T13:58:39.833690Z","shell.execute_reply":"2026-01-30T13:58:39.901294Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df, temp_df = train_test_split(\n    df, test_size=0.30, stratify=df[\"diagnosis\"], random_state=42\n)\n\nval_df, test_df = train_test_split(\n    temp_df, test_size=0.50, stratify=temp_df[\"diagnosis\"], random_state=42\n)\n\nprint(\"Train:\", len(train_df))\nprint(\"Val  :\", len(val_df))\nprint(\"Test :\", len(test_df))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-30T13:58:39.904033Z","iopub.execute_input":"2026-01-30T13:58:39.904306Z","iopub.status.idle":"2026-01-30T13:58:39.918516Z","shell.execute_reply.started":"2026-01-30T13:58:39.904285Z","shell.execute_reply":"2026-01-30T13:58:39.917953Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_tfms = transforms.Compose([\n    transforms.Resize((224,224)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(20),\n    transforms.ColorJitter(0.2,0.2,0.2),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485,0.456,0.406],\n                         [0.229,0.224,0.225])\n])\n\ntest_tfms = transforms.Compose([\n    transforms.Resize((224,224)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485,0.456,0.406],\n                         [0.229,0.224,0.225])\n])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-30T13:58:39.919238Z","iopub.execute_input":"2026-01-30T13:58:39.919497Z","iopub.status.idle":"2026-01-30T13:58:39.924377Z","shell.execute_reply.started":"2026-01-30T13:58:39.919465Z","shell.execute_reply":"2026-01-30T13:58:39.923709Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class APTOSDataset(Dataset):\n    def __init__(self, df, img_dir, tfms):\n        self.df = df.reset_index(drop=True)\n        self.img_dir = img_dir\n        self.tfms = tfms\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        img_id = self.df.loc[idx, \"id_code\"]\n        label  = self.df.loc[idx, \"diagnosis\"]\n\n        img_path = os.path.join(self.img_dir, img_id + \".png\")\n        image = Image.open(img_path).convert(\"RGB\")\n        image = self.tfms(image)\n\n        return image, label\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-30T13:58:39.925249Z","iopub.execute_input":"2026-01-30T13:58:39.925426Z","iopub.status.idle":"2026-01-30T13:58:39.943372Z","shell.execute_reply.started":"2026-01-30T13:58:39.925408Z","shell.execute_reply":"2026-01-30T13:58:39.942700Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loader = DataLoader(\n    APTOSDataset(train_df, IMG_DIR, train_tfms),\n    batch_size=16, shuffle=True, num_workers=2\n)\n\nval_loader = DataLoader(\n    APTOSDataset(val_df, IMG_DIR, test_tfms),\n    batch_size=16, num_workers=2\n)\n\ntest_loader = DataLoader(\n    APTOSDataset(test_df, IMG_DIR, test_tfms),\n    batch_size=16, num_workers=2\n)\n\nprint(\"✅ DataLoaders ready\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-30T13:58:39.944221Z","iopub.execute_input":"2026-01-30T13:58:39.944514Z","iopub.status.idle":"2026-01-30T13:58:39.956332Z","shell.execute_reply.started":"2026-01-30T13:58:39.944493Z","shell.execute_reply":"2026-01-30T13:58:39.955573Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CNNTransformer(nn.Module):\n    def __init__(self, num_classes=5):\n        super().__init__()\n\n        self.cnn = timm.create_model(\n            \"efficientnetv2_rw_s\",\n            pretrained=True,\n            num_classes=0\n        )\n\n        dim = self.cnn.num_features\n\n        encoder = nn.TransformerEncoderLayer(\n            d_model=dim, nhead=8, batch_first=True\n        )\n        self.transformer = nn.TransformerEncoder(encoder, num_layers=2)\n\n        self.fc = nn.Linear(dim, num_classes)\n\n    def forward(self, x):\n        x = self.cnn(x)\n        x = x.unsqueeze(1)\n        x = self.transformer(x)\n        x = x.mean(dim=1)\n        return self.fc(x)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-30T13:58:39.957086Z","iopub.execute_input":"2026-01-30T13:58:39.957400Z","iopub.status.idle":"2026-01-30T13:58:39.968892Z","shell.execute_reply.started":"2026-01-30T13:58:39.957379Z","shell.execute_reply":"2026-01-30T13:58:39.968196Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nprint(\"Using device:\", device)\n\nclass_counts = train_df[\"diagnosis\"].value_counts().sort_index().values\nweights = torch.tensor(1.0 / class_counts, dtype=torch.float)\nweights = (weights / weights.sum()).to(device)\n\ncriterion = nn.CrossEntropyLoss(\n    weight=weights,\n    label_smoothing=0.1\n)\n\nmodel = CNNTransformer().to(device)\noptimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-30T13:58:39.969828Z","iopub.execute_input":"2026-01-30T13:58:39.970374Z","iopub.status.idle":"2026-01-30T13:58:45.238890Z","shell.execute_reply.started":"2026-01-30T13:58:39.970340Z","shell.execute_reply":"2026-01-30T13:58:45.238099Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def run_epoch(loader, train=True):\n    model.train() if train else model.eval()\n    preds, labels_all = [], []\n\n    with torch.set_grad_enabled(train):\n        for imgs, labels in tqdm(loader):\n            imgs, labels = imgs.to(device), labels.to(device)\n\n            if train:\n                optimizer.zero_grad()\n\n            outputs = model(imgs)\n            loss = criterion(outputs, labels)\n\n            if train:\n                loss.backward()\n                optimizer.step()\n\n            preds.extend(outputs.argmax(1).detach().cpu().numpy())\n            labels_all.extend(labels.detach().cpu().numpy())\n\n    return labels_all, preds\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-30T13:58:45.243479Z","iopub.execute_input":"2026-01-30T13:58:45.243786Z","iopub.status.idle":"2026-01-30T13:58:45.251284Z","shell.execute_reply.started":"2026-01-30T13:58:45.243755Z","shell.execute_reply":"2026-01-30T13:58:45.250654Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 10\n\nfor epoch in range(EPOCHS):\n    print(f\"\\nEpoch {epoch+1}/{EPOCHS}\")\n    run_epoch(train_loader, train=True)\n\n    y_true, y_pred = run_epoch(val_loader, train=False)\n    print(\"Validation Accuracy:\", accuracy_score(y_true, y_pred))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-30T13:58:45.252171Z","iopub.execute_input":"2026-01-30T13:58:45.252504Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_true, y_pred = run_epoch(test_loader, train=False)\n\nprint(\"Test Accuracy:\", accuracy_score(y_true, y_pred))\nprint(classification_report(y_true, y_pred))\n","metadata":{"trusted":true},"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    confusion_matrix,\n    classification_report\n)\nimport numpy as np\n\nmodel.eval()\n\ny_true, y_pred = [], []\n\nwith torch.no_grad():\n    for images, labels in test_loader:\n        images = images.to(device)\n        outputs = model(images)\n        preds = outputs.argmax(1).cpu().numpy()\n\n        y_pred.extend(preds)\n        y_true.extend(labels.numpy())\n\ny_true = np.array(y_true)\ny_pred = np.array(y_pred)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"acc = accuracy_score(y_true, y_pred)\nprec_macro = precision_score(y_true, y_pred, average=\"macro\")\nrec_macro  = recall_score(y_true, y_pred, average=\"macro\")\nf1_macro   = f1_score(y_true, y_pred, average=\"macro\")\n\nprec_weighted = precision_score(y_true, y_pred, average=\"weighted\")\nrec_weighted  = recall_score(y_true, y_pred, average=\"weighted\")\nf1_weighted   = f1_score(y_true, y_pred, average=\"weighted\")\n\nprint(\"Accuracy:\", acc)\nprint(\"Precision (Macro):\", prec_macro)\nprint(\"Recall (Macro):\", rec_macro)\nprint(\"F1-score (Macro):\", f1_macro)\n\nprint(\"\\nPrecision (Weighted):\", prec_weighted)\nprint(\"Recall (Weighted):\", rec_weighted)\nprint(\"F1-score (Weighted):\", f1_weighted)\n\nprint(\"\\nClassification Report:\\n\")\nprint(classification_report(y_true, y_pred))\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\n\ncm = confusion_matrix(y_true, y_pred)\n\nplt.figure(figsize=(7,6))\nsns.heatmap(\n    cm,\n    annot=True,\n    fmt=\"d\",\n    cmap=\"Blues\",\n    xticklabels=[\"No DR\",\"Mild\",\"Moderate\",\"Severe\",\"Proliferative\"],\n    yticklabels=[\"No DR\",\"Mild\",\"Moderate\",\"Severe\",\"Proliferative\"]\n)\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"Actual\")\nplt.title(\"Confusion Matrix (APTOS 2019 – Multi-Class)\")\nplt.show()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import f1_score\n\nclass_f1 = f1_score(y_true, y_pred, average=None)\n\nclasses = [\"No DR\",\"Mild\",\"Moderate\",\"Severe\",\"Proliferative\"]\n\nplt.figure(figsize=(8,5))\nplt.bar(classes, class_f1)\nplt.ylim(0,1)\nplt.ylabel(\"F1-score\")\nplt.title(\"Per-Class F1 Scores\")\nplt.show()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_accs = []\nval_accs = []\n\nfor epoch in range(EPOCHS):\n    print(f\"\\nEpoch {epoch+1}/{EPOCHS}\")\n\n    run_epoch(train_loader, train=True)\n\n    y_true_tr, y_pred_tr = run_epoch(train_loader, train=False)\n    y_true_v, y_pred_v = run_epoch(val_loader, train=False)\n\n    train_acc = accuracy_score(y_true_tr, y_pred_tr)\n    val_acc = accuracy_score(y_true_v, y_pred_v)\n\n    train_accs.append(train_acc)\n    val_accs.append(val_acc)\n\n    print(\"Train Acc:\", train_acc)\n    print(\"Val Acc  :\", val_acc)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(7,5))\nplt.plot(train_accs, label=\"Training Accuracy\")\nplt.plot(val_accs, label=\"Validation Accuracy\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.title(\"Training vs Validation Accuracy\")\nplt.legend()\nplt.grid()\nplt.show()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}