{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"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":"none","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"}],"dockerImageVersionId":31260,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import glob\n\n# check our work directory\nimport os\n\n# to unzip datasets\nimport zipfile\n\n# visualize some datasets\nimport matplotlib.pyplot as plt\n\n# using numpy\nimport numpy as np\n\n# for data load or save\nimport pandas as pd\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom PIL import Image\nfrom torch.utils.data import DataLoader, Dataset\nfrom torchvision import datasets, models, transforms\n\nplt.style.use([\"dark_background\", \"bmh\"])\nplt.rc(\"axes\", facecolor=\"k\")\nplt.rc(\"figure\", facecolor=\"k\")\nplt.rc(\"figure\", figsize=(10, 10), dpi=100)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T11:30:11.140010Z","iopub.execute_input":"2026-01-20T11:30:11.140774Z","iopub.status.idle":"2026-01-20T11:30:11.146386Z","shell.execute_reply.started":"2026-01-20T11:30:11.140745Z","shell.execute_reply":"2026-01-20T11:30:11.145509Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\ntorch.manual_seed(42)\nif device == \"cuda\":\n    torch.cuda.manual_seed_all(42)\ndevice","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T11:30:13.286560Z","iopub.execute_input":"2026-01-20T11:30:13.287237Z","iopub.status.idle":"2026-01-20T11:30:13.294127Z","shell.execute_reply.started":"2026-01-20T11:30:13.287209Z","shell.execute_reply":"2026-01-20T11:30:13.293411Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Read-only source (competition data)\ninput_dir = \"/kaggle/input/dogs-vs-cats-redux-kernels-edition\"\n\n# Writable destination \nbase_dir = \"/kaggle/working/dogs-vs-cats\"\nos.makedirs(base_dir, exist_ok=True)\n\n# Extract train.zip\ntrain_dir = os.path.join(base_dir, \"train\")\nif not os.path.isdir(train_dir):\n    with zipfile.ZipFile(os.path.join(input_dir, \"train.zip\")) as train_zip:\n        train_zip.extractall(base_dir)\n\n# Extract test.zip\ntest_dir = os.path.join(base_dir, \"test\")\nif not os.path.isdir(test_dir):\n    with zipfile.ZipFile(os.path.join(input_dir, \"test.zip\")) as test_zip:\n        test_zip.extractall(base_dir)\n# by now, you have a folder dogs-vs-cats with both train and test folders that have 25000 and 12500 images in them, respectively","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T11:31:07.903650Z","iopub.execute_input":"2026-01-20T11:31:07.904252Z","iopub.status.idle":"2026-01-20T11:31:18.024576Z","shell.execute_reply.started":"2026-01-20T11:31:07.904224Z","shell.execute_reply":"2026-01-20T11:31:18.023801Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dir = f\"{base_dir}/train\"\ntest_dir = f\"{base_dir}/test\"\n\n# you get two lists with the paths for all of the train images, and the paths for all of the test images\ntrain_list = glob.glob(os.path.join(train_dir, \"*.jpg\"))\ntest_list = glob.glob(os.path.join(test_dir, \"*.jpg\"))\n\nprint(f\"TRAIN LIST -> {train_list[:10]}\\nlen -> {len(train_list)}\")\n\nprint(f\"TEST LIST -> {test_list[:10]}\\nlen -> {len(test_list)}\")\n\n\nprint(f\"Training samples: {len(train_list)}, Tests samples: {len(test_list)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T11:31:23.207727Z","iopub.execute_input":"2026-01-20T11:31:23.208011Z","iopub.status.idle":"2026-01-20T11:31:23.276195Z","shell.execute_reply.started":"2026-01-20T11:31:23.207987Z","shell.execute_reply":"2026-01-20T11:31:23.275610Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"random_idx = np.random.randint(1, 25000, size=10)\n\nfig = plt.figure(figsize=(16, 6))\ni = 1\nfor idx in random_idx:\n    ax = fig.add_subplot(2, 5, i)\n    img = Image.open(train_list[idx])\n    plt.imshow(img)\n    i += 1\n\nplt.axis(\"off\")\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\n    f\"Instances dog: {sum('dog.' in s for s in train_list)}, Instances Cat: {sum('cat.' in s for s in train_list)}\"\n)","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class DogVsCatDataset(Dataset):\n    def __init__(self, file_list, transform=None):\n        self.file_list = file_list\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.file_list)\n\n    def __getitem__(self, idx):\n        if torch.is_tensor(idx):\n            idx = idx.tolist()\n\n        img_name = self.file_list[idx]\n        image = Image.open(img_name)\n        if self.transform:\n            image = self.transform(image)\n\n        label_category = label = img_name.split(\"/\")[-1].split(\".\")[0]\n        if label_category == \"dog\":\n            label = 1\n        elif label_category == \"cat\":\n            label = 0\n\n        return image, label","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transforms =  transforms.Compose([\n        transforms.Resize((224, 224)),\n        transforms.RandomResizedCrop(224),\n        ,#TODO: ADD RANDOM HORIZONTAL FLIP\n        transforms.ToTensor(),\n    ])\ntest_transforms =  transforms.Compose([\n        transforms.Resize((224, 224)),\n        transforms.RandomResizedCrop(224),\n        transforms.ToTensor(),\n    ])","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_data = DogVsCatDataset(train_list, transform=train_transforms)\ntest_data = DogVsCatDataset(test_list, transform=test_transforms)","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"batch_size = 64\ntrain_loader = torch.utils.data.DataLoader(\n    dataset=train_data, batch_size=batch_size, shuffle=True\n)\ntest_loader = torch.utils.data.DataLoader(dataset=test_data, shuffle=True)","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(len(train_data), len(train_loader))","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class CNN(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n        self.layer1 = nn.Sequential(\n            nn.Conv2d(3, 16, kernel_size=3, padding=0, stride=2),\n            nn.BatchNorm2d(16),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n        )\n\n        self.layer2 = nn.Sequential(\n            nn.Conv2d(16, 32, kernel_size=3, padding=0, stride=2),\n            nn.BatchNorm2d(32),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n        )\n\n        self.layer3 = nn.Sequential(\n            nn.Conv2d(32, 64, kernel_size=3, padding=0, stride=2),\n            nn.BatchNorm2d(64),\n            nn.ReLU(),\n            nn.MaxPool2d(2),\n        )\n\n        self.fc1 = nn.Linear(3 * 3 * 64, 10)\n        self.dropout = nn.Dropout(0.5)\n        self.fc2 = nn.Linear(10, 2)\n        self.relu = nn.ReLU()\n\n    # Helps\n    # https://datascience.stackexchange.com/questions/40906/determining-size-of-fc-layer-after-conv-layer-in-pytorch\n    def forward(self, x):\n        out = self.layer1(x)\n        out = self.layer2(out)\n        out = self.layer3(out)\n        out = out.view(out.size(0), -1)\n        out = self.relu(self.fc1(out))\n        out = self.fc2(out)\n        return out","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = CNN().to(device)\nmodel.train()","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# TODO: DEFINE THE OPTIMIZER AND LOSS \noptimizer = #adam\ncriterion = #cross entropy","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epochs = 1\nfor epoch in range(epochs):\n    epoch_loss = 0\n    epoch_accuracy = 0\n\n    for batch_idx, (data, target) in enumerate(train_loader):\n\n        # TODO: get data\n\n        # TODO: zero grad\n\n        # TODO: predictions\n\n        # TODO: compute loss and backward\n\n        # TODO: adjust weights\n\n        acc = (output.argmax(dim=1) == target).float().mean()\n        epoch_accuracy += acc / len(train_loader)\n        epoch_loss += loss / len(train_loader)\n\n        if batch_idx % 100 == 0:\n            print(\n                \"Train Epoch: {} [{}/{} ({:.0f}%)]\\tLoss: {:.6f}\".format(\n                    epoch,\n                    batch_idx * len(data),\n                    len(train_loader.dataset),\n                    100.0 * batch_idx / len(train_loader),\n                    loss.item(),\n                )\n            )\n    print(\n        f\"Epoch : {epoch+1}, train accuracy : {epoch_accuracy}, train loss : {epoch_loss}\"\n    )","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"probs = []\nmodel.eval()\nwith torch.no_grad():\n    for data, fileid in test_loader:\n        data = data.to(device)\n        preds = model(data)\n        preds_list = F.softmax(preds, dim=1)[:, 1].tolist()\n        probs += list(zip(list(fileid), preds_list))","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"code","source":"random_idx = np.random.randint(1, len(test_loader), size=10)\n\nclass_ = {0: \"cat\", 1: \"dog\"}\n\n\nfig = plt.figure(figsize=(16, 6))\ni = 1\n\nfor idx in random_idx:\n    ax = fig.add_subplot(2, 5, i)\n\n    img_id = probs[idx][0]\n    pred = probs[idx][1]\n\n    if pred > 0.5:\n        label = 1\n    else:\n        label = 0\n\n    img_path = os.path.join(test_dir, f\"{img_id}.jpg\")\n    img = Image.open(img_path)\n    plt.imshow(img)\n    plt.title(class_[label])\n\n    i += 1\n\nplt.axis(\"off\")\nplt.show()","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### What to do?\n\n- Change the code in order to improve the performance. Tips: add more transformations, increase the number of epochs, add dropout, change the optimizer/LR, you can even try to change the model. Maybe you can also use a validation test, by spliting the train_list, and check the validation performance on the end of each epoch\n- Try to fine tune a model, the tips are below. ","metadata":{}},{"cell_type":"code","source":"# TIPS FOR FINE TUNNING\n#  model_conv = models.resnet18(pretrained=True)\n# for param in model_conv.parameters():\n#     param.requires_grad = False\n\n# # Parameters of newly constructed modules have requires_grad=True by default\n# num_ftrs = model_conv.fc.in_features\n# model_conv.fc = nn.Linear(num_ftrs, 2)\n\n# model_conv = model_conv.to(device)\n\n\n# model_conv.fc.parameters() #ADD THIS IN THE OPTIMIZER","metadata":{},"outputs":[],"execution_count":null}]}