{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.13.15","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport zipfile\nimport random\nfrom pathlib import Path\nfrom collections import Counter\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nfrom PIL import Image\n\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\n\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import classification_report, confusion_matrix","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:18:52.585117Z","iopub.execute_input":"2026-10-18T17:18:52.585866Z","iopub.status.idle":"2026-10-18T17:18:52.591322Z","shell.execute_reply.started":"2026-10-18T17:18:52.585834Z","shell.execute_reply":"2026-10-18T17:18:52.59043Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATASET_DIR = Path(\"/kaggle/input/competitions/dogs-vs-cats\")\nWORK_DIR = Path(\"/kaggle/working/dogs_vs_cats\")\n\nTRAIN_DIR = WORK_DIR / \"train\"\nTEST_DIR = WORK_DIR / \"test1\"\n\nWORK_DIR.mkdir(parents=True, exist_ok=True)\n\ndevice = torch.device(\n    \"cuda\" if torch.cuda.is_available() else \"cpu\"\n)\n\nprint(\"PyTorch version:\", torch.__version__)\nprint(\"Device:\", device)\nprint(\"Dataset directory exists:\", DATASET_DIR.exists())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:18:57.52907Z","iopub.execute_input":"2026-10-18T17:18:57.529398Z","iopub.status.idle":"2026-10-18T17:18:57.536495Z","shell.execute_reply.started":"2026-10-18T17:18:57.529372Z","shell.execute_reply":"2026-10-18T17:18:57.535478Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def extract_zip(zip_path, destination):\n    if not zip_path.exists():\n        raise FileNotFoundError(f\"ZIP file not found: {zip_path}\")\n\n    destination.mkdir(parents=True, exist_ok=True)\n\n    with zipfile.ZipFile(zip_path, \"r\") as zip_ref:\n        zip_ref.extractall(destination)\n\n    print(f\"Extracted: {zip_path.name}\")\n\n\nextract_zip(DATASET_DIR / \"train.zip\", TRAIN_DIR)\nextract_zip(DATASET_DIR / \"test1.zip\", TEST_DIR)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:19:02.594276Z","iopub.execute_input":"2026-10-18T17:19:02.594665Z","iopub.status.idle":"2026-10-18T17:19:13.706191Z","shell.execute_reply.started":"2026-10-18T17:19:02.594634Z","shell.execute_reply":"2026-10-18T17:19:13.705374Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_files = sorted(TRAIN_DIR.rglob(\"*.jpg\"))\ntest_files = sorted(TEST_DIR.rglob(\"*.jpg\"))\n\nprint(\"Training images:\", len(train_files))\nprint(\"Test images:\", len(test_files))\n\nprint(\"\\nSample training files:\")\nfor path in train_files[:10]:\n    print(path.name)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:19:23.585949Z","iopub.execute_input":"2026-10-18T17:19:23.586282Z","iopub.status.idle":"2026-10-18T17:19:24.088145Z","shell.execute_reply.started":"2026-10-18T17:19:23.586254Z","shell.execute_reply":"2026-10-18T17:19:24.087277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_label(path):\n    filename = Path(path).name.lower()\n\n    if filename.startswith(\"cat.\"):\n        return 0\n    elif filename.startswith(\"dog.\"):\n        return 1\n    else:\n        raise ValueError(f\"Unknown class in filename: {filename}\")\n\n\nlabels = [get_label(path) for path in train_files]\n\nclass_counts = Counter(labels)\n\nprint(\"Cats:\", class_counts[0])\nprint(\"Dogs:\", class_counts[1])\n\nprint(\"Label mapping: 0 = Cat, 1 = Dog\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:19:30.046109Z","iopub.execute_input":"2026-10-18T17:19:30.046997Z","iopub.status.idle":"2026-10-18T17:19:30.219502Z","shell.execute_reply.started":"2026-10-18T17:19:30.046961Z","shell.execute_reply":"2026-10-18T17:19:30.218699Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"random.seed(42)\n\nsamples = random.sample(\n    train_files, min(8, len(train_files))\n)\n\nfig, axes = plt.subplots(2, 4, figsize=(12, 7))\n\nfor ax, path in zip(axes.flatten(), samples):\n    with Image.open(path) as img:\n        ax.imshow(img.convert(\"RGB\"))\n\n    label = \"Cat\" if get_label(path) == 0 else \"Dog\"\n    ax.set_title(label)\n    ax.axis(\"off\")\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:19:38.182011Z","iopub.execute_input":"2026-10-18T17:19:38.18261Z","iopub.status.idle":"2026-10-18T17:19:39.168563Z","shell.execute_reply.started":"2026-10-18T17:19:38.18258Z","shell.execute_reply":"2026-10-18T17:19:39.167459Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMG_SIZE = 128\nBATCH_SIZE = 32\n\ntrain_transform = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.RandomHorizontalFlip(),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.5, 0.5, 0.5],\n        std=[0.5, 0.5, 0.5]\n    )\n])\n\nval_transform = transforms.Compose([\n    transforms.Resize((IMG_SIZE, IMG_SIZE)),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.5, 0.5, 0.5],\n        std=[0.5, 0.5, 0.5]\n    )\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:21:57.085486Z","iopub.execute_input":"2026-10-18T17:21:57.08635Z","iopub.status.idle":"2026-10-18T17:21:57.092203Z","shell.execute_reply.started":"2026-10-18T17:21:57.086319Z","shell.execute_reply":"2026-10-18T17:21:57.09126Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DogsCatsDataset(Dataset):\n    def __init__(self, image_paths, transform=None):\n        self.image_paths = list(image_paths)\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.image_paths)\n\n    def __getitem__(self, index):\n        image_path = self.image_paths[index]\n\n        with Image.open(image_path) as img:\n            image = img.convert(\"RGB\")\n\n        label = get_label(image_path)\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:22:03.805373Z","iopub.execute_input":"2026-10-18T17:22:03.805722Z","iopub.status.idle":"2026-10-18T17:22:03.812203Z","shell.execute_reply.started":"2026-10-18T17:22:03.805695Z","shell.execute_reply":"2026-10-18T17:22:03.811278Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_paths, val_paths = train_test_split(\n    train_files,\n    test_size=0.2,\n    random_state=42,\n    stratify=labels\n)\n\ntrain_dataset = DogsCatsDataset(\n    train_paths,\n    transform=train_transform\n)\n\nval_dataset = DogsCatsDataset(\n    val_paths,\n    transform=val_transform\n)\n\nprint(\"Training samples:\", len(train_dataset))\nprint(\"Validation samples:\", len(val_dataset))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:22:07.995408Z","iopub.execute_input":"2026-10-18T17:22:07.996006Z","iopub.status.idle":"2026-10-18T17:22:08.032057Z","shell.execute_reply.started":"2026-10-18T17:22:07.995977Z","shell.execute_reply":"2026-10-18T17:22:08.0312Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_loader = DataLoader(\n    train_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=True,\n    num_workers=2,\n    pin_memory=torch.cuda.is_available()\n)\n\nval_loader = DataLoader(\n    val_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=False,\n    num_workers=2,\n    pin_memory=torch.cuda.is_available()\n)\n\nimages, labels_batch = next(iter(train_loader))\n\nprint(\"Image batch shape:\", images.shape)\nprint(\"Labels shape:\", labels_batch.shape)\nprint(\"Labels:\", labels_batch[:10])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:22:13.165989Z","iopub.execute_input":"2026-10-18T17:22:13.166673Z","iopub.status.idle":"2026-10-18T17:22:13.661534Z","shell.execute_reply.started":"2026-10-18T17:22:13.166632Z","shell.execute_reply":"2026-10-18T17:22:13.66052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CNN(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n        self.features = nn.Sequential(\n            nn.Conv2d(3, 16, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n\n            nn.Conv2d(16, 32, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n\n            nn.Conv2d(32, 64, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2)\n        )\n\n        self.classifier = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(64 * 16 * 16, 128),\n            nn.ReLU(),\n            nn.Dropout(0.3),\n            nn.Linear(128, 2)\n        )\n\n    def forward(self, x):\n        x = self.features(x)\n        x = self.classifier(x)\n        return x\n\n\nmodel = CNN().to(device)\n\nprint(model)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:22:26.650934Z","iopub.execute_input":"2026-10-18T17:22:26.651305Z","iopub.status.idle":"2026-10-18T17:22:26.693154Z","shell.execute_reply.started":"2026-10-18T17:22:26.651276Z","shell.execute_reply":"2026-10-18T17:22:26.692366Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\n\noptimizer = optim.Adam(\n    model.parameters(),\n    lr=0.001\n)\n\nprint(\"Loss function:\", criterion)\nprint(\"Optimizer:\", optimizer)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:22:35.379316Z","iopub.execute_input":"2026-10-18T17:22:35.380209Z","iopub.status.idle":"2026-10-18T17:22:35.386037Z","shell.execute_reply.started":"2026-10-18T17:22:35.380175Z","shell.execute_reply":"2026-10-18T17:22:35.384998Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"EPOCHS = 5\n\nhistory = {\n    \"train_loss\": [],\n    \"val_loss\": [],\n    \"train_acc\": [],\n    \"val_acc\": []\n}\n\nbest_val_acc = 0.0\nbest_model_path = WORK_DIR / \"best_model.pth\"\n\nfor epoch in range(EPOCHS):\n\n    # Training phase\n    model.train()\n\n    total_loss = 0.0\n    correct = 0\n    total = 0\n\n    for images, targets in train_loader:\n        images = images.to(device)\n        targets = targets.to(device)\n\n        optimizer.zero_grad()\n\n        outputs = model(images)\n        loss = criterion(outputs, targets)\n\n        loss.backward()\n        optimizer.step()\n\n        total_loss += loss.item() * images.size(0)\n\n        predictions = outputs.argmax(dim=1)\n        correct += (predictions == targets).sum().item()\n        total += targets.size(0)\n\n    train_loss = total_loss / total\n    train_acc = correct / total\n\n    # Validation phase\n    model.eval()\n\n    val_loss_sum = 0.0\n    val_correct = 0\n    val_total = 0\n\n    with torch.no_grad():\n        for images, targets in val_loader:\n            images = images.to(device)\n            targets = targets.to(device)\n\n            outputs = model(images)\n            loss = criterion(outputs, targets)\n\n            val_loss_sum += loss.item() * images.size(0)\n\n            predictions = outputs.argmax(dim=1)\n            val_correct += (\n                predictions == targets\n            ).sum().item()\n\n            val_total += targets.size(0)\n\n    val_loss = val_loss_sum / val_total\n    val_acc = val_correct / val_total\n\n    history[\"train_loss\"].append(train_loss)\n    history[\"val_loss\"].append(val_loss)\n    history[\"train_acc\"].append(train_acc)\n    history[\"val_acc\"].append(val_acc)\n\n    if val_acc > best_val_acc:\n        best_val_acc = val_acc\n\n        torch.save({\n            \"model_state_dict\": model.state_dict(),\n            \"classes\": [\"Cat\", \"Dog\"],\n            \"image_size\": IMG_SIZE\n        }, best_model_path)\n\n    print(\n        f\"Epoch [{epoch+1}/{EPOCHS}] \"\n        f\"Train Loss: {train_loss:.4f} | \"\n        f\"Train Acc: {train_acc:.2%} | \"\n        f\"Val Loss: {val_loss:.4f} | \"\n        f\"Val Acc: {val_acc:.2%}\"\n    )\n\nprint(f\"\\nBest validation accuracy: {best_val_acc:.2%}\")\nprint(\"Best model saved to:\", best_model_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T17:22:42.092969Z","iopub.execute_input":"2026-10-18T17:22:42.093309Z","iopub.status.idle":"2026-10-18T17:43:21.243007Z","shell.execute_reply.started":"2026-10-18T17:22:42.093283Z","shell.execute_reply":"2026-10-18T17:43:21.241878Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(12, 4))\n\nplt.subplot(1, 2, 1)\nplt.plot(history[\"train_loss\"], label=\"Train Loss\")\nplt.plot(history[\"val_loss\"], label=\"Validation Loss\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.title(\"Loss\")\nplt.legend()\n\nplt.subplot(1, 2, 2)\nplt.plot(history[\"train_acc\"], label=\"Train Accuracy\")\nplt.plot(history[\"val_acc\"], label=\"Validation Accuracy\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.title(\"Accuracy\")\nplt.legend()\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T18:14:54.968532Z","iopub.execute_input":"2026-10-18T18:14:54.970818Z","iopub.status.idle":"2026-10-18T18:14:55.413433Z","shell.execute_reply.started":"2026-10-18T18:14:54.970761Z","shell.execute_reply":"2026-10-18T18:14:55.412371Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"checkpoint = torch.load(\n    best_model_path,\n    map_location=device,\n    weights_only=True\n)\n\nmodel.load_state_dict(checkpoint[\"model_state_dict\"])\nmodel.eval()\n\nall_predictions = []\nall_targets = []\n\nwith torch.no_grad():\n    for images, targets in val_loader:\n        images = images.to(device)\n\n        outputs = model(images)\n        predictions = outputs.argmax(dim=1).cpu().numpy()\n\n        all_predictions.extend(predictions)\n        all_targets.extend(targets.numpy())\n\nprint(classification_report(\n    all_targets,\n    all_predictions,\n    labels=[0, 1],\n    target_names=[\"Cat\", \"Dog\"],\n    zero_division=0\n))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T18:15:00.787483Z","iopub.execute_input":"2026-10-18T18:15:00.787875Z","iopub.status.idle":"2026-10-18T18:15:31.007006Z","shell.execute_reply.started":"2026-10-18T18:15:00.787846Z","shell.execute_reply":"2026-10-18T18:15:31.00599Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cm = confusion_matrix(\n    all_targets,\n    all_predictions,\n    labels=[0, 1]\n)\n\nplt.figure(figsize=(6, 5))\nplt.imshow(cm)\nplt.colorbar()\n\nplt.xticks([0, 1], [\"Cat\", \"Dog\"])\nplt.yticks([0, 1], [\"Cat\", \"Dog\"])\n\nplt.xlabel(\"Predicted Label\")\nplt.ylabel(\"Actual Label\")\nplt.title(\"Confusion Matrix\")\n\nfor i in range(2):\n    for j in range(2):\n        plt.text(j, i, str(cm[i, j]),\n                 ha=\"center\", va=\"center\")\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T18:15:52.817576Z","iopub.execute_input":"2026-10-18T18:15:52.818465Z","iopub.status.idle":"2026-10-18T18:15:53.023792Z","shell.execute_reply.started":"2026-10-18T18:15:52.818427Z","shell.execute_reply":"2026-10-18T18:15:53.022907Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict_image(image_path):\n    with Image.open(image_path) as img:\n        image = img.convert(\"RGB\")\n\n    input_tensor = val_transform(image).unsqueeze(0).to(device)\n\n    model.eval()\n\n    with torch.no_grad():\n        outputs = model(input_tensor)\n        probabilities = torch.softmax(outputs, dim=1)\n\n        confidence, predicted_idx = probabilities.max(dim=1)\n\n    class_names = [\"Cat\", \"Dog\"]\n    predicted_class = class_names[predicted_idx.item()]\n\n    print(\"Prediction:\", predicted_class)\n    print(f\"Confidence: {confidence.item():.2%}\")\n\n    plt.imshow(image)\n    plt.title(\n        f\"{predicted_class} ({confidence.item():.2%})\"\n    )\n    plt.axis(\"off\")\n    plt.show()\n\n\n# Replace this with a real image path.\npredict_image(str(train_files[0]))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-18T18:15:58.052368Z","iopub.execute_input":"2026-10-18T18:15:58.053443Z","iopub.status.idle":"2026-10-18T18:15:58.287288Z","shell.execute_reply.started":"2026-10-18T18:15:58.053402Z","shell.execute_reply":"2026-10-18T18:15:58.286352Z"}},"outputs":[],"execution_count":null}]}