{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":3362,"databundleVersionId":31148,"sourceType":"competition"}],"dockerImageVersionId":30918,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"Ekstraksi file .zip kedalam output","metadata":{}},{"cell_type":"code","source":"import os\nimport zipfile\n\n# Directory containing ZIP files\nzip_dir = \"/kaggle/input/dogs-vs-cats\"\nextract_path = \"/kaggle/working/\"\n\n# Extract all ZIP files in the directory\nfor file in os.listdir(zip_dir):\n    if file.endswith(\".zip\"):\n        with zipfile.ZipFile(os.path.join(zip_dir, file), 'r') as zip_ref:\n            zip_ref.extractall(extract_path)\n            print(f\"Extracted: {file}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T03:53:27.706613Z","iopub.execute_input":"2025-03-18T03:53:27.706936Z","iopub.status.idle":"2025-03-18T03:53:44.684013Z","shell.execute_reply.started":"2025-03-18T03:53:27.706910Z","shell.execute_reply":"2025-03-18T03:53:44.682876Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Exploratory Data Analysis**\nPerlu dilakukan analisa data, untuk mengetahui keseragaman gambar, serta distribusi gambar agar dapat dimasukkan selanjutnya kedalam neuron untuk proses data input","metadata":{}},{"cell_type":"code","source":"import os\nimport zipfile\nimport matplotlib.pyplot as plt\nimport cv2\nimport numpy as np\nfrom collections import Counter\nfrom PIL import Image\n\n# Paths to dataset\ntrain_dir = \"/kaggle/input/dogs-vs-cats/train.zip\"\nextract_path = \"/kaggle/working\"\n\n# Extract files if not already extracted\nif not os.path.exists(extract_path):\n    with zipfile.ZipFile(train_dir, 'r') as zip_ref:\n        zip_ref.extractall(extract_path)\n\n# Define dataset path\ntrain_path = os.path.join(extract_path, \"train\")\n\n# Get list of all images\nall_images = os.listdir(train_path)\n\n# Count number of images for each class\nlabels = [img.split(\".\")[0] for img in all_images]  # Extract \"dog\" or \"cat\"\nlabel_counts = Counter(labels)\n\nprint(\"Number of Dog images:\", label_counts[\"dog\"])\nprint(\"Number of Cat images:\", label_counts[\"cat\"])\n\n# Display a few sample images\nfig, axes = plt.subplots(3, 4, figsize=(12, 9))  # 3 rows, 4 columns\nfig.suptitle(\"Sample Images from Dataset\", fontsize=14)\n\nfor i, ax in enumerate(axes.flat):\n    img_path = os.path.join(train_path, all_images[i])\n    img = Image.open(img_path)  # Open image using PIL\n    ax.imshow(img)\n    ax.set_title(f\"Class: {labels[i]}\")\n    ax.axis(\"off\")\n\nplt.tight_layout()\nplt.show()\n\n# Check image sizes\nimage_sizes = []\nfor img_name in all_images[:200]:  # Checking first 200 images\n    img_path = os.path.join(train_path, img_name)\n    img = Image.open(img_path)\n    image_sizes.append(img.size)\n\n# Count unique image sizes\nunique_sizes = Counter(image_sizes)\nprint(\"Unique Image Sizes:\", unique_sizes)\n\n# Display Image Size Distribution\nsizes, counts = zip(*unique_sizes.items())\nplt.figure(figsize=(10, 5))\nplt.bar(range(len(sizes)), counts, tick_label=[f\"{w}x{h}\" for w, h in sizes])\nplt.xlabel(\"Image Resolution\")\nplt.ylabel(\"Count\")\nplt.title(\"Distribution of Image Sizes in Dataset\")\nplt.xticks(rotation=45)\nplt.show()\n\n# Check for corrupted images\ncorrupted_files = []\nfor img_name in all_images:\n    img_path = os.path.join(train_path, img_name)\n    try:\n        img = Image.open(img_path)\n        img.verify()  # Verify if image is valid\n    except Exception as e:\n        corrupted_files.append(img_name)\n\nprint(\"Number of Corrupted Images:\", len(corrupted_files))\nif corrupted_files:\n    print(\"Sample Corrupted Image:\", corrupted_files[:5])  # Show first few corrupted images\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-18T03:53:55.808487Z","iopub.execute_input":"2025-03-18T03:53:55.808898Z","iopub.status.idle":"2025-03-18T03:54:02.132029Z","shell.execute_reply.started":"2025-03-18T03:53:55.808870Z","shell.execute_reply":"2025-03-18T03:54:02.131120Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"****CNN Vanilla****","metadata":{}},{"cell_type":"code","source":"import os\nimport glob\nimport time\nimport random  import os\nimport glob\nimport time\nimport random  \nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nimport torchvision.models as models  # <-- Added import\nfrom torch.utils.data import Dataset, DataLoader, random_split\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport seaborn as sns\nfrom sklearn.metrics import confusion_matrix, accuracy_score\nfrom PIL import Image\n\n# Check if GPU is available\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")\n\n# Define dataset paths\nextract_path = \"/kaggle/working/\"\ntrain_path = os.path.join(extract_path, \"train\")  \n\n# Load all images and split them into cats and dogs\nall_images = glob.glob(os.path.join(train_path, \"*.jpg\"))\ncat_images = [img for img in all_images if \"cat\" in os.path.basename(img)]\ndog_images = [img for img in all_images if \"dog\" in os.path.basename(img)]\n\n# Reduce dataset size by 30% (keep only 70%)\nnum_cats = int(len(cat_images) * 0.3)\nnum_dogs = int(len(dog_images) * 0.3)\n\n# Randomly select 70% of images\nselected_cat_images = random.sample(cat_images, num_cats)\nselected_dog_images = random.sample(dog_images, num_dogs)\n\n# Combine selected images for training\nreduced_dataset = selected_cat_images + selected_dog_images\nrandom.shuffle(reduced_dataset)  \nprint(f\"Reduced dataset size: {len(reduced_dataset)} images (Cats: {num_cats}, Dogs: {num_dogs})\")\n\n# Define transforms (optimized with smaller image size)\ntransform = transforms.Compose([\n    transforms.Resize((128, 128)),  \n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize([0.5], [0.5])\n])\n\n# ====== Custom Dataset Loader ======\nclass CatsDogsDataset(Dataset):\n    def __init__(self, image_list, transform=None):\n        self.image_list = image_list\n        self.transform = transform\n        self.labels = [1 if \"dog\" in os.path.basename(f) else 0 for f in self.image_list]\n\n    def __len__(self):\n        return len(self.image_list)\n\n    def __getitem__(self, idx):\n        img_path = self.image_list[idx]\n        image = Image.open(img_path).convert(\"RGB\")\n        \n        if self.transform:\n            image = self.transform(image)\n\n        label = self.labels[idx]\n        return image, label\n\n# Load dataset with reduced size\ndataset = CatsDogsDataset(reduced_dataset, transform=transform)\n\n# Split dataset into training (80%) and validation (20%)\ntrain_size = int(0.8 * len(dataset))\nval_size = len(dataset) - train_size\ntrain_dataset, val_dataset = random_split(dataset, [train_size, val_size])\n\n# Create DataLoaders\ntrain_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2, pin_memory=True)\nval_loader = DataLoader(val_dataset, batch_size=64, shuffle=False, num_workers=2, pin_memory=True)\n\n# ===== REPLACE CNN WITH RESNET-18 =====\n# We remove the custom CNNModel and use ResNet-18 with a modified final layer.\nmodel = models.resnet18(pretrained=True)\nnum_ftrs = model.fc.in_features\nmodel.fc = nn.Linear(num_ftrs, 2)\nmodel = model.to(device)\n\n# Define Loss and Optimizer\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)\n\n# ====== Training Loop ======\nnum_epochs = 10  \ntrain_losses = []\n\nstart_time = time.time()\n\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    \n    for images, labels in train_loader:\n        images, labels = images.to(device, non_blocking=True), labels.to(device, non_blocking=True)  \n\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item()\n\n    avg_train_loss = running_loss / len(train_loader)\n    train_losses.append(avg_train_loss)\n\n    print(f\"Epoch [{epoch+1}/{num_epochs}], Train Loss: {avg_train_loss:.4f}\")\n\n# Total training time\ntotal_time = time.time() - start_time\nprint(f\"Total Training Time: {total_time:.2f} seconds\")\n\n# Save model\ntorch.save(model.state_dict(), \"cats_vs_dogs_cnn.pth\")\n\n# ====== Model Evaluation ======\nmodel.eval()\nall_preds = []\nall_labels = []\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 = torch.max(outputs, 1)\n        all_preds.extend(predicted.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n\n# Compute Accuracy\naccuracy = accuracy_score(all_labels, all_preds)\nprint(f\"Validation Accuracy: {accuracy * 100:.2f}%\")\n\n# Generate Confusion Matrix\nconf_matrix = confusion_matrix(all_labels, all_preds)\n\n# Plot Confusion Matrix\nplt.figure(figsize=(6, 6))\nsns.heatmap(conf_matrix, annot=True, fmt=\"d\", cmap=\"Blues\", xticklabels=[\"Cat\", \"Dog\"], yticklabels=[\"Cat\", \"Dog\"])\nplt.xlabel(\"Predicted Label\")\nplt.ylabel(\"Actual Label\")\nplt.title(\"Confusion Matrix\")\nplt.show()\n\n# ====== Compare Predictions vs Actual ======\nfig, axes = plt.subplots(3, 4, figsize=(12, 9))  \n\nfor i, ax in enumerate(axes.flat):\n    img_path = val_dataset[i][0].permute(1, 2, 0).cpu().numpy()  # Convert tensor to numpy\n    ax.imshow(img_path)\n    actual_label = \"Dog\" if all_labels[i] == 1 else \"Cat\"\n    predicted_label = \"Dog\" if all_preds[i] == 1 else \"Cat\"\n    ax.set_title(f\"Actual: {actual_label}\\nPredicted: {predicted_label}\", fontsize=10)\n    ax.axis(\"off\")\n\nplt.tight_layout()\nplt.show()\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nfrom torch.utils.data import Dataset, DataLoader, random_split\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport seaborn as sns\nfrom sklearn.metrics import confusion_matrix, accuracy_score\nfrom PIL import Image\n\n# Check if GPU is available\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")\n\n# Define dataset paths\nextract_path = \"/kaggle/working/\"\ntrain_path = os.path.join(extract_path, \"train\")  \n\n# Load all images and split them into cats and dogs\nall_images = glob.glob(os.path.join(train_path, \"*.jpg\"))\ncat_images = [img for img in all_images if \"cat\" in os.path.basename(img)]\ndog_images = [img for img in all_images if \"dog\" in os.path.basename(img)]\n\n# Reduce dataset size by 30% (keep only 70%)\nnum_cats = int(len(cat_images) * 0.3)\nnum_dogs = int(len(dog_images) * 0.3)\n\n# Randomly select 70% of images\nselected_cat_images = random.sample(cat_images, num_cats)\nselected_dog_images = random.sample(dog_images, num_dogs)\n\n# Combine selected images for training\nreduced_dataset = selected_cat_images + selected_dog_images\nrandom.shuffle(reduced_dataset)  \nprint(f\"Reduced dataset size: {len(reduced_dataset)} images (Cats: {num_cats}, Dogs: {num_dogs})\")\n\n# Define transforms (optimized with smaller image size)\ntransform = transforms.Compose([\n    transforms.Resize((128, 128)),  \n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize([0.5], [0.5])\n])\n\n# ====== Custom Dataset Loader ======\nclass CatsDogsDataset(Dataset):\n    def __init__(self, image_list, transform=None):\n        self.image_list = image_list\n        self.transform = transform\n        self.labels = [1 if \"dog\" in os.path.basename(f) else 0 for f in self.image_list]\n\n    def __len__(self):\n        return len(self.image_list)\n\n    def __getitem__(self, idx):\n        img_path = self.image_list[idx]\n        image = Image.open(img_path).convert(\"RGB\")\n        \n        if self.transform:\n            image = self.transform(image)\n\n        label = self.labels[idx]\n        return image, label\n\n# Load dataset with reduced size\ndataset = CatsDogsDataset(reduced_dataset, transform=transform)\n\n# Split dataset into training (80%) and validation (20%)\ntrain_size = int(0.8 * len(dataset))\nval_size = len(dataset) - train_size\ntrain_dataset, val_dataset = random_split(dataset, [train_size, val_size])\n\n# Create DataLoaders\ntrain_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2, pin_memory=True)\nval_loader = DataLoader(val_dataset, batch_size=64, shuffle=False, num_workers=2, pin_memory=True)\n\n# ===== Define CNN Model =====\nclass CNNModel(nn.Module):\n    def __init__(self):\n        super(CNNModel, self).__init__()\n        self.conv_layers = nn.Sequential(\n            nn.Conv2d(3, 32, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n\n            nn.Conv2d(32, 64, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size=2, stride=2)\n        )\n\n        self.fc_layers = nn.Sequential(\n            nn.Linear(64 * 32 * 32, 256),  \n            nn.ReLU(),\n            nn.Dropout(0.3),\n            nn.Linear(256, 2)  \n        )\n\n    def forward(self, x):\n        x = self.conv_layers(x)\n        x = x.view(x.size(0), -1)  \n        x = self.fc_layers(x)\n        return x\n\n# Initialize model\nmodel = CNNModel().to(device)\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)\n\n# ====== Training Loop ======\nnum_epochs = 10  \ntrain_losses = []\n\nstart_time = time.time()\n\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    \n    for images, labels in train_loader:\n        images, labels = images.to(device, non_blocking=True), labels.to(device, non_blocking=True)  \n\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item()\n\n    avg_train_loss = running_loss / len(train_loader)\n    train_losses.append(avg_train_loss)\n\n    print(f\"Epoch [{epoch+1}/{num_epochs}], Train Loss: {avg_train_loss:.4f}\")\n\n# Total training time\ntotal_time = time.time() - start_time\nprint(f\"Total Training Time: {total_time:.2f} seconds\")\n\n# Save model\ntorch.save(model.state_dict(), \"cats_vs_dogs_cnn.pth\")\n\n# ====== Model Evaluation ======\nmodel.eval()\nall_preds = []\nall_labels = []\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 = torch.max(outputs, 1)\n        all_preds.extend(predicted.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n\n# Compute Accuracy\naccuracy = accuracy_score(all_labels, all_preds)\nprint(f\"Validation Accuracy: {accuracy * 100:.2f}%\")\n\n# Generate Confusion Matrix\nconf_matrix = confusion_matrix(all_labels, all_preds)\n\n# Plot Confusion Matrix\nplt.figure(figsize=(6, 6))\nsns.heatmap(conf_matrix, annot=True, fmt=\"d\", cmap=\"Blues\", xticklabels=[\"Cat\", \"Dog\"], yticklabels=[\"Cat\", \"Dog\"])\nplt.xlabel(\"Predicted Label\")\nplt.ylabel(\"Actual Label\")\nplt.title(\"Confusion Matrix\")\nplt.show()\n\n# ====== Compare Predictions vs Actual ======\nfig, axes = plt.subplots(3, 4, figsize=(12, 9))  \n\nfor i, ax in enumerate(axes.flat):\n    img_path = val_dataset[i][0].permute(1, 2, 0).cpu().numpy()  # Convert tensor to numpy\n    ax.imshow(img_path)\n    actual_label = \"Dog\" if all_labels[i] == 1 else \"Cat\"\n    predicted_label = \"Dog\" if all_preds[i] == 1 else \"Cat\"\n    ax.set_title(f\"Actual: {actual_label}\\nPredicted: {predicted_label}\", fontsize=10)\n    ax.axis(\"off\")\n\nplt.tight_layout()\nplt.show()\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-03-17T10:59:25.646733Z","iopub.execute_input":"2025-03-17T10:59:25.647132Z","iopub.status.idle":"2025-03-17T10:59:42.232718Z","shell.execute_reply.started":"2025-03-17T10:59:25.647100Z","shell.execute_reply":"2025-03-17T10:59:42.231077Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport glob\nimport time\nimport random  \nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision.transforms as transforms\nimport torchvision.models as models  # <-- Added import\nfrom torch.utils.data import Dataset, DataLoader, random_split\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport seaborn as sns\nfrom sklearn.metrics import confusion_matrix, accuracy_score\nfrom PIL import Image\n\n# Check if GPU is available\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")\n\n# Define dataset paths\nextract_path = \"/kaggle/working/\"\ntrain_path = os.path.join(extract_path, \"train\")  \n\n# Load all images and split them into cats and dogs\nall_images = glob.glob(os.path.join(train_path, \"*.jpg\"))\ncat_images = [img for img in all_images if \"cat\" in os.path.basename(img)]\ndog_images = [img for img in all_images if \"dog\" in os.path.basename(img)]\n\n# Reduce dataset size by 30% (keep only 70%)\nnum_cats = int(len(cat_images) * 0.3)\nnum_dogs = int(len(dog_images) * 0.3)\n\n# Randomly select 70% of images\nselected_cat_images = random.sample(cat_images, num_cats)\nselected_dog_images = random.sample(dog_images, num_dogs)\n\n# Combine selected images for training\nreduced_dataset = selected_cat_images + selected_dog_images\nrandom.shuffle(reduced_dataset)  \nprint(f\"Reduced dataset size: {len(reduced_dataset)} images (Cats: {num_cats}, Dogs: {num_dogs})\")\n\n# Define transforms (optimized with smaller image size)\ntransform = transforms.Compose([\n    transforms.Resize((128, 128)),  \n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize([0.5], [0.5])\n])\n\n# ====== Custom Dataset Loader ======\nclass CatsDogsDataset(Dataset):\n    def __init__(self, image_list, transform=None):\n        self.image_list = image_list\n        self.transform = transform\n        self.labels = [1 if \"dog\" in os.path.basename(f) else 0 for f in self.image_list]\n\n    def __len__(self):\n        return len(self.image_list)\n\n    def __getitem__(self, idx):\n        img_path = self.image_list[idx]\n        image = Image.open(img_path).convert(\"RGB\")\n        \n        if self.transform:\n            image = self.transform(image)\n\n        label = self.labels[idx]\n        return image, label\n\n# Load dataset with reduced size\ndataset = CatsDogsDataset(reduced_dataset, transform=transform)\n\n# Split dataset into training (80%) and validation (20%)\ntrain_size = int(0.8 * len(dataset))\nval_size = len(dataset) - train_size\ntrain_dataset, val_dataset = random_split(dataset, [train_size, val_size])\n\n# Create DataLoaders\ntrain_loader = DataLoader(train_dataset, batch_size=64, shuffle=True, num_workers=2, pin_memory=True)\nval_loader = DataLoader(val_dataset, batch_size=64, shuffle=False, num_workers=2, pin_memory=True)\n\n# ===== REPLACE CNN WITH RESNET-18 =====\n# We remove the custom CNNModel and use ResNet-18 with a modified final layer.\nmodel = models.resnet18(pretrained=True)\nnum_ftrs = model.fc.in_features\nmodel.fc = nn.Linear(num_ftrs, 2)\nmodel = model.to(device)\n\n# Define Loss and Optimizer\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)\n\n# ====== Training Loop ======\nnum_epochs = 10  \ntrain_losses = []\n\nstart_time = time.time()\n\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    \n    for images, labels in train_loader:\n        images, labels = images.to(device, non_blocking=True), labels.to(device, non_blocking=True)  \n\n        optimizer.zero_grad()\n        outputs = model(images)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n        \n        running_loss += loss.item()\n\n    avg_train_loss = running_loss / len(train_loader)\n    train_losses.append(avg_train_loss)\n\n    print(f\"Epoch [{epoch+1}/{num_epochs}], Train Loss: {avg_train_loss:.4f}\")\n\n# Total training time\ntotal_time = time.time() - start_time\nprint(f\"Total Training Time: {total_time:.2f} seconds\")\n\n# Save model\ntorch.save(model.state_dict(), \"cats_vs_dogs_cnn.pth\")\n\n# ====== Model Evaluation ======\nmodel.eval()\nall_preds = []\nall_labels = []\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 = torch.max(outputs, 1)\n        all_preds.extend(predicted.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n\n# Compute Accuracy\naccuracy = accuracy_score(all_labels, all_preds)\nprint(f\"Validation Accuracy: {accuracy * 100:.2f}%\")\n\n# Generate Confusion Matrix\nconf_matrix = confusion_matrix(all_labels, all_preds)\n\n# Plot Confusion Matrix\nplt.figure(figsize=(6, 6))\nsns.heatmap(conf_matrix, annot=True, fmt=\"d\", cmap=\"Blues\", xticklabels=[\"Cat\", \"Dog\"], yticklabels=[\"Cat\", \"Dog\"])\nplt.xlabel(\"Predicted Label\")\nplt.ylabel(\"Actual Label\")\nplt.title(\"Confusion Matrix\")\nplt.show()\n\n# ====== Compare Predictions vs Actual ======\nfig, axes = plt.subplots(3, 4, figsize=(12, 9))  \n\nfor i, ax in enumerate(axes.flat):\n    img_path = val_dataset[i][0].permute(1, 2, 0).cpu().numpy()  # Convert tensor to numpy\n    ax.imshow(img_path)\n    actual_label = \"Dog\" if all_labels[i] == 1 else \"Cat\"\n    predicted_label = \"Dog\" if all_preds[i] == 1 else \"Cat\"\n    ax.set_title(f\"Actual: {actual_label}\\nPredicted: {predicted_label}\", fontsize=10)\n    ax.axis(\"off\")\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}