{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.11"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":3362,"databundleVersionId":31148,"sourceType":"competition"},{"sourceId":236815520,"sourceType":"kernelVersion"}],"dockerImageVersionId":31012,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false},"papermill":{"default_parameters":{},"duration":17408.888845,"end_time":"2025-04-29T16:38:10.505724","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-04-29T11:48:01.616879","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"48627c0e","cell_type":"code","source":"import zipfile\nimport os\n\n# Path to the ZIP file\nzip_path = '/kaggle/input/dogs-vs-cats/train.zip'\nextract_dir = '/kaggle/working'\n\n# Extract the zip file\nwith zipfile.ZipFile(zip_path, 'r') as zip_ref:\n    zip_ref.extractall(extract_dir)\n\n","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.status.busy":"2025-04-29T17:15:56.517974Z","iopub.execute_input":"2025-04-29T17:15:56.518955Z","iopub.status.idle":"2025-04-29T17:16:08.829385Z","shell.execute_reply.started":"2025-04-29T17:15:56.518919Z","shell.execute_reply":"2025-04-29T17:16:08.828401Z"},"papermill":{"duration":14.053412,"end_time":"2025-04-29T11:48:21.805915","exception":false,"start_time":"2025-04-29T11:48:07.752503","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"e28472a9","cell_type":"code","source":"import os\n\n# Check the root extraction path\nroot_dir = '/kaggle/working'\ntrain_dir = os.path.join(root_dir, 'train')\ntest_dir= os.path.join(root_dir, 'test1')\n\n# Count and preview files\nall_files = os.listdir(train_dir)\nprint(f\"Total files in 'train/': {len(all_files)}\")\n\n# Separate by class for info\ncat_files = [f for f in all_files if f.startswith('cat')]\ndog_files = [f for f in all_files if f.startswith('dog')]\n\nprint(f\"Number of cat images: {len(cat_files)}\")\nprint(f\"Number of dog images: {len(dog_files)}\")\nprint(\"\\nSample filenames:\")\nprint(\"Cats:\", cat_files[:5])\nprint(\"Dogs:\", dog_files[:5])\n","metadata":{"execution":{"iopub.status.busy":"2025-04-29T17:16:17.425021Z","iopub.execute_input":"2025-04-29T17:16:17.425390Z","iopub.status.idle":"2025-04-29T17:16:17.456026Z","shell.execute_reply.started":"2025-04-29T17:16:17.425360Z","shell.execute_reply":"2025-04-29T17:16:17.454904Z"},"papermill":{"duration":0.034757,"end_time":"2025-04-29T11:48:21.844328","exception":false,"start_time":"2025-04-29T11:48:21.809571","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"1ce91929","cell_type":"code","source":"","metadata":{"papermill":{"duration":0.003114,"end_time":"2025-04-29T11:48:21.851012","exception":false,"start_time":"2025-04-29T11:48:21.847898","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"a817d54e","cell_type":"code","source":"import matplotlib.pyplot as plt\nfrom PIL import Image\nimport random\n\n\n# Get file lists\ncat_files = [f for f in os.listdir(train_dir) if f.startswith('cat')]\ndog_files = [f for f in os.listdir(train_dir) if f.startswith('dog')]\n\n# Randomly select 5 images from each class\nsample_cats = random.sample(cat_files, 5)\nsample_dogs = random.sample(dog_files, 5)\n\n# Plot cats\nplt.figure(figsize=(15, 3))\nfor i, file in enumerate(sample_cats):\n    img = Image.open(os.path.join(train_dir, file))\n    plt.subplot(1, 5, i + 1)\n    plt.imshow(img)\n    plt.title(\"Cat\")\n    plt.axis('off')\nplt.suptitle(\"Sample Cat Images\", fontsize=16)\nplt.show()\n\n# Plot dogs\nplt.figure(figsize=(15, 3))\nfor i, file in enumerate(sample_dogs):\n    img = Image.open(os.path.join(train_dir, file))\n    plt.subplot(1, 5, i + 1)\n    plt.imshow(img)\n    plt.title(\"Dog\")\n    plt.axis('off')\nplt.suptitle(\"Sample Dog Images\", fontsize=16)\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2025-04-29T17:16:25.255916Z","iopub.execute_input":"2025-04-29T17:16:25.256257Z","iopub.status.idle":"2025-04-29T17:16:26.377684Z","shell.execute_reply.started":"2025-04-29T17:16:25.256233Z","shell.execute_reply":"2025-04-29T17:16:26.376494Z"},"papermill":{"duration":1.300846,"end_time":"2025-04-29T11:48:23.155200","exception":false,"start_time":"2025-04-29T11:48:21.854354","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"1b6def6b","cell_type":"code","source":"import os\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom PIL import Image\nfrom torchvision import transforms\n\n# Constants\nDATA_DIR = '/kaggle/working/train'\nIMG_SIZE = 224  # EfficientNetB0 prefers 224x224\n\n# Define preprocessing transforms\nefficientnet_transforms = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],  # Imagenet mean\n        std=[0.229, 0.224, 0.225]    # Imagenet std\n    )\n])\n\n# Custom Dataset\nclass CatDogDataset(Dataset):\n    def __init__(self, file_list, root_dir, transform=None):\n        self.file_list = file_list\n        self.root_dir = root_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.file_list)\n\n    def __getitem__(self, idx):\n        img_name = self.file_list[idx]\n        img_path = os.path.join(self.root_dir, img_name)\n        image = Image.open(img_path).convert('RGB')\n\n        # Label extraction from filename\n        label = 1 if 'dog' in img_name else 0\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label\n\n# Prepare file list and split\nall_images = os.listdir(DATA_DIR)\nrandom.shuffle(all_images)\nsplit_idx = int(0.8 * len(all_images))  # 80% train, 20% val\ntrain_files = all_images[:split_idx]\nval_files = all_images[split_idx:]\n\n# Dataset & DataLoader\ntrain_dataset = CatDogDataset(train_files, DATA_DIR, transform=efficientnet_transforms)\nval_dataset = CatDogDataset(val_files, DATA_DIR, transform=efficientnet_transforms)\n\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=4)\nval_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=2)\n","metadata":{"execution":{"iopub.status.busy":"2025-04-29T17:16:35.958904Z","iopub.execute_input":"2025-04-29T17:16:35.959364Z","iopub.status.idle":"2025-04-29T17:16:45.936501Z","shell.execute_reply.started":"2025-04-29T17:16:35.959329Z","shell.execute_reply":"2025-04-29T17:16:45.935502Z"},"papermill":{"duration":14.755871,"end_time":"2025-04-29T11:48:37.938052","exception":false,"start_time":"2025-04-29T11:48:23.182181","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"1c0ef2a3","cell_type":"code","source":"\nprint(f\"Training samples: {len(train_dataset)}\")\nprint(f\"Validation samples: {len(val_dataset)}\")\n\n","metadata":{"execution":{"iopub.status.busy":"2025-04-29T17:16:51.355710Z","iopub.execute_input":"2025-04-29T17:16:51.356213Z","iopub.status.idle":"2025-04-29T17:16:51.361950Z","shell.execute_reply.started":"2025-04-29T17:16:51.356187Z","shell.execute_reply":"2025-04-29T17:16:51.361001Z"},"papermill":{"duration":0.034542,"end_time":"2025-04-29T11:48:38.002743","exception":false,"start_time":"2025-04-29T11:48:37.968201","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"24e326cc","cell_type":"code","source":"from collections import Counter\n\n# Get all labels from training dataset\ntrain_labels = [label for _, label in train_dataset]\nlabel_counts = Counter(train_labels)\n\n# Print label distribution\nprint(\"Label distribution in training dataset:\")\nprint(f\"Cats (label 0): {label_counts[0]}\")\nprint(f\"Dogs (label 1): {label_counts[1]}\")\n","metadata":{"execution":{"iopub.execute_input":"2025-04-29T11:48:38.045374Z","iopub.status.busy":"2025-04-29T11:48:38.045070Z","iopub.status.idle":"2025-04-29T11:49:54.143164Z","shell.execute_reply":"2025-04-29T11:49:54.142254Z"},"papermill":{"duration":76.14285,"end_time":"2025-04-29T11:49:54.166063","exception":false,"start_time":"2025-04-29T11:48:38.023213","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"b8a099f6","cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Function to visualize transformed images from the dataset\ndef show_transformed_images(dataset, num_images=5):\n    plt.figure(figsize=(15, 3))\n    for i in range(num_images):\n        idx = random.randint(0, len(dataset) - 1)\n        img_tensor, label = dataset[idx]\n        \n        # Convert tensor to numpy image for display\n        img = img_tensor.permute(1, 2, 0).numpy()\n        img = img * [0.229, 0.224, 0.225] + [0.485, 0.456, 0.406]  # Unnormalize\n        img = img.clip(0, 1)\n        \n        plt.subplot(1, num_images, i + 1)\n        plt.imshow(img)\n        plt.title(\"Dog\" if label == 1 else \"Cat\")\n        plt.axis('off')\n    plt.suptitle(\"Transformed Sample Images from Training Set\", fontsize=16)\n    plt.show()\n\n# Show images\nshow_transformed_images(train_dataset)\n","metadata":{"execution":{"iopub.execute_input":"2025-04-29T11:49:54.209547Z","iopub.status.busy":"2025-04-29T11:49:54.209193Z","iopub.status.idle":"2025-04-29T11:49:54.720051Z","shell.execute_reply":"2025-04-29T11:49:54.719103Z"},"papermill":{"duration":0.54082,"end_time":"2025-04-29T11:49:54.728274","exception":false,"start_time":"2025-04-29T11:49:54.187454","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"6293ca3c","cell_type":"code","source":"","metadata":{"papermill":{"duration":0.031162,"end_time":"2025-04-29T11:49:54.790920","exception":false,"start_time":"2025-04-29T11:49:54.759758","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"a3a8a50e-c0cd-4a22-8197-e25979e981dd","cell_type":"markdown","source":"## Model Selection: Why EfficientNet?\n\nFor this binary image classification task (Cats vs. Dogs), I chose **EfficientNet-B0** for the following reasons:\n\n- **Lightweight and Fast**: EfficientNet models are known for achieving high accuracy while being computationally efficient. Given that the Cats vs. Dogs dataset is relatively simple (only two classes and fairly distinguishable features), a heavy model like ResNet50 or deeper architectures would be unnecessary overkill.\n\n- **Good Trade-off Between Performance and Complexity**: EfficientNet-B0 uses a compound scaling method (balancing network depth, width, and resolution), which allows it to maintain excellent performance with fewer parameters compared to traditional CNNs.\n\n- **Pretrained Weights Availability**: Leveraging pretrained weights from ImageNet allows the model to generalize better and converge faster, even with a relatively small number of training epochs.\n\n- **Suitability for the Task**: Since the classification task is not overly complex (binary class separation), a lightweight and fast model like EfficientNet can achieve very high accuracy without risking heavy overfitting or requiring massive computational resources.\n\n### One Improvement with More Time\nIf more time and compute resources were available, I would experiment with:\n- **Model Ensembling**: Combining predictions from multiple lightweight models (e.g., EfficientNet, MobileNet) to further boost generalization.\n- **Advanced Augmentation Techniques**: Using stronger data augmentation strategies like CutMix, MixUp, or AutoAugment to improve robustness.\n- **Hyperparameter Tuning**: Systematically optimizing learning rates, batch sizes, and dropout rates to fine-tune the model for even better performance.\n\n","metadata":{}},{"id":"258e7dba","cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.models as models\n\n# Load EfficientNet-B0 with pretrained weights\nmodel = models.efficientnet_b0(pretrained=True)\n\n# Freeze feature extractor if desired (optional)\nfor param in model.features.parameters():\n    param.requires_grad = True  # Set to False if you want to fine-tune only classifier\n\n# Modify the classifier for binary classification\nmodel.classifier[1] = nn.Linear(model.classifier[1].in_features, 1)\n\nmodel = model.to('cuda' if torch.cuda.is_available() else 'cpu')\n","metadata":{"execution":{"iopub.status.busy":"2025-04-29T17:17:17.145242Z","iopub.execute_input":"2025-04-29T17:17:17.145586Z","iopub.status.idle":"2025-04-29T17:17:17.711827Z","shell.execute_reply.started":"2025-04-29T17:17:17.145529Z","shell.execute_reply":"2025-04-29T17:17:17.710739Z"},"papermill":{"duration":0.742388,"end_time":"2025-04-29T11:49:55.564741","exception":false,"start_time":"2025-04-29T11:49:54.822353","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"310e020a-ff39-4513-b9b7-3f76cb947653","cell_type":"code","source":"device = 'cuda' if torch.cuda.is_available() else 'cpu'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-29T17:18:15.113857Z","iopub.execute_input":"2025-04-29T17:18:15.114172Z","iopub.status.idle":"2025-04-29T17:18:15.118988Z","shell.execute_reply.started":"2025-04-29T17:18:15.114147Z","shell.execute_reply":"2025-04-29T17:18:15.117952Z"}},"outputs":[],"execution_count":null},{"id":"b72d27ba","cell_type":"code","source":"from torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom torch.optim import Adam\nfrom torch.nn import BCEWithLogitsLoss\nfrom tqdm import tqdm\n\n# Loss, optimizer, and scheduler\ncriterion = BCEWithLogitsLoss()\noptimizer = Adam(model.parameters(), lr=1e-4)\nscheduler = ReduceLROnPlateau(optimizer, mode='min', patience=2, factor=0.5, verbose=True)\n\n# Early stopping settings\nbest_val_loss = float('inf')\npatience_counter = 0\nearly_stop_patience = 5  # stop if no improvement in 5 epochs\n\n# Track loss and accuracy\ntrain_losses, val_losses = [], []\ntrain_accuracies, val_accuracies = [], []\n\n\n\nEPOCHS = 10\nfor epoch in range(EPOCHS):\n    model.train()\n    running_loss, correct, total = 0, 0, 0\n    for images, labels in tqdm(train_loader):\n        images, labels = images.to(device), labels.to(device).float().unsqueeze(1)\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        preds = torch.sigmoid(outputs) > 0.5\n        correct += (preds == labels).sum().item()\n        total += labels.size(0)\n\n    train_loss = running_loss / len(train_loader)\n    train_acc = correct / total\n    train_losses.append(train_loss)\n    train_accuracies.append(train_acc)\n\n    # Validation\n    model.eval()\n    val_loss_total, correct, total = 0, 0, 0\n    with torch.no_grad():\n        for images, labels in val_loader:\n            images, labels = images.to(device), labels.to(device).float().unsqueeze(1)\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            val_loss_total += loss.item()\n            preds = torch.sigmoid(outputs) > 0.5\n            correct += (preds == labels).sum().item()\n            total += labels.size(0)\n\n    val_loss = val_loss_total / len(val_loader)\n    val_acc = correct / total\n    val_losses.append(val_loss)\n    val_accuracies.append(val_acc)\n\n    print(f\"Epoch {epoch+1}/{EPOCHS} | Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f} | Train Acc: {train_acc:.4f}, Val Acc: {val_acc:.4f}\")\n\n    # Scheduler step\n    scheduler.step(val_loss)\n\n    # Early stopping\n    if val_loss < best_val_loss:\n        best_val_loss = val_loss\n        patience_counter = 0\n        torch.save(model.state_dict(), 'best_model.pt')  # Save best model\n    else:\n        patience_counter += 1\n        if patience_counter >= early_stop_patience:\n            print(f\"Early stopping triggered at epoch {epoch+1}\")\n            break\n","metadata":{"execution":{"iopub.execute_input":"2025-04-29T11:49:55.627581Z","iopub.status.busy":"2025-04-29T11:49:55.627208Z","iopub.status.idle":"2025-04-29T16:32:10.896210Z","shell.execute_reply":"2025-04-29T16:32:10.894760Z"},"papermill":{"duration":16935.521722,"end_time":"2025-04-29T16:32:11.116800","exception":false,"start_time":"2025-04-29T11:49:55.595078","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"84b7cfd4-4fc5-44ee-8864-fffcf73fd783","cell_type":"markdown","source":"## Final Training Observations and Overfitting Analysis\n\nDuring training, the model achieved very high training accuracy (almost 100%) and validation accuracy (~99%), indicating excellent performance. \n\nHowever, some signs of **overfitting** were noticed:\n- Training loss continuously decreased across epochs, reaching near zero.\n- Validation loss initially decreased but started slightly increasing after epoch 3–4.\n- Validation accuracy remained stable but did not continue improving, suggesting overfitting risk.\n\nTo mitigate overfitting:\n- **Early Stopping** was applied based on validation loss, halting training at epoch 6.\n- **Learning Rate Scheduler** (`ReduceLROnPlateau`) dynamically adjusted the learning rate during plateaus.\n\n### Further Improvements\nTo further control overfitting, additional strategies could be applied:\n- **Adding Dropout Layers** in the classifier head to randomly deactivate neurons during training.\n- **Using Weight Decay (L2 Regularization)** in the optimizer to penalize overly large weights.\n- **Applying Stronger Data Augmentation** (random crops, flips, color jitter) to make the model more robust.\n\nOverall, the model achieved excellent generalization while minimizing overfitting effects.\n\n","metadata":{}},{"id":"ddd3ff94","cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Accuracy\nplt.figure(figsize=(12, 5))\nplt.subplot(1, 2, 1)\nplt.plot(train_accuracies, label='Train Accuracy')\nplt.plot(val_accuracies, label='Validation Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.title('Training vs Validation Accuracy')\nplt.legend()\n\n# Loss\nplt.subplot(1, 2, 2)\nplt.plot(train_losses, label='Train Loss')\nplt.plot(val_losses, label='Validation Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.title('Training vs Validation Loss')\nplt.legend()\nplt.show()\n","metadata":{"execution":{"iopub.execute_input":"2025-04-29T16:32:11.565015Z","iopub.status.busy":"2025-04-29T16:32:11.564607Z","iopub.status.idle":"2025-04-29T16:32:11.996776Z","shell.execute_reply":"2025-04-29T16:32:11.994438Z"},"papermill":{"duration":0.658783,"end_time":"2025-04-29T16:32:11.998639","exception":false,"start_time":"2025-04-29T16:32:11.339856","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"bdbac4f4-05fa-45e2-9f57-ec20cc4724e1","cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"id":"d6a5923f","cell_type":"code","source":"# Load best model weights\nmodel.load_state_dict(torch.load('/kaggle/input/cats-vs-dog-efficientnet/best_model.pt', map_location=device))\nmodel.eval()\n","metadata":{"execution":{"iopub.status.busy":"2025-04-29T17:18:24.679305Z","iopub.execute_input":"2025-04-29T17:18:24.679619Z","iopub.status.idle":"2025-04-29T17:18:25.058012Z","shell.execute_reply.started":"2025-04-29T17:18:24.679597Z","shell.execute_reply":"2025-04-29T17:18:25.057035Z"},"papermill":{"duration":0.326911,"end_time":"2025-04-29T16:32:12.673981","exception":false,"start_time":"2025-04-29T16:32:12.347070","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"1c5cb15c","cell_type":"code","source":"from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix, classification_report\nimport seaborn as sns\nimport numpy as np\n\nall_preds = []\nall_labels = []\n\nmodel.eval()\nwith torch.no_grad():\n    for images, labels in val_loader:\n        images = images.to(device)\n        outputs = model(images)\n        preds = (torch.sigmoid(outputs) > 0.5).cpu().numpy().astype(int)\n        all_preds.extend(preds.flatten())\n        all_labels.extend(labels.numpy())\n\n# Metrics\naccuracy = accuracy_score(all_labels, all_preds)\nprecision = precision_score(all_labels, all_preds)\nrecall = recall_score(all_labels, all_preds)\nf1 = f1_score(all_labels, all_preds)\n\nprint(f\"📊 Validation Set Evaluation:\")\nprint(f\"Accuracy:  {accuracy:.4f}\")\nprint(f\"Precision: {precision:.4f}\")\nprint(f\"Recall:    {recall:.4f}\")\nprint(f\"F1 Score:  {f1:.4f}\")\n\n# Classification Report\nprint(\"\\nClassification Report:\")\nprint(classification_report(all_labels, all_preds, target_names=['Cat', 'Dog']))\n\n# Confusion Matrix\ncm = confusion_matrix(all_labels, all_preds)\nplt.figure(figsize=(6, 5))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=['Cat', 'Dog'], yticklabels=['Cat', 'Dog'])\nplt.xlabel('Predicted')\nplt.ylabel('Actual')\nplt.title(\"Confusion Matrix on Validation Set\")\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2025-04-29T17:19:04.938686Z","iopub.execute_input":"2025-04-29T17:19:04.939031Z","iopub.status.idle":"2025-04-29T17:24:25.193409Z","shell.execute_reply.started":"2025-04-29T17:19:04.939008Z","shell.execute_reply":"2025-04-29T17:24:25.192088Z"},"papermill":{"duration":353.368389,"end_time":"2025-04-29T16:38:06.269250","exception":false,"start_time":"2025-04-29T16:32:12.900861","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"52c52730-cc2d-45e8-bc22-06f2814a43c7","cell_type":"markdown","source":"## Evaluation Analysis on Validation Set\n\nThe final model achieved exceptional performance on the validation set, with near-perfect accuracy and balanced class performance.\n\n### 📊 Metrics Summary:\n- **Accuracy**: 99.58%\n- **Precision**: 99.60%\n- **Recall**: 99.55%\n- **F1 Score**: 99.57%\n\n### 📈 Confusion Matrix Insights:\n- The model correctly predicted **2519 out of 2529 cats** and **2460 out of 2471 dogs**.\n- Only **21 total misclassifications** occurred out of 5000 validation samples.\n- This indicates the model is both **highly precise** (few false positives) and **highly sensitive** (few false negatives).\n\n### ✅ Key Takeaways:\n- The model generalizes very well and performs consistently across both classes.\n- There is **no significant class imbalance** in predictions, and no class is favored.\n- This validates the choice of architecture (EfficientNet-B0), training strategy (early stopping, learning rate scheduler), and preprocessing pipeline.\n\nOverall, the model is reliable and robust for binary image classification tasks like this one.\n","metadata":{}},{"id":"b34ec5f9-711b-4c3e-8a4d-2f5df02631a0","cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"id":"0af841f7-7e00-4fb5-85d8-2225927e751d","cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"id":"65e12307-a941-452d-a3d0-120f15308648","cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"id":"6b1ea398-29ff-4e16-a53c-64e08963231f","cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}