{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","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":"markdown","source":"# Fetching Raw Data","metadata":{"_uuid":"5104c369-1f5b-4211-a7f8-9d9a9a4881b3","_cell_guid":"62c2bce7-1bfc-4aa2-9811-9f7eb2199b04","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"import os\nimport zipfile\n\ntrain_zip_path = '/kaggle/input/competitions/dogs-vs-cats/train.zip'\ntest_zip_path = '/kaggle/input/competitions/dogs-vs-cats/test1.zip'\nworking_dir = '/kaggle/working/'\n\n# Extract train.zip\nprint(\"Extracting training images...\")\nwith zipfile.ZipFile(train_zip_path, 'r') as zip_ref:\n    zip_ref.extractall(working_dir)\n\n# Extract test1.zip\nprint(\"Extracting testing images...\")\nwith zipfile.ZipFile(test_zip_path, 'r') as zip_ref:\n    zip_ref.extractall(working_dir)\n\nprint(\"Extraction complete!\")","metadata":{"_uuid":"11cdddc0-c8ad-4ff4-a1e4-5121c630fd48","_cell_guid":"ae64af9f-f4f3-4020-8b84-3fce2f8c54b0","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:11:49.364494Z","iopub.execute_input":"2026-08-31T20:11:49.365322Z","iopub.status.idle":"2026-08-31T20:11:58.673506Z","shell.execute_reply.started":"2026-08-31T20:11:49.365288Z","shell.execute_reply":"2026-08-31T20:11:58.672495Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim","metadata":{"_uuid":"eebcb7ab-b0eb-4395-880c-2e771b8b0d4e","_cell_guid":"c7012f82-b05d-4d0c-9830-10a34bc617e5","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:11:58.675153Z","iopub.execute_input":"2026-08-31T20:11:58.675372Z","iopub.status.idle":"2026-08-31T20:11:58.679338Z","shell.execute_reply.started":"2026-08-31T20:11:58.675350Z","shell.execute_reply":"2026-08-31T20:11:58.678766Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#To check whether GPU is available or not\nprint(\"Positive\") if torch.accelerator.is_available() else print(\"Negative\")","metadata":{"_uuid":"4e893c19-dde5-496e-85fb-02986d0d4459","_cell_guid":"dfaaa3aa-ab84-4bf0-82fc-49267998d606","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:11:58.680212Z","iopub.execute_input":"2026-08-31T20:11:58.680391Z","iopub.status.idle":"2026-08-31T20:11:58.692915Z","shell.execute_reply.started":"2026-08-31T20:11:58.680373Z","shell.execute_reply":"2026-08-31T20:11:58.692076Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Example showing tensor moved to GPU\n# if torch.accelerator.is_available():\n#     tensor = tensor.to(torch.accelerator.current_accelerator())","metadata":{"_uuid":"b7ffe1f2-f1e1-4366-9b2c-b1dd598a89d8","_cell_guid":"200344ee-b357-453f-9709-04d42613e797","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:11:58.693888Z","iopub.execute_input":"2026-08-31T20:11:58.694367Z","iopub.status.idle":"2026-08-31T20:11:58.704993Z","shell.execute_reply.started":"2026-08-31T20:11:58.694334Z","shell.execute_reply":"2026-08-31T20:11:58.704332Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Processing and Loading","metadata":{"_uuid":"747c2b06-3f19-4af0-b583-5a2c69952161","_cell_guid":"f883c29d-0015-49c5-a2fb-e0d255274613","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"from PIL import Image\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision.transforms import v2\n\ntransform = v2.Compose([\n    v2.Resize((128,128)),\n    v2.ToImage(),    #Converts image to tensor\n    v2.ToDtype(torch.float32, scale=True),    #Scales the tensor\n    v2.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))    #Normalizes the tensor\n])","metadata":{"_uuid":"99bf732d-7ef3-4453-b939-85dfd60a04a3","_cell_guid":"dc7b765c-4a25-40b0-9f88-dc546b8380ef","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:11:58.706949Z","iopub.execute_input":"2026-08-31T20:11:58.707340Z","iopub.status.idle":"2026-08-31T20:11:58.717970Z","shell.execute_reply.started":"2026-08-31T20:11:58.707317Z","shell.execute_reply":"2026-08-31T20:11:58.717291Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Creating a class (literally the class acts like a format in which the dataset of images is formatted to be processed subsequently)\n# Custom Dataset\n\nclass KaggleCatsAndDogs(Dataset):\n    def __init__(self, root_dir, transform=None):\n        self.root_dir = root_dir\n        self.transform = transform\n        self.filenames = os.listdir(root_dir)    #Create list of images (only the filenames)\n\n    def __len__(self):\n        return len(self.filenames)    #To get the number of datas\n\n    def __getitem__(self, idx):\n        img_name = self.filenames[idx]\n        img_path = os.path.join(self.root_dir, img_name)\n\n        image = Image.open(img_path)\n\n        category = img_name.split('.')[0]\n        label = 1 if category == \"dog\" else 0    #Categorize the image into dogs and cats\n\n        if self.transform:    #To check whether transform!=None\n            image = self.transform(image)    #Converts the PIL image as per the transform pipeline\n\n        return image, torch.tensor(label, dtype=torch.long)    #Returns the tranformed image and the labels is converted to tensor and exported","metadata":{"_uuid":"415851b6-0418-4dae-9ac6-bab46e6dcb0c","_cell_guid":"4267f2f4-711e-48a5-9746-1ad8d7127fb3","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:11:58.718761Z","iopub.execute_input":"2026-08-31T20:11:58.718942Z","iopub.status.idle":"2026-08-31T20:11:58.727959Z","shell.execute_reply.started":"2026-08-31T20:11:58.718924Z","shell.execute_reply":"2026-08-31T20:11:58.727357Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.utils.data import random_split\n\ndata_dir = '/kaggle/working/train/'\nfull_trainset = KaggleCatsAndDogs(data_dir, transform)    #this is an object\n\ntrain_size = int(0.8 * len(full_trainset))    #Training Set = 80%\nval_size = len(full_trainset) - train_size\ntrainset, valset = random_split(full_trainset, [train_size, val_size])\n\ntrain_loader = DataLoader(trainset, \n                          batch_size=32, \n                          shuffle=True, \n                          num_workers=4,          # Uses multi-threading on CPU to load images in parallel\n                          pin_memory=True         # Speeds up tensor transfer from CPU memory to GPU memory)\n                         )\nprint(f\"Successfully loaded {len(trainset)} images into PyTorch\")\n\nval_loader = DataLoader(valset, \n                          batch_size=32, \n                          shuffle=True, \n                          num_workers=4,          # Uses multi-threading on CPU to load images in parallel\n                          pin_memory=True         # Speeds up tensor transfer from CPU memory to GPU memory)\n                         )\nprint(f\"Successfully loaded {len(valset)} images into PyTorch\")","metadata":{"_uuid":"022b2239-f8f7-4b93-b120-0d2263a7db74","_cell_guid":"d2908e9e-b438-4f70-8f03-dd002798b884","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:11:58.728759Z","iopub.execute_input":"2026-08-31T20:11:58.729078Z","iopub.status.idle":"2026-08-31T20:11:58.762103Z","shell.execute_reply.started":"2026-08-31T20:11:58.729057Z","shell.execute_reply":"2026-08-31T20:11:58.761504Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Visualize the dataset\n\nimport matplotlib.pyplot as plt\nlabels_map = {\n    0: \"Cat\",\n    1: \"Dog\",\n}\nfigure = plt.figure(figsize=(10, 10))\ncols, rows = 4, 3\n\nfor i in range(1, cols*rows+1):\n    sample_idx = torch.randint(len(trainset), size=(1,)).item()\n    img, label = trainset[sample_idx]\n    \n    figure.add_subplot(rows, cols, i)\n    plt.title(labels_map[label.item()])\n    plt.axis(\"off\")\n\n    if trainset.dataset.transform is not None:\n        # UN-NORMALIZE: Reverse the (x - 0.5) / 0.5 math\n        img = (img * 0.5) + 0.5    # Un-normalize image otherwise image shown will be underexposed due to the -ve values changed to 0\n        \n        # Fix the dimensions for Matplotlib (Color, Height, Width -> Height, Width, Color)\n        img_plot = img.permute(1, 2, 0)\n        \n        # Display the image without the grayscale enforcer\n        plt.imshow(img_plot)\n        \n    else:\n        plt.imshow(img)\n    \nplt.show()","metadata":{"_uuid":"9d1f37d9-bae2-4797-99da-9eaf44a74c4b","_cell_guid":"b6883ffe-d378-45c0-bade-9b1f3a62117f","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:11:58.767689Z","iopub.execute_input":"2026-08-31T20:11:58.767987Z","iopub.status.idle":"2026-08-31T20:11:59.392882Z","shell.execute_reply.started":"2026-08-31T20:11:58.767956Z","shell.execute_reply":"2026-08-31T20:11:59.392008Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Building the CNN","metadata":{"_uuid":"4045416f-9936-4042-abef-565c838cc302","_cell_guid":"80c4f19c-a9c8-4f4d-8912-0864b5588453","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"device = torch.accelerator.current_accelerator().type if torch.accelerator.is_available() else \"cpu\"\nprint(f\"Using {device} device\")","metadata":{"_uuid":"1a2269f3-c959-476f-89be-bbe5efd3abde","_cell_guid":"d84eef32-a6be-4f68-a6ac-ab6eba17fe4a","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:11:59.394130Z","iopub.execute_input":"2026-08-31T20:11:59.394450Z","iopub.status.idle":"2026-08-31T20:11:59.399876Z","shell.execute_reply.started":"2026-08-31T20:11:59.394417Z","shell.execute_reply":"2026-08-31T20:11:59.398993Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CatDogCNN(nn.Module):\n    def __init__(self):\n        super().__init__()    #Set-up nn.Module\n\n        self.conv_layers = nn.Sequential(\n            #Layer 1    128x128 -> 64x64\n            nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, stride=1, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n\n            #Layer 2    64x64 -> 32x32\n            nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n\n            #Layer 3    32x32 -> 16x16\n            nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, stride=1, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n\n            # Dimension of image = [128 channels, 16 height, 16 width]\n        )\n\n        self.fc_layer = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(in_features=128*16*16, out_features=256),\n            nn.ReLU(),\n            nn.Linear(256, 2)    # 2 output classes\n        )\n\n    def forward(self, x):\n        x = self.conv_layers(x)\n        x = self.fc_layer(x)\n        return x","metadata":{"_uuid":"4379fe36-1e91-48c8-9c33-ceda2bc6ccd3","_cell_guid":"c4525d4d-91dc-49ba-8236-ad64921117ef","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:11:59.400732Z","iopub.execute_input":"2026-08-31T20:11:59.400937Z","iopub.status.idle":"2026-08-31T20:11:59.415987Z","shell.execute_reply.started":"2026-08-31T20:11:59.400917Z","shell.execute_reply":"2026-08-31T20:11:59.415325Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = CatDogCNN().to(device)\n\n# Enable Multi-GPU if more than 1 GPU is available\nif torch.cuda.device_count() > 1:\n    print(f\"Using {torch.cuda.device_count()} GPUs!\")\n    model = nn.DataParallel(model)\n\nmodel","metadata":{"_uuid":"c80e0316-e0ad-490d-af14-9bed4e2e43a5","_cell_guid":"cb9e323b-58fb-4a8a-b1ad-c59fbd1c2e28","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:11:59.416830Z","iopub.execute_input":"2026-08-31T20:11:59.417013Z","iopub.status.idle":"2026-08-31T20:11:59.502477Z","shell.execute_reply.started":"2026-08-31T20:11:59.416994Z","shell.execute_reply":"2026-08-31T20:11:59.501910Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters())","metadata":{"_uuid":"2528a25a-15ea-4e66-a054-cc93da00ec24","_cell_guid":"0b1b4a62-1c2e-48da-8c3f-9dd8a1257e8d","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:11:59.503279Z","iopub.execute_input":"2026-08-31T20:11:59.503579Z","iopub.status.idle":"2026-08-31T20:11:59.508267Z","shell.execute_reply.started":"2026-08-31T20:11:59.503556Z","shell.execute_reply":"2026-08-31T20:11:59.507477Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epochs = 30\n\nmodel.train()\n\nfor epoch in range(epochs):\n    epoch_training_loss = 0.0\n\n    for images, labels in train_loader:  #Iterate thru each batch\n        # Move batches to GPU if available\n        images = images.to(device)\n        labels = labels.to(device)\n        \n        optimizer.zero_grad()\n\n        outputs = model(images)  #FP\n        loss = criterion(outputs, labels)  #Loss fnx\n        loss.backward()     #BP\n        optimizer.step()    #Update params\n\n        epoch_training_loss += loss.item()\n\n    avg_loss = epoch_training_loss/len(train_loader)\n\n    print(f\"Epoch: {epoch+1}/{epochs} \\tLoss: {avg_loss:.6f}\")","metadata":{"_uuid":"f3215b7a-c2c6-40ff-8e18-7536a884692e","_cell_guid":"5035d4fa-8ef6-4db8-a83e-faed1a029578","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:12:21.654887Z","iopub.execute_input":"2026-08-31T20:12:21.655166Z","iopub.status.idle":"2026-08-31T20:12:48.416529Z","shell.execute_reply.started":"2026-08-31T20:12:21.655137Z","shell.execute_reply":"2026-08-31T20:12:48.415867Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Evaluate the CNN","metadata":{"_uuid":"403ea37b-0c2f-428e-a51f-ec177ded449f","_cell_guid":"98a4da18-5099-4375-8f9f-4dccbac74fe4","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"correct_labels = 0\ntotal_labels = 0\n\nall_preds, all_labels = [], []\n\nmodel.eval()\n\nwith 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        _, predicted = torch.max(outputs, 1)\n\n        all_preds.extend(predicted.cpu().numpy())\n        all_labels.extend(labels.cpu().numpy())\n\n        correct_labels += (predicted == labels).sum().item()\n        total_labels += labels.shape[0]","metadata":{"_uuid":"ac973c4f-5cdc-47fd-a2c3-75be4b7167c1","_cell_guid":"0d061c56-7229-44a8-8229-61ebf108b66f","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:13:52.099577Z","iopub.execute_input":"2026-08-31T20:13:52.100096Z","iopub.status.idle":"2026-08-31T20:13:58.432746Z","shell.execute_reply.started":"2026-08-31T20:13:52.100061Z","shell.execute_reply":"2026-08-31T20:13:58.431957Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import classification_report, confusion_matrix\nprint(f\"Accuracy Score: {correct_labels/total_labels*100}%\")\nprint()\nprint(classification_report(all_labels, all_preds, target_names=[\"Cat\", \"Dog\"]))\nprint(\"Confusion Matrix:\\n\", confusion_matrix(all_labels, all_preds))","metadata":{"_uuid":"5026e5c9-2634-4c83-8303-ea9abea6ef68","_cell_guid":"2fc9f08e-6bb7-4e0f-8bdc-be82e6c3c71a","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-08-31T20:15:21.779380Z","iopub.execute_input":"2026-08-31T20:15:21.780200Z","iopub.status.idle":"2026-08-31T20:15:21.801688Z","shell.execute_reply.started":"2026-08-31T20:15:21.780168Z","shell.execute_reply":"2026-08-31T20:15:21.800713Z"}},"outputs":[],"execution_count":null}]}