{"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},{"sourceType":"competition","sourceId":4104,"databundleVersionId":46661},{"sourceType":"kernelVersion","sourceId":299966990},{"sourceType":"kernelVersion","sourceId":307260411}],"dockerImageVersionId":31287,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport cv2\nimport time\nimport torch\nimport shutil\nimport pandas as pd\nimport numpy as np\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom torch.utils.data import Dataset, DataLoader, Subset\nfrom torchvision import transforms\nfrom sklearn.metrics import cohen_kappa_score, confusion_matrix, accuracy_score, precision_score, recall_score, f1_score\nfrom sklearn.model_selection import train_test_split\nfrom tqdm import tqdm\n\nCONFIG = {\n    \"image_resolution\": 1024,\n    \"batch_size\": 1, \n    \"total_epochs\": 100,\n    \"lr\": 5e-5,\n    \"embed_dim\": 256,\n    \"n_layers\": 12,\n    \"ckpt_dir\": \"/kaggle/working/checkpoints\",\n    \"aptos_path\": \"/kaggle/input/competitions/aptos2019-blindness-detection\",\n    \"dr_2015_path\": \"/kaggle/input/competitions/diabetic-retinopathy-detection\"\n}\n\ndef init_environment():\n    if not os.path.exists(CONFIG[\"ckpt_dir\"]):\n        os.makedirs(CONFIG[\"ckpt_dir\"], exist_ok=True)\n    if torch.cuda.is_available():\n        print(f\"[LOG] System Device: {torch.cuda.get_device_name(0)}\")\n    else:\n        print(\"[WARNING] CUDA is not available. Using CPU.\")\n\nclass SelectiveScanCell(nn.Module):\n    def __init__(self, dim, d_state=16, expand=2):\n        super().__init__()\n        self.d_inner = dim * expand\n        self.in_proj = nn.Linear(dim, self.d_inner * 2)\n        self.conv1d = nn.Conv1d(self.d_inner, self.d_inner, kernel_size=4, groups=self.d_inner, padding=3)\n        self.x_proj = nn.Linear(self.d_inner, d_state * 2 + self.d_inner)\n        self.dt_proj = nn.Linear(self.d_inner, self.d_inner)\n        self.A_log = nn.Parameter(torch.log(torch.arange(1, d_state + 1).float().repeat(self.d_inner, 1)))\n        self.D = nn.Parameter(torch.ones(self.d_inner))\n        self.out_proj = nn.Linear(self.d_inner, dim)\n\n    def forward(self, x):\n        b, l, d = x.shape\n        z = self.in_proj(x)\n        x, res = z.chunk(2, dim=-1)\n        x = x.transpose(1, 2)\n        x = self.conv1d(x)[:, :, :l]\n        x = F.silu(x).transpose(1, 2)\n        ssm_data = self.x_proj(x)\n        dt, B, C = torch.split(ssm_data, [self.d_inner, 16, 16], dim=-1)\n        dt = F.softplus(self.dt_proj(x))\n        A = -torch.exp(self.A_log)\n        y = x * torch.sigmoid(dt * A.mean(-1).view(1, 1, -1)) + res * torch.sigmoid(x)\n        return self.out_proj(y)\n\nclass ParallelVimLayer(nn.Module):\n    def __init__(self, dim):\n        super().__init__()\n        self.n_branches = 4\n        self.branch_dim = dim // self.n_branches\n        self.branches = nn.ModuleList([SelectiveScanCell(self.branch_dim) for _ in range(self.n_branches)])\n        self.norm = nn.LayerNorm(dim)\n        self.fusion = nn.Linear(dim, dim)\n\n    def forward(self, x):\n        h = self.norm(x)\n        chunks = torch.chunk(h, self.n_branches, dim=-1)\n        outs = [branch(chunk) for branch, chunk in zip(self.branches, chunks)]\n        return x + self.fusion(torch.cat(outs, dim=-1))\n\nclass VesselAwareBlock(nn.Module):\n    def __init__(self, dim):\n        super().__init__()\n        self.vessel_prior = nn.Sequential(\n            nn.Conv2d(dim, dim, kernel_size=3, padding=1, groups=dim),\n            nn.Sigmoid()\n        )\n        self.mamba = SelectiveScanCell(dim)\n        self.norm = nn.LayerNorm(dim)\n\n    def forward(self, x, spatial_shape):\n        b, l, d = x.shape\n        h, w = spatial_shape\n        feat_map = x.transpose(1, 2).view(b, d, h, w)\n        prior = self.vessel_prior(feat_map).view(b, d, l).transpose(1, 2)\n        out = self.mamba(self.norm(x))\n        return x + (out * prior)\n\nclass VimDRNetwork(nn.Module):\n    def __init__(self, embed_dim=256, depth=12):\n        super().__init__()\n        self.patch_embed = nn.Conv2d(3, embed_dim, kernel_size=16, stride=16)\n        self.layers = nn.ModuleList()\n        for i in range(depth):\n            if i % 2 == 0:\n                self.layers.append(ParallelVimLayer(embed_dim))\n            else:\n                self.layers.append(VesselAwareBlock(embed_dim))\n        self.reg_head = nn.Linear(embed_dim, 1)\n        self.pool = nn.AdaptiveAvgPool1d(1)\n\n    def forward(self, x):\n        b, c, h, w = x.shape\n        x = self.patch_embed(x)\n        spatial_shape = (x.shape[2], x.shape[3])\n        x = x.flatten(2).transpose(1, 2)\n        for layer in self.layers:\n            if isinstance(layer, VesselAwareBlock):\n                x = layer(x, spatial_shape)\n            else:\n                x = layer(x)\n        feat = self.pool(x.transpose(1, 2)).flatten(1)\n        return self.reg_head(feat)\n\nclass UnifiedDRDataset(Dataset):\n    def __init__(self, dataframe, train=True):\n        self.df = dataframe\n        self.tf = transforms.Compose([\n            transforms.ToPILImage(),\n            transforms.RandomHorizontalFlip() if train else transforms.Lambda(lambda x: x),\n            transforms.ToTensor(),\n            transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n        ])\n\n    def __len__(self): return len(self.df)\n\n    def _apply_ben_graham(self, path):\n        img = cv2.imread(path)\n        if img is None: return np.zeros((1024, 1024, 3), dtype=np.uint8)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        img = cv2.resize(img, (CONFIG[\"image_resolution\"], CONFIG[\"image_resolution\"]))\n        return cv2.addWeighted(img, 4, cv2.GaussianBlur(img, (0,0), 10), -4, 128)\n\n    def __getitem__(self, i):\n        row = self.df.iloc[i]\n        img = self._apply_ben_graham(row['path'])\n        return self.tf(img), torch.tensor(row['diagnosis'], dtype=torch.float32)\n\ndef find_latest_checkpoint():\n    latest_epoch = 0\n    latest_path = None\n    search_path = \"/kaggle/input\"\n    if os.path.exists(search_path):\n        for root, _, files in os.walk(search_path):\n            for f in files:\n                if \"vim_dr_epoch_\" in f and f.endswith(\".pth\"):\n                    try:\n                        num = int(f.split(\"_\")[-1].split(\".\")[0])\n                        if num > latest_epoch:\n                            latest_epoch = num\n                            latest_path = os.path.join(root, f)\n                    except: continue\n    return latest_epoch, latest_path\n\ndef run_experiment():\n    init_environment()\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    \n    aptos_csv = f'{CONFIG[\"aptos_path\"]}/train.csv'\n    aptos_df = pd.read_csv(aptos_csv)\n    aptos_df['path'] = aptos_df['id_code'].apply(lambda x: f'{CONFIG[\"aptos_path\"]}/train_images/{x}.png')\n    \n    dr2015_csv = f'{CONFIG[\"dr_2015_path\"]}/trainLabels.csv'\n    if os.path.exists(dr2015_csv):\n        dr2015_df = pd.read_csv(dr2015_csv)\n        dr2015_df['path'] = dr2015_df['image'].apply(lambda x: f'{CONFIG[\"dr_2015_path\"]}/train/{x}.jpeg')\n        dr2015_df.rename(columns={'level': 'diagnosis'}, inplace=True)\n        full_df = pd.concat([aptos_df[['path', 'diagnosis']], dr2015_df[['path', 'diagnosis']]], ignore_index=True)\n    else:\n        full_df = aptos_df[['path', 'diagnosis']]\n    \n    tr_idx, val_idx = train_test_split(full_df.index, test_size=0.15, random_state=42, stratify=full_df['diagnosis'])\n    train_loader = DataLoader(Subset(UnifiedDRDataset(full_df, True), tr_idx), batch_size=CONFIG[\"batch_size\"], shuffle=True)\n    val_loader = DataLoader(Subset(UnifiedDRDataset(full_df, False), val_idx), batch_size=CONFIG[\"batch_size\"])\n    \n    model = VimDRNetwork(embed_dim=CONFIG[\"embed_dim\"], depth=CONFIG[\"n_layers\"]).to(device)\n\n    latest_epoch, ckpt_path = find_latest_checkpoint()\n    start_epoch = 1\n    if ckpt_path:\n        print(f\"[RELOAD] Found latest checkpoint: {ckpt_path}\")\n        print(f\"[RELOAD] Resuming training from Epoch {latest_epoch + 1}...\")\n        model.load_state_dict(torch.load(ckpt_path, map_location=device))\n        start_epoch = latest_epoch + 1\n    else:\n        print(\"[NOTICE] No checkpoint found in input. Starting from scratch.\")\n\n    optimizer = torch.optim.AdamW(model.parameters(), lr=CONFIG[\"lr\"])\n    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=CONFIG[\"total_epochs\"])\n    \n    for _ in range(start_epoch - 1):\n        scheduler.step()\n\n    criterion = nn.MSELoss()\n    \n    for epoch in range(start_epoch, CONFIG[\"total_epochs\"] + 1):\n        model.train()\n        train_mse = 0\n        pbar = tqdm(train_loader, desc=f\"Epoch {epoch}/{CONFIG['total_epochs']}\")\n        for img, lbl in pbar:\n            img, lbl = img.to(device), lbl.to(device).view(-1, 1)\n            optimizer.zero_grad()\n            out = model(img)\n            loss = criterion(out, lbl)\n            loss.backward()\n            optimizer.step()\n            train_mse += loss.item()\n            pbar.set_postfix({'MSE': f\"{loss.item():.4f}\"})\n        \n        scheduler.step()\n        model.eval()\n        \n        save_path = f\"{CONFIG['ckpt_dir']}/vim_dr_epoch_{epoch}.pth\"\n        torch.save(model.state_dict(), save_path)\n        print(f\"[SAVE] Epoch {epoch} completed and saved.\")\n\nif __name__ == \"__main__\":\n    run_experiment()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- FINAL EVALUATION & REPORT GENERATION BLOCK (EXACT PATH FIXED) ---\nimport os\nimport cv2\nimport torch\nimport numpy as np\nimport pandas as pd\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm import tqdm\nfrom torch.utils.data import Dataset, DataLoader, Subset\nfrom torchvision import transforms\nfrom sklearn.metrics import confusion_matrix, cohen_kappa_score, accuracy_score, precision_score, recall_score, f1_score\nfrom sklearn.model_selection import train_test_split\n\n# 1. Device and Basic Config\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"[LOG] Using device: {device}\")\n\n\nCHECKPOINT_PATH = \"/kaggle/input/notebooks/mshahadath/fork-of-mamba-based-retinal-disease-diagnosis/checkpoints/vim_dr_epoch_100.pth\"\n\nCONFIG = {\n    \"image_resolution\": 1024,\n    \"batch_size\": 1,\n    \"embed_dim\": 256,\n    \"n_layers\": 12\n}\n\n# --- মডেল আর্কিটেকচার (VimDRNetwork এবং অন্যান্য ক্লাস) ---\nclass SelectiveScanCell(nn.Module):\n    def __init__(self, dim, d_state=16, expand=2):\n        super().__init__()\n        self.d_inner = dim * expand\n        self.in_proj = nn.Linear(dim, self.d_inner * 2)\n        self.conv1d = nn.Conv1d(self.d_inner, self.d_inner, kernel_size=4, groups=self.d_inner, padding=3)\n        self.x_proj = nn.Linear(self.d_inner, d_state * 2 + self.d_inner)\n        self.dt_proj = nn.Linear(self.d_inner, self.d_inner)\n        self.A_log = nn.Parameter(torch.log(torch.arange(1, d_state + 1).float().repeat(self.d_inner, 1)))\n        self.D = nn.Parameter(torch.ones(self.d_inner))\n        self.out_proj = nn.Linear(self.d_inner, dim)\n    def forward(self, x):\n        b, l, d = x.shape\n        z = self.in_proj(x)\n        x, res = z.chunk(2, dim=-1)\n        x = x.transpose(1, 2)\n        x = self.conv1d(x)[:, :, :l]\n        x = F.silu(x).transpose(1, 2)\n        ssm_data = self.x_proj(x)\n        dt, B, C = torch.split(ssm_data, [self.d_inner, 16, 16], dim=-1)\n        dt = F.softplus(self.dt_proj(x))\n        A = -torch.exp(self.A_log)\n        y = x * torch.sigmoid(dt * A.mean(-1).view(1, 1, -1)) + res * torch.sigmoid(x)\n        return self.out_proj(y)\n\nclass ParallelVimLayer(nn.Module):\n    def __init__(self, dim):\n        super().__init__()\n        self.n_branches = 4\n        self.branch_dim = dim // self.n_branches\n        self.branches = nn.ModuleList([SelectiveScanCell(self.branch_dim) for _ in range(self.n_branches)])\n        self.norm = nn.LayerNorm(dim)\n        self.fusion = nn.Linear(dim, dim)\n    def forward(self, x):\n        h = self.norm(x)\n        chunks = torch.chunk(h, self.n_branches, dim=-1)\n        outs = [branch(chunk) for branch, chunk in zip(self.branches, chunks)]\n        return x + self.fusion(torch.cat(outs, dim=-1))\n\nclass VesselAwareBlock(nn.Module):\n    def __init__(self, dim):\n        super().__init__()\n        self.vessel_prior = nn.Sequential(nn.Conv2d(dim, dim, kernel_size=3, padding=1, groups=dim), nn.Sigmoid())\n        self.mamba = SelectiveScanCell(dim)\n        self.norm = nn.LayerNorm(dim)\n    def forward(self, x, spatial_shape):\n        b, l, d = x.shape\n        h, w = spatial_shape\n        feat_map = x.transpose(1, 2).view(b, d, h, w)\n        prior = self.vessel_prior(feat_map).view(b, d, l).transpose(1, 2)\n        out = self.mamba(self.norm(x))\n        return x + (out * prior)\n\nclass VimDRNetwork(nn.Module):\n    def __init__(self, embed_dim=256, depth=12):\n        super().__init__()\n        self.patch_embed = nn.Conv2d(3, embed_dim, kernel_size=16, stride=16)\n        self.layers = nn.ModuleList()\n        for i in range(depth):\n            if i % 2 == 0: self.layers.append(ParallelVimLayer(embed_dim))\n            else: self.layers.append(VesselAwareBlock(embed_dim))\n        self.reg_head = nn.Linear(embed_dim, 1)\n        self.pool = nn.AdaptiveAvgPool1d(1)\n    def forward(self, x):\n        b, c, h, w = x.shape\n        x = self.patch_embed(x)\n        spatial_shape = (x.shape[2], x.shape[3])\n        x = x.flatten(2).transpose(1, 2)\n        for layer in self.layers:\n            if isinstance(layer, VesselAwareBlock): x = layer(x, spatial_shape)\n            else: x = layer(x)\n        feat = self.pool(x.transpose(1, 2)).flatten(1)\n        return self.reg_head(feat)\n\nclass UnifiedDRDataset(Dataset):\n    def __init__(self, dataframe):\n        self.df = dataframe\n        self.tf = transforms.Compose([transforms.ToPILImage(), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])\n    def __len__(self): return len(self.df)\n    def _apply_ben_graham(self, path):\n        img = cv2.imread(path)\n        if img is None: return np.zeros((1024, 1024, 3), dtype=np.uint8)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        img = cv2.resize(img, (1024, 1024))\n        return cv2.addWeighted(img, 4, cv2.GaussianBlur(img, (0,0), 10), -4, 128)\n    def __getitem__(self, i):\n        row = self.df.iloc[i]\n        img = self._apply_ben_graham(row['path'])\n        return self.tf(img), torch.tensor(row['diagnosis'], dtype=torch.float32)\n\n# --- EXECUTION ---\nprint(\"[STEP 1] Detecting APTOS Dataset...\")\n\ndataset_root = None\nfor root, dirs, files in os.walk('/kaggle/input'):\n    if \"aptos2019-blindness-detection\" in root and \"train.csv\" in files:\n        dataset_root = root\n        break\n\nif dataset_root:\n    aptos_csv = os.path.join(dataset_root, \"train.csv\")\n    aptos_df = pd.read_csv(aptos_csv)\n    aptos_df['path'] = aptos_df['id_code'].apply(lambda x: os.path.join(dataset_root, \"train_images\", f\"{x}.png\"))\n    _, val_idx = train_test_split(aptos_df.index, test_size=0.15, random_state=42, stratify=aptos_df['diagnosis'])\n    val_loader = DataLoader(Subset(UnifiedDRDataset(aptos_df), val_idx), batch_size=1)\n    \n    print(\"[STEP 2] Loading Weights from Input Path...\")\n    model = VimDRNetwork(embed_dim=CONFIG[\"embed_dim\"], depth=CONFIG[\"n_layers\"]).to(device)\n\n    if os.path.exists(CHECKPOINT_PATH):\n        model.load_state_dict(torch.load(CHECKPOINT_PATH, map_location=device))\n        model.eval()\n        print(f\"[SUCCESS] Weights Loaded from: {CHECKPOINT_PATH}\")\n        \n        final_preds, final_targets = [], []\n        with torch.no_grad():\n            for img, lbl in tqdm(val_loader, desc=\"Evaluation\"):\n                img = img.to(device)\n                out = model(img)\n                final_preds.extend(torch.clamp(torch.round(out), 0, 4).cpu().numpy().flatten())\n                final_targets.extend(lbl.numpy().flatten())\n\n        y_true, y_pred = np.array(final_targets).astype(int), np.array(final_preds).astype(int)\n        \n        print(\"\\n\" + \"=\"*40 + \"\\nFINAL RESEARCH EVALUATION\\n\" + \"=\"*40)\n        print(f\"Quadratic Kappa: {cohen_kappa_score(y_true, y_pred, weights='quadratic'):.4f}\")\n        print(f\"Accuracy: {accuracy_score(y_true, y_pred):.4f}\")\n        print(f\"Precision Score: {precision_score(y_true, y_pred, average='weighted'):.4f}\")\n        print(f\"Recall (Sensitivity): {recall_score(y_true, y_pred, average='weighted'):.4f}\")\n        print(f\"F1-Score: {f1_score(y_true, y_pred, average='weighted'):.4f}\")\n\n        cm = confusion_matrix(y_true, y_pred)\n        plt.figure(figsize=(8, 6)); sns.heatmap(cm, annot=True, fmt='d', cmap='viridis')\n        plt.title('Final Confusion Matrix (Epoch 100)'); plt.xlabel('Predicted'); plt.ylabel('Actual'); plt.show()\n    else:\n        print(f\"[ERROR] Checkpoint not found at: {CHECKPOINT_PATH}\")\nelse:\n    print(\"[ERROR] APTOS Dataset folder not found. Please ensure it is added to the notebook.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T06:38:19.136026Z","iopub.execute_input":"2026-03-29T06:38:19.136385Z","iopub.status.idle":"2026-03-29T06:40:29.185680Z","shell.execute_reply.started":"2026-03-29T06:38:19.136357Z","shell.execute_reply":"2026-03-29T06:40:29.184896Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- ADVANCED RESEARCH METRICS & CURVES BLOCK ---\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.metrics import classification_report, roc_curve, auc, precision_recall_curve, average_precision_score\nfrom sklearn.preprocessing import label_binarize\nfrom itertools import cycle\n\n# 1. Detailed Classification Report (Class-wise Precision, Recall, F1)\ntarget_names = ['Normal (0)', 'Mild (1)', 'Moderate (2)', 'Severe (3)', 'Proliferative (4)']\nprint(\"\\n\" + \"=\"*60)\nprint(\"       CLASS-WISE PERFORMANCE EVALUATION (EPOCH 100)\")\nprint(\"=\"*60)\n\n# Ensure y_true and y_pred are available from the previous cell\ntry:\n    print(classification_report(y_true, y_pred, target_names=target_names))\nexcept NameError:\n    print(\"[ERROR] y_true or y_pred not found. Please run the evaluation cell first.\")\n\n# 2. Data Preparation for ROC and PR Curves\n# Binarize labels for multi-class plotting\ny_true_bin = label_binarize(y_true, classes=[0, 1, 2, 3, 4])\ny_pred_bin = label_binarize(y_pred, classes=[0, 1, 2, 3, 4])\nn_classes = y_true_bin.shape[1]\n\n# 3. Visualization (Plots)\nplt.figure(figsize=(18, 7))\ncolors = cycle(['aqua', 'darkorange', 'cornflowerblue', 'red', 'darkmagenta'])\n\n# --- Subplot 1: ROC Curve (Receiver Operating Characteristic) ---\nplt.subplot(1, 2, 1)\nfor i, color in zip(range(n_classes), colors):\n    fpr, tpr, _ = roc_curve(y_true_bin[:, i], y_pred_bin[:, i])\n    roc_auc = auc(fpr, tpr)\n    plt.plot(fpr, tpr, color=color, lw=2,\n             label=f'Class {i} ROC (area = {roc_auc:.2f})')\n\nplt.plot([0, 1], [0, 1], 'k--', lw=2)\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel('False Positive Rate (1-Specificity)')\nplt.ylabel('True Positive Rate (Sensitivity)')\nplt.title('Multi-class ROC Curve Analysis')\nplt.legend(loc=\"lower right\")\nplt.grid(alpha=0.3)\n\n# --- Subplot 2: Precision-Recall Curve ---\nplt.subplot(1, 2, 2)\nfor i, color in zip(range(n_classes), colors):\n    precision, recall, _ = precision_recall_curve(y_true_bin[:, i], y_pred_bin[:, i])\n    avg_precision = average_precision_score(y_true_bin[:, i], y_pred_bin[:, i])\n    plt.plot(recall, precision, color=color, lw=2,\n             label=f'Class {i} PR (Avg = {avg_precision:.2f})')\n\nplt.xlabel('Recall (Sensitivity)')\nplt.ylabel('Precision (Positive Predictive Value)')\nplt.title('Multi-class Precision-Recall Curve Analysis')\nplt.legend(loc=\"lower left\")\nplt.grid(alpha=0.3)\n\nplt.tight_layout()\n# Save as high-resolution image for thesis/paper\nplt.savefig('/kaggle/working/final_research_evaluation_curves.png', dpi=300)\nplt.show()\n\nprint(\"\\n[INFO] All curves and reports have been generated and saved to /kaggle/working/\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T06:52:00.907402Z","iopub.execute_input":"2026-03-29T06:52:00.908269Z","iopub.status.idle":"2026-03-29T06:52:02.597013Z","shell.execute_reply.started":"2026-03-29T06:52:00.908234Z","shell.execute_reply":"2026-03-29T06:52:02.596191Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- SAMPLE PREDICTION VISUALIZER ---\ndef visualize_predictions(model, loader, n_samples=5):\n    model.eval()\n    samples_shown = 0\n    plt.figure(figsize=(20, 10))\n    \n    with torch.no_grad():\n        for img, lbl in loader:\n            if samples_shown >= n_samples: break\n            \n            img_in = img.to(device)\n            out = model(img_in)\n            pred = torch.clamp(torch.round(out), 0, 4).item()\n            actual = lbl.item()\n            \n           \n            img_display = img[0].permute(1, 2, 0).cpu().numpy()\n            img_display = (img_display * 0.229) + 0.485 # Standard ImageNet denormalization\n            \n            plt.subplot(1, n_samples, samples_shown + 1)\n            plt.imshow(np.clip(img_display, 0, 1))\n            color = 'green' if int(pred) == int(actual) else 'red'\n            plt.title(f\"Actual: {int(actual)}\\nPred: {int(pred)}\", color=color)\n            plt.axis('off')\n            samples_shown += 1\n            \n    plt.tight_layout()\n    plt.show()\n\n\nvisualize_predictions(model, val_loader, n_samples=5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T06:53:30.600692Z","iopub.execute_input":"2026-03-29T06:53:30.601585Z","iopub.status.idle":"2026-03-29T06:53:33.288896Z","shell.execute_reply.started":"2026-03-29T06:53:30.601539Z","shell.execute_reply":"2026-03-29T06:53:33.288041Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- FINAL SAMPLE PREDICTIONS FOR THESIS ---\ndef show_samples(model, loader, num=4):\n    model.eval()\n    plt.figure(figsize=(16, 8))\n    with torch.no_grad():\n        for i, (img, lbl) in enumerate(loader):\n            if i >= num: break\n            out = model(img.to(device))\n            pred = torch.clamp(torch.round(out), 0, 4).item()\n            \n            img_np = img[0].permute(1, 2, 0).cpu().numpy()\n            img_np = (img_np * 0.225) + 0.45 # Denormalize\n            \n            plt.subplot(1, num, i+1)\n            plt.imshow(np.clip(img_np, 0, 1))\n            plt.title(f\"Actual: {int(lbl.item())}\\nPredicted: {int(pred)}\", \n                      color='green' if int(pred)==int(lbl.item()) else 'red')\n            plt.axis('off')\n    plt.savefig('/kaggle/working/sample_predictions.png')\n    plt.show()\n\nshow_samples(model, val_loader)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T06:56:09.772176Z","iopub.execute_input":"2026-03-29T06:56:09.772893Z","iopub.status.idle":"2026-03-29T06:56:12.314810Z","shell.execute_reply.started":"2026-03-29T06:56:09.772862Z","shell.execute_reply":"2026-03-29T06:56:12.313798Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install ptflops","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T08:15:08.295451Z","iopub.execute_input":"2026-03-29T08:15:08.296060Z","iopub.status.idle":"2026-03-29T08:15:13.968308Z","shell.execute_reply.started":"2026-03-29T08:15:08.296029Z","shell.execute_reply":"2026-03-29T08:15:13.967543Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"try:\n    from ptflops import get_model_complexity_info\nexcept ImportError:\n    import subprocess\n    import sys\n    subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"ptflops\"])\n    from ptflops import get_model_complexity_info\n\nimport torch\nimport torchvision.models as models\n\ndef calculate_model_metrics(model_name='resnet50'):\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    \n    try:\n        model = getattr(models, model_name)(weights=None).to(device)\n    except (TypeError, AttributeError):\n        model = getattr(models, model_name)(pretrained=False).to(device)\n\n    macs, params = get_model_complexity_info(model, (3, 224, 224), \n                                            as_strings=False,\n                                            print_per_layer_stat=False, \n                                            verbose=False)\n\n    params_m = params / 1e6\n    gflops = (macs * 2) / 1e9  \n    energy_wh = (gflops * 7.2) / 3600 \n\n    return params_m, energy_wh\n\np_m, e_wh = calculate_model_metrics('resnet50')\nprint(f\"Total Parameters: {p_m:.2f} M\")\nprint(f\"Energy/Inference: {e_wh:.10f} Wh\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T08:16:08.314023Z","iopub.execute_input":"2026-03-29T08:16:08.314370Z","iopub.status.idle":"2026-03-29T08:16:15.756785Z","shell.execute_reply.started":"2026-03-29T08:16:08.314337Z","shell.execute_reply":"2026-03-29T08:16:15.756083Z"}},"outputs":[],"execution_count":null}]}