{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.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":30636,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"Kaggle link: https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition","metadata":{}},{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.status.busy":"2024-01-15T10:39:29.193508Z","iopub.execute_input":"2024-01-15T10:39:29.193857Z","iopub.status.idle":"2024-01-15T10:39:30.106962Z","shell.execute_reply.started":"2024-01-15T10:39:29.193822Z","shell.execute_reply":"2024-01-15T10:39:30.106011Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Import everything needed","metadata":{}},{"cell_type":"code","source":"import zipfile\nimport glob\nfrom PIL import Image\nimport matplotlib.pyplot as plt\n\nimport wandb\n\nfrom sklearn.model_selection import train_test_split\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, Dataset\nfrom torchvision import datasets, transforms\nnp.random.seed(0)\ntorch.manual_seed(0)\ntorch.cuda.manual_seed(0)","metadata":{"execution":{"iopub.status.busy":"2024-01-15T10:54:07.479522Z","iopub.execute_input":"2024-01-15T10:54:07.480365Z","iopub.status.idle":"2024-01-15T10:54:08.117516Z","shell.execute_reply.started":"2024-01-15T10:54:07.480331Z","shell.execute_reply":"2024-01-15T10:54:08.116599Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Unzip datasets","metadata":{}},{"cell_type":"code","source":"train_dir = 'train'\ntest_dir = 'test'\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip') as train_zip:\n    train_zip.extractall('')\n    \nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip') as test_zip:\n    test_zip.extractall('')\ntrain_list = glob.glob(os.path.join(train_dir,'*.jpg'))\ntest_list = glob.glob(os.path.join(test_dir, '*.jpg'))\nprint(f\"Train Data: {len(train_list)}\")\nprint(f\"Test Data: {len(test_list)}\")","metadata":{"execution":{"iopub.status.busy":"2024-01-15T10:39:34.604579Z","iopub.execute_input":"2024-01-15T10:39:34.605002Z","iopub.status.idle":"2024-01-15T10:39:50.278444Z","shell.execute_reply.started":"2024-01-15T10:39:34.604976Z","shell.execute_reply":"2024-01-15T10:39:50.277558Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = [path.split('/')[-1].split('.')[0] for path in train_list]","metadata":{"execution":{"iopub.status.busy":"2024-01-15T10:39:50.280585Z","iopub.execute_input":"2024-01-15T10:39:50.281037Z","iopub.status.idle":"2024-01-15T10:39:50.300476Z","shell.execute_reply.started":"2024-01-15T10:39:50.281002Z","shell.execute_reply":"2024-01-15T10:39:50.299741Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Plot random image with their label","metadata":{}},{"cell_type":"code","source":"random_idx = np.random.randint(1, len(train_list), size=9)\nfig, axes = plt.subplots(3, 3, figsize=(16, 12))\n\nfor idx, ax in enumerate(axes.ravel()):\n    img = Image.open(train_list[idx])\n    ax.set_title(labels[idx])\n    ax.imshow(img)","metadata":{"execution":{"iopub.status.busy":"2024-01-15T10:39:59.780305Z","iopub.execute_input":"2024-01-15T10:39:59.780741Z","iopub.status.idle":"2024-01-15T10:40:02.433904Z","shell.execute_reply.started":"2024-01-15T10:39:59.780703Z","shell.execute_reply":"2024-01-15T10:40:02.432578Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Use Sklearn to split the training data","metadata":{}},{"cell_type":"code","source":"train_list, valid_list = train_test_split(train_list, \n                                          test_size=0.2,\n                                          stratify=labels,\n                                          random_state=0)","metadata":{"execution":{"iopub.status.busy":"2024-01-15T10:40:16.286897Z","iopub.execute_input":"2024-01-15T10:40:16.287252Z","iopub.status.idle":"2024-01-15T10:40:16.318080Z","shell.execute_reply.started":"2024-01-15T10:40:16.287223Z","shell.execute_reply":"2024-01-15T10:40:16.317343Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f\"Train Data: {len(train_list)}\")\nprint(f\"Validation Data: {len(valid_list)}\")\nprint(f\"Test Data: {len(test_list)}\")","metadata":{"execution":{"iopub.status.busy":"2024-01-15T10:40:18.952016Z","iopub.execute_input":"2024-01-15T10:40:18.952375Z","iopub.status.idle":"2024-01-15T10:40:18.958135Z","shell.execute_reply.started":"2024-01-15T10:40:18.952346Z","shell.execute_reply":"2024-01-15T10:40:18.957263Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We will discuss this in more detail in a near future...","metadata":{}},{"cell_type":"code","source":"\"\"\"\ntrain_transforms = transforms.Compose([\n    # TODO: define traininig augmentations \n])\n\ntest_transforms = transforms.Compose([\n    # TODO: define test augmentations \n])\n\"\"\"\n\ntrain_transforms = transforms.Compose([\n    transforms.RandomResizedCrop(224), # Randomly resize and crop the image to 224x224 pixels. This adds variability and helps the model learn scale invariance.\n    transforms.RandomHorizontalFlip(), # Randomly flip the image horizontally. This increases dataset diversity without affecting the label.\n    transforms.RandomRotation(10), # Randomly rotate the image within a range of +/-10 degrees. This helps in learning orientation invariance.\n    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2), # Randomly change brightness, contrast, and saturation to make the model robust against color variation.\n    transforms.ToTensor(), # Convert PIL image or numpy.ndarray to tensor. This is required for input into PyTorch models.\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # Normalize the image with mean and standard deviation. This is a common practice to help model convergence.\n])\n\ntest_transforms = transforms.Compose([\n    transforms.Resize(256), # Resize the image to a fixed size (larger than final crop size).\n    transforms.CenterCrop(224), # Crop the central part of the image to ensure consistency in input size for the model.\n    transforms.ToTensor(), # Convert to tensor for model input.\n    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) # Normalize with the same mean and standard deviation as the training data.\n])","metadata":{"execution":{"iopub.status.busy":"2024-01-15T10:47:14.173532Z","iopub.execute_input":"2024-01-15T10:47:14.174174Z","iopub.status.idle":"2024-01-15T10:47:14.183412Z","shell.execute_reply.started":"2024-01-15T10:47:14.174142Z","shell.execute_reply":"2024-01-15T10:47:14.182407Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Define the dataset using PIL to read image","metadata":{}},{"cell_type":"code","source":"class CatsDogsDataset(Dataset):\n    def __init__(self, file_list, transform=None):\n        self.file_list = file_list\n        self.transform = transform\n        self.filelength = len(file_list)\n\n    def __len__(self):\n        return self.filelength\n\n    def __getitem__(self, idx):\n        img_path = self.file_list[idx]\n        img = Image.open(img_path)\n        img_transformed = self.transform(img)\n        label = img_path.split(\"/\")[-1].split(\".\")[0]\n        label = 1 if label == \"dog\" else 0\n        return img_transformed, label","metadata":{"execution":{"iopub.status.busy":"2024-01-15T10:47:19.782446Z","iopub.execute_input":"2024-01-15T10:47:19.783175Z","iopub.status.idle":"2024-01-15T10:47:19.789843Z","shell.execute_reply.started":"2024-01-15T10:47:19.783140Z","shell.execute_reply":"2024-01-15T10:47:19.788627Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data = CatsDogsDataset(train_list, transform=train_transforms)\nvalid_data = CatsDogsDataset(valid_list, transform=test_transforms)\ntest_data = CatsDogsDataset(test_list, transform=test_transforms)","metadata":{"execution":{"iopub.status.busy":"2024-01-15T10:47:24.552535Z","iopub.execute_input":"2024-01-15T10:47:24.553351Z","iopub.status.idle":"2024-01-15T10:47:24.558508Z","shell.execute_reply.started":"2024-01-15T10:47:24.553316Z","shell.execute_reply":"2024-01-15T10:47:24.557606Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Create dataloader, you can modify the batch size if needed","metadata":{}},{"cell_type":"code","source":"batch_size = 32\ntrain_loader = DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True)\nvalid_loader = DataLoader(dataset=valid_data, batch_size=batch_size, shuffle=False)\ntest_loader = DataLoader(dataset=test_data, batch_size=batch_size, shuffle=False)","metadata":{"execution":{"iopub.status.busy":"2024-01-15T11:31:12.655308Z","iopub.execute_input":"2024-01-15T11:31:12.655972Z","iopub.status.idle":"2024-01-15T11:31:12.661921Z","shell.execute_reply.started":"2024-01-15T11:31:12.655937Z","shell.execute_reply":"2024-01-15T11:31:12.661034Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# TODO: define a convolutional neural network, train it and output the log \n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nclass SimpleCNN(nn.Module):\n    def __init__(self):\n        super(SimpleCNN, self).__init__()\n\n        # First convolutional layer: takes 3 input channels (RGB), produces 32 output channels\n        self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=1)\n\n        # Second convolutional layer: takes 32 input channels, produces 64 output channels\n        self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=1)\n\n        # Third convolutional layer: takes 64 input channels, produces 128 output channels\n        self.conv3 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=1)\n        \n        # Pooling layer: reduces spatial dimensions by half, helps to make the model more robust\n        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)\n\n        # First fully connected layer: flattening followed by a linear transformation\n        # Assuming input images are 224x224, the feature map size is reduced to 28x28 after pooling layers\n        self.fc1 = nn.Linear(128 * 28 * 28, 512)\n\n        # Second fully connected layer: output layer with 2 units for binary classification (dogs vs cats)\n        self.fc2 = nn.Linear(512, 2)\n\n        # Dropout layer: randomly zeroes some of the elements of the input tensor with probability 0.5\n        # Helps in reducing overfitting\n        self.dropout = nn.Dropout(0.5)\n\n    def forward(self, x):\n        # Apply the first convolutional layer followed by ReLU activation and pooling\n        x = self.pool(F.relu(self.conv1(x)))\n\n        # Apply the second convolutional layer followed by ReLU activation and pooling\n        x = self.pool(F.relu(self.conv2(x)))\n\n        # Apply the third convolutional layer followed by ReLU activation and pooling\n        x = self.pool(F.relu(self.conv3(x)))\n\n        # Flatten the output from the convolutional layers before passing it to the fully connected layer\n        x = x.view(-1, 128 * 28 * 28)\n\n        # Apply the first fully connected layer with ReLU activation and dropout\n        x = self.dropout(F.relu(self.fc1(x)))\n\n        # Apply the second fully connected layer (output layer)\n        x = self.fc2(x)\n\n        return x\n","metadata":{"execution":{"iopub.status.busy":"2024-01-15T10:53:36.387222Z","iopub.execute_input":"2024-01-15T10:53:36.387938Z","iopub.status.idle":"2024-01-15T10:53:36.398251Z","shell.execute_reply.started":"2024-01-15T10:53:36.387905Z","shell.execute_reply":"2024-01-15T10:53:36.397296Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Initialize a new wandb run\n#wandb.init(project='cnn-dogs-vs-cats', save_code=True)\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nnum_epochs = 10\nlearning_rate = 0.001\n\n# Initialize the network\nmodel = SimpleCNN()\nmodel.to(device) # Send the model to GPU if available\n\n# Loss function and optimizer\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=learning_rate)\n\n# Automatically log gradients, model parameters, and training metrics\n#wandb.watch(model)\n\nfor epoch in range(num_epochs):\n    model.train()\n    train_loss = 0.0\n    train_correct = 0\n    train_total = 0\n\n    for inputs, labels in train_loader:\n        inputs, labels = inputs.to(device), labels.to(device)\n\n        optimizer.zero_grad()\n        outputs = model(inputs)\n        loss = criterion(outputs, labels)\n        loss.backward()\n        optimizer.step()\n\n        train_loss += loss.item()\n        _, predicted = torch.max(outputs.data, 1)\n        train_total += labels.size(0)\n        train_correct += (predicted == labels).sum().item()\n\n    train_accuracy = 100 * train_correct / train_total\n    #wandb.log({\"train_loss\": train_loss / len(train_loader), \"train_accuracy\": train_accuracy})\n\n    # Validation\n    model.eval()\n    valid_loss = 0.0\n    valid_correct = 0\n    valid_total = 0\n\n    with torch.no_grad():\n        for inputs, labels in valid_loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n            valid_loss += loss.item()\n            _, predicted = torch.max(outputs.data, 1)\n            valid_total += labels.size(0)\n            valid_correct += (predicted == labels).sum().item()\n\n    valid_accuracy = 100 * valid_correct / valid_total\n    #wandb.log({\"valid_loss\": valid_loss / len(valid_loader), \"valid_accuracy\": valid_accuracy})\n\n    print(f'Epoch {epoch+1}, Train Loss: {train_loss/len(train_loader)}, Train Accuracy: {train_accuracy}%, Valid Loss: {valid_loss/len(valid_loader)}, Valid Accuracy: {valid_accuracy}%')\n\n#wandb.finish()","metadata":{"execution":{"iopub.status.busy":"2024-01-15T11:03:06.901206Z","iopub.execute_input":"2024-01-15T11:03:06.901903Z","iopub.status.idle":"2024-01-15T11:28:45.394137Z","shell.execute_reply.started":"2024-01-15T11:03:06.901870Z","shell.execute_reply":"2024-01-15T11:28:45.392977Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.eval()  # Set the model to evaluation mode\n# Prepare a list for storing predictions\npredictions = []\n\nwith torch.no_grad():\n    for batch in test_loader:\n        inputs = batch[0]\n        inputs = inputs.to(device)\n        outputs = model(inputs)\n\n        # Convert outputs to probabilities using softmax\n        probs = torch.nn.functional.softmax(outputs, dim=1)\n\n        # We're interested in the probability of the class 'dog', typically class index 1\n        dog_probs = probs[:, 1]\n        predictions.extend(dog_probs.cpu().numpy())\n\n# Create a DataFrame with id and label columns\nsubmission = pd.DataFrame({\n    'id': range(1, len(predictions) + 1),\n    'label': predictions\n})\n\n# Save the DataFrame to a CSV file\nsubmission.to_csv('submission.csv', index=False)\n","metadata":{"execution":{"iopub.status.busy":"2024-01-15T11:33:37.470116Z","iopub.execute_input":"2024-01-15T11:33:37.470949Z","iopub.status.idle":"2024-01-15T11:34:28.620544Z","shell.execute_reply.started":"2024-01-15T11:33:37.470913Z","shell.execute_reply":"2024-01-15T11:34:28.619596Z"},"trusted":true},"execution_count":null,"outputs":[]}]}