{"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-19T12:15:04.935727Z","iopub.execute_input":"2026-03-19T12:15:04.936246Z","iopub.status.idle":"2026-03-19T12:15:13.276655Z","shell.execute_reply.started":"2026-03-19T12:15:04.936218Z","shell.execute_reply":"2026-03-19T12:15:13.275750Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install timm albumentations","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:13.278097Z","iopub.execute_input":"2026-03-19T12:15:13.278517Z","iopub.status.idle":"2026-03-19T12:15:18.830430Z","shell.execute_reply.started":"2026-03-19T12:15:13.278491Z","shell.execute_reply":"2026-03-19T12:15:18.829730Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport torch\nimport timm\nimport pandas as pd\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom sklearn.model_selection import train_test_split\nimport albumentations as A","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:18.831650Z","iopub.execute_input":"2026-03-19T12:15:18.831904Z","iopub.status.idle":"2026-03-19T12:15:36.694466Z","shell.execute_reply.started":"2026-03-19T12:15:18.831865Z","shell.execute_reply":"2026-03-19T12:15:36.693641Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"BASE_PATH = \"/kaggle/input/competitions/aptos2019-blindness-detection\"\n\nCSV_PATH = BASE_PATH + \"/train.csv\"\nIMG_PATH = BASE_PATH + \"/train_images\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:36.696001Z","iopub.execute_input":"2026-03-19T12:15:36.696508Z","iopub.status.idle":"2026-03-19T12:15:36.700021Z","shell.execute_reply.started":"2026-03-19T12:15:36.696481Z","shell.execute_reply":"2026-03-19T12:15:36.699324Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(CSV_PATH)\n\ntrain_df, val_df = train_test_split(df, test_size=0.2, random_state=42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:36.700926Z","iopub.execute_input":"2026-03-19T12:15:36.701236Z","iopub.status.idle":"2026-03-19T12:15:36.748623Z","shell.execute_reply.started":"2026-03-19T12:15:36.701198Z","shell.execute_reply":"2026-03-19T12:15:36.748096Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transform = A.Compose([\n    A.Resize(300, 300),\n    A.HorizontalFlip(p=0.5),\n    A.Rotate(limit=20, p=0.5),\n    A.RandomBrightnessContrast(p=0.5),\n    A.GaussianBlur(p=0.3),\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:36.749390Z","iopub.execute_input":"2026-03-19T12:15:36.749683Z","iopub.status.idle":"2026-03-19T12:15:36.756614Z","shell.execute_reply.started":"2026-03-19T12:15:36.749650Z","shell.execute_reply":"2026-03-19T12:15:36.755959Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DRDataset(Dataset):\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        img_name = self.df.iloc[idx]['id_code']\n        label = self.df.iloc[idx]['diagnosis']\n\n        path = os.path.join(self.img_dir, img_name + \".png\")\n\n        img = cv2.imread(path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n        if self.transform:\n            img = self.transform(image=img)['image']\n\n        img = torch.tensor(img).permute(2,0,1).float() / 255.0\n        return img, torch.tensor(label).long()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:36.757518Z","iopub.execute_input":"2026-03-19T12:15:36.757722Z","iopub.status.idle":"2026-03-19T12:15:36.770664Z","shell.execute_reply.started":"2026-03-19T12:15:36.757700Z","shell.execute_reply":"2026-03-19T12:15:36.770102Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loader = DataLoader(\n    DRDataset(train_df, IMG_PATH, transform),\n    batch_size=8,\n    shuffle=True,\n    num_workers=2\n)\n\nval_loader = DataLoader(\n    DRDataset(val_df, IMG_PATH, transform),\n    batch_size=8,\n    shuffle=False\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:36.771474Z","iopub.execute_input":"2026-03-19T12:15:36.771747Z","iopub.status.idle":"2026-03-19T12:15:36.787376Z","shell.execute_reply.started":"2026-03-19T12:15:36.771718Z","shell.execute_reply":"2026-03-19T12:15:36.786847Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class ResidualAttentionBlock(nn.Module):\n    def __init__(self, channels):\n        super().__init__()\n\n        self.conv = nn.Conv2d(channels, channels, 3, padding=1)\n        self.bn = nn.BatchNorm2d(channels)\n\n        self.attn = nn.Sequential(\n            nn.Conv2d(channels, channels//2, 1),\n            nn.ReLU(),\n            nn.Conv2d(channels//2, channels, 1),\n            nn.Sigmoid()\n        )\n\n    def forward(self, x):\n        res = x\n        out = torch.relu(self.bn(self.conv(x)))\n        attn = self.attn(out)\n        return out * attn + res","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:36.788130Z","iopub.execute_input":"2026-03-19T12:15:36.788406Z","iopub.status.idle":"2026-03-19T12:15:36.800013Z","shell.execute_reply.started":"2026-03-19T12:15:36.788385Z","shell.execute_reply":"2026-03-19T12:15:36.799507Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class RAEfficientNet(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n        self.backbone = timm.create_model(\n            'efficientnet_b3',\n            pretrained=True,\n            features_only=True\n        )\n\n        self.ra = ResidualAttentionBlock(384)   # ✅ FIXED\n\n        self.pool = nn.AdaptiveAvgPool2d(1)\n        self.fc = nn.Linear(384, 5)             # ✅ FIXED\n\n    def forward(self, x):\n        x = self.backbone(x)[-1]\n        x = self.ra(x)\n        x = self.pool(x).view(x.size(0), -1)\n        return self.fc(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:36.802148Z","iopub.execute_input":"2026-03-19T12:15:36.802462Z","iopub.status.idle":"2026-03-19T12:15:36.812896Z","shell.execute_reply.started":"2026-03-19T12:15:36.802436Z","shell.execute_reply":"2026-03-19T12:15:36.812271Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nmodel = RAEfficientNet().to(device)\n\nweights = torch.tensor([1.0, 2.0, 2.0, 3.0, 4.0]).to(device)\ncriterion = nn.CrossEntropyLoss(weight=weights)\n\noptimizer = torch.optim.Adam(model.parameters(), lr=3e-4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:36.813658Z","iopub.execute_input":"2026-03-19T12:15:36.813849Z","iopub.status.idle":"2026-03-19T12:15:39.059303Z","shell.execute_reply.started":"2026-03-19T12:15:36.813830Z","shell.execute_reply":"2026-03-19T12:15:39.058707Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def accuracy(outputs, labels):\n    _, preds = torch.max(outputs, 1)\n    return (preds == labels).float().mean()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:39.060329Z","iopub.execute_input":"2026-03-19T12:15:39.061168Z","iopub.status.idle":"2026-03-19T12:15:39.064842Z","shell.execute_reply.started":"2026-03-19T12:15:39.061130Z","shell.execute_reply":"2026-03-19T12:15:39.064252Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_epoch(loader):\n    model.train()\n    total_loss = 0\n\n    for imgs, labels in loader:\n        imgs, labels = imgs.to(device), labels.to(device)\n\n        optimizer.zero_grad()\n        out = model(imgs)\n        loss = criterion(out, labels)\n\n        loss.backward()\n        optimizer.step()\n\n        total_loss += loss.item()\n\n    return total_loss / len(loader)\n\n\ndef validate(loader):\n    model.eval()\n    acc = 0\n\n    with torch.no_grad():\n        for imgs, labels in loader:\n            imgs, labels = imgs.to(device), labels.to(device)\n            out = model(imgs)\n            acc += accuracy(out, labels).item()\n\n    return acc / len(loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:39.065720Z","iopub.execute_input":"2026-03-19T12:15:39.066019Z","iopub.status.idle":"2026-03-19T12:15:39.092724Z","shell.execute_reply.started":"2026-03-19T12:15:39.065985Z","shell.execute_reply":"2026-03-19T12:15:39.092219Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 10\nbest_acc = 0\npatience = 2\ncounter = 0\n\nfor epoch in range(EPOCHS):\n    loss = train_epoch(train_loader)\n    val_acc = validate(val_loader)\n\n    print(f\"Epoch {epoch+1}\")\n    print(f\"Loss: {loss:.4f}, Val Acc: {val_acc:.4f}\")\n\n    if val_acc > best_acc:\n        best_acc = val_acc\n        counter = 0\n        torch.save(model.state_dict(), \"/kaggle/working/best_model.pth\")\n    else:\n        counter += 1\n\n    # if counter >= patience:\n    #     print(\"Early stopping triggered\")\n    #     break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-19T12:15:39.093489Z","iopub.execute_input":"2026-03-19T12:15:39.093779Z","iopub.status.idle":"2026-03-19T12:56:01.419250Z","shell.execute_reply.started":"2026-03-19T12:15:39.093747Z","shell.execute_reply":"2026-03-19T12:56:01.418362Z"}},"outputs":[],"execution_count":null}]}