{"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":[{"sourceId":13836,"databundleVersionId":1718836,"sourceType":"competition"},{"sourceId":11681709,"sourceType":"datasetVersion","datasetId":7331722},{"sourceId":11715121,"sourceType":"datasetVersion","datasetId":7353604}],"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":"2025-12-02T07:21:09.078828Z","iopub.execute_input":"2025-12-02T07:21:09.079125Z","iopub.status.idle":"2025-12-02T07:21:09.084355Z","shell.execute_reply.started":"2025-12-02T07:21:09.079105Z","shell.execute_reply":"2025-12-02T07:21:09.083452Z"}},"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":"2025-12-02T07:21:09.085404Z","iopub.execute_input":"2025-12-02T07:21:09.085675Z","iopub.status.idle":"2025-12-02T07:21:09.114138Z","shell.execute_reply.started":"2025-12-02T07:21:09.085640Z","shell.execute_reply":"2025-12-02T07:21:09.113669Z"}},"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":"2025-12-02T07:21:09.114902Z","iopub.execute_input":"2025-12-02T07:21:09.115065Z","iopub.status.idle":"2025-12-02T07:21:09.119479Z","shell.execute_reply.started":"2025-12-02T07:21:09.115053Z","shell.execute_reply":"2025-12-02T07:21:09.118726Z"}},"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":"2025-12-02T07:21:09.120613Z","iopub.execute_input":"2025-12-02T07:21:09.120790Z","iopub.status.idle":"2025-12-02T07:21:09.135868Z","shell.execute_reply.started":"2025-12-02T07:21:09.120777Z","shell.execute_reply":"2025-12-02T07:21:09.135116Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)\ntrain_df[\"fold\"] = -1\n\nfor fold, (_, val_idx) in enumerate(skf.split(train_df[\"image_id\"], train_df[\"label\"])):\n    train_df.loc[val_idx, \"fold\"] = fold\n\nFOLD = 0\n\ndf_train = train_df[train_df.fold != FOLD]\ndf_val   = train_df[train_df.fold == FOLD]\n\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":"2025-12-02T07:21:09.136604Z","iopub.execute_input":"2025-12-02T07:21:09.136788Z","iopub.status.idle":"2025-12-02T07:21:09.162698Z","shell.execute_reply.started":"2025-12-02T07:21:09.136773Z","shell.execute_reply":"2025-12-02T07:21:09.161898Z"}},"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":"2025-12-02T07:21:09.163535Z","iopub.execute_input":"2025-12-02T07:21:09.164392Z","iopub.status.idle":"2025-12-02T07:21:09.171078Z","shell.execute_reply.started":"2025-12-02T07:21:09.164374Z","shell.execute_reply":"2025-12-02T07:21:09.170427Z"}},"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":"2025-12-02T07:36:05.106388Z","iopub.execute_input":"2025-12-02T07:36:05.107235Z","iopub.status.idle":"2025-12-02T07:36:05.111168Z","shell.execute_reply.started":"2025-12-02T07:36:05.107207Z","shell.execute_reply":"2025-12-02T07:36:05.110265Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nprint(\"Device:\", device)\n\nEPOCHS = 2\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":"2025-12-02T07:36:26.742721Z","iopub.execute_input":"2025-12-02T07:36:26.743347Z","iopub.status.idle":"2025-12-02T07:43:30.219781Z","shell.execute_reply.started":"2025-12-02T07:36:26.743326Z","shell.execute_reply":"2025-12-02T07:43:30.219111Z"}},"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":"2025-12-02T07:43:35.643963Z","iopub.execute_input":"2025-12-02T07:43:35.644254Z","iopub.status.idle":"2025-12-02T07:43:36.005321Z","shell.execute_reply.started":"2025-12-02T07:43:35.644231Z","shell.execute_reply":"2025-12-02T07:43:36.004670Z"}},"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":"2025-12-02T07:43:47.956975Z","iopub.execute_input":"2025-12-02T07:43:47.957285Z","iopub.status.idle":"2025-12-02T07:43:48.465973Z","shell.execute_reply.started":"2025-12-02T07:43:47.957260Z","shell.execute_reply":"2025-12-02T07:43:48.465127Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SimpleCNN(nn.Module):\n    def __init__(self, num_classes=5):\n        super(SimpleCNN, self).__init__()\n        self.features = nn.Sequential(\n            nn.Conv2d(3, 32, 3, padding=1), nn.ReLU(),\n            nn.MaxPool2d(2),\n\n            nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(),\n            nn.MaxPool2d(2),\n\n            nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(),\n            nn.MaxPool2d(2),\n        )\n        self.classifier = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(128 * (IMG_SIZE//8) * (IMG_SIZE//8), 256),\n            nn.ReLU(),\n            nn.Dropout(0.3),\n            nn.Linear(256, num_classes)\n        )\n\n    def forward(self, x):\n        x = self.features(x)\n        x = self.classifier(x)\n        return x\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T07:45:38.430322Z","iopub.execute_input":"2025-12-02T07:45:38.430677Z","iopub.status.idle":"2025-12-02T07:45:38.436714Z","shell.execute_reply.started":"2025-12-02T07:45:38.430657Z","shell.execute_reply":"2025-12-02T07:45:38.435861Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nbase_model = SimpleCNN(num_classes=5).to(device)\noptimizer_cnn = torch.optim.Adam(base_model.parameters(), lr=1e-3)\ncriterion = nn.CrossEntropyLoss()\n\nEPOCHS_CNN = 3\n\ntrain_losses_cnn, val_losses_cnn = [], []\ntrain_accs_cnn, val_accs_cnn = [], []\n\nfor epoch in range(EPOCHS_CNN):\n    tr_loss, tr_acc = train_one_epoch(base_model, optimizer_cnn, train_loader, criterion)\n    val_loss, val_acc, _, _ = validate(base_model, val_loader, criterion)\n\n    train_losses_cnn.append(tr_loss)\n    val_losses_cnn.append(val_loss)\n    train_accs_cnn.append(tr_acc)\n    val_accs_cnn.append(val_acc)\n\n    print(f\"[CNN] Epoch {epoch+1}/{EPOCHS_CNN} \"\n          f\"Train Acc={tr_acc:.4f} Val Acc={val_acc:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T07:51:21.589265Z","iopub.execute_input":"2025-12-02T07:51:21.590104Z","iopub.status.idle":"2025-12-02T07:59:53.391267Z","shell.execute_reply.started":"2025-12-02T07:51:21.590076Z","shell.execute_reply":"2025-12-02T07:59:53.390465Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CNN Accuracy Curve\nplt.figure(figsize=(6,4))\nplt.plot(train_accs_cnn, label=\"Train Accuracy\")\nplt.plot(val_accs_cnn, label=\"Validation Accuracy\")\nplt.title(\"CNN Accuracy Curve\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.legend()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:00:13.434915Z","iopub.execute_input":"2025-12-02T08:00:13.435195Z","iopub.status.idle":"2025-12-02T08:00:13.626180Z","shell.execute_reply.started":"2025-12-02T08:00:13.435175Z","shell.execute_reply":"2025-12-02T08:00:13.625314Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# CNN Loss Curve\nplt.figure(figsize=(6,4))\nplt.plot(train_losses_cnn, label=\"Train Loss\")\nplt.plot(val_losses_cnn, label=\"Validation Loss\")\nplt.title(\"CNN Loss Curve\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:00:17.668974Z","iopub.execute_input":"2025-12-02T08:00:17.669270Z","iopub.status.idle":"2025-12-02T08:00:17.853379Z","shell.execute_reply.started":"2025-12-02T08:00:17.669249Z","shell.execute_reply":"2025-12-02T08:00:17.852666Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix\nimport seaborn as sns\n\n# Get predictions on validation set\n_, _, y_true_cnn, y_pred_cnn = validate(base_model, val_loader, criterion)\n\ncm = confusion_matrix(y_true_cnn, y_pred_cnn)\nplt.figure(figsize=(6,6))\nsns.heatmap(cm, annot=True, fmt=\"d\", cmap=\"Blues\")\nplt.title(\"CNN Confusion Matrix\")\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"Actual\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:01:55.828020Z","iopub.execute_input":"2025-12-02T08:01:55.828789Z","iopub.status.idle":"2025-12-02T08:02:26.050712Z","shell.execute_reply.started":"2025-12-02T08:01:55.828761Z","shell.execute_reply":"2025-12-02T08:02:26.049933Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"@torch.no_grad()\ndef ensemble_predict(model1, model2, loader):\n    model1.eval()\n    model2.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        p1 = F.softmax(model1(x), dim=1)   # CNN\n        p2 = F.softmax(model2(x), dim=1)   # ResNet50\n\n        avg = (p1 + p2) / 2\n        preds = avg.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":"2025-12-02T08:05:24.028088Z","iopub.execute_input":"2025-12-02T08:05:24.028702Z","iopub.status.idle":"2025-12-02T08:05:24.034216Z","shell.execute_reply.started":"2025-12-02T08:05:24.028677Z","shell.execute_reply":"2025-12-02T08:05:24.033513Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Running Ensemble...\")\ny_true_ens, y_pred_ens = ensemble_predict(base_model, resnet_model, val_loader)\n\nensemble_accuracy = (y_true_ens == y_pred_ens).mean()\nprint(\"Ensemble Accuracy:\", ensemble_accuracy)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:05:33.315085Z","iopub.execute_input":"2025-12-02T08:05:33.315786Z","iopub.status.idle":"2025-12-02T08:06:08.563447Z","shell.execute_reply.started":"2025-12-02T08:05:33.315759Z","shell.execute_reply":"2025-12-02T08:06:08.562783Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import seaborn as sns\nfrom sklearn.metrics import confusion_matrix\n\ncm = confusion_matrix(y_true_ens, y_pred_ens)\n\nplt.figure(figsize=(6,6))\nsns.heatmap(cm, annot=True, fmt=\"d\", cmap=\"Purples\")\nplt.title(\"Hybrid Ensemble Confusion Matrix\")\nplt.xlabel(\"Predicted\")\nplt.ylabel(\"Actual\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:06:20.707679Z","iopub.execute_input":"2025-12-02T08:06:20.708267Z","iopub.status.idle":"2025-12-02T08:06:20.917929Z","shell.execute_reply.started":"2025-12-02T08:06:20.708243Z","shell.execute_reply":"2025-12-02T08:06:20.917002Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(7,5))\nplt.bar(\n    [\"CNN\", \"ResNet50\", \"Ensemble\"],\n    [\n        val_accs_cnn[-1],        # last epoch CNN val accuracy\n        val_accs_res[-1],        # last epoch ResNet val accuracy\n        ensemble_accuracy        # final ensemble acc\n    ],\n    color=[\"skyblue\", \"orange\", \"purple\"]\n)\nplt.ylabel(\"Accuracy\")\nplt.title(\"Model Comparison: CNN vs ResNet50 vs Ensemble\")\nplt.ylim(0, 1)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:06:32.645925Z","iopub.execute_input":"2025-12-02T08:06:32.646388Z","iopub.status.idle":"2025-12-02T08:06:32.772565Z","shell.execute_reply.started":"2025-12-02T08:06:32.646365Z","shell.execute_reply":"2025-12-02T08:06:32.771960Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(7,5))\nplt.plot(train_accs_cnn, label=\"CNN Train Acc\", linestyle=\"--\")\nplt.plot(val_accs_cnn, label=\"CNN Val Acc\")\n\nplt.plot(train_accs_res, label=\"ResNet50 Train Acc\", linestyle=\"--\")\nplt.plot(val_accs_res, label=\"ResNet50 Val Acc\")\n\nplt.title(\"Accuracy Curve: CNN vs ResNet50\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.legend()\nplt.grid(True)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:07:18.503179Z","iopub.execute_input":"2025-12-02T08:07:18.503848Z","iopub.status.idle":"2025-12-02T08:07:18.719838Z","shell.execute_reply.started":"2025-12-02T08:07:18.503822Z","shell.execute_reply":"2025-12-02T08:07:18.719139Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(7,5))\nplt.plot(train_losses_cnn, label=\"CNN Train Loss\", linestyle=\"--\")\nplt.plot(val_losses_cnn, label=\"CNN Val Loss\")\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: CNN vs ResNet50\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.grid(True)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:07:30.803179Z","iopub.execute_input":"2025-12-02T08:07:30.803569Z","iopub.status.idle":"2025-12-02T08:07:31.006693Z","shell.execute_reply.started":"2025-12-02T08:07:30.803547Z","shell.execute_reply":"2025-12-02T08:07:31.006063Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"accuracies = [\n    val_accs_cnn[-1],\n    val_accs_res[-1],\n    ensemble_accuracy\n]\n\nplt.figure(figsize=(7,5))\nbars = plt.bar([\"CNN\", \"ResNet50\", \"Ensemble\"], accuracies,\n               color=[\"skyblue\", \"orange\", \"purple\"], 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":"2025-12-02T08:07:41.306289Z","iopub.execute_input":"2025-12-02T08:07:41.306943Z","iopub.status.idle":"2025-12-02T08:07:41.434806Z","shell.execute_reply.started":"2025-12-02T08:07:41.306918Z","shell.execute_reply":"2025-12-02T08:07:41.434105Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, ax = plt.subplots(1, 3, figsize=(18,5))\n\ncm_cnn = confusion_matrix(y_true_cnn, y_pred_cnn)\ncm_res = confusion_matrix(y_true_res, y_pred_res)\ncm_ens = confusion_matrix(y_true_ens, y_pred_ens)\n\nsns.heatmap(cm_cnn, annot=True, fmt=\"d\", cmap=\"Blues\", ax=ax[0])\nax[0].set_title(\"CNN Confusion Matrix\")\n\nsns.heatmap(cm_res, annot=True, fmt=\"d\", cmap=\"Greens\", ax=ax[1])\nax[1].set_title(\"ResNet50 Confusion Matrix\")\n\nsns.heatmap(cm_ens, annot=True, fmt=\"d\", cmap=\"Purples\", ax=ax[2])\nax[2].set_title(\"Ensemble Confusion Matrix\")\n\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:07:51.719180Z","iopub.execute_input":"2025-12-02T08:07:51.719553Z","iopub.status.idle":"2025-12-02T08:07:52.520489Z","shell.execute_reply.started":"2025-12-02T08:07:51.719526Z","shell.execute_reply":"2025-12-02T08:07:52.519681Z"}},"outputs":[],"execution_count":null}]}