{"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":131587,"databundleVersionId":15786880}],"dockerImageVersionId":31287,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nfrom PIL import Image\nfrom tqdm import tqdm\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.nn.functional as F\n\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import datasets, transforms, models\n\n# import tensorflow as tf\n# from tensorflow import keras\n# from tensorflow.keras import layers\n# from tensorflow.keras.models import Sequential\n# from tensorflow.keras.preprocessing.image import ImageDataGenerator","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:43.847402Z","iopub.execute_input":"2026-03-05T00:50:43.848240Z","iopub.status.idle":"2026-03-05T00:50:43.853214Z","shell.execute_reply.started":"2026-03-05T00:50:43.848206Z","shell.execute_reply":"2026-03-05T00:50:43.852518Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nfolder_path = \"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge\"\n\nfiles = os.listdir(folder_path)\n\nprint(files)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:43.857664Z","iopub.execute_input":"2026-03-05T00:50:43.858408Z","iopub.status.idle":"2026-03-05T00:50:43.870961Z","shell.execute_reply.started":"2026-03-05T00:50:43.858375Z","shell.execute_reply":"2026-03-05T00:50:43.870307Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\n\nfolder = Path(\"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/train\")\n\nfor file in folder.iterdir():\n    print(file)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:43.872206Z","iopub.execute_input":"2026-03-05T00:50:43.872499Z","iopub.status.idle":"2026-03-05T00:50:43.882440Z","shell.execute_reply.started":"2026-03-05T00:50:43.872480Z","shell.execute_reply":"2026-03-05T00:50:43.881842Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nfrom PIL import Image\nfrom collections import Counter\n\ntrain_path = Path(\"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/train\")  # change if needed\n\nimage_sizes = []\n\nfor img_path in train_path.rglob(\"*\"):\n    if img_path.suffix.lower() in [\".jpg\", \".jpeg\", \".png\"]:\n        with Image.open(img_path) as img:\n            image_sizes.append(img.size)   # (width, height)\n\n# Count unique sizes\nsize_counts = Counter(image_sizes)\n\nprint(\"Unique Image Sizes and Their Counts:\\n\")\nfor size, count in size_counts.items():\n    print(f\"{size} : {count}\")   # we have image of different sizes . let of them transform","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:43.883549Z","iopub.execute_input":"2026-03-05T00:50:43.883892Z","iopub.status.idle":"2026-03-05T00:50:46.604150Z","shell.execute_reply.started":"2026-03-05T00:50:43.883872Z","shell.execute_reply":"2026-03-05T00:50:46.603318Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport random\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\ndef show_raw_images(folder_path, num_images=9):\n    \"\"\"\n    folder_path: path containing class subfolders (ImageFolder style)\n    num_images: number of images to display\n    \"\"\"\n    \n    image_paths = []\n    labels = []\n\n    # Collect all image paths and labels\n    for label in os.listdir(folder_path):\n        class_path = os.path.join(folder_path, label)\n        \n        if os.path.isdir(class_path):\n            for file in os.listdir(class_path):\n                if file.lower().endswith((\".jpg\", \".jpeg\", \".png\")):\n                    image_paths.append(os.path.join(class_path, file))\n                    labels.append(label)\n\n    # Shuffle\n    combined = list(zip(image_paths, labels))\n    random.shuffle(combined)\n\n    # Take first N\n    selected = combined[:num_images]\n\n    # Plot\n    plt.figure(figsize=(10, 10))\n\n    for i, (img_path, label) in enumerate(selected):\n        image = Image.open(img_path).convert(\"RGB\")\n\n        plt.subplot(int(num_images**0.5), int(num_images**0.5), i+1)\n        plt.imshow(image)\n        plt.title(label)\n        plt.axis(\"off\")\n\n    plt.tight_layout()\n    plt.show()\n\nshow_raw_images(\"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/train\", num_images=9)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:46.605578Z","iopub.execute_input":"2026-03-05T00:50:46.605946Z","iopub.status.idle":"2026-03-05T00:50:47.520036Z","shell.execute_reply.started":"2026-03-05T00:50:46.605924Z","shell.execute_reply":"2026-03-05T00:50:47.519206Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom torchvision import datasets, transforms\nfrom torch.utils.data import DataLoader, random_split\n\n# Instead of forcing shape, preserve aspect ratio:\n\ntrain_transform = transforms.Compose([\n    transforms.Resize(256),          # resize shorter side to 256 (keeps aspect ratio)\n    # transforms.CenterCrop(224),      # crop center 224x224\n    transforms.RandomResizedCrop(224, scale=(0.8, 1.0)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(10),\n\n    transforms.RandomAffine(\n        degrees=10,\n        translate=(0.05, 0.05),\n        scale=(0.9, 1.1)\n    ),\n    transforms.ColorJitter(0.1, 0.1, 0.1, 0.02),\n    transforms.ToTensor(),           # convert image to tensor\n    \n    # Required if using pretrained models\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],\n        std=[0.229, 0.224, 0.225]\n    ),\n    transforms.RandomErasing(p=0.3)\n])\n\nval_transform = transforms.Compose([\n    transforms.Resize(256),\n    transforms.CenterCrop(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# transform = transforms.Compose([\n#     transforms.Resize(256),\n#     transforms.RandomResizedCrop(224, scale=(0.9, 1.0)),\n    \n#     transforms.RandomHorizontalFlip(),\n    \n#     transforms.RandomAffine(\n#         degrees=5,\n#         translate=(0.05, 0.05),\n#         scale=(0.95, 1.05)\n#     ),\n    \n#     transforms.ColorJitter(\n#         brightness=0.05,\n#         contrast=0.05\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\n\n\"\"\"USE ONLY WHEN NO VALIDATION DATASET IS USED HERE\"\"\"\n\n# train_dataset = datasets.ImageFolder(\n#     root=\"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/train\",      # path to your train folder\n#     transform=train_transform\n# )\n\n# train_loader = DataLoader(\n#     train_dataset,\n#     batch_size=32,\n#     shuffle=True,\n#     num_workers=2,      # use >0 for GPU\n#     pin_memory=True     # improves GPU transfer speed\n# )\n\n# print(train_dataset.classes)\n\n# images, labels = next(iter(train_loader))\n\n# print(images.shape)   # (batch_size, 3, 224, 224)\n# print(labels.shape)   # (batch_size)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:47.520910Z","iopub.execute_input":"2026-03-05T00:50:47.521122Z","iopub.status.idle":"2026-03-05T00:50:47.530419Z","shell.execute_reply.started":"2026-03-05T00:50:47.521103Z","shell.execute_reply":"2026-03-05T00:50:47.529846Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\"\"\"USE WHEN VALIDATION DATSET IS USED HERE \"\"\"\n\nfrom sklearn.model_selection import train_test_split\nfrom torch.utils.data import DataLoader, Subset\n\nfull_dataset = datasets.ImageFolder(\n    root=\"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/train\"\n)\n\ntargets = full_dataset.targets\nindices = np.arange(len(full_dataset))\n\n\ntrain_indices, val_indices = train_test_split(\n    indices,\n    test_size=0.2,\n    stratify=targets,\n    random_state=42\n)\n\ntrain_dataset = Subset(full_dataset, train_indices)\nval_dataset = Subset(full_dataset, val_indices)\n\n\ntrain_dataset.dataset.transform = train_transform\nval_dataset.dataset.transform = val_transform\n\n\n\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\n\nprint(train_dataset.dataset.classes)\n\nimages, labels = next(iter(train_loader))\n\nprint(images.shape)   # (batch_size, 3, 224, 224)\nprint(labels.shape)   # (batch_size)\n\nprint(\"Train size:\", len(train_dataset))\nprint(\"Validation size:\", len(val_dataset))\n\n#USE HERE THE ✅ Stratified split version (better) FOR VALIDATION SPLIT HERE ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:47.532023Z","iopub.execute_input":"2026-03-05T00:50:47.532313Z","iopub.status.idle":"2026-03-05T00:50:48.489705Z","shell.execute_reply.started":"2026-03-05T00:50:47.532293Z","shell.execute_reply":"2026-03-05T00:50:48.488886Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Get labels for train subset\ntrain_labels = [full_dataset.targets[i] for i in train_indices]\nval_labels   = [full_dataset.targets[i] for i in val_indices]\n\nclasses = full_dataset.classes\n\nprint(\"\\nTrain class distribution:\")\nfor i, class_name in enumerate(classes):\n    print(f\"{class_name}: {(np.array(train_labels) == i).sum()}\")\n\nprint(\"\\nValidation class distribution:\")\nfor i, class_name in enumerate(classes):\n    print(f\"{class_name}: {(np.array(val_labels) == i).sum()}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:48.491009Z","iopub.execute_input":"2026-03-05T00:50:48.491335Z","iopub.status.idle":"2026-03-05T00:50:48.499122Z","shell.execute_reply.started":"2026-03-05T00:50:48.491309Z","shell.execute_reply":"2026-03-05T00:50:48.498444Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nfrom PIL import Image\nfrom torch.utils.data import Dataset\n\nclass TestDataset(Dataset):\n    def __init__(self, folder_path, transform=None):\n        self.folder_path = folder_path\n        self.transform = transform\n        \n        self.image_names = sorted([\n            f for f in os.listdir(folder_path)\n            if f.lower().endswith((\".jpg\", \".jpeg\", \".png\"))\n        ])\n\n    def __len__(self):\n        return len(self.image_names)\n\n    def __getitem__(self, idx):\n        img_name = self.image_names[idx]\n        img_path = os.path.join(self.folder_path, img_name)\n\n        image = Image.open(img_path).convert(\"RGB\")\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, img_name","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:48.500207Z","iopub.execute_input":"2026-03-05T00:50:48.500808Z","iopub.status.idle":"2026-03-05T00:50:48.512862Z","shell.execute_reply.started":"2026-03-05T00:50:48.500786Z","shell.execute_reply":"2026-03-05T00:50:48.512165Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_transform = transforms.Compose([\n    transforms.Resize(256),\n    transforms.CenterCrop(224),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        [0.485, 0.456, 0.406],\n        [0.229, 0.224, 0.225]\n    )\n])\n\n\n\ntest_dataset = TestDataset(\n    folder_path=\"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/kaggle_test_final\",\n    transform=test_transform\n)\n\ntest_loader = DataLoader(\n    test_dataset,\n    batch_size=32,\n    shuffle=False,\n    num_workers=0,   \n    pin_memory=True\n)\n\n\nimages, filenames = next(iter(test_loader))\n\nprint(images.shape)      # (batch_size, 3, 224, 224)\nprint(filenames[:5])     # image names\nprint(\"Total Test Images:\", len(test_dataset))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:48.513743Z","iopub.execute_input":"2026-03-05T00:50:48.514029Z","iopub.status.idle":"2026-03-05T00:50:48.925072Z","shell.execute_reply.started":"2026-03-05T00:50:48.514008Z","shell.execute_reply":"2026-03-05T00:50:48.924451Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\n# class SmallCNN(nn.Module):\n#     def __init__(self):\n#         super(SmallCNN, self).__init__()\n        \n#         # Convolution Block 1\n#         self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)\n#         self.bn1 = nn.BatchNorm2d(32)\n        \n#         # Convolution Block 2\n#         self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)\n#         self.bn2 = nn.BatchNorm2d(64)\n        \n#         # Convolution Block 3\n#         self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1)\n#         self.bn3 = nn.BatchNorm2d(128)\n        \n#         self.pool = nn.MaxPool2d(2, 2)\n#         self.dropout = nn.Dropout(0.3)\n        \n#         # Fully connected layer\n#         self.fc1 = nn.Linear(128 * 28 * 28, 256)\n#         self.fc2 = nn.Linear(256, 2)   # 2 classes\n        \n#     def forward(self, x):\n#         x = self.pool(F.relu(self.bn1(self.conv1(x))))  # 224 -> 112\n#         x = self.pool(F.relu(self.bn2(self.conv2(x))))  # 112 -> 56\n#         x = self.pool(F.relu(self.bn3(self.conv3(x))))  # 56 -> 28\n        \n#         x = x.view(x.size(0), -1)\n#         x = self.dropout(F.relu(self.fc1(x)))\n#         x = self.fc2(x)\n        \n#         return x\n\nfrom torchvision import models\n\nclass SmallCNN(nn.Module):    # last time we used this \n    def __init__(self):\n        super(SmallCNN, self).__init__()\n        \n        # Load pretrained ResNet18\n        self.model = models.resnet18(pretrained=True)\n        \n        # Replace final layer for 2 classes\n        num_features = self.model.fc.in_features\n        self.model.fc = nn.Linear(num_features, 2)\n\n        # num_features = self.model.fc.in_features\n        \n        # Replace classifier with deeper head\n        # self.model.fc = nn.Sequential(\n        #     nn.Linear(num_features, 256),\n        #     nn.BatchNorm1d(256),\n        #     nn.ReLU(),\n        #     nn.Dropout(0.4),\n            \n        #     nn.Linear(256, 64),\n        #     nn.ReLU(),\n        #     nn.Dropout(0.3),\n            \n        #     nn.Linear(64, 2)\n        # )\n\n    def forward(self, x):\n        return self.model(x)\n\n# # Freeze Backbone (For Small Dataset)  Prevents overfitting\n# class SmallCNN(nn.Module):\n#     def __init__(self):\n#         super(SmallCNN, self).__init__()\n        \n#         self.model = models.resnet18(pretrained=True)\n        \n#         # Freeze all layers\n#         for param in self.model.parameters():\n#             param.requires_grad = False\n        \n#         # Replace final layer\n#         num_features = self.model.fc.in_features\n#         self.model.fc = nn.Linear(num_features, 2)\n\n#     def forward(self, x):\n#         return self.model(x)\n\n# class SmallCNN(nn.Module):\n#     def __init__(self):\n#         super(SmallCNN, self).__init__()\n        \n#         self.model = models.resnet18(pretrained=True)\n        \n#         # Freeze backbone\n#         for param in self.model.parameters():\n#             param.requires_grad = False\n        \n#         num_features = self.model.fc.in_features\n        \n#         # Replace classifier with deeper head\n#         self.model.fc = nn.Sequential(\n#             nn.Linear(num_features, 256),\n#             nn.BatchNorm1d(256),\n#             nn.ReLU(),\n#             nn.Dropout(0.4),\n            \n#             nn.Linear(256, 64),\n#             nn.ReLU(),\n#             nn.Dropout(0.3),\n            \n#             nn.Linear(64, 2)\n#         )\n\n#     def forward(self, x):\n#         return self.model(x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:48.925905Z","iopub.execute_input":"2026-03-05T00:50:48.926275Z","iopub.status.idle":"2026-03-05T00:50:48.932956Z","shell.execute_reply.started":"2026-03-05T00:50:48.926253Z","shell.execute_reply":"2026-03-05T00:50:48.932322Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nmodel = SmallCNN().to(device)\n\ntorch.backends.cudnn.benchmark = True  # speeds up training\n\nprint(model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:48.933802Z","iopub.execute_input":"2026-03-05T00:50:48.934163Z","iopub.status.idle":"2026-03-05T00:50:49.128411Z","shell.execute_reply.started":"2026-03-05T00:50:48.934141Z","shell.execute_reply":"2026-03-05T00:50:49.127549Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=0.001)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T00:50:49.130978Z","iopub.execute_input":"2026-03-05T00:50:49.131696Z","iopub.status.idle":"2026-03-05T00:50:49.137505Z","shell.execute_reply.started":"2026-03-05T00:50:49.131653Z","shell.execute_reply":"2026-03-05T00:50:49.136664Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_losses = []\ntrain_accuracies = []\n\nval_losses = []\nval_accuracies = []\n\nepochs = 20\npatience = 3               # how many epochs to wait\nbest_val_loss = float(\"inf\")\ncounter = 0                # counts epochs without improvement\n\nfor epoch in range(epochs):\n    \n    # =======================\n    # 🔥 TRAINING\n    # =======================\n    model.train()\n    running_loss = 0.0\n    correct = 0\n    total = 0\n    \n    for images, labels in train_loader:\n        images = images.to(device)\n        labels = labels.to(device)\n        \n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        \n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item()\n        \n        _, predicted = torch.max(outputs, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n    \n    epoch_loss = running_loss / len(train_loader)\n    epoch_acc = 100 * correct / total\n    \n    train_losses.append(epoch_loss)\n    train_accuracies.append(epoch_acc)\n    \n    \n    # =======================\n    # 🔵 VALIDATION\n    # =======================\n    model.eval()\n    val_running_loss = 0.0\n    val_correct = 0\n    val_total = 0\n    \n    with torch.no_grad():\n        for images, labels in val_loader:\n            images = images.to(device)\n            labels = labels.to(device)\n            \n            outputs = model(images)\n            loss = criterion(outputs, labels)\n            \n            val_running_loss += loss.item()\n            \n            _, predicted = torch.max(outputs, 1)\n            val_total += labels.size(0)\n            val_correct += (predicted == labels).sum().item()\n    \n    val_epoch_loss = val_running_loss / len(val_loader)\n    val_epoch_acc = 100 * val_correct / val_total\n    \n    val_losses.append(val_epoch_loss)\n    val_accuracies.append(val_epoch_acc)\n    \n    \n    # =======================\n    # 📊 PRINT RESULTS\n    # =======================\n    print(f\"Epoch [{epoch+1}/{epochs}]\")\n    print(f\"Train Loss: {epoch_loss:.4f} | Train Acc: {epoch_acc:.2f}%\")\n    print(f\"Val   Loss: {val_epoch_loss:.4f} | Val   Acc: {val_epoch_acc:.2f}%\")\n    print(\"-\" * 50)\n    \n    \n    # =======================\n    # 🛑 EARLY STOPPING\n    # =======================\n    if val_epoch_loss < best_val_loss:\n        best_val_loss = val_epoch_loss\n        counter = 0\n        torch.save(model.state_dict(), \"best_model.pth\")\n        print(\"✅ Validation loss improved. Model saved.\\n\")\n    else:\n        counter += 1\n        print(f\"⚠️ No improvement for {counter} epoch(s)\\n\")\n        \n        if counter >= patience:\n            print(\"🛑 Early stopping triggered!\")\n            break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T01:05:08.068895Z","iopub.execute_input":"2026-03-05T01:05:08.069588Z","iopub.status.idle":"2026-03-05T01:07:12.505693Z","shell.execute_reply.started":"2026-03-05T01:05:08.069558Z","shell.execute_reply":"2026-03-05T01:07:12.504687Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nnum_epochs_ran = len(train_losses)\n\nplt.figure(figsize=(12, 5))\n\n# ---- Loss Plot ----\nplt.subplot(1, 2, 1)\nplt.plot(range(1, num_epochs_ran + 1), train_losses, label=\"Train Loss\")\nplt.plot(range(1, num_epochs_ran + 1), val_losses, label=\"Validation Loss\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.title(\"Training vs Validation Loss\")\nplt.legend()\nplt.grid(True)\n\n# ---- Accuracy Plot ----\nplt.subplot(1, 2, 2)\nplt.plot(range(1, num_epochs_ran + 1), train_accuracies, label=\"Train Accuracy\")\nplt.plot(range(1, num_epochs_ran + 1), val_accuracies, label=\"Validation Accuracy\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy (%)\")\nplt.title(\"Training vs Validation Accuracy\")\nplt.legend()\nplt.grid(True)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T01:08:59.006826Z","iopub.execute_input":"2026-03-05T01:08:59.007502Z","iopub.status.idle":"2026-03-05T01:08:59.308964Z","shell.execute_reply.started":"2026-03-05T01:08:59.007474Z","shell.execute_reply":"2026-03-05T01:08:59.308321Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"full_dataset_pred = datasets.ImageFolder(\n    root=\"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/train\",\n    transform=test_transform\n)\n\ntrain_loader_full = DataLoader(\n    full_dataset_pred,\n    batch_size=32,\n    shuffle=True,\n    num_workers=2,      # use >0 for GPU\n    pin_memory=True     # improves GPU transfer speed\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T01:14:40.255314Z","iopub.execute_input":"2026-03-05T01:14:40.255752Z","iopub.status.idle":"2026-03-05T01:14:41.790253Z","shell.execute_reply.started":"2026-03-05T01:14:40.255723Z","shell.execute_reply":"2026-03-05T01:14:41.789356Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nif os.path.exists(\"best_model.pth\"):\n    model.load_state_dict(torch.load(\"best_model.pth\"))\n    print(\"✅ Loaded best_model.pth\")\nelse:\n    print(\"⚠️ best_model.pth not found — using current model weights\")\n\n\n# Load best saved model\nmodel.load_state_dict(torch.load(\"best_model.pth\"))\nmodel.to(device)\nmodel.eval()\n\nall_preds = []\nall_labels = []\nall_filenames = []\n\nwith torch.no_grad():\n    for images, labels in train_loader_full:\n        images = images.to(device)\n        \n        outputs = model(images)\n        _, predicted = torch.max(outputs, 1)\n        \n        all_preds.extend(predicted.cpu().numpy())\n        all_labels.extend(labels.numpy())\n\n# 🔥 Get filenames in correct order\nall_filenames = [sample[0] for sample in full_dataset_pred.samples]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T01:15:05.642287Z","iopub.execute_input":"2026-03-05T01:15:05.642909Z","iopub.status.idle":"2026-03-05T01:15:34.646493Z","shell.execute_reply.started":"2026-03-05T01:15:05.642879Z","shell.execute_reply":"2026-03-05T01:15:34.645378Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class_names = full_dataset_pred.classes\n\npredicted_classes = [class_names[p] for p in all_preds]\ntrue_classes = [class_names[l] for l in all_labels]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T01:15:43.437336Z","iopub.execute_input":"2026-03-05T01:15:43.437860Z","iopub.status.idle":"2026-03-05T01:15:43.442144Z","shell.execute_reply.started":"2026-03-05T01:15:43.437834Z","shell.execute_reply":"2026-03-05T01:15:43.441429Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\n\ndf = pd.DataFrame({\n    \"filename\": all_filenames,\n    \"true_label\": true_classes,\n    \"predicted_label\": predicted_classes\n})\n\n\n# Function to show wrong predictions\n\n# Confusion matrix\n\n# Precision / Recall per class\n\n# Hard example mining visualization\n\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T01:15:46.124592Z","iopub.execute_input":"2026-03-05T01:15:46.125270Z","iopub.status.idle":"2026-03-05T01:15:46.138008Z","shell.execute_reply.started":"2026-03-05T01:15:46.125240Z","shell.execute_reply":"2026-03-05T01:15:46.137370Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\nfrom sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\n\ndef plot_confusion_matrix_from_df(df):\n    \n    # Get sorted unique class names\n    labels = sorted(df[\"true_label\"].unique())\n    \n    # Compute confusion matrix\n    cm = confusion_matrix(\n        df[\"true_label\"],\n        df[\"predicted_label\"],\n        labels=labels\n    )\n    \n    # Plot\n    plt.figure(figsize=(6, 6))\n    disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=labels)\n    disp.plot(cmap=\"Blues\", values_format=\"d\")\n    plt.title(\"Confusion Matrix\")\n    plt.show()\n\n\nplot_confusion_matrix_from_df(df)\n\naccuracy = (df[\"true_label\"] == df[\"predicted_label\"]).mean()\nprint(f\"Accuracy: {accuracy*100:.2f}%\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T01:15:51.552890Z","iopub.execute_input":"2026-03-05T01:15:51.553508Z","iopub.status.idle":"2026-03-05T01:15:51.969560Z","shell.execute_reply.started":"2026-03-05T01:15:51.553482Z","shell.execute_reply":"2026-03-05T01:15:51.968839Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport random\nfrom PIL import Image\n\n\ndef show_misclassified_from_one_class(df, target_class, num_images=10):\n    \n    # 1️⃣ Filter wrong predictions from specific true class\n    wrong_df = df[\n        (df[\"true_label\"] == target_class) &\n        (df[\"predicted_label\"] != target_class)\n    ]\n    \n    if len(wrong_df) == 0:\n        print(f\"🔥 No misclassified images found for class '{target_class}'\")\n        return\n    \n    # 2️⃣ Randomly sample\n    sample_df = wrong_df.sample(\n        n=min(num_images, len(wrong_df)),\n        random_state=None\n    )\n    \n    # 3️⃣ Plot\n    plt.figure(figsize=(12, 12))\n    \n    grid_size = int(num_images**0.5) + 1\n    \n    for i, (_, row) in enumerate(sample_df.iterrows()):\n        image = Image.open(row[\"filename\"]).convert(\"RGB\")\n        \n        plt.subplot(grid_size, grid_size, i+1)\n        plt.imshow(image)\n        \n        plt.title(\n            f\"T: {row['true_label']}\\nP: {row['predicted_label']}\",\n            color=\"red\"\n        )\n        plt.axis(\"off\")\n    \n    plt.tight_layout()\n    plt.show()\n\n\ndef show_correct_predictions_from_df(df, num_images=10):\n    \n    # 1️⃣ Filter correct predictions\n    correct_df = df[df[\"true_label\"] == df[\"predicted_label\"]]\n    \n    if len(correct_df) == 0:\n        print(\"No correct predictions found.\")\n        return\n    \n    # 2️⃣ Randomly sample\n    sample_df = correct_df.sample(\n        n=min(num_images, len(correct_df)),\n        random_state=None\n    )\n    \n    # 3️⃣ Plot\n    plt.figure(figsize=(12, 12))\n    \n    grid_size = int(num_images**0.5) + 1\n    \n    for i, (_, row) in enumerate(sample_df.iterrows()):\n        image = Image.open(row[\"filename\"]).convert(\"RGB\")\n        \n        plt.subplot(grid_size, grid_size, i+1)\n        plt.imshow(image)\n        \n        plt.title(\n            f\"T: {row['true_label']}\\nP: {row['predicted_label']}\",\n            color=\"green\"\n        )\n        plt.axis(\"off\")\n    \n    plt.tight_layout()\n    plt.show()\n\n\nshow_misclassified_from_one_class(df, target_class=\"chihuahua\", num_images=10)\n\nprint(\"*\"*50)\n\nshow_misclassified_from_one_class(df, target_class=\"muffin\", num_images=10)\n\nprint(\"*\"*50)\n\nshow_correct_predictions_from_df(df, num_images=20)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T01:16:04.015758Z","iopub.execute_input":"2026-03-05T01:16:04.016177Z","iopub.status.idle":"2026-03-05T01:16:06.917203Z","shell.execute_reply.started":"2026-03-05T01:16:04.016149Z","shell.execute_reply":"2026-03-05T01:16:06.916342Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# model.eval()\n\nall_preds = []\nall_filenames = []\n\nwith torch.no_grad():\n    for images, filenames in test_loader:\n        images = images.to(device)\n        \n        outputs = model(images)\n        _, predicted = torch.max(outputs, 1)\n        \n        all_preds.extend(predicted.cpu().numpy())\n        all_filenames.extend(filenames)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T01:18:26.281269Z","iopub.execute_input":"2026-03-05T01:18:26.281623Z","iopub.status.idle":"2026-03-05T01:18:40.847163Z","shell.execute_reply.started":"2026-03-05T01:18:26.281577Z","shell.execute_reply":"2026-03-05T01:18:40.846536Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"final_labels = [full_dataset_pred.classes[p] for p in all_preds]\npred_df = pd.DataFrame({\n    \"ID\": all_filenames,\n    \"Label\": final_labels\n})\n\nimport pandas as pd\n\nsubmission = pd.read_csv(\n    \"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/test_solution_01.csv\"\n)\n\nprint(submission.head())\n\n# Merge to ensure correct order\nsubmission = submission[[\"ID\"]].merge(pred_df, on=\"ID\", how=\"left\")\nsubmission","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T01:19:37.876416Z","iopub.execute_input":"2026-03-05T01:19:37.876842Z","iopub.status.idle":"2026-03-05T01:19:37.896974Z","shell.execute_reply.started":"2026-03-05T01:19:37.876814Z","shell.execute_reply":"2026-03-05T01:19:37.896199Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.Label.value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T01:19:42.699809Z","iopub.execute_input":"2026-03-05T01:19:42.700070Z","iopub.status.idle":"2026-03-05T01:19:42.706655Z","shell.execute_reply.started":"2026-03-05T01:19:42.700049Z","shell.execute_reply":"2026-03-05T01:19:42.706052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission.to_csv(\"submission.csv\", index=False)\nprint(\"Submission file created successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T01:19:46.024220Z","iopub.execute_input":"2026-03-05T01:19:46.024881Z","iopub.status.idle":"2026-03-05T01:19:46.031969Z","shell.execute_reply.started":"2026-03-05T01:19:46.024851Z","shell.execute_reply":"2026-03-05T01:19:46.031220Z"}},"outputs":[],"execution_count":null}]}