{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"}],"dockerImageVersionId":30498,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<center><b> Please do consider leaving feedback so that I can further improve and create even better notebooks. Thanks! :) </b></center>","metadata":{}},{"cell_type":"markdown","source":"## Libraries and Modules\n\nThe following libraries and modules are imported at the beginning of the code:\n\n- `numpy`: a library for numerical computing in Python\n- `pandas`: a library for data manipulation and analysis\n- `os`: a module for interacting with the operating system\n- `glob`: a module for finding all the pathnames matching a specified pattern\n- `time`: a module for working with time-related functions\n- `copy`: a module for creating copies of objects\n- `random`: a module for generating random numbers\n- `zipfile`: a module for working with ZIP archives\n- `matplotlib.pyplot`: a module for creating visualizations in Python\n- `PIL.Image`: a module for working with images in Python\n- `torch`: a library for machine learning in Python\n- `torch.nn`: a module for building neural networks in PyTorch\n- `torch.optim`: a module for optimizing neural networks in PyTorch\n- `torch.nn.functional`: a module for applying various functions to tensors in PyTorch\n- `torchvision`: a library for computer vision tasks in PyTorch\n- `torchvision.models`: a module containing pre-trained models for computer vision tasks in PyTorch\n- `torch.utils.data`: a module for working with datasets in PyTorch\n","metadata":{}},{"cell_type":"code","source":"%matplotlib inline\n\n# Importing necessary libraries and modules\nimport numpy as np # for numerical computing\nimport pandas as pd # for manipulation and analysis\nimport os, glob, time, copy, random, zipfile # for interacting with the operating system and working with files\nimport matplotlib.pyplot as plt # for creating visualizations\nfrom PIL import Image # for working with images\n\nimport torch # for machine learning\nimport torch.nn as nn # for building neural networks in PyTorch\nimport torch.optim as optim # for optimizing neural networks in PyTorch\nimport torchvision # for computer vision tasks in PyTorch\nfrom torchvision import models, transforms # for loading and preprocessing image data\nimport torchvision.models as models # for using pre-trained models in PyTorch\nfrom torch.utils.data import DataLoader, Dataset # for working with datasets in PyTorch\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-06-10T13:16:47.077433Z","iopub.execute_input":"2023-06-10T13:16:47.077803Z","iopub.status.idle":"2023-06-10T13:16:47.087184Z","shell.execute_reply.started":"2023-06-10T13:16:47.077774Z","shell.execute_reply":"2023-06-10T13:16:47.086233Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Setup\n\nThe code begins by creating a new directory to store the data using the `os.mkdir()` function. It then defines the paths for the training and testing data using variables.\n\nThe code then uses the `zipfile` module to extract the training and testing data from the ZIP files. The `with` statement is used to ensure that the ZIP files are properly closed after the data has been extracted.\n\nFinally, the code uses the `glob` module to get the file paths for the training and testing images. The `glob.glob()` function is used to find all files in the specified directory that match the specified pattern. The device(ie. GPU/CPU) which we will be using for the model is then defined\n","metadata":{}},{"cell_type":"code","source":"# Creating a new directory to store the data\nos.mkdir('../data')\n\n# Defining the paths for the training and testing data\nbase_dir = '../input/dogs-vs-cats-redux-kernels-edition'\ntrain_dir = '../data/train'\ntest_dir = '../data/test'\n\n# Extracting the training and testing data from the ZIP files\nwith zipfile.ZipFile(os.path.join(base_dir, 'train.zip')) as train_zip:\n    train_zip.extractall('../data')\n    \nwith zipfile.ZipFile(os.path.join(base_dir, 'test.zip')) as test_zip:\n    test_zip.extractall('../data')\n\n# Getting the file paths for the training and testing images\ntrain_x = glob.glob(os.path.join(train_dir,'*.jpg'))\ntest_x = glob.glob(os.path.join(test_dir,'*.jpg'))\n\n\n# Set the device which we will be using for the model\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n","metadata":{"execution":{"iopub.status.busy":"2023-06-10T13:16:49.288694Z","iopub.execute_input":"2023-06-10T13:16:49.289068Z","iopub.status.idle":"2023-06-10T13:17:07.331716Z","shell.execute_reply.started":"2023-06-10T13:16:49.289019Z","shell.execute_reply":"2023-06-10T13:17:07.330636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Dataset Setup\n\nThe code above defines a custom dataset class for loading and preprocessing image data. It includes several functions for working with image files and applying transformations to the images.\n\n\nThe `manualDataset` class is defined with several parameters, including the file paths for the images, the split between training and testing data, and whether or not to use a validation split. The class includes functions for loading and preprocessing the image data, including applying transformations to the images and extracting the labels from the file names.\n\nThe `__init__()` function initializes the class with the specified parameters and defines the transformations to be applied to the images. The `__len__()` function returns the length of the dataset, and the `__getitem__()` function loads and preprocesses the image data and returns it along with the corresponding label.","metadata":{}},{"cell_type":"code","source":"# Defining a custom dataset class for loading and preprocessing image data\nclass manualDataset(Dataset):\n    def __init__(self, files, split='train', val_split=False):\n        self.raw_files = files\n        self.split = split\n        self.val_split = val_split\n        \n        # Defining the transformations to be applied to the images\n        self.train_transform = transforms.Compose([transforms.Resize((224,224)), transforms.RandomHorizontalFlip(), transforms.ToTensor()])\n        self.test_transform = transforms.Compose([transforms.Resize((224,224)), transforms.ToTensor()])\n      \n        # Splitting the data into training and testing sets\n        if split == 'test':\n            if val_split:\n                self.raw_files = self.raw_files[:10]\n            else:\n                self.raw_files = self.raw_files[10:]\n        \n    def __len__(self):\n        return len(self.raw_files)\n    \n    def __getitem__(self, idx):\n        # Reading in the image file\n        raw_file = self.raw_files[idx]\n        raw = Image.open(raw_file)\n        \n        # Applying the appropriate transformation based on the split\n        if self.split == 'train':\n            raw = self.train_transform(raw)\n        elif self.split == 'test':\n            raw = self.test_transform(raw)\n        \n        # Extracting the label from the file name\n        label = raw_file.split('/')[-1].split('.')[0]\n        if label == 'dog':\n            label = 1\n        elif label == 'cat':\n            label = 0\n        \n        return raw, label\n","metadata":{"execution":{"iopub.status.busy":"2023-06-10T13:17:07.333937Z","iopub.execute_input":"2023-06-10T13:17:07.334314Z","iopub.status.idle":"2023-06-10T13:17:07.349245Z","shell.execute_reply.started":"2023-06-10T13:17:07.334281Z","shell.execute_reply":"2023-06-10T13:17:07.348279Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Datasets and Dataloaders Setup\n\n\nThe code creates instances of the `manualDataset` class for the training, testing, and validation data. The `split` parameter is used to specify whether the data is for training or testing, and the `val_split` parameter is used to specify whether to use a validation split.\n\nThe code then defines the batch sizes for the training and testing data, and creates instances of the `DataLoader` class for the training, testing, and validation data. The `batch_size` parameter is used to specify the number of samples per batch, and the `shuffle` parameter is used to specify whether to shuffle the training data.\n\n","metadata":{}},{"cell_type":"code","source":"# Creating instances of the manualDataset class for the training, testing, and validation datasets\ntrain_dataset = manualDataset(train_x, split='train')\ntest_dataset = manualDataset(test_x, split='test')\nval_dataset = manualDataset(train_x, split='test', val_split=True)\n\n# Defining the batch sizes for the training and testing data\ntrain_batch_size = 32\ntest_batch_size = 1\n\n# Creating instances of the DataLoader class for the training, testing, and validation data\ntrain_loader = DataLoader(train_dataset, batch_size=train_batch_size, shuffle=True)\ntest_loader = DataLoader(test_dataset, batch_size=test_batch_size)\nval_loader = DataLoader(val_dataset, batch_size=test_batch_size)\n","metadata":{"execution":{"iopub.status.busy":"2023-06-10T13:17:07.355175Z","iopub.execute_input":"2023-06-10T13:17:07.355487Z","iopub.status.idle":"2023-06-10T13:17:07.374382Z","shell.execute_reply.started":"2023-06-10T13:17:07.355463Z","shell.execute_reply":"2023-06-10T13:17:07.373483Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Transfer Learning using Resnet\n\nThe code begins by loading the ResNet50 model with pre-trained weights using the `models.resnet50()` function. It then modifies the last fully connected layer (fc) to customize the model for binary classification.\n\nThe code defines the number of features in the last layer using the `resnet50.fc.in_features` attribute, and creates a custom classifier using the `nn.Sequential()` function. The custom classifier includes two linear layers with ReLU activation functions and a final sigmoid activation function.\n\nThe code attaches the custom classifier to the ResNet50 model using the `resnet50.fc = custom_classifier` statement. It then moves the model to the appropriate device (GPU or CPU) using the `torch.device()` function.","metadata":{}},{"cell_type":"code","source":"\nresnet50 = models.resnet50(pretrained=True)\n\n\n    \n\n# Remove the last fully connected layer (fc) to customize the model\nnum_features = resnet50.fc.in_features\nresnet50.fc = nn.Sequential()\n\n# Add custom layers for binary classification\ncustom_classifier = nn.Sequential(\n    nn.Linear(num_features, 512),\n    nn.ReLU(),\n\n    nn.Linear(512, 1),\n    nn.Sigmoid()\n)\n\n# Attach the custom classifier to the ResNet50 model\nresnet50.fc = custom_classifier\n\n# Move the model to the appropriate device (GPU or CPU)\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nresnet50.to(device)\n","metadata":{"execution":{"iopub.status.busy":"2023-06-10T13:17:07.376279Z","iopub.execute_input":"2023-06-10T13:17:07.376619Z","iopub.status.idle":"2023-06-10T13:17:08.095033Z","shell.execute_reply.started":"2023-06-10T13:17:07.376578Z","shell.execute_reply":"2023-06-10T13:17:08.094114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Freezing Untouched Layers of Resnet\n\n\nThe code begins by defining an empty list called `param_to_optimize` to store the parameters of the custom classifier that will be optimized. It then iterates through the named parameters of the ResNet50 model using the `resnet50.named_parameters()` function.\n\nFor each parameter, the code checks if the name contains the string 'fc' using the `if('fc' in name)` statement. If the name contains 'fc', the `requires_grad` attribute is set to `True` for the parameter and the parameter is added to the `param_to_optimize` list. If the name does not contain 'fc', the `requires_grad` attribute is set to `False' for the parameter.\n","metadata":{}},{"cell_type":"code","source":"param_to_optimize=[]\nfor name, param in resnet50.named_parameters():\n    print(name)\n    if('fc' in name): \n        param.requires_grad = True\n        param_to_optimize.append(param)\n    else:\n        param.requires_grad = False","metadata":{"execution":{"iopub.status.busy":"2023-06-10T13:17:19.606543Z","iopub.execute_input":"2023-06-10T13:17:19.606916Z","iopub.status.idle":"2023-06-10T13:17:19.615643Z","shell.execute_reply.started":"2023-06-10T13:17:19.606889Z","shell.execute_reply":"2023-06-10T13:17:19.614675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model Setup & Training\n\nThe code begins by defining the loss function and optimizer for training the model using `nn.BCELoss()` and `optim.SGD()`, respectively. The `params` parameter is used to specify the parameters to optimize, which are the parameters of the custom classifier defined in the previous code block.\n\nThe number of epochs to train the model is defined using the `epochs` variable. The model is then set to training mode using `resnet50.train()`.\n\nThe model is trained for the specified number of epochs using a nested loop. The outer loop iterates through the epochs, and the inner loop iterates through the training data using the `train_loader` DataLoader.\n\nFor each batch of data, the data and labels are moved to the appropriate device (GPU or CPU) using `to()`. The output of the model is computed using `resnet50()`, the loss is calculated using `nn.BCELoss()`, and backpropagation is performed using `backward()`. The `optimizer.step()` function is used to update the model parameters.\n\nThe epoch loss and accuracy are computed for each epoch using `epoch_loss` and `epoch_accuracy`. The epoch number and epoch loss are printed using `print()`.\n\nAfter training the model for each epoch, the model is set to evaluation mode using `resnet50.eval()`. The validation data is then iterated through using the `val_loader` DataLoader object.\n\nFor each batch of data, the output of the model is computed using `resnet50()`, the validation loss is calculated using `nn.BCELoss()`, and the epoch validation loss is computed using `epoch_val_loss`. The epoch number and epoch validation loss are printed using `print()`.\n\n","metadata":{}},{"cell_type":"code","source":"# Defining the loss function and optimizer for training the model\ncriterion = nn.BCELoss()\noptimizer = optim.SGD(params=param_to_optimize, lr=0.001, momentum=0.9)\n\n# Defining the number of epochs to train the model\nepochs = 10\n\n# Setting the model to training mode\nresnet50.train()\n\n# Training the model for the specified number of epochs\nfor epoch in range(epochs):\n    epoch_loss = 0\n    epoch_accuracy = 0\n    \n    # Iterating through the training data\n    for data, label in train_loader:\n        data = data.to(device)\n        label = label.to(device)\n        label = label.type(torch.float)\n        output = resnet50(data)\n        label = label.unsqueeze(1)\n        loss = criterion(output, label)\n        \n        optimizer.zero_grad()\n        loss.backward()\n        optimizer.step()\n        \n        epoch_loss += (loss.item())/len(train_loader)\n        \n    print('Epoch : {},  train loss : {}'.format(epoch+1,epoch_loss))\n    \n    # Setting the model to evaluation mode\n    resnet50.eval()\n    with torch.no_grad():\n        epoch_val_accuracy=0\n        epoch_val_loss =0\n        \n        # Iterating through the validation data\n        for data, label in val_loader:\n            data = data.to(device)\n            label = label.to(device)\n            label = label.type(torch.float)\n            output = resnet50(data)\n            label = label.unsqueeze(1)\n            \n            val_output = resnet50(data)\n            val_loss = criterion(val_output,label)\n            \n            epoch_val_loss += val_loss/ len(val_loader)\n            \n        print('Epoch : {} , val_loss : {}'.format(epoch+1,epoch_val_loss))\n","metadata":{"execution":{"iopub.status.busy":"2023-06-10T13:17:22.206465Z","iopub.execute_input":"2023-06-10T13:17:22.207088Z","iopub.status.idle":"2023-06-10T13:17:32.135717Z","shell.execute_reply.started":"2023-06-10T13:17:22.207052Z","shell.execute_reply":"2023-06-10T13:17:32.133762Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Submission\n\nFinally, we evaluate it on the test dataset for submission\n\nThe code begins by creating empty lists to store the IDs and predictions. The model is then set to evaluation mode using `resnet50.eval()`.\n\nGradient calculation is disabled to save memory using the `torch.no_grad()` statement. The code iterates through the test data using a `for` loop and opens each image using `Image.open()`. The ID is extracted from the file path using string manipulation.\n\nThe test transformations are applied to the image using `transforms.Compose()` and the output is computed using `resnet50()`. The output is converted to a list using `tolist()` and added to the `pred_list` list along with the ID.\n\nThe prediction list is converted to a numpy array using `np.array()` and rounded to the nearest integer using `np.round()` and `astype(int)`.\n\nA DataFrame is created with the IDs and predictions using `pd.DataFrame()`. The DataFrame is sorted by ID using `sort_values()` and the index is reset using `reset_index()`. Finally, the DataFrame is saved to a CSV file named 'submission.csv' using `to_csv()`.\n","metadata":{}},{"cell_type":"code","source":"# Creating empty lists to store the IDs and predictions\nid_list = []\npred_list = []\n\n# Setting the model to evaluation mode\nresnet50.eval()\n\n# Disabling gradient calculation to save memory\nwith torch.no_grad():\n    # Iterating through the test data\n    for test_path in (test_x):\n        # Opening the image and extracting the ID\n        img = Image.open(test_path)\n        _id = int(test_path.split('/')[-1].split('.')[0])\n        \n        # Applying the test transformations to the image\n        test_transform = transforms.Compose([transforms.Resize((224,224)),transforms.ToTensor() ])\n        img = test_transform(img)\n        img = img.unsqueeze(0)\n        img = img.to(device)\n\n        # Computing the output of the model and converting it to a list\n        outputs = (resnet50(img)).tolist()\n        \n        # Adding the ID and prediction to the respective lists\n        id_list.append(_id)\n        pred_list.append(outputs[0])\n    \n# Converting the prediction list to a numpy array and rounding it to the nearest integer\npred_list = np.array(pred_list)\npred_list = np.round(pred_list.flatten()).astype(int)\n\n# Creating a DataFrame with the IDs and predictions\nres = pd.DataFrame({\n    'id': id_list,\n    'label': pred_list\n})\n\n# Sorting the DataFrame by ID and resetting the index\nres.sort_values(by='id', inplace=True)\nres.reset_index(drop=True, inplace=True)\n\n# Saving the DataFrame to a CSV file\nres.to_csv('submission.csv', index=False)\n","metadata":{"execution":{"iopub.status.busy":"2023-06-10T13:17:32.136758Z","iopub.status.idle":"2023-06-10T13:17:32.137486Z","shell.execute_reply.started":"2023-06-10T13:17:32.137200Z","shell.execute_reply":"2023-06-10T13:17:32.137228Z"},"jupyter":{"source_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}