{"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\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>')\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","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},{"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":"import torch\n\nprint(\"PyTorch version:\", torch.__version__)\nprint(\"GPU available:\", torch.cuda.is_available())\n\nif torch.cuda.is_available():\n    print(\"GPU:\", torch.cuda.get_device_name(0))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport os\n\nDATA_DIR = \"/kaggle/input/competitions/aptos2019-blindness-detection\"\n\ntrain_csv = os.path.join(DATA_DIR, \"train.csv\")\nimage_dir = os.path.join(DATA_DIR, \"train_images\")\n\ndf = pd.read_csv(train_csv)\n\nprint(\"Number of images:\", len(df))\nprint(\"Columns:\", list(df.columns))\nprint(\"Image folder exists:\", os.path.exists(image_dir))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\nfrom sklearn.model_selection import train_test_split\n\nDATA_DIR = \"/kaggle/input/competitions/aptos2019-blindness-detection\"\nIMAGE_DIR = os.path.join(DATA_DIR, \"train_images\")\n\ndf = pd.read_csv(os.path.join(DATA_DIR, \"train.csv\"))\n\n# Add complete image path\ndf[\"image_path\"] = df[\"id_code\"].apply(\n    lambda x: os.path.join(IMAGE_DIR, x + \".png\")\n)\n\n# Stratified split: keeps the 5 disease classes balanced\ntrain_df, val_df = train_test_split(\n    df,\n    test_size=0.20,\n    random_state=42,\n    stratify=df[\"diagnosis\"]\n)\n\nprint(\"Training images:\", len(train_df))\nprint(\"Validation images:\", len(val_df))\n\nprint(\"\\nTraining class distribution:\")\nprint(train_df[\"diagnosis\"].value_counts().sort_index())\n\nprint(\"\\nValidation class distribution:\")\nprint(val_df[\"diagnosis\"].value_counts().sort_index())","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom PIL import Image\n\nclass APTOSDataset(Dataset):\n    def __init__(self, dataframe, transform=None):\n        self.data = dataframe.reset_index(drop=True)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        row = self.data.iloc[idx]\n\n        image = Image.open(row[\"image_path\"]).convert(\"RGB\")\n        label = int(row[\"diagnosis\"])\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label\n\n\ntrain_transform = transforms.Compose([\n    transforms.Resize((224, 224)),\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((224, 224)),\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\ntrain_dataset = APTOSDataset(train_df, train_transform)\nval_dataset = APTOSDataset(val_df, val_transform)\n\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=32,\n    shuffle=True,\n    num_workers=2,\n    pin_memory=True\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=32,\n    shuffle=False,\n    num_workers=2,\n    pin_memory=True\n)\n\nprint(\"Train batches:\", len(train_loader))\nprint(\"Validation batches:\", len(val_loader))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport timm\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nmodel = timm.create_model(\n    \"efficientnet_b0\",\n    pretrained=True,\n    num_classes=5\n)\n\nmodel = model.to(device)\n\nprint(\"Device:\", device)\nprint(\"Model: EfficientNet-B0\")\nprint(\"Classes: 5\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom tqdm.auto import tqdm\n\n# Loss function\ncriterion = nn.CrossEntropyLoss()\n\n# Optimizer\noptimizer = optim.AdamW(\n    model.parameters(),\n    lr=1e-4,\n    weight_decay=1e-4\n)\n\n# Learning-rate scheduler\nscheduler = optim.lr_scheduler.ReduceLROnPlateau(\n    optimizer,\n    mode=\"max\",\n    factor=0.5,\n    patience=2\n)\n\n# Number of epochs\nEPOCHS = 10\n\n# Track best validation accuracy\nbest_val_acc = 0.0\n\nprint(\"Training setup complete!\")\nprint(\"Epochs:\", EPOCHS)\nprint(\"Learning rate:\", 1e-4)\nprint(\"Device:\", device)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score, f1_score\nimport numpy as np\n\nfor epoch in range(EPOCHS):\n\n    # =========================\n    # TRAINING\n    # =========================\n    model.train()\n\n    train_loss = 0.0\n    train_preds = []\n    train_labels = []\n\n    progress_bar = tqdm(\n        train_loader,\n        desc=f\"Epoch {epoch+1}/{EPOCHS} - Training\"\n    )\n\n    for images, labels in progress_bar:\n\n        images = images.to(device, non_blocking=True)\n        labels = labels.to(device, non_blocking=True)\n\n        optimizer.zero_grad()\n\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n        optimizer.step()\n\n        train_loss += loss.item()\n\n        predictions = torch.argmax(outputs, dim=1)\n\n        train_preds.extend(predictions.detach().cpu().numpy())\n        train_labels.extend(labels.detach().cpu().numpy())\n\n        progress_bar.set_postfix(loss=loss.item())\n\n    train_loss /= len(train_loader)\n\n    train_acc = accuracy_score(\n        train_labels,\n        train_preds\n    )\n\n    train_f1 = f1_score(\n        train_labels,\n        train_preds,\n        average=\"weighted\"\n    )\n\n    # =========================\n    # VALIDATION\n    # =========================\n    model.eval()\n\n    val_loss = 0.0\n    val_preds = []\n    val_labels = []\n\n    with torch.no_grad():\n\n        for images, labels in tqdm(\n            val_loader,\n            desc=f\"Epoch {epoch+1}/{EPOCHS} - Validation\"\n        ):\n\n            images = images.to(device, non_blocking=True)\n            labels = labels.to(device, non_blocking=True)\n\n            outputs = model(images)\n\n            loss = criterion(outputs, labels)\n\n            val_loss += loss.item()\n\n            predictions = torch.argmax(outputs, dim=1)\n\n            val_preds.extend(\n                predictions.cpu().numpy()\n            )\n\n            val_labels.extend(\n                labels.cpu().numpy()\n            )\n\n    val_loss /= len(val_loader)\n\n    val_acc = accuracy_score(\n        val_labels,\n        val_preds\n    )\n\n    val_f1 = f1_score(\n        val_labels,\n        val_preds,\n        average=\"weighted\"\n    )\n\n    # Update learning rate\n    scheduler.step(val_acc)\n\n    # =========================\n    # SAVE BEST MODEL\n    # =========================\n    if val_acc > best_val_acc:\n\n        best_val_acc = val_acc\n\n        torch.save(\n            model.state_dict(),\n            \"/kaggle/working/dr_model_best.pth\"\n        )\n\n        print(\"✓ Best model saved!\")\n\n    # =========================\n    # RESULTS\n    # =========================\n    print()\n    print(\"=\" * 60)\n    print(f\"Epoch {epoch+1}/{EPOCHS}\")\n    print(f\"Train Loss : {train_loss:.4f}\")\n    print(f\"Train Acc  : {train_acc:.4f}\")\n    print(f\"Train F1   : {train_f1:.4f}\")\n    print(f\"Val Loss   : {val_loss:.4f}\")\n    print(f\"Val Acc    : {val_acc:.4f}\")\n    print(f\"Val F1     : {val_f1:.4f}\")\n    print(f\"Best Val   : {best_val_acc:.4f}\")\n    print(\"=\" * 60)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Recreate DataLoaders with no multiprocessing workers\n# This avoids the worker freezing issue.\n\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=32,\n    shuffle=True,\n    num_workers=0,\n    pin_memory=True\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=32,\n    shuffle=False,\n    num_workers=0,\n    pin_memory=True\n)\n\nprint(\"Train batches:\", len(train_loader))\nprint(\"Validation batches:\", len(val_loader))\nprint(\"DataLoader fixed: num_workers=0\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\n\nBEST_MODEL = \"/kaggle/working/dr_model_best.pth\"\n\nmodel.load_state_dict(torch.load(BEST_MODEL, map_location=device))\nmodel = model.to(device)\n\nprint(\"Best model loaded successfully!\")\nprint(\"Starting validation accuracy was:\", best_val_acc)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch.nn as nn\nimport torch.optim as optim\n\ncriterion = nn.CrossEntropyLoss()\n\noptimizer = optim.AdamW(\n    model.parameters(),\n    lr=1e-4,\n    weight_decay=1e-4\n)\n\nscheduler = optim.lr_scheduler.ReduceLROnPlateau(\n    optimizer,\n    mode=\"max\",\n    factor=0.5,\n    patience=2\n)\n\nEPOCHS_REMAINING = 7\n\nprint(\"Training will continue for 7 more epochs.\")\nprint(\"DataLoader workers: 0\")\nprint(\"GPU:\", torch.cuda.get_device_name(0))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# EVALUATE BEST DR MODEL\n# ============================================================\n\nimport torch\nimport numpy as np\nfrom sklearn.metrics import (\n    accuracy_score,\n    f1_score,\n    classification_report,\n    confusion_matrix\n)\n\n# ------------------------------------------------------------\n# 1. Load the BEST checkpoint\n# ------------------------------------------------------------\n\nbest_checkpoint = \"/kaggle/working/dr_model_best.pth\"\n\ncheckpoint = torch.load(\n    best_checkpoint,\n    map_location=device\n)\n\nmodel.load_state_dict(checkpoint[\"model_state_dict\"])\nmodel.to(device)\nmodel.eval()\n\nprint(\"Loaded BEST checkpoint successfully.\")\nprint(f\"Best validation accuracy saved: {checkpoint['val_acc']:.4f}\")\nprint()\n\n# ------------------------------------------------------------\n# 2. Run prediction on validation set\n# ------------------------------------------------------------\n\nall_preds = []\nall_labels = []\n\nwith torch.no_grad():\n\n    for images, labels in val_loader:\n\n        images = images.to(device, non_blocking=True)\n        labels = labels.to(device, non_blocking=True)\n\n        outputs = model(images)\n        preds = torch.argmax(outputs, dim=1)\n\n        all_preds.extend(preds.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n\n# ------------------------------------------------------------\n# 3. Calculate metrics\n# ------------------------------------------------------------\n\naccuracy = accuracy_score(all_labels, all_preds)\n\nf1_weighted = f1_score(\n    all_labels,\n    all_preds,\n    average=\"weighted\"\n)\n\nprint(\"=\" * 60)\nprint(\"BEST MODEL VALIDATION RESULTS\")\nprint(\"=\" * 60)\n\nprint(f\"Accuracy      : {accuracy * 100:.2f}%\")\nprint(f\"Weighted F1   : {f1_weighted * 100:.2f}%\")\n\n# ------------------------------------------------------------\n# 4. Per-class performance\n# ------------------------------------------------------------\n\nclass_names = [\n    \"No DR\",\n    \"Mild\",\n    \"Moderate\",\n    \"Severe\",\n    \"Proliferative DR\"\n]\n\nprint(\"\\nClassification Report:\")\nprint(\"=\" * 60)\n\nprint(\n    classification_report(\n        all_labels,\n        all_preds,\n        target_names=class_names,\n        digits=4,\n        zero_division=0\n    )\n)\n\n# ------------------------------------------------------------\n# 5. Confusion Matrix\n# ------------------------------------------------------------\n\ncm = confusion_matrix(\n    all_labels,\n    all_preds\n)\n\nprint(\"\\nConfusion Matrix:\")\nprint(\"=\" * 60)\n\nprint(cm)\n\nprint(\"\\nClass order:\")\nfor i, name in enumerate(class_names):\n    print(f\"{i} = {name}\")\n\nprint(\"\\n\" + \"=\" * 60)\nprint(\"EVALUATION COMPLETE\")\nprint(\"=\" * 60)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# EVALUATE BEST DR MODEL\n# ============================================================\n\nimport torch\nimport numpy as np\nfrom sklearn.metrics import (\n    accuracy_score,\n    f1_score,\n    classification_report,\n    confusion_matrix\n)\n\n# ------------------------------------------------------------\n# 1. Define device\n# ------------------------------------------------------------\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nprint(\"Device:\", device)\n\n# ------------------------------------------------------------\n# 2. Load the BEST checkpoint\n# ------------------------------------------------------------\n\nbest_checkpoint = \"/kaggle/working/dr_model_best.pth\"\n\ncheckpoint = torch.load(\n    best_checkpoint,\n    map_location=device\n)\n\nmodel.load_state_dict(checkpoint[\"model_state_dict\"])\nmodel.to(device)\nmodel.eval()\n\nprint(\"BEST checkpoint loaded successfully.\")\n\nif \"val_acc\" in checkpoint:\n    print(f\"Saved best validation accuracy: {checkpoint['val_acc'] * 100:.2f}%\")\n\nprint()\n\n# ------------------------------------------------------------\n# 3. Run prediction on validation set\n# ------------------------------------------------------------\n\nall_preds = []\nall_labels = []\n\nwith torch.no_grad():\n\n    for images, labels in val_loader:\n\n        images = images.to(device)\n        labels = labels.to(device)\n\n        outputs = model(images)\n\n        preds = torch.argmax(outputs, dim=1)\n\n        all_preds.extend(preds.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n\n# ------------------------------------------------------------\n# 4. Calculate overall metrics\n# ------------------------------------------------------------\n\naccuracy = accuracy_score(all_labels, all_preds)\n\nf1_weighted = f1_score(\n    all_labels,\n    all_preds,\n    average=\"weighted\"\n)\n\nprint(\"=\" * 60)\nprint(\"BEST MODEL VALIDATION RESULTS\")\nprint(\"=\" * 60)\n\nprint(f\"Accuracy    : {accuracy * 100:.2f}%\")\nprint(f\"Weighted F1 : {f1_weighted * 100:.2f}%\")\n\n# ------------------------------------------------------------\n# 5. Per-class performance\n# ------------------------------------------------------------\n\nclass_names = [\n    \"No DR\",\n    \"Mild\",\n    \"Moderate\",\n    \"Severe\",\n    \"Proliferative DR\"\n]\n\nprint(\"\\nClassification Report\")\nprint(\"=\" * 60)\n\nprint(\n    classification_report(\n        all_labels,\n        all_preds,\n        target_names=class_names,\n        digits=4,\n        zero_division=0\n    )\n)\n\n# ------------------------------------------------------------\n# 6. Confusion Matrix\n# ------------------------------------------------------------\n\ncm = confusion_matrix(\n    all_labels,\n    all_preds\n)\n\nprint(\"\\nConfusion Matrix\")\nprint(\"=\" * 60)\n\nprint(cm)\n\nprint(\"\\nClass order:\")\nfor i, name in enumerate(class_names):\n    print(f\"{i} = {name}\")\n\nprint(\"\\n\" + \"=\" * 60)\nprint(\"EVALUATION COMPLETE\")\nprint(\"=\" * 60)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nprint(\"Files currently in /kaggle/working:\")\nprint(\"=\" * 60)\n\nfor file in os.listdir(\"/kaggle/working\"):\n    print(file)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Checking whether trained model is still in memory...\")\n\ntry:\n    print(\"Model:\", model)\n    print(\"\\nModel is still available in memory!\")\nexcept NameError:\n    print(\"\\nModel is NOT available in memory.\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport timm\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nprint(\"Device:\", device)\n\nmodel = timm.create_model(\n    \"efficientnet_b0\",\n    pretrained=True,\n    num_classes=5\n)\n\nmodel = model.to(device)\n\nprint(\"Model recreated successfully.\")\nprint(\"Parameters:\", sum(p.numel() for p in model.parameters()))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# REBUILD APTOS DATASET + DATALOADERS\n# ============================================================\n\nimport os\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\nfrom PIL import Image\n\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\n\n# ------------------------------------------------------------\n# 1. Dataset paths\n# ------------------------------------------------------------\n\nDATA_DIR = \"/kaggle/input/competitions/aptos2019-blindness-detection\"\n\nCSV_PATH = os.path.join(DATA_DIR, \"train.csv\")\nIMAGE_DIR = os.path.join(DATA_DIR, \"train_images\")\n\ndf = pd.read_csv(CSV_PATH)\n\nprint(\"Total images:\", len(df))\nprint(\"Columns:\", df.columns.tolist())\n\n# ------------------------------------------------------------\n# 2. Train / validation split\n# ------------------------------------------------------------\n\ntrain_df, val_df = train_test_split(\n    df,\n    test_size=0.20,\n    random_state=42,\n    stratify=df[\"diagnosis\"]\n)\n\ntrain_df = train_df.reset_index(drop=True)\nval_df = val_df.reset_index(drop=True)\n\nprint(\"Training images  :\", len(train_df))\nprint(\"Validation images:\", len(val_df))\n\n# ------------------------------------------------------------\n# 3. Add image paths\n# ------------------------------------------------------------\n\ntrain_df[\"image_path\"] = train_df[\"id_code\"].apply(\n    lambda x: os.path.join(IMAGE_DIR, x + \".png\")\n)\n\nval_df[\"image_path\"] = val_df[\"id_code\"].apply(\n    lambda x: os.path.join(IMAGE_DIR, x + \".png\")\n)\n\n# ------------------------------------------------------------\n# 4. Transforms\n# ------------------------------------------------------------\n\ntrain_transform = transforms.Compose([\n    transforms.Resize((224, 224)),\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((224, 224)),\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\n# ------------------------------------------------------------\n# 5. Dataset class\n# ------------------------------------------------------------\n\nclass APTOSDataset(Dataset):\n\n    def __init__(self, dataframe, transform=None):\n        self.dataframe = dataframe\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n\n        row = self.dataframe.iloc[idx]\n\n        image = Image.open(row[\"image_path\"]).convert(\"RGB\")\n        label = int(row[\"diagnosis\"])\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label\n\n# ------------------------------------------------------------\n# 6. Create datasets\n# ------------------------------------------------------------\n\ntrain_dataset = APTOSDataset(\n    train_df,\n    transform=train_transform\n)\n\nval_dataset = APTOSDataset(\n    val_df,\n    transform=val_transform\n)\n\n# ------------------------------------------------------------\n# 7. Create DataLoaders\n# ------------------------------------------------------------\n\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=32,\n    shuffle=True,\n    num_workers=0,\n    pin_memory=True\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=32,\n    shuffle=False,\n    num_workers=0,\n    pin_memory=True\n)\n\nprint(\"\\nDataLoaders created successfully.\")\n\nprint(\"Training batches  :\", len(train_loader))\nprint(\"Validation batches:\", len(val_loader))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# TRAINING SETUP\n# ============================================================\n\nimport torch\nimport torch.nn as nn\nfrom torch.optim import AdamW\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom sklearn.metrics import f1_score\n\n# ------------------------------------------------------------\n# Loss function\n# ------------------------------------------------------------\n\ncriterion = nn.CrossEntropyLoss()\n\n# ------------------------------------------------------------\n# Optimizer\n# ------------------------------------------------------------\n\noptimizer = AdamW(\n    model.parameters(),\n    lr=1e-4,\n    weight_decay=1e-4\n)\n\n# ------------------------------------------------------------\n# Learning-rate scheduler\n# ------------------------------------------------------------\n\nscheduler = ReduceLROnPlateau(\n    optimizer,\n    mode=\"max\",\n    factor=0.5,\n    patience=2\n)\n\n# ------------------------------------------------------------\n# Training configuration\n# ------------------------------------------------------------\n\nEPOCHS = 10\n\nbest_val_acc = 0.0\n\nprint(\"=\" * 60)\nprint(\"TRAINING SETUP READY\")\nprint(\"=\" * 60)\n\nprint(\"Device           :\", device)\nprint(\"Model            : EfficientNet-B0\")\nprint(\"Parameters       :\", sum(p.numel() for p in model.parameters()))\nprint(\"Training samples :\", len(train_dataset))\nprint(\"Validation       :\", len(val_dataset))\nprint(\"Batch size       :\", train_loader.batch_size)\nprint(\"Learning rate    :\", optimizer.param_groups[0][\"lr\"])\nprint(\"Epochs           :\", EPOCHS)\nprint(\"=\" * 60)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# TRAIN EFFICIENTNET-B0 — ROBUST CHECKPOINT VERSION\n# ============================================================\n\nimport os\nimport shutil\nimport torch\nfrom sklearn.metrics import f1_score\n\nCHECKPOINT_PATH = \"/kaggle/working/dr_model_best.pth\"\nBACKUP_PATH = \"/kaggle/working/dr_model_best_backup.pth\"\n\nEPOCHS = 10\nbest_val_acc = 0.0\n\nprint(\"=\" * 60)\nprint(\"STARTING TRAINING\")\nprint(\"=\" * 60)\n\nfor epoch in range(EPOCHS):\n\n    # --------------------------------------------------------\n    # TRAIN\n    # --------------------------------------------------------\n\n    model.train()\n\n    train_loss = 0.0\n    train_preds = []\n    train_labels = []\n\n    for images, labels in train_loader:\n\n        images = images.to(device)\n        labels = labels.to(device)\n\n        optimizer.zero_grad()\n\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        loss.backward()\n        optimizer.step()\n\n        train_loss += loss.item()\n\n        preds = torch.argmax(outputs, dim=1)\n\n        train_preds.extend(preds.detach().cpu().numpy())\n        train_labels.extend(labels.detach().cpu().numpy())\n\n    train_loss /= len(train_loader)\n\n    train_acc = sum(\n        p == l for p, l in zip(train_preds, train_labels)\n    ) / len(train_labels)\n\n    train_f1 = f1_score(\n        train_labels,\n        train_preds,\n        average=\"weighted\"\n    )\n\n    # --------------------------------------------------------\n    # VALIDATION\n    # --------------------------------------------------------\n\n    model.eval()\n\n    val_loss = 0.0\n    val_preds = []\n    val_labels = []\n\n    with torch.no_grad():\n\n        for images, labels in val_loader:\n\n            images = images.to(device)\n            labels = labels.to(device)\n\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            val_loss += loss.item()\n\n            preds = torch.argmax(outputs, dim=1)\n\n            val_preds.extend(preds.cpu().numpy())\n            val_labels.extend(labels.cpu().numpy())\n\n    val_loss /= len(val_loader)\n\n    val_acc = sum(\n        p == l for p, l in zip(val_preds, val_labels)\n    ) / len(val_labels)\n\n    val_f1 = f1_score(\n        val_labels,\n        val_preds,\n        average=\"weighted\"\n    )\n\n    # --------------------------------------------------------\n    # LEARNING RATE SCHEDULER\n    # --------------------------------------------------------\n\n    scheduler.step(val_acc)\n\n    # --------------------------------------------------------\n    # SAVE BEST CHECKPOINT\n    # --------------------------------------------------------\n\n    if val_acc > best_val_acc:\n\n        best_val_acc = val_acc\n\n        checkpoint = {\n            \"epoch\": epoch + 1,\n            \"model_state_dict\": model.state_dict(),\n            \"optimizer_state_dict\": optimizer.state_dict(),\n            \"scheduler_state_dict\": scheduler.state_dict(),\n            \"val_acc\": val_acc,\n            \"val_f1\": val_f1\n        }\n\n        torch.save(checkpoint, CHECKPOINT_PATH)\n\n        # Create a second backup copy\n        shutil.copy2(\n            CHECKPOINT_PATH,\n            BACKUP_PATH\n        )\n\n        print(\"\\n🔥 NEW BEST MODEL SAVED!\")\n        print(f\"   Validation Accuracy: {val_acc * 100:.2f}%\")\n        print(f\"   Validation F1      : {val_f1 * 100:.2f}%\")\n\n    # --------------------------------------------------------\n    # EPOCH SUMMARY\n    # --------------------------------------------------------\n\n    print(\"\\n\" + \"=\" * 60)\n    print(f\"Epoch {epoch + 1}/{EPOCHS}\")\n    print(f\"Train Loss : {train_loss:.4f}\")\n    print(f\"Train Acc  : {train_acc * 100:.2f}%\")\n    print(f\"Train F1   : {train_f1 * 100:.2f}%\")\n    print(f\"Val Loss   : {val_loss:.4f}\")\n    print(f\"Val Acc    : {val_acc * 100:.2f}%\")\n    print(f\"Val F1     : {val_f1 * 100:.2f}%\")\n    print(f\"Best Val   : {best_val_acc * 100:.2f}%\")\n    print(\"=\" * 60)\n\nprint(\"\\n\" + \"=\" * 60)\nprint(\"TRAINING COMPLETE\")\nprint(\"=\" * 60)\nprint(f\"Best Validation Accuracy: {best_val_acc * 100:.2f}%\")\nprint(f\"Checkpoint: {CHECKPOINT_PATH}\")\nprint(f\"Backup    : {BACKUP_PATH}\")\nprint(\"=\" * 60)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import time\nfrom PIL import Image\n\ntest_path = train_df.iloc[0][\"image_path\"]\n\nprint(\"Testing image:\", test_path)\n\nstart = time.time()\n\nimg = Image.open(test_path).convert(\"RGB\")\nimg = img.resize((224, 224))\n\nelapsed = time.time() - start\n\nprint(f\"Image loading + resize time: {elapsed:.3f} seconds\")\nprint(\"Image size:\", img.size)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# TEST DATALOADER SPEED\n# ============================================================\n\nimport time\n\nprint(\"Testing DataLoader speed...\")\nprint(\"=\" * 60)\n\nstart = time.time()\n\nimages, labels = next(iter(train_loader))\n\nelapsed = time.time() - start\n\nprint(f\"First batch loading time : {elapsed:.2f} seconds\")\nprint(f\"Batch shape              : {images.shape}\")\nprint(f\"Labels shape             : {labels.shape}\")\n\n# Test another batch\nstart = time.time()\n\nimages, labels = next(iter(train_loader))\n\nelapsed = time.time() - start\n\nprint(f\"Second batch loading time: {elapsed:.2f} seconds\")\n\nprint(\"=\" * 60)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# CACHE APTOS IMAGES IN RAM\n# ============================================================\n\nimport numpy as np\nfrom PIL import Image\nfrom tqdm.auto import tqdm\n\nprint(\"=\" * 60)\nprint(\"CACHING TRAINING IMAGES\")\nprint(\"=\" * 60)\n\ndef cache_images(dataframe):\n    images = []\n    labels = []\n\n    for _, row in tqdm(\n        dataframe.iterrows(),\n        total=len(dataframe)\n    ):\n        img = Image.open(row[\"image_path\"]).convert(\"RGB\")\n        img = img.resize((224, 224))\n\n        # Store as uint8 to save RAM\n        img = np.array(img, dtype=np.uint8)\n\n        images.append(img)\n        labels.append(int(row[\"diagnosis\"]))\n\n    return np.stack(images), np.array(labels, dtype=np.int64)\n\n\ntrain_images_cached, train_labels_cached = cache_images(train_df)\n\nprint(\"\\nTraining cache complete!\")\nprint(\"Training images shape:\", train_images_cached.shape)\nprint(\"Training labels shape:\", train_labels_cached.shape)\n\nprint(\"\\n\" + \"=\" * 60)\nprint(\"CACHING VALIDATION IMAGES\")\nprint(\"=\" * 60)\n\nval_images_cached, val_labels_cached = cache_images(val_df)\n\nprint(\"\\nValidation cache complete!\")\nprint(\"Validation images shape:\", val_images_cached.shape)\nprint(\"Validation labels shape:\", val_labels_cached.shape)\n\nprint(\"\\n\" + \"=\" * 60)\nprint(\"CACHE COMPLETE\")\nprint(\"=\" * 60)\n\nprint(\n    f\"Training RAM: \"\n    f\"{train_images_cached.nbytes / (1024**2):.1f} MB\"\n)\n\nprint(\n    f\"Validation RAM: \"\n    f\"{val_images_cached.nbytes / (1024**2):.1f} MB\"\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# APTOS 2019 — FAST HIGH-ACCURACY TRAINING\n# EfficientNet-B0 + Cached RAM Dataset + AMP\n# ============================================================\n\nimport os\nimport copy\nimport time\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport timm\n\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom sklearn.metrics import accuracy_score, f1_score\nfrom collections import Counter\nfrom tqdm.auto import tqdm\n\n# ------------------------------------------------------------\n# 1. DEVICE\n# ------------------------------------------------------------\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nprint(\"=\" * 65)\nprint(\"FAST APTOS TRAINING\")\nprint(\"=\" * 65)\nprint(\"Device:\", device)\n\nif torch.cuda.is_available():\n    print(\"GPU:\", torch.cuda.get_device_name(0))\n    print(\"CUDA:\", torch.version.cuda)\n\n# ------------------------------------------------------------\n# 2. SPEED OPTIMIZATIONS\n# ------------------------------------------------------------\n\ntorch.backends.cudnn.benchmark = True\n\n# ------------------------------------------------------------\n# 3. DATASET USING RAM CACHE\n# ------------------------------------------------------------\n\nclass CachedAPTOSDataset(Dataset):\n\n    def __init__(self, images, labels, train=False):\n\n        self.images = images\n        self.labels = labels\n        self.train = train\n\n        if train:\n            self.transform = transforms.Compose([\n                transforms.ToPILImage(),\n\n                transforms.RandomHorizontalFlip(p=0.5),\n\n                transforms.RandomRotation(\n                    degrees=12,\n                    fill=0\n                ),\n\n                transforms.ColorJitter(\n                    brightness=0.15,\n                    contrast=0.15,\n                    saturation=0.10\n                ),\n\n                transforms.ToTensor(),\n\n                transforms.Normalize(\n                    mean=[0.485, 0.456, 0.406],\n                    std=[0.229, 0.224, 0.225]\n                )\n            ])\n\n        else:\n\n            self.transform = transforms.Compose([\n                transforms.ToPILImage(),\n\n                transforms.ToTensor(),\n\n                transforms.Normalize(\n                    mean=[0.485, 0.456, 0.406],\n                    std=[0.229, 0.224, 0.225]\n                )\n            ])\n\n    def __len__(self):\n        return len(self.labels)\n\n    def __getitem__(self, idx):\n\n        image = self.images[idx]\n        label = int(self.labels[idx])\n\n        image = self.transform(image)\n\n        return image, label\n\n\ntrain_dataset = CachedAPTOSDataset(\n    train_images_cached,\n    train_labels_cached,\n    train=True\n)\n\nval_dataset = CachedAPTOSDataset(\n    val_images_cached,\n    val_labels_cached,\n    train=False\n)\n\n# ------------------------------------------------------------\n# 4. DATALOADERS\n# ------------------------------------------------------------\n\nBATCH_SIZE = 64\n\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=True,\n    num_workers=0,\n    pin_memory=True\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=False,\n    num_workers=0,\n    pin_memory=True\n)\n\nprint()\nprint(\"Train images:\", len(train_dataset))\nprint(\"Validation images:\", len(val_dataset))\nprint(\"Batch size:\", BATCH_SIZE)\nprint(\"Train batches:\", len(train_loader))\nprint(\"Validation batches:\", len(val_loader))\n\n# ------------------------------------------------------------\n# 5. CLASS DISTRIBUTION\n# ------------------------------------------------------------\n\nclass_counts = Counter(train_labels_cached)\n\nprint()\nprint(\"Class distribution:\")\n\nfor c in range(5):\n    print(f\"Class {c}: {class_counts[c]}\")\n\n# ------------------------------------------------------------\n# 6. CLASS WEIGHTS\n# ------------------------------------------------------------\n\ncounts = np.array(\n    [class_counts[i] for i in range(5)],\n    dtype=np.float32\n)\n\n# Effective inverse-frequency weighting\nweights = len(train_labels_cached) / (\n    5.0 * counts\n)\n\n# Keep weights from becoming excessively large\nweights = np.sqrt(weights)\n\nclass_weights = torch.tensor(\n    weights,\n    dtype=torch.float32\n).to(device)\n\nprint()\nprint(\"Class weights:\", weights)\n\n# ------------------------------------------------------------\n# 7. MODEL\n# ------------------------------------------------------------\n\nmodel = timm.create_model(\n    \"efficientnet_b0\",\n    pretrained=True,\n    num_classes=5\n)\n\nmodel = model.to(device)\n\nprint()\nprint(\"Model: EfficientNet-B0\")\nprint(\"Parameters:\", sum(p.numel() for p in model.parameters()))\n\n# ------------------------------------------------------------\n# 8. LOSS\n# ------------------------------------------------------------\n\ncriterion = nn.CrossEntropyLoss(\n    weight=class_weights,\n    label_smoothing=0.05\n)\n\n# ------------------------------------------------------------\n# 9. OPTIMIZER\n# ------------------------------------------------------------\n\noptimizer = torch.optim.AdamW(\n    model.parameters(),\n    lr=3e-4,\n    weight_decay=1e-4\n)\n\n# ------------------------------------------------------------\n# 10. COSINE LR\n# ------------------------------------------------------------\n\nEPOCHS = 15\n\nscheduler = torch.optim.lr_scheduler.CosineAnnealingLR(\n    optimizer,\n    T_max=EPOCHS,\n    eta_min=1e-6\n)\n\n# ------------------------------------------------------------\n# 11. MIXED PRECISION\n# ------------------------------------------------------------\n\nscaler = torch.amp.GradScaler(\n    \"cuda\",\n    enabled=torch.cuda.is_available()\n)\n\n# ------------------------------------------------------------\n# 12. CHECKPOINT\n# ------------------------------------------------------------\n\nBEST_PATH = \"/kaggle/working/dr_model_best.pth\"\n\nbest_val_acc = 0.0\nbest_val_f1 = 0.0\nbest_epoch = 0\n\npatience = 5\nepochs_without_improvement = 0\n\n# ------------------------------------------------------------\n# 13. TRAINING\n# ------------------------------------------------------------\n\nprint()\nprint(\"=\" * 65)\nprint(\"TRAINING STARTED\")\nprint(\"=\" * 65)\n\ntraining_start = time.time()\n\nfor epoch in range(EPOCHS):\n\n    epoch_start = time.time()\n\n    # ========================================================\n    # TRAIN\n    # ========================================================\n\n    model.train()\n\n    train_loss = 0.0\n    train_preds = []\n    train_targets = []\n\n    progress = tqdm(\n        train_loader,\n        desc=f\"Epoch {epoch+1}/{EPOCHS}\"\n    )\n\n    for images, labels in progress:\n\n        images = images.to(\n            device,\n            non_blocking=True\n        )\n\n        labels = labels.to(\n            device,\n            non_blocking=True\n        )\n\n        optimizer.zero_grad(\n            set_to_none=True\n        )\n\n        with torch.amp.autocast(\n            device_type=\"cuda\",\n            enabled=torch.cuda.is_available()\n        ):\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        scaler.update()\n\n        train_loss += loss.item()\n\n        preds = outputs.argmax(dim=1)\n\n        train_preds.extend(\n            preds.detach().cpu().numpy()\n        )\n\n        train_targets.extend(\n            labels.detach().cpu().numpy()\n        )\n\n        progress.set_postfix(\n            loss=f\"{loss.item():.3f}\",\n            lr=f\"{optimizer.param_groups[0]['lr']:.2e}\"\n        )\n\n    # ========================================================\n    # TRAIN METRICS\n    # ========================================================\n\n    train_loss /= len(train_loader)\n\n    train_acc = accuracy_score(\n        train_targets,\n        train_preds\n    )\n\n    train_f1 = f1_score(\n        train_targets,\n        train_preds,\n        average=\"weighted\"\n    )\n\n    # ========================================================\n    # VALIDATION\n    # ========================================================\n\n    model.eval()\n\n    val_loss = 0.0\n    val_preds = []\n    val_targets = []\n\n    with torch.no_grad():\n\n        for images, labels in val_loader:\n\n            images = images.to(\n                device,\n                non_blocking=True\n            )\n\n            labels = labels.to(\n                device,\n                non_blocking=True\n            )\n\n            with torch.amp.autocast(\n                device_type=\"cuda\",\n                enabled=torch.cuda.is_available()\n            ):\n\n                outputs = model(images)\n\n                loss = criterion(\n                    outputs,\n                    labels\n                )\n\n            val_loss += loss.item()\n\n            preds = outputs.argmax(dim=1)\n\n            val_preds.extend(\n                preds.cpu().numpy()\n            )\n\n            val_targets.extend(\n                labels.cpu().numpy()\n            )\n\n    # ========================================================\n    # VALIDATION METRICS\n    # ========================================================\n\n    val_loss /= len(val_loader)\n\n    val_acc = accuracy_score(\n        val_targets,\n        val_preds\n    )\n\n    val_f1 = f1_score(\n        val_targets,\n        val_preds,\n        average=\"weighted\"\n    )\n\n    scheduler.step()\n\n    epoch_time = time.time() - epoch_start\n\n    # ========================================================\n    # PRINT\n    # ========================================================\n\n    print()\n    print(\"-\" * 65)\n\n    print(\n        f\"Epoch {epoch+1}/{EPOCHS} | \"\n        f\"Time: {epoch_time/60:.1f} min\"\n    )\n\n    print(\n        f\"Train Loss: {train_loss:.4f} | \"\n        f\"Train Acc: {train_acc*100:.2f}% | \"\n        f\"Train F1: {train_f1*100:.2f}%\"\n    )\n\n    print(\n        f\"Val Loss: {val_loss:.4f} | \"\n        f\"Val Acc: {val_acc*100:.2f}% | \"\n        f\"Val F1: {val_f1*100:.2f}%\"\n    )\n\n    print(\n        f\"Learning Rate: \"\n        f\"{optimizer.param_groups[0]['lr']:.2e}\"\n    )\n\n    # ========================================================\n    # SAVE BEST\n    # ========================================================\n\n    if val_acc > best_val_acc:\n\n        best_val_acc = val_acc\n        best_val_f1 = val_f1\n        best_epoch = epoch + 1\n        epochs_without_improvement = 0\n\n        torch.save(\n            {\n                \"epoch\": epoch + 1,\n                \"model_state_dict\": model.state_dict(),\n                \"optimizer_state_dict\": optimizer.state_dict(),\n                \"val_acc\": val_acc,\n                \"val_f1\": val_f1\n            },\n            BEST_PATH\n        )\n\n        print()\n        print(\"🔥 NEW BEST MODEL SAVED!\")\n        print(\n            f\"Best Val Accuracy: \"\n            f\"{best_val_acc*100:.2f}%\"\n        )\n        print(\n            f\"Best Val F1: \"\n            f\"{best_val_f1*100:.2f}%\"\n        )\n\n    else:\n\n        epochs_without_improvement += 1\n\n        print(\n            f\"No improvement: \"\n            f\"{epochs_without_improvement}/{patience}\"\n        )\n\n    # ========================================================\n    # EARLY STOPPING\n    # ========================================================\n\n    if epochs_without_improvement >= patience:\n\n        print()\n        print(\"Early stopping triggered.\")\n        break\n\n\n# ============================================================\n# 14. LOAD BEST MODEL\n# ============================================================\n\nprint()\nprint(\"=\" * 65)\nprint(\"LOADING BEST MODEL\")\nprint(\"=\" * 65)\n\ncheckpoint = torch.load(\n    BEST_PATH,\n    map_location=device\n)\n\nmodel.load_state_dict(\n    checkpoint[\"model_state_dict\"]\n)\n\nmodel.eval()\n\nprint(\n    f\"Best Epoch: {checkpoint['epoch']}\"\n)\n\nprint(\n    f\"Best Validation Accuracy: \"\n    f\"{checkpoint['val_acc']*100:.2f}%\"\n)\n\nprint(\n    f\"Best Validation F1: \"\n    f\"{checkpoint['val_f1']*100:.2f}%\"\n)\n\nprint(\n    f\"Total training time: \"\n    f\"{(time.time()-training_start)/60:.1f} minutes\"\n)\n\n# ============================================================\n# 15. FINAL ONNX EXPORT\n# ============================================================\n\nprint()\nprint(\"=\" * 65)\nprint(\"EXPORTING ONNX\")\nprint(\"=\" * 65)\n\nonnx_path = \"/kaggle/working/dr_model.onnx\"\n\ndummy_input = torch.randn(\n    1, 3, 224, 224,\n    device=device\n)\n\ntorch.onnx.export(\n    model,\n    dummy_input,\n    onnx_path,\n    export_params=True,\n    opset_version=17,\n    do_constant_folding=True,\n    input_names=[\"input\"],\n    output_names=[\"output\"],\n    dynamic_axes={\n        \"input\": {\n            0: \"batch_size\"\n        },\n        \"output\": {\n            0: \"batch_size\"\n        }\n    }\n)\n\nprint()\nprint(\"✅ ONNX EXPORT COMPLETE\")\nprint(\"File:\", onnx_path)\n\nprint()\nprint(\"=\" * 65)\nprint(\"FINAL RESULT\")\nprint(\"=\" * 65)\n\nprint(\n    f\"Best Validation Accuracy: \"\n    f\"{checkpoint['val_acc']*100:.2f}%\"\n)\n\nprint(\n    f\"Best Validation F1: \"\n    f\"{checkpoint['val_f1']*100:.2f}%\"\n)\n\nprint()\nprint(\"Files created:\")\nprint(\"• dr_model_best.pth\")\nprint(\"• dr_model.onnx\")\n\nprint()\nprint(\"🔥 PHASE 1 COMPLETE\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport os\n\n# ============================================================\n# LOAD BEST TRAINED MODEL\n# ============================================================\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\ncheckpoint_path = \"/kaggle/working/dr_model_best.pth\"\nonnx_path = \"/kaggle/working/dr_model.onnx\"\n\nprint(\"Loading best checkpoint...\")\n\ncheckpoint = torch.load(\n    checkpoint_path,\n    map_location=device\n)\n\n# Recreate model\nmodel = timm.create_model(\n    \"efficientnet_b0\",\n    pretrained=False,\n    num_classes=5\n)\n\nmodel.load_state_dict(\n    checkpoint[\"model_state_dict\"]\n)\n\nmodel = model.to(device)\nmodel.eval()\n\nprint(\"Best Epoch:\", checkpoint[\"epoch\"])\nprint(\n    \"Validation Accuracy:\",\n    f\"{checkpoint['val_acc'] * 100:.2f}%\"\n)\nprint(\n    \"Validation F1:\",\n    f\"{checkpoint['val_f1'] * 100:.2f}%\"\n)\n\n# ============================================================\n# ONNX EXPORT\n# ============================================================\n\ndummy_input = torch.randn(\n    1, 3, 224, 224,\n    device=device\n)\n\nprint()\nprint(\"Exporting ONNX...\")\n\ntorch.onnx.export(\n    model,\n    dummy_input,\n    onnx_path,\n    export_params=True,\n    opset_version=17,\n    do_constant_folding=True,\n    input_names=[\"input\"],\n    output_names=[\"output\"],\n    dynamic_axes={\n        \"input\": {0: \"batch_size\"},\n        \"output\": {0: \"batch_size\"}\n    }\n)\n\nprint()\nprint(\"=\" * 60)\nprint(\"✅ ONNX EXPORT SUCCESSFUL\")\nprint(\"=\" * 60)\n\nprint(\"Path:\", onnx_path)\nprint(\"Size:\", f\"{os.path.getsize(onnx_path) / (1024**2):.2f} MB\")\nprint()\nprint(\"PHASE 1 COMPLETE 🔥\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import onnx\nimport onnxscript\nprint(\"ONNX:\", onnx.__version__)\nprint(\"ONNXScript:\", onnxscript.__version__)\nprint(\"✅ Ready\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-04T04:44:09.458251Z","iopub.execute_input":"2026-09-04T04:44:09.459182Z","iopub.status.idle":"2026-09-04T04:44:10.499323Z","shell.execute_reply.started":"2026-09-04T04:44:09.459125Z","shell.execute_reply":"2026-09-04T04:44:10.498563Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nprint(os.path.exists(\"/kaggle/working/dr_model_best.pth\"))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-04T04:44:25.748412Z","iopub.execute_input":"2026-09-04T04:44:25.749653Z","iopub.status.idle":"2026-09-04T04:44:25.754886Z","shell.execute_reply.started":"2026-09-04T04:44:25.749613Z","shell.execute_reply":"2026-09-04T04:44:25.753879Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport timm\nimport os\n\ncheckpoint_path = \"/kaggle/working/dr_model_best.pth\"\nonnx_path = \"/kaggle/working/dr_model.onnx\"\n\ndevice = torch.device(\"cpu\")\n\nprint(\"Loading best model...\")\n\ncheckpoint = torch.load(\n    checkpoint_path,\n    map_location=device\n)\n\nmodel = timm.create_model(\n    \"efficientnet_b0\",\n    pretrained=False,\n    num_classes=5\n)\n\nmodel.load_state_dict(checkpoint[\"model_state_dict\"])\nmodel = model.to(device)\nmodel.eval()\n\nprint(\"Best Epoch:\", checkpoint[\"epoch\"])\nprint(f\"Best Validation Accuracy: {checkpoint['val_acc']*100:.2f}%\")\nprint(f\"Best Validation F1: {checkpoint['val_f1']*100:.2f}%\")\n\ndummy_input = torch.randn(\n    1, 3, 224, 224,\n    device=device\n)\n\nprint(\"\\nExporting ONNX...\")\n\ntorch.onnx.export(\n    model,\n    dummy_input,\n    onnx_path,\n    export_params=True,\n    opset_version=17,\n    do_constant_folding=True,\n    input_names=[\"input\"],\n    output_names=[\"output\"],\n    dynamic_axes={\n        \"input\": {0: \"batch_size\"},\n        \"output\": {0: \"batch_size\"}\n    },\n    dynamo=False\n)\n\nprint(\"\\n\" + \"=\"*60)\nprint(\"✅ ONNX EXPORT SUCCESSFUL\")\nprint(\"=\"*60)\nprint(\"File:\", onnx_path)\nprint(\n    \"Size:\",\n    f\"{os.path.getsize(onnx_path)/(1024**2):.2f} MB\"\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-04T04:45:09.113166Z","iopub.execute_input":"2026-09-04T04:45:09.11402Z","iopub.status.idle":"2026-09-04T04:45:15.944599Z","shell.execute_reply.started":"2026-09-04T04:45:09.113987Z","shell.execute_reply":"2026-09-04T04:45:15.943568Z"}},"outputs":[],"execution_count":null}]}