{"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":"gpu","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"}],"dockerImageVersionId":31259,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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-20T14:29:17.814750Z","iopub.execute_input":"2026-01-20T14:29:17.815094Z","iopub.status.idle":"2026-01-20T14:29:17.821286Z","shell.execute_reply.started":"2026-01-20T14:29:17.815064Z","shell.execute_reply":"2026-01-20T14:29:17.820464Z"}},"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-20T15:41:25.523169Z","iopub.execute_input":"2026-01-20T15:41:25.523727Z","iopub.status.idle":"2026-01-20T15:41:25.532031Z","shell.execute_reply.started":"2026-01-20T15:41:25.523696Z","shell.execute_reply":"2026-01-20T15:41:25.530974Z"}},"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-20T14:29:23.241743Z","iopub.execute_input":"2026-01-20T14:29:23.242047Z","iopub.status.idle":"2026-01-20T14:29:39.157611Z","shell.execute_reply.started":"2026-01-20T14:29:23.242023Z","shell.execute_reply":"2026-01-20T14:29:39.156985Z"}},"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-20T14:29:50.648718Z","iopub.execute_input":"2026-01-20T14:29:50.649076Z","iopub.status.idle":"2026-01-20T14:29:50.724158Z","shell.execute_reply.started":"2026-01-20T14:29:50.649048Z","shell.execute_reply":"2026-01-20T14:29:50.723436Z"}},"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.grid(\"off\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T14:30:16.317533Z","iopub.execute_input":"2026-01-20T14:30:16.318319Z","iopub.status.idle":"2026-01-20T14:30:17.176667Z","shell.execute_reply.started":"2026-01-20T14:30:16.318287Z","shell.execute_reply":"2026-01-20T14:30:17.175706Z"}},"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":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T14:31:34.327449Z","iopub.execute_input":"2026-01-20T14:31:34.328195Z","iopub.status.idle":"2026-01-20T14:31:34.336786Z","shell.execute_reply.started":"2026-01-20T14:31:34.328167Z","shell.execute_reply":"2026-01-20T14:31:34.336150Z"}},"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":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T14:42:50.916750Z","iopub.execute_input":"2026-01-20T14:42:50.917023Z","iopub.status.idle":"2026-01-20T14:42:50.924656Z","shell.execute_reply.started":"2026-01-20T14:42:50.917003Z","shell.execute_reply":"2026-01-20T14:42:50.924082Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_transforms =  transforms.Compose([\n        transforms.Resize((224, 224)),\n        transforms.RandomResizedCrop(224),\n        transforms.RandomHorizontalFlip(),\n        transforms.ToTensor(),\n    ])\ntest_transforms =  transforms.Compose([\n        transforms.Resize((224, 224)),\n        transforms.RandomResizedCrop(224),\n        transforms.ToTensor(),\n    ])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T14:46:09.685322Z","iopub.execute_input":"2026-01-20T14:46:09.685701Z","iopub.status.idle":"2026-01-20T14:46:09.690597Z","shell.execute_reply.started":"2026-01-20T14:46:09.685659Z","shell.execute_reply":"2026-01-20T14:46:09.689720Z"}},"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":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T14:46:48.063445Z","iopub.execute_input":"2026-01-20T14:46:48.064109Z","iopub.status.idle":"2026-01-20T14:46:48.068171Z","shell.execute_reply.started":"2026-01-20T14:46:48.064082Z","shell.execute_reply":"2026-01-20T14:46:48.067146Z"}},"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)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T14:56:49.155833Z","iopub.execute_input":"2026-01-20T14:56:49.156216Z","iopub.status.idle":"2026-01-20T14:56:49.160820Z","shell.execute_reply.started":"2026-01-20T14:56:49.156188Z","shell.execute_reply":"2026-01-20T14:56:49.160195Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(len(train_data), len(train_loader))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T14:57:02.186681Z","iopub.execute_input":"2026-01-20T14:57:02.187226Z","iopub.status.idle":"2026-01-20T14:57:02.191106Z","shell.execute_reply.started":"2026-01-20T14:57:02.187200Z","shell.execute_reply":"2026-01-20T14:57:02.190419Z"}},"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), #Normaliza o batch\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) #Decide aleatoriamente para um dado batch se desativa um determinado neurónio ou nao para prevenir o overfitting\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":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T15:40:58.508840Z","iopub.execute_input":"2026-01-20T15:40:58.509222Z","iopub.status.idle":"2026-01-20T15:40:58.518508Z","shell.execute_reply.started":"2026-01-20T15:40:58.509193Z","shell.execute_reply":"2026-01-20T15:40:58.517582Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = CNN()\nmodel = model.to(device)\nmodel.train()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T15:42:44.745510Z","iopub.execute_input":"2026-01-20T15:42:44.746265Z","iopub.status.idle":"2026-01-20T15:42:44.755852Z","shell.execute_reply.started":"2026-01-20T15:42:44.746234Z","shell.execute_reply":"2026-01-20T15:42:44.755069Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# TODO: DEFINE THE OPTIMIZER AND LOSS \noptimizer = optim.Adam(model.parameters(),lr=0.001)\ncriterion = nn.CrossEntropyLoss()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T15:42:47.413945Z","iopub.execute_input":"2026-01-20T15:42:47.414294Z","iopub.status.idle":"2026-01-20T15:42:47.419828Z","shell.execute_reply.started":"2026-01-20T15:42:47.414264Z","shell.execute_reply":"2026-01-20T15:42:47.419070Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"epochs = 5\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        data = data.to(device)\n        target = target.to(device)  \n        \n        # TODO: zero grad\n        optimizer.zero_grad() \n\n        # TODO: predictions\n        output = model(data) \n        \n        # TODO: compute loss and backward\n        loss = criterion(output, target)\n        loss.backward() #Backprop\n\n        # TODO: adjust weights\n        optimizer.step() #Atualizar parâmetros\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":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T15:43:08.694396Z","iopub.execute_input":"2026-01-20T15:43:08.695294Z","iopub.status.idle":"2026-01-20T15:53:44.924301Z","shell.execute_reply.started":"2026-01-20T15:43:08.695259Z","shell.execute_reply":"2026-01-20T15:53:44.923397Z"}},"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":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T15:55:04.619584Z","iopub.execute_input":"2026-01-20T15:55:04.619983Z","iopub.status.idle":"2026-01-20T15:56:26.544920Z","shell.execute_reply.started":"2026-01-20T15:55:04.619951Z","shell.execute_reply":"2026-01-20T15:56:26.543909Z"}},"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":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-20T15:56:57.956972Z","iopub.execute_input":"2026-01-20T15:56:57.957817Z","iopub.status.idle":"2026-01-20T15:56:59.387692Z","shell.execute_reply.started":"2026-01-20T15:56:57.957783Z","shell.execute_reply":"2026-01-20T15:56:59.386714Z"}},"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}]}