{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.11.13","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":13836,"databundleVersionId":1718836},{"sourceType":"datasetVersion","sourceId":11715121,"datasetId":7353604,"databundleVersionId":12192679},{"sourceType":"datasetVersion","sourceId":11681709,"datasetId":7331722,"databundleVersionId":12154950}],"dockerImageVersionId":31193,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":88.283622,"end_time":"2025-05-07T09:49:59.044636","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-05-07T09:48:30.761014","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport json\nimport random\nimport numpy as np\nimport pandas as pd\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport torchvision.models as models\n\nfrom PIL import Image\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import confusion_matrix, classification_report\n\nimport matplotlib.pyplot as plt\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:03:19.049394Z","iopub.execute_input":"2026-04-12T15:03:19.049734Z","iopub.status.idle":"2026-04-12T15:03:27.022593Z","shell.execute_reply.started":"2026-04-12T15:03:19.049705Z","shell.execute_reply":"2026-04-12T15:03:27.021937Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATA_DIR = \"../input/cassava-leaf-disease-classification\"\nTRAIN_DIR = os.path.join(DATA_DIR, \"train_images\")\nTEST_DIR = os.path.join(DATA_DIR, \"test_images\")\n\ntrain_df = pd.read_csv(os.path.join(DATA_DIR, \"train.csv\"))\nlabel_map = json.load(open(os.path.join(DATA_DIR, \"label_num_to_disease_map.json\")))\nlabel_map = {int(k): v for k, v in label_map.items()}\n\nIMG_SIZE = 128\n\ntrain_transforms = transforms.Compose([\n    transforms.Resize((150,150)),\n    transforms.RandomResizedCrop(128),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])\n])\n\nval_transforms = transforms.Compose([\n    transforms.Resize((128,128)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])\n])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:03:27.023606Z","iopub.execute_input":"2026-04-12T15:03:27.024026Z","iopub.status.idle":"2026-04-12T15:03:27.102700Z","shell.execute_reply.started":"2026-04-12T15:03:27.024006Z","shell.execute_reply":"2026-04-12T15:03:27.102109Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CassavaDataset(Dataset):\n    def __init__(self, df, img_dir, transform=None):\n        self.df = df.reset_index(drop=True)\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        row = self.df.iloc[idx]\n        img = Image.open(os.path.join(self.img_dir, row[\"image_id\"])).convert(\"RGB\")\n\n        if self.transform:\n            img = self.transform(img)\n\n        return img, int(row[\"label\"])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:03:27.103353Z","iopub.execute_input":"2026-04-12T15:03:27.103530Z","iopub.status.idle":"2026-04-12T15:03:27.108683Z","shell.execute_reply.started":"2026-04-12T15:03:27.103515Z","shell.execute_reply":"2026-04-12T15:03:27.107719Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_resnet50_model(num_classes=5, freeze_backbone=True):\n    model = models.resnet50(weights=None)   # <<< NO DOWNLOAD\n\n    # Replace final FC layer\n    model.fc = nn.Linear(model.fc.in_features, num_classes)\n\n    if freeze_backbone:\n        for name, p in model.named_parameters():\n            if \"fc\" not in name:\n                p.requires_grad = False\n\n    return model\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:03:27.110438Z","iopub.execute_input":"2026-04-12T15:03:27.110885Z","iopub.status.idle":"2026-04-12T15:03:27.123772Z","shell.execute_reply.started":"2026-04-12T15:03:27.110866Z","shell.execute_reply":"2026-04-12T15:03:27.122840Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import random_split\n\n# Define the split sizes (e.g., 80% train, 20% validation)\ntrain_size = int(0.8 * len(train_df))\nval_size = len(train_df) - train_size\n\n# Perform the split\ntrain_subset, val_subset = random_split(train_df, [train_size, val_size])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:17:32.178429Z","iopub.execute_input":"2026-04-12T15:17:32.179248Z","iopub.status.idle":"2026-04-12T15:17:32.184609Z","shell.execute_reply.started":"2026-04-12T15:17:32.179220Z","shell.execute_reply":"2026-04-12T15:17:32.183637Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import StratifiedKFold\n\n# 1. Initialize Fold column on the original DataFrame\nskf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\ntrain_df[\"fold\"] = -1\n\n# 2. Assign folds based on the 'label' to keep classes balanced\nfor fold, (_, val_idx) in enumerate(skf.split(train_df[\"image_id\"], train_df[\"label\"])):\n    train_df.loc[val_idx, \"fold\"] = fold\n\n# 3. Pick which fold to use for validation (e.g., Fold 0)\nFOLD = 0 \n\ndf_train = train_df[train_df.fold != FOLD].reset_index(drop=True)\ndf_val   = train_df[train_df.fold == FOLD].reset_index(drop=True)\n\n# 4. Create your Loaders\ntrain_loader = DataLoader(CassavaDataset(df_train, TRAIN_DIR, train_transforms),\n                          batch_size=32, shuffle=True)\nval_loader   = DataLoader(CassavaDataset(df_val, TRAIN_DIR, val_transforms),\n                          batch_size=32, shuffle=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:20:58.036184Z","iopub.execute_input":"2026-04-12T15:20:58.036665Z","iopub.status.idle":"2026-04-12T15:20:58.056175Z","shell.execute_reply.started":"2026-04-12T15:20:58.036641Z","shell.execute_reply":"2026-04-12T15:20:58.055102Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(f\"Items in Train Loader: {len(train_loader.dataset)}\")\nprint(f\"Items in Val Loader: {len(val_loader.dataset)}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:21:01.143542Z","iopub.execute_input":"2026-04-12T15:21:01.144222Z","iopub.status.idle":"2026-04-12T15:21:01.148177Z","shell.execute_reply.started":"2026-04-12T15:21:01.144187Z","shell.execute_reply":"2026-04-12T15:21:01.147328Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_one_epoch(model, optimizer, loader, criterion):\n    model.train()\n    total, correct, loss_sum = 0, 0, 0\n\n    for x, y in loader:\n        x, y = x.to(device), y.to(device)\n\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() * x.size(0)\n        correct += (out.argmax(1) == y).sum().item()\n        total += y.size(0)\n\n    return loss_sum / total, correct / total\n\ndef validate(model, loader, criterion):\n    model.eval()\n    total, correct, loss_sum = 0, 0, 0\n    y_true, y_pred = [], []\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            loss = criterion(out, y)\n\n            loss_sum += loss.item() * x.size(0)\n            preds = out.argmax(1)\n\n            correct += (preds == y).sum().item()\n            total += y.size(0)\n\n            y_true.extend(y.cpu().numpy())\n            y_pred.extend(preds.cpu().numpy())\n\n    return loss_sum / total, correct / total, y_true, y_pred\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:21:03.953893Z","iopub.execute_input":"2026-04-12T15:21:03.954877Z","iopub.status.idle":"2026-04-12T15:21:03.962109Z","shell.execute_reply.started":"2026-04-12T15:21:03.954847Z","shell.execute_reply":"2026-04-12T15:21:03.961290Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_losses_res, val_losses_res = [], []\ntrain_accs_res, val_accs_res = [], []\n\ny_true_res = []\ny_pred_res = []\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:21:07.753505Z","iopub.execute_input":"2026-04-12T15:21:07.753996Z","iopub.status.idle":"2026-04-12T15:21:07.758041Z","shell.execute_reply.started":"2026-04-12T15:21:07.753971Z","shell.execute_reply":"2026-04-12T15:21:07.757193Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nprint(\"Device:\", device)\n\nEPOCHS = 20\ncriterion = nn.CrossEntropyLoss()\n\nresnet_model = get_resnet50_model(num_classes=5, freeze_backbone=True).to(device)\noptimizer_resnet = torch.optim.Adam(\n    filter(lambda p: p.requires_grad, resnet_model.parameters()), \n    lr=2e-4\n)\n\nfor epoch in range(EPOCHS):\n    tr_loss, tr_acc = train_one_epoch(resnet_model, optimizer_resnet, train_loader, criterion)\n    val_loss, val_acc, y_t, y_p = validate(resnet_model, val_loader, criterion)\n\n    train_losses_res.append(tr_loss)\n    val_losses_res.append(val_loss)\n    train_accs_res.append(tr_acc)\n    val_accs_res.append(val_acc)\n\n    y_true_res = y_t\n    y_pred_res = y_p\n\n    print(f\"Epoch {epoch+1}/{EPOCHS}\",\n          f\"Train Acc={tr_acc:.4f}\", f\"Val Acc={val_acc:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:21:10.506707Z","iopub.execute_input":"2026-04-12T15:21:10.507491Z","iopub.status.idle":"2026-04-12T15:25:50.421747Z","shell.execute_reply.started":"2026-04-12T15:21:10.507463Z","shell.execute_reply":"2026-04-12T15:25:50.420874Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.plot(train_accs_res, label=\"Train Accuracy\")\nplt.plot(val_accs_res, label=\"Val Accuracy\")\nplt.legend()\nplt.title(\"ResNet50 Accuracy Curve\")\nplt.show()\n\nplt.plot(train_losses_res, label=\"Train Loss\")\nplt.plot(val_losses_res, label=\"Val Loss\")\nplt.legend()\nplt.title(\"ResNet50 Loss Curve\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:25:54.893665Z","iopub.execute_input":"2026-04-12T15:25:54.894445Z","iopub.status.idle":"2026-04-12T15:25:55.267589Z","shell.execute_reply.started":"2026-04-12T15:25:54.894408Z","shell.execute_reply":"2026-04-12T15:25:55.266770Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix\nimport seaborn as sns\n\ncm = confusion_matrix(y_true_res, y_pred_res)\nplt.figure(figsize=(6,6))\nsns.heatmap(cm, annot=True, fmt=\"d\", cmap=\"Blues\")\nplt.title(\"ResNet50 Confusion Matrix\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:26:18.058499Z","iopub.execute_input":"2026-04-12T15:26:18.059231Z","iopub.status.idle":"2026-04-12T15:26:18.758224Z","shell.execute_reply.started":"2026-04-12T15:26:18.059206Z","shell.execute_reply":"2026-04-12T15:26:18.757327Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"@torch.no_grad()\ndef resnet_predict(model,loader):\n    model.eval()\n    \n    final_preds = []\n    final_labels = []\n\n    for x, y in loader:\n        x = x.to(device)\n        y = y.to(device)\n\n        p2 = F.softmax(model(x), dim=1)   # ResNet50\n\n        preds = p2.argmax(1)\n\n        final_preds.extend(preds.cpu().numpy())\n        final_labels.extend(y.cpu().numpy())\n\n    return np.array(final_labels), np.array(final_preds)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:27:49.508326Z","iopub.execute_input":"2026-04-12T15:27:49.508731Z","iopub.status.idle":"2026-04-12T15:27:49.514182Z","shell.execute_reply.started":"2026-04-12T15:27:49.508706Z","shell.execute_reply":"2026-04-12T15:27:49.513338Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Running ResNet...\")\ny_true_res, y_pred_res = resnet_predict(resnet_model, val_loader)\n\nresnet_accuracy = (y_true_res == y_pred_res).mean()\nprint(\"ResNet Accuracy:\", resnet_accuracy)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:29:00.893551Z","iopub.execute_input":"2026-04-12T15:29:00.894361Z","iopub.status.idle":"2026-04-12T15:29:43.231482Z","shell.execute_reply.started":"2026-04-12T15:29:00.894330Z","shell.execute_reply":"2026-04-12T15:29:43.230768Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(7,5))\n\nplt.plot(train_losses_res, label=\"ResNet50 Train Loss\", linestyle=\"--\")\nplt.plot(val_losses_res, label=\"ResNet50 Val Loss\")\n\nplt.title(\"Loss Curve: ResNet50\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.grid(True)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:47:06.983310Z","iopub.execute_input":"2026-04-12T15:47:06.984218Z","iopub.status.idle":"2026-04-12T15:47:07.160954Z","shell.execute_reply.started":"2026-04-12T15:47:06.984193Z","shell.execute_reply":"2026-04-12T15:47:07.160371Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"accuracies = [\n    val_accs_res[-1],\n]\n\nplt.figure(figsize=(7,5))\nbars = plt.bar([\"ResNet50\"], accuracies,\n               color=[\"skyblue\"], width=0.6)\n\nfor bar in bars:\n    yval = bar.get_height()\n    plt.text(bar.get_x() + bar.get_width()/2, yval + 0.005,\n             f\"{yval:.3f}\", ha=\"center\", fontsize=12)\n\nplt.title(\"Final Accuracy Comparison\")\nplt.ylabel(\"Accuracy\")\nplt.ylim(0, 1)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:47:10.896807Z","iopub.execute_input":"2026-04-12T15:47:10.897041Z","iopub.status.idle":"2026-04-12T15:47:11.015887Z","shell.execute_reply.started":"2026-04-12T15:47:10.897025Z","shell.execute_reply":"2026-04-12T15:47:11.015207Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, ax = plt.subplots(1, 3, figsize=(18,5))\n\ncm_res = confusion_matrix(y_true_res, y_pred_res)\n\n\n\nsns.heatmap(cm_res, annot=True, fmt=\"d\", cmap=\"Greens\", ax=ax[1])\nax[1].set_title(\"ResNet50 Confusion Matrix\")\n\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T15:47:16.526905Z","iopub.execute_input":"2026-04-12T15:47:16.527682Z","iopub.status.idle":"2026-04-12T15:47:16.937041Z","shell.execute_reply.started":"2026-04-12T15:47:16.527656Z","shell.execute_reply":"2026-04-12T15:47:16.936418Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import glob\n\n# 1. Prepare Test Data\ntest_image_paths = glob.glob(os.path.join(TEST_DIR, \"*.jpg\"))\ntest_df = pd.DataFrame({'image_id': [os.path.basename(x) for x in test_image_paths]})\n\nclass CassavaTestDataset(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    def __len__(self):\n        return len(self.df)\n    def __getitem__(self, idx):\n        img_id = self.df.iloc[idx]['image_id']\n        img = Image.open(os.path.join(self.img_dir, img_id)).convert(\"RGB\")\n        if self.transform:\n            img = self.transform(img)\n        return img\n\ntest_loader = DataLoader(\n    CassavaTestDataset(test_df, TEST_DIR, val_transforms), \n    batch_size=32, shuffle=False\n)\n\n# 2. Perform Inference\nresnet_model.eval()\ntest_preds = []\n\nwith torch.no_grad():\n    for imgs in test_loader:\n        imgs = imgs.to(device)\n        outputs = resnet_model(imgs)\n        # Get the index of the highest probability class\n        preds = torch.argmax(outputs, dim=1)\n        test_preds.extend(preds.cpu().numpy())\n\n# 3. Create Submission File\ntest_df['label'] = test_preds\ntest_df.to_csv(\"submission.csv\", index=False)\n\nprint(f\"✅ Successfully predicted {len(test_df)} images.\")\nprint(test_df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-12T16:38:21.923628Z","iopub.execute_input":"2026-04-12T16:38:21.924291Z","iopub.status.idle":"2026-04-12T16:38:22.051409Z","shell.execute_reply.started":"2026-04-12T16:38:21.924261Z","shell.execute_reply":"2026-04-12T16:38:22.050732Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}