{"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"}},"nbformat_minor":4,"nbformat":4,"cells":[{"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":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-06-06T02:11:27.048343Z","iopub.execute_input":"2023-06-06T02:11:27.048752Z","iopub.status.idle":"2023-06-06T02:11:27.058447Z","shell.execute_reply.started":"2023-06-06T02:11:27.048705Z","shell.execute_reply":"2023-06-06T02:11:27.057128Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import skimage\nfrom torchvision import datasets, transforms\nimport torch","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:11:28.103342Z","iopub.execute_input":"2023-06-06T02:11:28.103813Z","iopub.status.idle":"2023-06-06T02:11:30.050297Z","shell.execute_reply.started":"2023-06-06T02:11:28.103771Z","shell.execute_reply":"2023-06-06T02:11:30.049406Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = 'cuda' if torch.cuda.is_available() else 'cpu'\ntorch.device(device)","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:11:48.116386Z","iopub.execute_input":"2023-06-06T02:11:48.117015Z","iopub.status.idle":"2023-06-06T02:11:48.125163Z","shell.execute_reply.started":"2023-06-06T02:11:48.116981Z","shell.execute_reply":"2023-06-06T02:11:48.124078Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Getting training data","metadata":{}},{"cell_type":"code","source":"# setting file paths\ntrain_zip_path = \"/kaggle/input/dogs-vs-cats/train.zip\"\ntrain_images_path = \"/kaggle/working/training/\"\n\n# creating training directory\nos.makedirs(train_images_path, exist_ok=True)\n\n# unzipping files to train folder\nimport zipfile\nwith zipfile.ZipFile(train_zip_path) as zip_f:\n    zip_f.extractall(train_images_path)","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:11:49.461700Z","iopub.execute_input":"2023-06-06T02:11:49.462125Z","iopub.status.idle":"2023-06-06T02:12:02.047193Z","shell.execute_reply.started":"2023-06-06T02:11:49.462093Z","shell.execute_reply":"2023-06-06T02:12:02.046075Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Creating training dataframe\n\n|Image|Category|\n|-|-|\n|filename|0 or 1|","metadata":{}},{"cell_type":"code","source":"# getting image names\nimages = os.listdir(\"training/train\")\n\n# extracting category from filename\ncategory = [0 if file.split(\".\")[0] == \"cat\" else 1 for file in images]\n\n# creating a training dataframe\ntrain_data = pd.DataFrame({\"Image\": images,\n                          \"Category\": category})\n\n# viewing first 5 training data observations\ntrain_data.head()","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:12:02.048988Z","iopub.execute_input":"2023-06-06T02:12:02.049383Z","iopub.status.idle":"2023-06-06T02:12:02.121367Z","shell.execute_reply.started":"2023-06-06T02:12:02.049344Z","shell.execute_reply":"2023-06-06T02:12:02.120204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# getting number of images for each category\ntrain_data.Category.value_counts()","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:12:20.096644Z","iopub.execute_input":"2023-06-06T02:12:20.097057Z","iopub.status.idle":"2023-06-06T02:12:20.111213Z","shell.execute_reply.started":"2023-06-06T02:12:20.097027Z","shell.execute_reply":"2023-06-06T02:12:20.110041Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The training data has equal number of observations for both categories which would prevent overfitting due to sample bias. But the training data is biased in that it does not have examples of any other objects which are not a cat or dog, which limits the utility of model in that it will always see an image as either a dog or a cat no matter how far it might be from either of them.","metadata":{}},{"cell_type":"code","source":"cat1 = skimage.io.imread(\"./training/train/cat.4215.jpg\")\nskimage.io.imshow(cat1)\ncat1.shape","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:12:21.986273Z","iopub.execute_input":"2023-06-06T02:12:21.986776Z","iopub.status.idle":"2023-06-06T02:12:22.783035Z","shell.execute_reply.started":"2023-06-06T02:12:21.986718Z","shell.execute_reply":"2023-06-06T02:12:22.781754Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dog1 = skimage.io.imread(\"./training/train/dog.9623.jpg\")\nskimage.io.imshow(dog1)\ndog1.shape","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:12:25.649285Z","iopub.execute_input":"2023-06-06T02:12:25.649874Z","iopub.status.idle":"2023-06-06T02:12:26.004649Z","shell.execute_reply.started":"2023-06-06T02:12:25.649832Z","shell.execute_reply":"2023-06-06T02:12:26.002990Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The images in the dataset are of varying size and will have to resized to a standard size for the neural network\n\nTo standardize the images, we will resize every image to smallest image size and fixing aspect ratio by retaining the center.","metadata":{}},{"cell_type":"code","source":"# starting with a large number\nmin_height = float(\"inf\")\nmin_width = float(\"inf\")\n\n# getting size of smallest image dimensions\nfor image in train_data[\"Image\"]:\n    height, width, _ = skimage.io.imread(f\"./training/train/{image}\").shape\n    if height < min_height:\n        min_height = height\n    if width < min_width:\n        min_width = width\n        \nmin_height, min_width","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:12:28.677698Z","iopub.execute_input":"2023-06-06T02:12:28.678073Z","iopub.status.idle":"2023-06-06T02:13:11.382233Z","shell.execute_reply.started":"2023-06-06T02:12:28.678045Z","shell.execute_reply":"2023-06-06T02:13:11.381149Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Reszing images to 32, 42 would result in loss of information for majority of images so we will resize the images to 100x100 dimension for our purposes","metadata":{}},{"cell_type":"code","source":"# creating transform loader\nimage_transform = transforms.Compose([transforms.Resize(110),\n                                     transforms.CenterCrop(100),\n                                     transforms.ToTensor()])","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:13:15.504719Z","iopub.execute_input":"2023-06-06T02:13:15.505127Z","iopub.status.idle":"2023-06-06T02:13:15.509971Z","shell.execute_reply.started":"2023-06-06T02:13:15.505096Z","shell.execute_reply":"2023-06-06T02:13:15.508690Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Before loading the data we need to separate each class to its own directory for ImageLoader to recognize each class","metadata":{}},{"cell_type":"code","source":"import shutil\n\nimages = os.listdir(\"training/train\")\n\n# creating directories for each class\nos.makedirs(\"training/cat\", exist_ok=True)\nos.makedirs(\"training/dog\", exist_ok=True)\n\nfor i, file in enumerate(images):\n    # getting category\n    dir_name = file.split(\".\")[0].lower()\n    \n    # copying to correct folder\n    os.rename(f\"/kaggle/working/training/train/{file}\",\n              f\"/kaggle/working/training/{dir_name}/{file}\")","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:13:17.656613Z","iopub.execute_input":"2023-06-06T02:13:17.657019Z","iopub.status.idle":"2023-06-06T02:13:18.271484Z","shell.execute_reply.started":"2023-06-06T02:13:17.656985Z","shell.execute_reply":"2023-06-06T02:13:18.270429Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#removing the train folder\nos.removedirs(\"training/train\")","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:13:23.862500Z","iopub.execute_input":"2023-06-06T02:13:23.862946Z","iopub.status.idle":"2023-06-06T02:13:23.868193Z","shell.execute_reply.started":"2023-06-06T02:13:23.862839Z","shell.execute_reply":"2023-06-06T02:13:23.867021Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# checking the folder strcuture\nos.listdir(\"training/\")","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:13:25.001402Z","iopub.execute_input":"2023-06-06T02:13:25.002229Z","iopub.status.idle":"2023-06-06T02:13:25.009462Z","shell.execute_reply.started":"2023-06-06T02:13:25.002178Z","shell.execute_reply":"2023-06-06T02:13:25.008466Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now that the folder strcuture is as expected by the ImageLoader we will now load the images","metadata":{}},{"cell_type":"code","source":"# loading the data\ndataset = datasets.ImageFolder(\"training/\", \n                               transform=image_transform)\n\n# viewing data information\ndataset","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:13:29.653222Z","iopub.execute_input":"2023-06-06T02:13:29.653581Z","iopub.status.idle":"2023-06-06T02:13:29.746327Z","shell.execute_reply.started":"2023-06-06T02:13:29.653555Z","shell.execute_reply":"2023-06-06T02:13:29.745345Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now that we have loaded the dataset, we will define CNN model that should be able to classify the images","metadata":{"execution":{"iopub.status.busy":"2023-06-06T00:36:38.469536Z","iopub.execute_input":"2023-06-06T00:36:38.469939Z","iopub.status.idle":"2023-06-06T00:36:38.480096Z","shell.execute_reply.started":"2023-06-06T00:36:38.469907Z","shell.execute_reply":"2023-06-06T00:36:38.478891Z"}}},{"cell_type":"code","source":"from torch import nn\n\nclass CNNModel(nn.Module):\n    def __init__(self):\n        super().__init__()\n        \n        # convolutional layers\n        self.conv_layers = nn.Sequential(\n            nn.Conv2d(3, 8, 3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2, 2),\n            nn.Conv2d(8, 4, 3, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2, 2)\n            )\n        \n        # linear layers\n        self.linear_layers = nn.Sequential(\n            nn.Linear(4*25*25, 128),\n            nn.Linear(128, 32),\n            nn.Linear(32, 4),\n            nn.Linear(4, 1)\n        )\n        \n    def forward(self, x):\n        # get convolution output\n        out = self.conv_layers(x)\n        \n        # flatten the output\n        out = out.reshape(out.size(0), -1)\n        \n        # run linear layers\n        out = self.linear_layers(out)\n        \n        return out","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:13:31.580583Z","iopub.execute_input":"2023-06-06T02:13:31.581778Z","iopub.status.idle":"2023-06-06T02:13:31.589409Z","shell.execute_reply.started":"2023-06-06T02:13:31.581723Z","shell.execute_reply":"2023-06-06T02:13:31.588265Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = CNNModel()\nmodel","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:13:35.220375Z","iopub.execute_input":"2023-06-06T02:13:35.220764Z","iopub.status.idle":"2023-06-06T02:13:35.255106Z","shell.execute_reply.started":"2023-06-06T02:13:35.220716Z","shell.execute_reply":"2023-06-06T02:13:35.254318Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"# testing model with an image\nmodel(dataset[0][0].unsqueeze(dim=0))","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:13:36.882861Z","iopub.execute_input":"2023-06-06T02:13:36.883562Z","iopub.status.idle":"2023-06-06T02:13:37.040342Z","shell.execute_reply.started":"2023-06-06T02:13:36.883529Z","shell.execute_reply":"2023-06-06T02:13:37.039598Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Instantiation loss function and optimizer","metadata":{}},{"cell_type":"code","source":"# classfication loss using cross entropy\nloss_fn = nn.BCEWithLogitsLoss()\n\n# stochastic gradient descent optimizer\noptimizer = torch.optim.SGD(params=model.parameters(),\n                           lr=0.05)","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:24:55.759844Z","iopub.execute_input":"2023-06-06T02:24:55.760253Z","iopub.status.idle":"2023-06-06T02:24:55.766235Z","shell.execute_reply.started":"2023-06-06T02:24:55.760222Z","shell.execute_reply":"2023-06-06T02:24:55.765093Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We will be training the model in batches using a data loader instance","metadata":{}},{"cell_type":"code","source":"# instantiatin data loader for batches\ndata_loader = torch.utils.data.DataLoader(dataset=dataset,\n                                         batch_size=32,\n                                         shuffle=True,\n                                          pin_memory=True)","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:15:15.927107Z","iopub.execute_input":"2023-06-06T02:15:15.927753Z","iopub.status.idle":"2023-06-06T02:15:15.932555Z","shell.execute_reply.started":"2023-06-06T02:15:15.927700Z","shell.execute_reply":"2023-06-06T02:15:15.931539Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Training the model","metadata":{}},{"cell_type":"code","source":"model = model.to(device)","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:15:18.278579Z","iopub.execute_input":"2023-06-06T02:15:18.279766Z","iopub.status.idle":"2023-06-06T02:15:18.285526Z","shell.execute_reply.started":"2023-06-06T02:15:18.279691Z","shell.execute_reply":"2023-06-06T02:15:18.284448Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epochs = 10\n\nfor epoch in range(epochs):\n    \n    # training mode\n    model.train()\n    \n    acc = 0\n    total = 0\n    for i, (batch_X, batch_Y) in enumerate(data_loader):\n        \n        batch_X = batch_X.to(device)\n        batch_Y = batch_Y.to(device).float()\n        \n        print(f\"Epoch iteration: {i:8d}/{len(data_loader)}\", end=\"\\r\")\n\n        # forward pass\n        y_preds = model(batch_X)\n        y_preds = torch.sigmoid(y_preds).squeeze()\n                \n        optimizer.zero_grad()\n        \n        loss = loss_fn(y_preds, batch_Y)\n        \n        acc += torch.sum((y_preds>0.5).float() == batch_Y)\n        total += len(y_preds)\n        \n        loss.backward()\n        optimizer.step()\n        \n    print(f\"Loss progress: {loss}, Accuracy: {acc/total}\")","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:32:19.794162Z","iopub.execute_input":"2023-06-06T02:32:19.794575Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This notebook does not dwell into the regular machine learning workflow, but intends to get a PyTorch model set up for image classification. It focuses of going throught a bare minimum workflow for using PyTorch for such image classification tasks.","metadata":{"execution":{"iopub.status.busy":"2023-06-06T02:28:18.448638Z","iopub.execute_input":"2023-06-06T02:28:18.449021Z","iopub.status.idle":"2023-06-06T02:28:18.458805Z","shell.execute_reply.started":"2023-06-06T02:28:18.448993Z","shell.execute_reply":"2023-06-06T02:28:18.457522Z"}}}]}