{"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,"isSourceIdPinned":false}],"dockerImageVersionId":31287,"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,"execution":{"iopub.status.busy":"2026-03-14T11:55:32.892011Z","iopub.execute_input":"2026-03-14T11:55:32.892636Z","iopub.status.idle":"2026-03-14T11:55:37.504493Z","shell.execute_reply.started":"2026-03-14T11:55:32.892597Z","shell.execute_reply":"2026-03-14T11:55:37.503724Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install timm albumentations --quiet","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:27.898438Z","iopub.execute_input":"2026-03-14T11:55:27.898706Z","iopub.status.idle":"2026-03-14T11:55:32.890233Z","shell.execute_reply.started":"2026-03-14T11:55:27.898681Z","shell.execute_reply":"2026-03-14T11:55:32.889197Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import (\n    accuracy_score, precision_score, recall_score,\n    f1_score, confusion_matrix, classification_report,\n    cohen_kappa_score, roc_auc_score\n)\nfrom sklearn.preprocessing import label_binarize\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\n\nimport timm\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nfrom PIL import Image\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:37.505389Z","iopub.execute_input":"2026-03-14T11:55:37.505853Z","iopub.status.idle":"2026-03-14T11:55:51.184759Z","shell.execute_reply.started":"2026-03-14T11:55:37.505829Z","shell.execute_reply":"2026-03-14T11:55:51.184146Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_DIR = \"/kaggle/input/competitions/aptos2019-blindness-detection\"\nIMG_DIR  = os.path.join(DATA_DIR, \"train_images\")\nCSV_PATH = os.path.join(DATA_DIR, \"train.csv\")\n\ndf = pd.read_csv(CSV_PATH)\ndf[\"image_path\"] = df[\"id_code\"].apply(lambda x: os.path.join(IMG_DIR, x + \".png\"))\n\nprint(\"Total images:\", len(df))\nprint(df[\"diagnosis\"].value_counts().sort_index())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:51.186303Z","iopub.execute_input":"2026-03-14T11:55:51.186669Z","iopub.status.idle":"2026-03-14T11:55:51.226757Z","shell.execute_reply.started":"2026-03-14T11:55:51.186646Z","shell.execute_reply":"2026-03-14T11:55:51.225836Z"}},"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))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:51.227964Z","iopub.execute_input":"2026-03-14T11:55:51.228831Z","iopub.status.idle":"2026-03-14T11:55:51.244321Z","shell.execute_reply.started":"2026-03-14T11:55:51.228802Z","shell.execute_reply":"2026-03-14T11:55:51.243453Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_tfms = A.Compose([\n    A.RandomResizedCrop(size=(224, 224), scale=(0.8, 1.0), p=1.0),\n    A.HorizontalFlip(p=0.5),\n    A.VerticalFlip(p=0.5),\n    A.Rotate(limit=30, p=0.5),\n    A.RandomBrightnessContrast(p=0.4),\n    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),\n    ToTensorV2()\n])\n\nval_tfms = A.Compose([\n    A.Resize(224, 224),\n    A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),\n    ToTensorV2()\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:51.245200Z","iopub.execute_input":"2026-03-14T11:55:51.245511Z","iopub.status.idle":"2026-03-14T11:55:51.256871Z","shell.execute_reply.started":"2026-03-14T11:55:51.245468Z","shell.execute_reply":"2026-03-14T11:55:51.256044Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class APTOSDataset(Dataset):\n    def __init__(self, df, transform):\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        img = Image.open(self.df.loc[idx, \"image_path\"]).convert(\"RGB\")\n        label = int(self.df.loc[idx, \"diagnosis\"])\n        img = self.transform(image=np.array(img))[\"image\"]\n        return img, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:51.257707Z","iopub.execute_input":"2026-03-14T11:55:51.257981Z","iopub.status.idle":"2026-03-14T11:55:51.270875Z","shell.execute_reply.started":"2026-03-14T11:55:51.257953Z","shell.execute_reply":"2026-03-14T11:55:51.270079Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_ds = APTOSDataset(train_df, train_tfms)\nval_ds   = APTOSDataset(val_df,   val_tfms)\ntest_ds  = APTOSDataset(test_df,  val_tfms)\n\ntrain_loader = DataLoader(train_ds, batch_size=16, shuffle=True,  num_workers=2)\nval_loader   = DataLoader(val_ds,   batch_size=16, shuffle=False, num_workers=2)\ntest_loader  = DataLoader(test_ds,  batch_size=16, shuffle=False, num_workers=2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:51.271941Z","iopub.execute_input":"2026-03-14T11:55:51.272489Z","iopub.status.idle":"2026-03-14T11:55:51.284154Z","shell.execute_reply.started":"2026-03-14T11:55:51.272467Z","shell.execute_reply":"2026-03-14T11:55:51.283274Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class EfficientNet_ViT(nn.Module):\n    def __init__(self, num_classes=5):\n        super().__init__()\n\n        self.cnn = timm.create_model(\n            \"efficientnet_b4\",\n            pretrained=True,\n            num_classes=0\n        )\n        cnn_dim = self.cnn.num_features\n\n        self.vit = timm.create_model(\n            \"vit_tiny_patch16_224\",\n            pretrained=True,\n            num_classes=0,\n            img_size=224\n        )\n        vit_dim = self.vit.num_features\n\n        self.classifier = nn.Sequential(\n            nn.Linear(cnn_dim + vit_dim, 512),\n            nn.ReLU(),\n            nn.Dropout(0.4),\n            nn.Linear(512, num_classes)\n        )\n\n    def forward(self, x):\n        cnn_feat = self.cnn(x)\n        vit_feat = self.vit(x)\n        feat = torch.cat([cnn_feat, vit_feat], dim=1)\n        return self.classifier(feat)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:51.285182Z","iopub.execute_input":"2026-03-14T11:55:51.285360Z","iopub.status.idle":"2026-03-14T11:55:51.292721Z","shell.execute_reply.started":"2026-03-14T11:55:51.285343Z","shell.execute_reply":"2026-03-14T11:55:51.292178Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class FocalLoss(nn.Module):\n    def __init__(self, gamma=2):\n        super().__init__()\n        self.gamma = gamma\n\n    def forward(self, logits, targets):\n        ce = F.cross_entropy(logits, targets, reduction=\"none\")\n        pt = torch.exp(-ce)\n        return ((1 - pt) ** self.gamma * ce).mean()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:51.297244Z","iopub.execute_input":"2026-03-14T11:55:51.297511Z","iopub.status.idle":"2026-03-14T11:55:51.308258Z","shell.execute_reply.started":"2026-03-14T11:55:51.297484Z","shell.execute_reply":"2026-03-14T11:55:51.307635Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(\"Device:\", device)\n\nmodel     = EfficientNet_ViT(num_classes=5).to(device)\ncriterion = FocalLoss()\noptimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)\nscheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:51.309790Z","iopub.execute_input":"2026-03-14T11:55:51.310130Z","iopub.status.idle":"2026-03-14T11:55:55.015929Z","shell.execute_reply.started":"2026-03-14T11:55:51.310097Z","shell.execute_reply":"2026-03-14T11:55:55.015078Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_epoch(loader):\n    model.train()\n    correct, total, loss_sum = 0, 0, 0\n\n    for x, y in loader:\n        x, y = x.to(device), y.to(device)\n        optimizer.zero_grad()\n        out  = model(x)\n        loss = criterion(out, y)\n        loss.backward()\n        optimizer.step()\n\n        loss_sum += loss.item()\n        correct  += (out.argmax(1) == y).sum().item()\n        total    += y.size(0)\n\n    return loss_sum / len(loader), correct / total","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:55.016856Z","iopub.execute_input":"2026-03-14T11:55:55.017234Z","iopub.status.idle":"2026-03-14T11:55:55.023937Z","shell.execute_reply.started":"2026-03-14T11:55:55.017210Z","shell.execute_reply":"2026-03-14T11:55:55.023218Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def eval_epoch(loader):\n    model.eval()\n    correct, total = 0, 0\n\n    with torch.no_grad():\n        for x, y in loader:\n            x, y = x.to(device), y.to(device)\n            out  = model(x)\n            correct += (out.argmax(1) == y).sum().item()\n            total   += y.size(0)\n\n    return correct / total","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:55.025015Z","iopub.execute_input":"2026-03-14T11:55:55.025399Z","iopub.status.idle":"2026-03-14T11:55:55.041690Z","shell.execute_reply.started":"2026-03-14T11:55:55.025366Z","shell.execute_reply":"2026-03-14T11:55:55.040969Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 10\n\nfor epoch in range(EPOCHS):\n    train_loss, train_acc = train_epoch(train_loader)\n    val_acc               = eval_epoch(val_loader)\n    scheduler.step()\n    print(f\"Epoch {epoch+1:02d}/{EPOCHS} | Loss: {train_loss:.4f} | Train Acc: {train_acc:.4f} | Val Acc: {val_acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T11:55:55.042600Z","iopub.execute_input":"2026-03-14T11:55:55.042944Z","iopub.status.idle":"2026-03-14T12:28:04.706589Z","shell.execute_reply.started":"2026-03-14T11:55:55.042910Z","shell.execute_reply":"2026-03-14T12:28:04.705579Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_acc = eval_epoch(test_loader)\nprint(\"Final Test Accuracy:\", round(test_acc, 4))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T12:28:04.708183Z","iopub.execute_input":"2026-03-14T12:28:04.708541Z","iopub.status.idle":"2026-03-14T12:28:40.218115Z","shell.execute_reply.started":"2026-03-14T12:28:04.708513Z","shell.execute_reply":"2026-03-14T12:28:40.217137Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_predictions(loader):\n    model.eval()\n    y_true, y_pred, y_prob = [], [], []\n\n    with torch.no_grad():\n        for x, y in loader:\n            x = x.to(device)\n            out   = model(x)\n            probs = torch.softmax(out, dim=1)\n            preds = torch.argmax(out, dim=1)\n\n            y_true.extend(y.numpy())\n            y_pred.extend(preds.cpu().numpy())\n            y_prob.extend(probs.cpu().numpy())\n\n    return np.array(y_true), np.array(y_pred), np.array(y_prob)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T12:28:40.219620Z","iopub.execute_input":"2026-03-14T12:28:40.220052Z","iopub.status.idle":"2026-03-14T12:28:40.225822Z","shell.execute_reply.started":"2026-03-14T12:28:40.220022Z","shell.execute_reply":"2026-03-14T12:28:40.225138Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def evaluate_all(loader, name=\"TEST\"):\n    y_true, y_pred, y_prob = get_predictions(loader)\n\n    acc              = accuracy_score(y_true, y_pred)\n    precision_macro  = precision_score(y_true, y_pred, average=\"macro\",    zero_division=0)\n    precision_weight = precision_score(y_true, y_pred, average=\"weighted\", zero_division=0)\n    recall_macro     = recall_score(y_true, y_pred, average=\"macro\",    zero_division=0)\n    recall_weight    = recall_score(y_true, y_pred, average=\"weighted\", zero_division=0)\n    f1_macro         = f1_score(y_true, y_pred, average=\"macro\",    zero_division=0)\n    f1_weight        = f1_score(y_true, y_pred, average=\"weighted\", zero_division=0)\n    qwk              = cohen_kappa_score(y_true, y_pred, weights=\"quadratic\")\n    cm               = confusion_matrix(y_true, y_pred)\n\n    y_true_bin = label_binarize(y_true, classes=[0,1,2,3,4])\n    auc = roc_auc_score(y_true_bin, y_prob, average=\"macro\", multi_class=\"ovr\")\n\n    print(f\"\\n========== {name} RESULTS ==========\")\n    print(f\"Accuracy              : {acc:.4f}\")\n    print(f\"Precision (Macro)     : {precision_macro:.4f}\")\n    print(f\"Precision (Weighted)  : {precision_weight:.4f}\")\n    print(f\"Recall    (Macro)     : {recall_macro:.4f}\")\n    print(f\"Recall    (Weighted)  : {recall_weight:.4f}\")\n    print(f\"F1-Score  (Macro)     : {f1_macro:.4f}\")\n    print(f\"F1-Score  (Weighted)  : {f1_weight:.4f}\")\n    print(f\"QWK                   : {qwk:.4f}\")\n    print(f\"ROC-AUC  (Macro)      : {auc:.4f}\")\n    print(\"\\nConfusion Matrix:\")\n    print(cm)\n    print(\"\\nClassification Report:\")\n    print(classification_report(y_true, y_pred, digits=4))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T12:28:40.226877Z","iopub.execute_input":"2026-03-14T12:28:40.227166Z","iopub.status.idle":"2026-03-14T12:28:40.243442Z","shell.execute_reply.started":"2026-03-14T12:28:40.227145Z","shell.execute_reply":"2026-03-14T12:28:40.242825Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"evaluate_all(train_loader, name=\"TRAIN\")\nevaluate_all(val_loader,   name=\"VALIDATION\")\nevaluate_all(test_loader,  name=\"TEST\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-14T12:28:40.244329Z","iopub.execute_input":"2026-03-14T12:28:40.244640Z","iopub.status.idle":"2026-03-14T12:32:20.244711Z","shell.execute_reply.started":"2026-03-14T12:28:40.244615Z","shell.execute_reply":"2026-03-14T12:32:20.243567Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}