{"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":5441,"databundleVersionId":38425}],"dockerImageVersionId":31328,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-05-01T01:59:39.489187Z","iopub.execute_input":"2026-05-01T01:59:39.489864Z","iopub.status.idle":"2026-05-01T01:59:39.750171Z","shell.execute_reply.started":"2026-05-01T01:59:39.489830Z","shell.execute_reply":"2026-05-01T01:59:39.749588Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Install Requirements","metadata":{}},{"cell_type":"code","source":"! pip install torch torchvision","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T01:59:41.869916Z","iopub.execute_input":"2026-05-01T01:59:41.870347Z","iopub.status.idle":"2026-05-01T01:59:45.949319Z","shell.execute_reply.started":"2026-05-01T01:59:41.870267Z","shell.execute_reply":"2026-05-01T01:59:45.948603Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Creating train and test folders","metadata":{}},{"cell_type":"code","source":"train = '/kaggle/input/competitions/dogs-vs-cats-redux-kernels-edition/train.zip'\ntest  = '/kaggle/input/competitions/dogs-vs-cats-redux-kernels-edition/test.zip'\n\nos.makedirs('/kaggle/working/dogs-vs-cats', exist_ok=True)\n\nimport zipfile\nwith zipfile.ZipFile(train, 'r') as z:\n    z.extractall('/kaggle/working/dogs-vs-cats')\n\nwith zipfile.ZipFile(test, 'r') as z:\n    z.extractall('/kaggle/working/dogs-vs-cats')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T01:59:50.865751Z","iopub.execute_input":"2026-05-01T01:59:50.866576Z","iopub.status.idle":"2026-05-01T02:00:00.708215Z","shell.execute_reply.started":"2026-05-01T01:59:50.866539Z","shell.execute_reply":"2026-05-01T02:00:00.707586Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport shutil\nimport random\n\nsource_dir = \"/kaggle/working/dogs-vs-cats/train\"\nbase_dir = \"dataset\"\n\ntrain_dir = os.path.join(base_dir,\"train\")\nval_dir = os.path.join(base_dir,\"val\")\n\nfor split in [\"train\",\"val\"]:\n    for cls in [\"cats\",\"dogs\"]:\n        os.makedirs(os.path.join(base_dir,split,cls), exist_ok=True)\n\n# Get files\nfiles = os.listdir(source_dir)\nrandom.shuffle(files)\n\n#Split ratio\nsplit_ratio = 0.8\nsplit_idx = int(len(files)*split_ratio)\n\ntrain_files = files[:split_idx]\nval_files = files[split_idx:]\n\ndef move_files(files_list, split):\n    for file in files_list:\n        if \"cat\" in file:\n            dst = os.path.join(base_dir, split, \"cats\",file)\n        else:\n            dst = os.path.join(base_dir, split,\"dogs\",file)\n        shutil.copy(os.path.join(source_dir, file),dst)\n\nmove_files(train_files, \"train\")\nmove_files(train_files, \"val\")\n\nprint(\"Train and Val set created\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T02:00:08.444359Z","iopub.execute_input":"2026-05-01T02:00:08.444971Z","iopub.status.idle":"2026-05-01T02:00:12.634815Z","shell.execute_reply.started":"2026-05-01T02:00:08.444934Z","shell.execute_reply":"2026-05-01T02:00:12.634067Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Load and transform data","metadata":{}},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import DataLoader, random_split\nfrom torchvision import datasets, transforms\n\ntransform = transforms.Compose([\n    transforms.Resize((128, 128)),\n    transforms.ToTensor(),\n    transforms.Normalize(mean=[0.5, 0.5, 0.5],\n                         std=[0.5, 0.5, 0.5])\n])\n\ntrain_dataset = datasets.ImageFolder(root='/kaggle/working/dataset/train', transform=transform)\nval_dataset = datasets.ImageFolder(root='//kaggle/working/dataset/val', transform=transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T02:00:14.339215Z","iopub.execute_input":"2026-05-01T02:00:14.339898Z","iopub.status.idle":"2026-05-01T02:00:22.206182Z","shell.execute_reply.started":"2026-05-01T02:00:14.339869Z","shell.execute_reply":"2026-05-01T02:00:22.205251Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Creating a simple CNN","metadata":{}},{"cell_type":"code","source":"import torch.nn as nn\n\nclass SimpleCNN(nn.Module):\n    def __init__(self):\n        super(SimpleCNN, self).__init__()\n        self.conv_layers = nn.Sequential(\n            nn.Conv2d(3, 32, kernel_size=3, stride=1, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2, 2),\n            \n            nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2, 2)\n        )\n        self.fc_layers = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(64 * 32 * 32, 128),\n            nn.ReLU(),\n            nn.Linear(128, 2)\n        )\n\n    def forward(self, x):\n        x = self.conv_layers(x)\n        x = self.fc_layers(x)\n        return x\n\nmodel = SimpleCNN()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T02:00:25.971690Z","iopub.execute_input":"2026-05-01T02:00:25.972817Z","iopub.status.idle":"2026-05-01T02:00:26.058363Z","shell.execute_reply.started":"2026-05-01T02:00:25.972781Z","shell.execute_reply":"2026-05-01T02:00:26.057518Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training the Neural Net","metadata":{}},{"cell_type":"code","source":"import torch.optim as optim\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.001)\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\n\nnum_epochs = 5\n\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    for images, labels in train_loader:\n        images, labels = images.to(device), labels.to(device)\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    print(f\"Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/len(train_loader):.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T02:00:28.483139Z","iopub.execute_input":"2026-05-01T02:00:28.483984Z","iopub.status.idle":"2026-05-01T02:05:36.004544Z","shell.execute_reply.started":"2026-05-01T02:00:28.483949Z","shell.execute_reply":"2026-05-01T02:05:36.003802Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Evaluation","metadata":{}},{"cell_type":"code","source":"from torchmetrics import Accuracy\n\nmodel.eval()\ncorrect = 0\ntotal = 0\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        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n\naccuracy = 100 * correct / total\nprint(f\"Validation Accuracy: {accuracy:.2f}%\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T02:05:47.877935Z","iopub.execute_input":"2026-05-01T02:05:47.878497Z","iopub.status.idle":"2026-05-01T02:06:50.943089Z","shell.execute_reply.started":"2026-05-01T02:05:47.878464Z","shell.execute_reply":"2026-05-01T02:06:50.942375Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Predictions vs True labels","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nimages, labels = next(iter(val_loader))\nimages = images.to(device)\noutputs = model(images)\n_, preds = torch.max(outputs, 1)\n\nplt.figure(figsize=(10,5))\nfor i in range(6):\n    plt.subplot(2,3,i+1)\n    img = images[i].cpu().permute(1, 2, 0).numpy()\n    plt.imshow((img * 0.5 + 0.5))  # unnormalize\n    plt.title(f\"Pred: {train_dataset.classes[preds[i]]}, True: {train_dataset.classes[labels[i]]}\")\n    plt.axis(\"off\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T02:07:11.640360Z","iopub.execute_input":"2026-05-01T02:07:11.641054Z","iopub.status.idle":"2026-05-01T02:07:12.048092Z","shell.execute_reply.started":"2026-05-01T02:07:11.641023Z","shell.execute_reply":"2026-05-01T02:07:12.047336Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Improvements","metadata":{}},{"cell_type":"code","source":"## Data Augmentation\ntrain_transform = transforms.Compose([\n    transforms.Resize((128, 128)),\n    transforms.RandomHorizontalFlip(),\n    transforms.RandomRotation(15),\n    transforms.ToTensor(),\n    transforms.Normalize([0.5]*3, [0.5]*3)\n])\ntrain_dataset = datasets.ImageFolder(root='/kaggle/working/dataset/train', transform=train_transform)\nval_dataset = datasets.ImageFolder(root='//kaggle/working/dataset/val', transform=train_transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T02:31:41.155094Z","iopub.execute_input":"2026-05-01T02:31:41.156014Z","iopub.status.idle":"2026-05-01T02:31:41.253000Z","shell.execute_reply.started":"2026-05-01T02:31:41.155979Z","shell.execute_reply":"2026-05-01T02:31:41.252355Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = SimpleCNN()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T02:31:43.600144Z","iopub.execute_input":"2026-05-01T02:31:43.600443Z","iopub.status.idle":"2026-05-01T02:31:43.668447Z","shell.execute_reply.started":"2026-05-01T02:31:43.600418Z","shell.execute_reply":"2026-05-01T02:31:43.667853Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.001)\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\n\nnum_epochs = 5\n\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n    for images, labels in train_loader:\n        images, labels = images.to(device), labels.to(device)\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    print(f\"Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/len(train_loader):.4f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T02:31:46.584577Z","iopub.execute_input":"2026-05-01T02:31:46.585457Z","iopub.status.idle":"2026-05-01T02:37:13.252873Z","shell.execute_reply.started":"2026-05-01T02:31:46.585422Z","shell.execute_reply":"2026-05-01T02:37:13.251970Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Val accuracy after improvements\nmodel.eval()\ncorrect = 0\ntotal = 0\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        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n\naccuracy = 100 * correct / total\nprint(f\"Validation Accuracy: {accuracy:.2f}%\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T02:37:48.901056Z","iopub.execute_input":"2026-05-01T02:37:48.901492Z","iopub.status.idle":"2026-05-01T02:38:44.827713Z","shell.execute_reply.started":"2026-05-01T02:37:48.901460Z","shell.execute_reply":"2026-05-01T02:38:44.826773Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Predictions vs True","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nimages, labels = next(iter(val_loader))\nimages = images.to(device)\noutputs = model(images)\n_, preds = torch.max(outputs, 1)\n\nplt.figure(figsize=(10,5))\nfor i in range(6):\n    plt.subplot(2,3,i+1)\n    img = images[i].cpu().permute(1, 2, 0).numpy()\n    plt.imshow((img * 0.5 + 0.5))  # unnormalize\n    plt.title(f\"Pred: {train_dataset.classes[preds[i]]}, True: {train_dataset.classes[labels[i]]}\")\n    plt.axis(\"off\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-01T02:46:28.404716Z","iopub.execute_input":"2026-05-01T02:46:28.405465Z","iopub.status.idle":"2026-05-01T02:46:28.836807Z","shell.execute_reply.started":"2026-05-01T02:46:28.405431Z","shell.execute_reply":"2026-05-01T02:46:28.835911Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Brief explanation of the model (2–3 sentences)\n\nThis SimpleCNN model is a basic convolutional neural network designed for image classification tasks with 3-channel (RGB) inputs. It consists of two convolutional blocks, each including a convolution layer, ReLU activation, along with max pooling to progressively extract spatial features. It also reduce image size. The feature maps are then flattened ,further passed through fully connected layers for classification. The first linear layer reduces features to 128 dimensions ,which is  followed by a final output layer producing predictions for 2 classes. In a nut shell ,it has  a straightforward architecture suitable for small-scale image classification problems.","metadata":{}},{"cell_type":"markdown","source":"## Description of at least one improvement made\n\nData augmentation hurt performance on the Kaggle Dogs vs Cats dataset when it introduces transformations which distort key class-specific features. For example, aggressive rotations, flips, or crops may remove or alter distinguishing traits like ear shape, snout structure, or fur patterns, making classification harder. Since the dataset is already large ,relatively clean. With heavy augmentation may add unnecessary variability rather than useful generalization. It also creates  a mismatch between training along with validation distributions if augmented images are less realistic. In such cases, simple resizing and normalization preserve the original data characteristics which  allows the model to learn more consistent ,discriminative features.\n\n","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}