{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"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"},"colab":{"machine_shape":"hm","gpuType":"T4"},"accelerator":"GPU","kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":13836,"databundleVersionId":1718836,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Local Inference on GPU \nModel page: https://huggingface.co/timm/densenet169.tv_in1k\n\n⚠️ If the generated code snippets do not work, please open an issue on either the [model repo](https://huggingface.co/timm/densenet169.tv_in1k)\n\t\t\tand/or on [huggingface.js](https://github.com/huggingface/huggingface.js/blob/main/packages/tasks/src/model-libraries-snippets.ts) 🙏","metadata":{}},{"cell_type":"code","source":"import timm\n\nmodel = timm.create_model(\"hf_hub:timm/densenet169.tv_in1k\", pretrained=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T15:37:51.696980Z","iopub.execute_input":"2025-08-19T15:37:51.697197Z","iopub.status.idle":"2025-08-19T15:38:11.869944Z","shell.execute_reply.started":"2025-08-19T15:37:51.697175Z","shell.execute_reply":"2025-08-19T15:38:11.869108Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nd=pd.read_csv('/kaggle/input/cassava-leaf-disease-classification/train.csv')\nprint(d.head(25))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T15:40:27.461011Z","iopub.execute_input":"2025-08-19T15:40:27.461280Z","iopub.status.idle":"2025-08-19T15:40:27.479845Z","shell.execute_reply.started":"2025-08-19T15:40:27.461260Z","shell.execute_reply":"2025-08-19T15:40:27.479140Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# Cassava Leaf Disease Detection using DenseNet169\n# 90:10 Train-Validation Split\n# ====================================================\n\nimport os\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\nfrom PIL import Image\nfrom tqdm import tqdm\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nimport timm\n\n# ====================================================\n# Step 1: Load Dataset\n# ====================================================\n\n# Load CSV\ndf = pd.read_csv('/kaggle/input/cassava-leaf-disease-classification/train.csv')\ndf['filepath'] = df['image_id'].apply(\n    lambda x: os.path.join('/kaggle/input/cassava-leaf-disease-classification/train_images', x)\n)\n\n# Train-Validation Split (90:10)\ntrain_df, val_df = train_test_split(\n    df, test_size=0.1, stratify=df['label'], random_state=42\n)\nprint(\"Train samples:\", len(train_df), \"Validation samples:\", len(val_df))\n\n# ====================================================\n# Step 2: Data Transforms & Dataset Class\n# ====================================================\n\nIMG_SIZE = 224\n\ntrain_transform = transforms.Compose([\n    transforms.RandomResizedCrop(IMG_SIZE),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomVerticalFlip(),\n    transforms.RandomRotation(20),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406],\n                         [0.229, 0.224, 0.225])\n])\n\nval_transform = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406],\n                         [0.229, 0.224, 0.225])\n])\n\nclass CassavaDataset(Dataset):\n    def __init__(self, df, transform=None):\n        self.df = df.reset_index(drop=True)\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        path = self.df.loc[idx, 'filepath']\n        label = self.df.loc[idx, 'label']\n        image = Image.open(path).convert(\"RGB\")\n        if self.transform:\n            image = self.transform(image)\n        return image, label\n\n# Dataloaders\ntrain_ds = CassavaDataset(train_df, train_transform)\nval_ds = CassavaDataset(val_df, val_transform)\n\ntrain_loader = DataLoader(train_ds, batch_size=32, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_ds, batch_size=32, shuffle=False, num_workers=2)\n\n# ====================================================\n# Step 3: Define Model (DenseNet169)\n# ====================================================\n\nNUM_CLASSES = df['label'].nunique()\n\nmodel = timm.create_model(\"densenet169\", pretrained=True)\nmodel.classifier = nn.Sequential(\n    nn.Linear(model.classifier.in_features, 512),\n    nn.ReLU(),\n    nn.Dropout(0.3),\n    nn.Linear(512, NUM_CLASSES)\n)\n\n# ====================================================\n# Step 4: Setup Training\n# ====================================================\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = model.to(device)\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4)\nscheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=10)\n\n# ====================================================\n# Step 5: Training Loop with Checkpointing\n# ====================================================\n\nEPOCHS = 10\nbest_acc = 0.0\n\nfor epoch in range(EPOCHS):\n    # ---- Training ----\n    model.train()\n    train_loss, correct, total = 0, 0, 0\n    \n    for images, labels in tqdm(train_loader, desc=f\"Epoch {epoch+1}/{EPOCHS} [Train]\"):\n        images, labels = images.to(device), labels.to(device)\n        \n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        \n        train_loss += loss.item() * images.size(0)\n        _, predicted = outputs.max(1)\n        correct += predicted.eq(labels).sum().item()\n        total += labels.size(0)\n    \n    train_acc = correct / total\n    train_loss /= total\n\n    # ---- Validation ----\n    model.eval()\n    val_loss, correct, total = 0, 0, 0\n    \n    with torch.no_grad():\n        for images, labels in tqdm(val_loader, desc=f\"Epoch {epoch+1}/{EPOCHS} [Val]\"):\n            images, labels = images.to(device), labels.to(device)\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            \n            val_loss += loss.item() * images.size(0)\n            _, predicted = outputs.max(1)\n            correct += predicted.eq(labels).sum().item()\n            total += labels.size(0)\n    \n    val_acc = correct / total\n    val_loss /= total\n    \n    print(f\"Epoch {epoch+1}/{EPOCHS} | Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.4f} | Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.4f}\")\n    \n    scheduler.step()\n    \n    # ---- Save Best Model ----\n    if val_acc > best_acc:\n        best_acc = val_acc\n        torch.save(model.state_dict(), \"best_densenet169.pth\")\n        print(\"✅ Saved Best Model with Val Acc:\", best_acc)\n\n# ====================================================\n# Step 6: Load Best Model (for inference later)\n# ====================================================\n\nmodel.load_state_dict(torch.load(\"best_densenet169.pth\"))\nmodel.eval()\nprint(\"Loaded best model with validation accuracy:\", best_acc)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T15:49:28.514266Z","iopub.execute_input":"2025-08-19T15:49:28.515121Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ====================================================\n# Resume training if notebook froze\n# ====================================================\n\n# Load previous best checkpoint\nmodel.load_state_dict(torch.load(\"best_densenet169.pth\"))\nmodel = model.to(device)\n\n# Continue from epoch 7 onward\nRESUME_EPOCH = 7\nEXTRA_EPOCHS = 10  # train 10 more epochs\n\nfor epoch in range(RESUME_EPOCH, RESUME_EPOCH + EXTRA_EPOCHS):\n    # ---- Training ----\n    model.train()\n    train_loss, correct, total = 0, 0, 0\n    \n    pbar = tqdm(train_loader, desc=f\"Epoch {epoch+1}/{RESUME_EPOCH+EXTRA_EPOCHS} [Train]\")\n    for images, labels in pbar:\n        images, labels = images.to(device), labels.to(device)\n\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n\n        train_loss += loss.item() * images.size(0)\n        _, predicted = outputs.max(1)\n        correct += predicted.eq(labels).sum().item()\n        total += labels.size(0)\n        pbar.set_postfix({\"Loss\": f\"{train_loss/total:.4f}\", \"Acc\": f\"{correct/total:.4f}\"})\n\n    train_acc = correct / total\n    train_loss /= total\n\n    # ---- Validation ----\n    model.eval()\n    val_loss, correct, total = 0, 0, 0\n    with torch.no_grad():\n        for images, labels in tqdm(val_loader, desc=f\"Epoch {epoch+1}/{RESUME_EPOCH+EXTRA_EPOCHS} [Val]\"):\n            images, labels = images.to(device), labels.to(device)\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            val_loss += loss.item() * images.size(0)\n            _, predicted = outputs.max(1)\n            correct += predicted.eq(labels).sum().item()\n            total += labels.size(0)\n\n    val_acc = correct / total\n    val_loss /= total\n\n    print(f\"Epoch {epoch+1}: Train Loss={train_loss:.4f}, Train Acc={train_acc:.4f} | Val Loss={val_loss:.4f}, Val Acc={val_acc:.4f}\")\n\n    scheduler.step()\n\n    # ---- Save Best Model ----\n    if val_acc > best_acc:\n        best_acc = val_acc\n        torch.save(model.state_dict(), \"best_densenet169.pth\")\n        print(\"✅ Saved New Best Model with Val Acc:\", best_acc)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T16:29:01.714179Z","iopub.execute_input":"2025-08-19T16:29:01.714509Z","iopub.status.idle":"2025-08-19T17:11:47.445545Z","shell.execute_reply.started":"2025-08-19T16:29:01.714461Z","shell.execute_reply":"2025-08-19T17:11:47.444547Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import timm\nimport torch\nimport torch.nn as nn\n\ndef create_densenet169_custom(num_classes=5):\n    model = timm.create_model(\"hf_hub:timm/densenet169.tv_in1k\", pretrained=False)\n    in_features = model.get_classifier().in_features\n    model.classifier = nn.Sequential(\n        nn.Linear(in_features, 512),\n        nn.ReLU(),\n        nn.Dropout(0.3),\n        nn.Linear(512, num_classes)\n    )\n    return model\n\n# Rebuild model\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = create_densenet169_custom(num_classes=5)\nmodel.load_state_dict(torch.load(\"best_densenet169.pth\", map_location=device))\nmodel = model.to(device)\nmodel.eval()\n\n\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Evaluation on validation set\nfrom tqdm import tqdm\n\nmodel.eval()\nval_loss, correct, total = 0, 0, 0\n\nwith torch.no_grad():\n    for images, labels in tqdm(val_loader, desc=\"Evaluating\"):\n        images, labels = images.to(device), labels.to(device)\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n\n        val_loss += loss.item() * images.size(0)\n        _, predicted = outputs.max(1)\n        correct += predicted.eq(labels).sum().item()\n        total += labels.size(0)\n\nval_acc = correct / total\nval_loss /= total\nprint(f\"✅ Final Model | Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T17:18:05.513544Z","iopub.execute_input":"2025-08-19T17:18:05.514068Z","iopub.status.idle":"2025-08-19T17:18:17.803237Z","shell.execute_reply.started":"2025-08-19T17:18:05.514044Z","shell.execute_reply":"2025-08-19T17:18:17.802356Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from PIL import Image\nfrom torchvision import transforms\n\n# Same preprocessing as training\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.485, 0.456, 0.406],\n                         std=[0.229, 0.224, 0.225])\n])\n\ndef predict_image(path):\n    img = Image.open(path).convert(\"RGB\")\n    img = transform(img).unsqueeze(0).to(device)\n    model.eval()\n    with torch.no_grad():\n        outputs = model(img)\n        _, predicted = outputs.max(1)\n    return predicted.item()\n\nprint(\"Predicted class:\", predict_image(\"/kaggle/input/cassava-leaf-disease-classification/test_images/2216849948.jpg\"))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T17:18:51.006588Z","iopub.execute_input":"2025-08-19T17:18:51.007446Z","iopub.status.idle":"2025-08-19T17:18:51.276880Z","shell.execute_reply.started":"2025-08-19T17:18:51.007405Z","shell.execute_reply":"2025-08-19T17:18:51.276280Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install grad-cam\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T17:25:08.045159Z","iopub.execute_input":"2025-08-19T17:25:08.045753Z","iopub.status.idle":"2025-08-19T17:26:00.519888Z","shell.execute_reply.started":"2025-08-19T17:25:08.045726Z","shell.execute_reply":"2025-08-19T17:26:00.518500Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================\n# Cassava Dataset Custom Loader\n# ============================================\nimport pandas as pd\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom PIL import Image\n\n# ----------------------------\n# CONFIG\n# ----------------------------\ncsv_file = \"/kaggle/input/cassava-leaf-disease-classification/train.csv\"\nimg_dir = \"/kaggle/input/cassava-leaf-disease-classification/train_images\"\nbatch_size = 32\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# ----------------------------\n# DATA\n# ----------------------------\ndf = pd.read_csv(csv_file)\nlabel_map = {\n    0: \"Cassava Bacterial Blight (CBB)\",\n    1: \"Cassava Brown Streak Disease (CBSD)\",\n    2: \"Cassava Green Mottle (CGM)\",\n    3: \"Cassava Mosaic Disease (CMD)\",\n    4: \"Healthy\"\n}\n\nval_tfms = transforms.Compose([\n    transforms.Resize((224,224)),\n    transforms.ToTensor()\n])\n\nclass CassavaDataset(Dataset):\n    def __init__(self, dataframe, img_dir, transform=None):\n        self.df = dataframe\n        self.img_dir = img_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        img_path = f\"{self.img_dir}/{self.df.iloc[idx, 0]}\"\n        image = Image.open(img_path).convert(\"RGB\")\n        label = self.df.iloc[idx, 1]\n        if self.transform:\n            image = self.transform(image)\n        return image, label\n\n# Full dataset (you can split into val/test if needed)\nval_dataset = CassavaDataset(df, img_dir, transform=val_tfms)\nval_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)\n\nprint(\"Loaded validation dataset:\", len(val_dataset), \"images\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T17:29:14.694297Z","iopub.execute_input":"2025-08-19T17:29:14.694827Z","iopub.status.idle":"2025-08-19T17:29:14.722756Z","shell.execute_reply.started":"2025-08-19T17:29:14.694805Z","shell.execute_reply":"2025-08-19T17:29:14.722204Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import timm\nimport torch.nn as nn\n\n# Base model\nmodel = timm.create_model(\"densenet169\", pretrained=False)\n\n# Rebuild classifier as it was during training\n# (looks like you had a small MLP head instead of single fc)\nmodel.classifier = nn.Sequential(\n    nn.Linear(model.classifier.in_features, 512),\n    nn.ReLU(),\n    nn.Dropout(0.5),\n    nn.Linear(512, 5)   # 5 cassava classes\n)\n\n# Load weights\nmodel.load_state_dict(torch.load(\"/kaggle/working/best_densenet169.pth\", map_location=device))\n\nmodel.to(device)\nmodel.eval()\n\nprint(\"Model loaded successfully!\")\n\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\n\n# === 1. Load CSV (image_id -> label mapping) ===\ncsv_path = \"/kaggle/input/cassava-leaf-disease-classification/train.csv\"\nimg_dir = \"/kaggle/input/cassava-leaf-disease-classification/train_images\"\n\ndf = pd.read_csv(csv_path)\nclass_names = ['Cassava Bacterial Blight (CBB)',\n               'Cassava Brown Streak Disease (CBSD)',\n               'Cassava Green Mottle (CGM)',\n               'Cassava Mosaic Disease (CMD)',\n               'Healthy']\n\n# === 2. Custom Dataset ===\nclass CassavaDataset(Dataset):\n    def __init__(self, dataframe, img_dir, transform=None):\n        self.dataframe = dataframe\n        self.img_dir = img_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n        img_path = f\"{self.img_dir}/{self.dataframe.iloc[idx]['image_id']}\"\n        image = Image.open(img_path).convert(\"RGB\")\n        label = self.dataframe.iloc[idx]['label']\n        \n        if self.transform:\n            image = self.transform(image)\n        return image, label\n\n# === 3. Validation transforms ===\nval_tfms = transforms.Compose([\n    transforms.Resize((224,224)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485,0.456,0.406], [0.229,0.224,0.225])\n])\n\n# === 4. Create dataset & dataloader ===\nval_dataset = CassavaDataset(df, img_dir, transform=val_tfms)\nval_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T17:31:55.610049Z","iopub.execute_input":"2025-08-19T17:31:55.610294Z","iopub.status.idle":"2025-08-19T17:31:55.628879Z","shell.execute_reply.started":"2025-08-19T17:31:55.610279Z","shell.execute_reply":"2025-08-19T17:31:55.628122Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport timm\nimport torch.nn as nn\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Load base DenseNet169\nmodel = timm.create_model(\"densenet169\", pretrained=False, num_classes=0)  # remove default head\n\n# Add the same custom classifier used during training\nmodel.classifier = nn.Sequential(\n    nn.Linear(1664, 512),   # bottleneck layer\n    nn.ReLU(),\n    nn.Dropout(0.3),\n    nn.Linear(512, 5)       # final output layer\n)\n\n# Load your trained weights\nstate_dict = torch.load(\"/kaggle/working/best_densenet169.pth\", map_location=device)\nmodel.load_state_dict(state_dict, strict=True)\n\nmodel.to(device)\nmodel.eval()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T17:32:43.762719Z","iopub.execute_input":"2025-08-19T17:32:43.763457Z","iopub.status.idle":"2025-08-19T17:32:44.324106Z","shell.execute_reply.started":"2025-08-19T17:32:43.763430Z","shell.execute_reply":"2025-08-19T17:32:44.323431Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom PIL import Image\nimport pandas as pd\nfrom sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\nimport matplotlib.pyplot as plt\nimport timm\n\n# --- Device ---\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# --- CSV and Image Directory ---\ncsv_path = \"/kaggle/input/cassava-leaf-disease-classification/train.csv\"\nimg_dir = \"/kaggle/input/cassava-leaf-disease-classification/train_images\"\ndf = pd.read_csv(csv_path)\n\n# --- Custom Dataset ---\nclass CassavaDataset(Dataset):\n    def __init__(self, dataframe, img_dir, transform=None):\n        self.dataframe = dataframe\n        self.img_dir = img_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.dataframe)\n\n    def __getitem__(self, idx):\n        img_path = f\"{self.img_dir}/{self.dataframe.iloc[idx]['image_id']}\"\n        image = Image.open(img_path).convert(\"RGB\")\n        label = int(self.dataframe.iloc[idx]['label'])\n        if self.transform:\n            image = self.transform(image)\n        return image, label\n\n# --- Validation Transforms ---\nval_tfms = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485, 0.456, 0.406],\n                         [0.229, 0.224, 0.225])\n])\n\n# --- Dataset and DataLoader ---\nval_dataset = CassavaDataset(df, img_dir, transform=val_tfms)\nval_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)\n\n# --- Model with same classifier as training ---\nmodel = timm.create_model(\"densenet169\", pretrained=False)\n# Define the same custom classifier as used in training\nmodel.classifier = nn.Sequential(\n    nn.Linear(model.classifier.in_features, 512),\n    nn.ReLU(),\n    nn.Dropout(0.3),\n    nn.Linear(512, 5)  # 5 classes for cassava disease\n)\n\n# Load the checkpoint\nmodel.load_state_dict(torch.load(\"/kaggle/working/best_densenet169.pth\", map_location=device))\nmodel = model.to(device)\nmodel.eval()\n\n# --- Validation Loop ---\nall_labels = []\nall_preds = []\ncorrect = 0\ntotal = 0\n\nwith torch.no_grad():\n    for images, labels in val_loader:\n        images, labels = images.to(device), labels.to(device)\n        outputs = model(images)\n        _, predicted = outputs.max(1)\n        correct += (predicted == labels).sum().item()\n        total += labels.size(0)\n        all_labels.extend(labels.cpu().numpy())\n        all_preds.extend(predicted.cpu().numpy())\n\nval_acc = correct / total\nprint(f\"Validation Accuracy: {val_acc:.4f}\")\n\n# --- Confusion Matrix ---\ncm = confusion_matrix(all_labels, all_preds)\ndisp = ConfusionMatrixDisplay(cm, display_labels=[0,1,2,3,4])\ndisp.plot(cmap=plt.cm.Blues)\nplt.title(\"Confusion Matrix\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T17:37:47.366103Z","iopub.execute_input":"2025-08-19T17:37:47.366655Z","iopub.status.idle":"2025-08-19T17:42:19.280627Z","shell.execute_reply.started":"2025-08-19T17:37:47.366634Z","shell.execute_reply":"2025-08-19T17:42:19.279962Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_loss /= total\nval_acc = correct / total\n\nprint(f\"Validation Loss: {val_loss:.4f}\")\nprint(f\"Validation Accuracy: {val_acc:.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T17:45:20.992106Z","iopub.execute_input":"2025-08-19T17:45:20.992742Z","iopub.status.idle":"2025-08-19T17:45:20.997365Z","shell.execute_reply.started":"2025-08-19T17:45:20.992708Z","shell.execute_reply":"2025-08-19T17:45:20.996547Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Example lists, replace these with your actual recorded values\nval_losses = [0.5290, 0.5399, 0.4494, 0.4486, 0.4270, 0.4315, 0.4156, 0.4125, 0.4132, 0.4083]\nval_accs   = [0.8051, 0.8042, 0.8379, 0.8439, 0.8514, 0.8500, 0.8607, 0.8636, 0.8612, 0.8598]\n\nepochs = range(1, len(val_losses)+1)\n\nplt.figure(figsize=(10,5))\n\n# Validation Loss\nplt.subplot(1,2,1)\nplt.plot(epochs, val_losses, marker='o', color='red')\nplt.title(\"Validation Loss per Epoch\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.grid(True)\n\n# Validation Accuracy\nplt.subplot(1,2,2)\nplt.plot\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T17:45:51.175129Z","iopub.execute_input":"2025-08-19T17:45:51.175669Z","iopub.status.idle":"2025-08-19T17:45:51.460448Z","shell.execute_reply.started":"2025-08-19T17:45:51.175643Z","shell.execute_reply":"2025-08-19T17:45:51.459270Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn.functional as F\nimport numpy as np\nimport cv2\nimport matplotlib.pyplot as plt\nfrom torchvision import transforms\nfrom PIL import Image\n\n# --- Load your trained model ---\nimport timm\nmodel = timm.create_model(\"densenet169\", pretrained=False, num_classes=5)\nmodel.load_state_dict(torch.load(\"/kaggle/working/best_densenet169.pth\", map_location=\"cuda\"))\nmodel = model.to(\"cuda\")\nmodel.eval()\n\n# --- Define Grad-CAM ---\nclass GradCAM:\n    def __init__(self, model, target_layer):\n        self.model = model\n        self.target_layer = target_layer\n        self.gradients = None\n        self.activations = None\n        self.hook_layers()\n\n    def hook_layers(self):\n        def forward_hook(module, input, output):\n            self.activations = output.detach()\n        def backward_hook(module, grad_in, grad_out):\n            self.gradients = grad_out[0].detach()\n        self.target_layer.register_forward_hook(forward_hook)\n        self.target_layer.register_backward_hook(backward_hook)\n\n    def __call__(self, x, class_idx=None):\n        output = self.model(x)\n        if class_idx is None:\n            class_idx = torch.argmax(output, 1).item()\n        self.model.zero_grad()\n        loss = output[0, class_idx]\n        loss.backward()\n        \n        weights = torch.mean(self.gradients, dim=(2,3), keepdim=True)\n        grad_cam_map = torch.sum(weights * self.activations, dim=1)[0]\n        grad_cam_map = F.relu(grad_cam_map)\n        grad_cam_map = grad_cam_map - grad_cam_map.min()\n        grad_cam_map = grad_cam_map / grad_cam_map.max()\n        grad_cam_map = grad_cam_map.cpu().numpy()\n        grad_cam_map = cv2.resize(grad_cam_map, (x.size(3), x.size(2)))\n        return grad_cam_map\n\n# --- Preprocess an image ---\nimg_path = \"/kaggle/input/cassava-leaf-disease-classification/train_images/1000015157.jpg\"\nimg = Image.open(img_path).convert(\"RGB\")\npreprocess = transforms.Compose([\n    transforms.Resize((224,224)),\n    transforms.ToTensor(),\n    transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])\n])\ninput_tensor = preprocess(img).unsqueeze(0).to(\"cuda\")\n\n# --- Apply Grad-CAM ---\ntarget_layer = model.features[-1]  # Last convolutional layer\ngrad_cam = GradCAM(model, target_layer)\nmask = grad_cam(input_tensor)\n\n# --- Overlay heatmap ---\nimg_np = np.array(img.resize((224,224)))\nheatmap = cv2.applyColorMap(np.uint8(255*mask), cv2.COLORMAP_JET)\noverlay = cv2.addWeighted(img_np, 0.6, heatmap, 0.4, 0)\n\nplt.figure(figsize=(8,8))\nplt.imshow(overlay)\nplt.axis('off')\nplt.title(\"Grad-CAM\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-19T17:47:25.179329Z","iopub.execute_input":"2025-08-19T17:47:25.180092Z","iopub.status.idle":"2025-08-19T17:47:26.460663Z","shell.execute_reply.started":"2025-08-19T17:47:25.180068Z","shell.execute_reply":"2025-08-19T17:47:26.459469Z"}},"outputs":[],"execution_count":null}]}