{"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":"markdown","source":"# <center> <span style=\"color:#00BFC2;\"> ClassiFication With Pytorch </span> </center>\n## <center><span style=\"color:#00BFC2;\">If you find this notebook useful, support with an upvote👍</span></center>","metadata":{"execution":{"iopub.status.busy":"2022-09-08T13:13:04.065537Z","iopub.execute_input":"2022-09-08T13:13:04.066151Z","iopub.status.idle":"2022-09-08T13:13:06.041762Z","shell.execute_reply.started":"2022-09-08T13:13:04.066065Z","shell.execute_reply":"2022-09-08T13:13:06.040708Z"}}},{"cell_type":"markdown","source":"Tensorflow, Keras, and Pytorch very popular model in deep learning. In this notebook, I'll show you to used pytorch how do classification on a dataset from scratch.","metadata":{}},{"cell_type":"markdown","source":"# <center><span style=\"color:#e76f51;\">Table of Contents</span>\n<a id=\"toc\"></a>\n- [1. Import Libraries](#1)\n- [2. Play with Data](#2)\n- [3.  View Images](#3)\n- [4. Data Preparation](#4)\n    - [4.1.Split Dataset](#4.1)\n    - [4.2.Load with DataLoader ](#4.2)\n    - [4.3.View Group with Torchvision ](#4.3) \n- [5. Defining the Model](#5)\n    - [5.1.How ConV works ](#5.1)\n    - [5.2.Start Building Model](#5.2)\n    - [5.3 Know About Your Device](#5.3)\n- [6.  Train the Model](#6)\n- [7. Lets Test Invidual Image ](#7)","metadata":{}},{"cell_type":"markdown","source":"<a id=\"1\"></a>\n# **<center><span style=\"color:#00BFC4;\">Import Libraries </span></center>**","metadata":{}},{"cell_type":"code","source":"#import libraries\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torchvision\nfrom torchvision.transforms import ToTensor\nfrom torchvision.datasets import ImageFolder\nimport matplotlib.pyplot as plt\nimport os\nfrom torch.utils.data import random_split","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"It contains 2 folders `train` and `test`, containing the training set (50000 images) and test set (10000 images) respectively. Each of them contains 10 folders, one for each class of images. Let's verify this using `os.listdir`.","metadata":{}},{"cell_type":"code","source":"data_dir = '../input/cifar10-classification-image/cifar10/'\n\nprint(os.listdir(data_dir))\nclasses = os.listdir(data_dir + \"/train\")\nprint(classes)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's look inside a couple of folders, one from the training set and another from the test set. As an exercise, you can verify that that there are an equal number of images for each class, 5000 in the training set and 1000 in the test set.","metadata":{}},{"cell_type":"markdown","source":"\n<a id=\"2\"></a>\n# **<center><span style=\"color:#00BFC4;\">Play with Data</span></center>**","metadata":{}},{"cell_type":"code","source":"airplane_files = os.listdir(data_dir + \"/train/airplane\")\nprint(\"No. of Training example for airplane\", len(airplane_files))\nprint(airplane_files[:5])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ship_test_files = os.listdir(data_dir + \"/test/ship\")\nprint(\"NO. of testing example for ship\", len(ship_test_files))\nprint(ship_test_files[:5])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":" We can use the `ImageFolder` class from `torchvision` to load the data as PyTorch tensors.","metadata":{}},{"cell_type":"code","source":"dataset = ImageFolder(data_dir + \"train\", transform=ToTensor())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's look at a sample element from the training dataset. Each element is a tuple, containing a image tensor and a label. Since the data consists of 32x32 px color images with 3 channels <b>(RGB)</b>, each image tensor has the shape `(3, 32, 32)`.","metadata":{}},{"cell_type":"code","source":"# dataset[0]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img, label = dataset[0]\nprint(img.shape, label)\nimg","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The list of classes is stored in the `.classes ` property of the dataset. The numeric label for each element corresponds to index of the element's label in the list of classes","metadata":{}},{"cell_type":"code","source":"print(dataset.classes)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n\n<a id=\"3\"></a>\n# **<center><span style=\"color:#00BFC4;\">View Images</span></center>**","metadata":{}},{"cell_type":"code","source":"def show_example(img, label):\n    print(\"label :\", dataset.classes[label], \"(\"+str(label)+\")\")\n    plt.imshow(img.permute(1, 2, 0))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_example(*dataset[0])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_example(*dataset[10])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"4\"></a>\n# **<center><span style=\"color:#00BFC4;\">Data Preparetion </span></center>**\n","metadata":{}},{"cell_type":"markdown","source":"\n<a id=4.1></a>\n### <span style=\"color:#e76f51;\">4.1 Training and Validation Datasets </span>\n\n- Training : Used to train a model and adjust the weightes with gradient descent.\n- Validation : Used to tuning those hyperparameters also learning rate and pick best version of the model.\n- Testing : Chose model based on performence form several models","metadata":{}},{"cell_type":"code","source":"randoem_seed = 42\ntorch.manual_seed(randoem_seed)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"val_size = 5000\ntrain_size = len(dataset) - val_size\n\ntrain_ds , val_ds = random_split(dataset, [train_size, val_size])\nlen(train_ds), len(val_ds)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can now create data loaders for training and validation, to load the data in batches","metadata":{}},{"cell_type":"markdown","source":"\n<a id=4.2></a>\n### <span style=\"color:#e76f51;\">4.1 Load Data With DataLoader </span>","metadata":{}},{"cell_type":"code","source":"from torch.utils.data.dataloader import DataLoader\nbatch_size= 128","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dl = DataLoader(train_ds, batch_size, shuffle = True, num_workers=4, pin_memory=True )\nval_dl= DataLoader(val_ds, batch_size, num_workers=4, pin_memory=4)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n\n<a id=4.3></a>\n### <span style=\"color:#e76f51;\">4.1 TorchVision and Group view of images </span>","metadata":{}},{"cell_type":"markdown","source":" We can look at batches of images from the dataset using the make_grid method from torchvision","metadata":{}},{"cell_type":"code","source":"from torchvision.utils import make_grid\n\ndef show_grid(imgs):\n    for images ,label in imgs:\n        fix, ax = plt.subplots(figsize=(12,6))\n        ax.set_xticks([]), ax.set_yticks([])\n        ax.imshow(make_grid(images, nrow=16).permute(1,2,0))\n        break","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"show_grid(train_dl)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"5\"></a>\n# **<center> <span style=\"color:#00BFC4;\">Defining the Model (Convolutional Neural Network)</span></center>**\n","metadata":{}},{"cell_type":"markdown","source":"Let us implement a convolution operation on a 1 channel image with a 3x3 kernel","metadata":{}},{"cell_type":"markdown","source":"<a href=\"#toc\" role=\"button\" aria-pressed=\"true\" >⬆️Back to Table of Contents ⬆️</a>","metadata":{}},{"cell_type":"code","source":"def apply_kernel(image, kernel):\n    ri, ci =image.shape   #image dimention\n    rk, ck= kernel.shape  #kernel dimenton\n    ro, co = ri-rk+1, ci-ck+1  #output dimention\n    output = torch.zeros([ro,co])\n    for i in range(ro):\n        for j in range(co):\n            output[i,j]=torch.sum(image[i: i+rk, j:j+ck] * kernel)\n    return output\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_image =torch.tensor([\n    [3, 3, 2, 1, 0], \n    [0, 0, 1, 3, 1], \n    [3, 1, 2, 2, 3], \n    [2, 0, 0, 2, 2], \n    [2, 0, 0, 0, 1]  \n], dtype=torch.float32)\n\nsample_kernel=torch.tensor([\n    [0, 1, 2],\n    [2, 2, 0],\n    [0, 1, 2]\n], dtype= torch.float32)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"apply_kernel(sample_image,sample_kernel)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"For multi-channel images, a different kernel is applied to each channels, and the outputs are added together pixel-wise.\n\nBefore we define the entire model, let's look at how a single convolutional layer followed by a max-pooling layer operates on the data.","metadata":{}},{"cell_type":"markdown","source":"There are certain advantages offered by convolutional layers when working with image data:\n\n* <b>Fewer parameters :</b> A small set of parameters (the kernel) is used to calculate outputs of the entire image, so the model has much fewer parameters compared to a fully connected layer.\n* <b>Sparsity of connections:</b> In each layer, each output element only depends on a small number of input elements, which makes the forward and backward passes more efficient.\n* <b>Parameter sharing and spatial invariance: </b> The features learned by a kernel in one part of the image can be used to detect similar pattern in a different part of another image.","metadata":{}},{"cell_type":"markdown","source":"\n<a id=5.1></a>\n### <span style=\"color:#e76f51;\">5.1  How Convolution works </span>","metadata":{}},{"cell_type":"code","source":"sample_model = nn.Sequential(nn.Conv2d(3, 8,kernel_size=3, stride=1, padding=1),\n                             nn.MaxPool2d(2,2)\n                            )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for images, labels in train_dl:\n    print(\"image shape\", images.shape)\n    out = sample_model(images)\n    print(\"out shape\", out.shape)\n    break","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's define the model by extending an `ImageClassificationBase` class which contains helper methods for `training & validation`","metadata":{}},{"cell_type":"markdown","source":"\n<a id=5.2></a>\n### <span style=\"color:#e76f51;\">5.2  Start Building Model</span>","metadata":{}},{"cell_type":"code","source":"class ImageClassificationBase(nn.Module):\n    def training_step(self,batch):\n        images, labels =batch\n        out = self(images)#generate prediction\n        loss =F.cross_entropy(out, labels)  #calculate loss\n        return loss\n    \n    def validation_step(self, batch):\n        images, labels =batch\n        out = self(images)\n        loss = F.cross_entropy(out, labels)\n        acc = accuracy(out, labels)\n        return {'val_loss': loss.detach(), 'val_acc': acc}\n    \n    def validation_epoch_end(self, outputs):\n        batch_losses = [x['val_loss'] for x in outputs]\n        epoch_loss = torch.stack(batch_losses).mean()   # Combine losses\n        batch_accs = [x['val_acc'] for x in outputs]\n        epoch_acc = torch.stack(batch_accs).mean()      # Combine accuracies\n        return {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}\n    \n    def epoch_end(self, epoch, result):\n        print(\"Epochs [{}], train_loss: {:.4f}, val_loss: {:.4f}, val_acc {:.4f}\".format(epoch, result['train_loss'],\n                                                                                        result['val_loss'],result['val_acc']))\n        \ndef accuracy(outputs, labels):\n    _, preds = torch.max(outputs,dim=1)\n    return torch.tensor(torch.sum(preds==labels).item()/len(preds))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n``` \n    class Cifar10CnnModel(ImageClassificationBase) :\n       def __init__(self):\n           super().__init__() \n```\n\nHere Cifar10CnnModel is a subclass of superclass ImageClassificationBase. `super().__init__()` used to called the  constructor of the parents class which is `ImageClassificationBase`.\nThis ensures that any attributes or methods defined in the parent class are properly initialized before the subclass constructor is executed.\n","metadata":{}},{"cell_type":"code","source":"class Cifar10CnnModel(ImageClassificationBase):\n    def __init__(self):\n        super().__init__()\n        self.network = nn.Sequential(\n            nn.Conv2d(3, 32, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2, 2), # output: 64 x 16 x 16\n\n            nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),\n            nn.ReLU(),\n            nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2, 2), # output: 128 x 8 x 8\n\n            nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1),\n            nn.ReLU(),\n            nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(2, 2), # output: 256 x 4 x 4\n\n            nn.Flatten(), \n            nn.Linear(256*4*4, 1024),\n            nn.ReLU(),\n            nn.Linear(1024, 512),\n            nn.ReLU(),\n            nn.Linear(512, 10))\n        \n    def forward(self, xb):\n        return self.network(xb)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = Cifar10CnnModel()\nmodel","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for images, labels in train_dl:\n    print('images.shape:', images.shape)\n    out = model(images)\n    print('out.shape:', out.shape)\n    print('out[0]:', out[0])\n    break","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n<a id=5.3></a>\n### <span style=\"color:#e76f51;\">5.3 Know about your Device </span>","metadata":{}},{"cell_type":"code","source":"def get_default_device():\n    \"\"\"Pick GPU if available, else CPU\"\"\"\n    if torch.cuda.is_available():\n        return torch.device('cuda')\n    else:\n        return torch.device('cpu')\n    \ndef to_device(data, device):\n    \"\"\"Move tensor(s) to chosen device\"\"\"\n    if isinstance(data, (list,tuple)):\n        return [to_device(x, device) for x in data]\n    return data.to(device, non_blocking=True)\n\nclass DeviceDataLoader():\n    \"\"\"Wrap a dataloader to move data to a device\"\"\"\n    def __init__(self, dl, device):\n        self.dl = dl\n        self.device = device\n        \n    def __iter__(self):\n        \"\"\"Yield a batch of data after moving it to device\"\"\"\n        for b in self.dl: \n            yield to_device(b, self.device)\n\n    def __len__(self):\n        \"\"\"Number of batches\"\"\"\n        return len(self.dl)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = get_default_device()\ndevice","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dl = DeviceDataLoader(train_dl, device)\nval_dl = DeviceDataLoader(val_dl, device)\nto_device(model, device);","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"6\"></a>\n# **<center><span style=\"color:#00BFC4;\">Training the Model</span></center>**","metadata":{}},{"cell_type":"markdown","source":"<a href=\"#toc\" role=\"button\" aria-pressed=\"true\" >⬆️Back to Table of Contents ⬆️</a>","metadata":{}},{"cell_type":"code","source":"@torch.no_grad()\ndef evaluate(model, val_loader):\n    model.eval()\n    outputs = [model.validation_step(batch) for batch in val_loader]\n    return model.validation_epoch_end(outputs)\n\ndef fit(epochs, lr, model, train_loader, val_loader, opt_func=torch.optim.SGD):\n    history = []\n    optimizer = opt_func(model.parameters(), lr)\n    for epoch in range(epochs):\n        # Training Phase \n        model.train()\n        train_losses = []\n        for batch in train_loader:\n            loss = model.training_step(batch)\n            train_losses.append(loss)\n            loss.backward()\n            optimizer.step()\n            optimizer.zero_grad()\n        # Validation phase\n        result = evaluate(model, val_loader)\n        result['train_loss'] = torch.stack(train_losses).mean().item()\n        model.epoch_end(epoch, result)\n        history.append(result)\n    return history","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = to_device(Cifar10CnnModel(), device)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"evaluate(model, val_dl)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"num_epochs = 30\nopt_func = torch.optim.Adam\nlr = 0.001","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history = fit(num_epochs, lr, model, train_dl, val_dl, opt_func)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can also plot the valdation set accuracies to study how the model improves over time.","metadata":{}},{"cell_type":"code","source":"def plot_accuracies(history):\n    accuracies = [x['val_acc'] for x in history]\n    plt.plot(accuracies, '-x')\n    plt.xlabel('epoch')\n    plt.ylabel('accuracy')\n    plt.title('Accuracy vs. No. of epochs');","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_accuracies(history)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can also plot the training and validation losses to study the trend.","metadata":{}},{"cell_type":"code","source":"def plot_losses(history):\n    train_losses = [x.get('train_loss') for x in history]\n    val_losses = [x['val_loss'] for x in history]\n    plt.plot(train_losses, '-bx')\n    plt.plot(val_losses, '-rx')\n    plt.xlabel('epoch')\n    plt.ylabel('loss')\n    plt.legend(['Training', 'Validation'])\n    plt.title('Loss vs. No. of epochs');","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_losses(history)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Initialy, both the training and validation losses seem to decrease over time. However, if you train the model for long enough, you will notice that the training loss continues to decrease, while the validation loss stops decreasing, and even starts to increase after a certain point!\n\nThis phenomenon is called <b>overfitting</b>, and it is the no. 1 why many machine learning models give rather terrible results on real-world data. It happens because the model, in an attempt to minimize the loss, starts to learn patters are are unique to the training data, sometimes even memorizing specific training examples. Because of this, the model does not generalize well to previously unseen data.\n\nFollowing are some common stragegies for avoiding overfitting:\n\n- Gathering and generating more training data, or adding noise to it\n- Using regularization techniques like batch normalization & dropout\n- Early stopping of model's training, when validation loss starts to increase","metadata":{}},{"cell_type":"markdown","source":"\n<a id=\"7\"></a>\n# **<center><span style=\"color:#00BFC4;\">Lets Testing Individuals Image</span></center>**\n\nWhile we have been tracking the overall accuracy of a model so far, it's also a good idea to look at model's results on some sample images. Let's test out our model with some images from the predefined test dataset of 10000 images. We begin by creating a test dataset using the ImageFolder class.","metadata":{}},{"cell_type":"code","source":"test_dataset = ImageFolder(data_dir+'/test', transform=ToTensor())","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's define a helper function `predict_image,` which returns the predicted label for a single image tensor","metadata":{}},{"cell_type":"code","source":"def predict_image(img, model):\n    # Convert to a batch of 1\n    xb = to_device(img.unsqueeze(0), device)\n    # Get predictions from model\n    yb = model(xb)\n    # Pick index with highest probability\n    _, preds  = torch.max(yb, dim=1)\n    # Retrieve the class label\n    return dataset.classes[preds[0].item()]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img, label = test_dataset[0]\nplt.imshow(img.permute(1, 2, 0))\nprint('Label:', dataset.classes[label], ', Predicted:', predict_image(img, model))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img, label = test_dataset[1002]\nplt.imshow(img.permute(1, 2, 0))\nprint('Label:', dataset.classes[label], ', Predicted:', predict_image(img, model))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img, label = test_dataset[6153]\nplt.imshow(img.permute(1, 2, 0))\nprint('Label:', dataset.classes[label], ', Predicted:', predict_image(img, model))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Identifying where our model performs poorly can help us improve the model, by collecting more training data, increasing/decreasing the complexity of the model, and changing the hypeparameters.","metadata":{}},{"cell_type":"markdown","source":"****Dont Forget to Upvote and comment****<br>\n<a href=\"#toc\" role=\"button\" aria-pressed=\"true\" >⬆️Back to Table of Contents ⬆️</a>","metadata":{}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}