{"cells":[{"metadata":{},"cell_type":"markdown","source":"In this notebook I am using the Cifar10 dataset to classify various images. \nI have coded the traditional LeNet model with some hyper parameter tuning for this purpose.\nAs seen I got 71% accuracy for this model and te model performed well on images it had never seen before. It correctly classified a random image from the internet.\nPlease find the code below","execution_count":null},{"metadata":{"id":"JukxQHA5Ezs3","trusted":true},"cell_type":"code","source":"import torch\nimport torch.nn.functional as F\nfrom torchvision import datasets,transforms\nfrom torch import nn\nimport matplotlib.pyplot as plt\nimport numpy as np","execution_count":null,"outputs":[]},{"metadata":{"id":"nurRYLvFFhO-"},"cell_type":"markdown","source":"Initializing GPU Usage","execution_count":null},{"metadata":{"id":"11C_r_YRFUGa","trusted":true},"cell_type":"code","source":"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")","execution_count":null,"outputs":[]},{"metadata":{"id":"CJvLY35DFtAq"},"cell_type":"markdown","source":"Transformations and Data Augmentation","execution_count":null},{"metadata":{"id":"xJ32chBeFoWd","outputId":"0ad7c337-165a-437b-ca4a-78c89009582e","trusted":true},"cell_type":"code","source":"transform_train = transforms.Compose([transforms.Resize((32,32)),  #resises the image so it can be perfect for our model.\n                                      transforms.RandomHorizontalFlip(), # FLips the image w.r.t horizontal axis\n                                      transforms.RandomRotation(10),     #Rotates the image to a specified angel\n                                      transforms.RandomAffine(0, shear=10, scale=(0.8,1.2)), #Performs actions like zooms, change shear angles.\n                                      transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2), # Set the color params\n                                      transforms.ToTensor(), # comvert the image to tensor so that it can work with torch\n                                      transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) #Normalize all the images\n                               ])\n \n \ntransform = transforms.Compose([transforms.Resize((32,32)),\n                               transforms.ToTensor(),\n                               transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))\n                               ])\ntraining_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train) # Data augmentation is only done on training images\nvalidation_dataset = datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)\n \ntraining_loader = torch.utils.data.DataLoader(training_dataset, batch_size=100, shuffle=True) # Batch size of 100 i.e to work with 100 images at a time\nvalidation_loader = torch.utils.data.DataLoader(validation_dataset, batch_size = 100, shuffle=False)","execution_count":null,"outputs":[]},{"metadata":{"id":"JWetPW3AMkF9"},"cell_type":"markdown","source":"Converting the Input images to plot using plt","execution_count":null},{"metadata":{"id":"bTtyjj2OI5KS","trusted":true},"cell_type":"code","source":"# We need to convert the images to numpy arrays as tensors are not compatible with matplotlib.\ndef im_convert(tensor):  \n  image = tensor.cpu().clone().detach().numpy() # This process will happen in normal cpu.\n  image = image.transpose(1, 2, 0)\n  image = image * np.array((0.5, 0.5, 0.5)) + np.array((0.5, 0.5, 0.5))\n  image = image.clip(0, 1)\n  return image","execution_count":null,"outputs":[]},{"metadata":{"id":"eidAN_9OJitC","trusted":true},"cell_type":"code","source":"# Different classes in CIPHAR 10 dataset. \nclasses = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')","execution_count":null,"outputs":[]},{"metadata":{"id":"64Ev5i6FJxxW","outputId":"9356a284-39ba-48e6-da1c-eab1c811fb01","trusted":true},"cell_type":"code","source":"# We iter the batch of images to display\ndataiter = iter(training_loader) # converting our train_dataloader to iterable so that we can iter through it. \nimages, labels = dataiter.next() #going from 1st batch of 100 images to the next batch\nfig = plt.figure(figsize=(25, 4)) \n\n# We plot 20 images from our train_dataset\nfor idx in np.arange(20):\n  ax = fig.add_subplot(2, 10, idx+1, xticks=[], yticks=[]) \n  plt.imshow(im_convert(images[idx])) #converting to numpy array as plt needs it.\n  ax.set_title(classes[labels[idx].item()])","execution_count":null,"outputs":[]},{"metadata":{"id":"sTh5PVEfMtn4"},"cell_type":"markdown","source":"Defining our Model","execution_count":null},{"metadata":{"id":"jxIIxm1aK2At","trusted":true},"cell_type":"code","source":"class LeNet(nn.Module):\n    def __init__(self):\n      super().__init__()\n      self.conv1 = nn.Conv2d(3, 16, 3, 1, padding=1) # input is color image, hence 3 i/p channels. 16 filters, kernal size is tuned to 3 to avoid overfitting, stride is 1 , padding is 1 extract all edge features.\n      self.conv2 = nn.Conv2d(16, 32, 3, 1, padding=1) # We double the feature maps for every conv layer as in pratice it is really good.\n      self.conv3 = nn.Conv2d(32, 64, 3, 1, padding=1)\n      self.fc1 = nn.Linear(4*4*64, 500) # I/p image size is 32*32, after 3 MaxPooling layers it reduces to 4*4 and 64 because our last conv layer has 64 outputs. Output nodes is 500\n      self.dropout1 = nn.Dropout(0.5)\n      self.fc2 = nn.Linear(500, 10) # output nodes are 10 because our dataset have 10 different categories\n    def forward(self, x):\n      x = F.relu(self.conv1(x)) #Apply relu to each output of conv layer.\n      x = F.max_pool2d(x, 2, 2) # Max pooling layer with kernal of 2 and stride of 2\n      x = F.relu(self.conv2(x))\n      x = F.max_pool2d(x, 2, 2)\n      x = F.relu(self.conv3(x))\n      x = F.max_pool2d(x, 2, 2)\n      x = x.view(-1, 4*4*64) # flatten our images to 1D to input it to the fully connected layers\n      x = F.relu(self.fc1(x))\n      x = self.dropout1(x) # Applying dropout b/t layers which exchange highest parameters. This is a good practice\n      x = self.fc2(x)\n      return x","execution_count":null,"outputs":[]},{"metadata":{"id":"Qa1M7e7vOre5","outputId":"4742bd2d-f356-44df-98cc-3ce48e473d1a","trusted":true},"cell_type":"code","source":"model = LeNet().to(device) # run our model on cuda GPU for faster results\nmodel","execution_count":null,"outputs":[]},{"metadata":{"id":"gaYkrdaGO0_A","trusted":true},"cell_type":"code","source":"criterion = nn.CrossEntropyLoss() # same as categorical_crossentropy loss used in Keras models which runs on Tensorflow\noptimizer = torch.optim.Adam(model.parameters(), lr = 0.001) # fine tuned the lr","execution_count":null,"outputs":[]},{"metadata":{"id":"ZEhqeE2QPWz3"},"cell_type":"markdown","source":"Fitting our model with the inputs to generate output. \nDisplaying the progress. ","execution_count":null},{"metadata":{"id":"wqIzL_FiPKto","outputId":"bee0b331-d45c-4a98-9448-a64a762bbdb1","trusted":true},"cell_type":"code","source":"epochs = 15\nrunning_loss_history = []\nrunning_corrects_history = []\nval_running_loss_history = []\nval_running_corrects_history = []\n\nfor e in range(epochs): # training our model, put input according to every batch.\n  \n  running_loss = 0.0\n  running_corrects = 0.0\n  val_running_loss = 0.0\n  val_running_corrects = 0.0\n  \n  for inputs, labels in training_loader:\n    inputs = inputs.to(device) # input to device as our model is running in mentioned device.\n    labels = labels.to(device)\n    outputs = model(inputs) # every batch of 100 images are put as an input.\n    loss = criterion(outputs, labels) # Calc loss after each batch i/p by comparing it to actual labels. \n    \n    optimizer.zero_grad() #setting the initial gradient to 0\n    loss.backward() # backpropagating the loss\n    optimizer.step() # updating the weights and bias values for every single step.\n    \n    _, preds = torch.max(outputs, 1) # taking the highest value of prediction.\n    running_loss += loss.item()\n    running_corrects += torch.sum(preds == labels.data) # calculating te accuracy by taking the sum of all the correct predictions in a batch.\n\n  else:\n    with torch.no_grad(): # we do not need gradient for validation.\n      for val_inputs, val_labels in validation_loader:\n        val_inputs = val_inputs.to(device)\n        val_labels = val_labels.to(device)\n        val_outputs = model(val_inputs)\n        val_loss = criterion(val_outputs, val_labels)\n        \n        _, val_preds = torch.max(val_outputs, 1)\n        val_running_loss += val_loss.item()\n        val_running_corrects += torch.sum(val_preds == val_labels.data)\n      \n    epoch_loss = running_loss/len(training_loader) # loss per epoch\n    epoch_acc = running_corrects.float()/ len(training_loader) # accuracy per epoch\n    running_loss_history.append(epoch_loss) # appending for displaying \n    running_corrects_history.append(epoch_acc)\n    \n    val_epoch_loss = val_running_loss/len(validation_loader)\n    val_epoch_acc = val_running_corrects.float()/ len(validation_loader)\n    val_running_loss_history.append(val_epoch_loss)\n    val_running_corrects_history.append(val_epoch_acc)\n    print('epoch :', (e+1))\n    print('training loss: {:.4f}, acc {:.4f} '.format(epoch_loss, epoch_acc.item()))\n    print('validation loss: {:.4f}, validation acc {:.4f} '.format(val_epoch_loss, val_epoch_acc.item()))\n\n","execution_count":null,"outputs":[]},{"metadata":{"id":"SkdvnviRSGib","outputId":"c2d47421-0d5f-4e5d-9bb8-a193c372b340","trusted":true},"cell_type":"code","source":"plt.style.use('ggplot')\nplt.plot(running_loss_history, label='training loss')\nplt.plot(val_running_loss_history, label='validation loss')\nplt.legend()","execution_count":null,"outputs":[]},{"metadata":{"id":"Tita9-UqUaXb","outputId":"b732e53d-a6e5-482e-edb4-0e22b8878ebe","trusted":true},"cell_type":"code","source":"plt.style.use('ggplot')\nplt.plot(running_corrects_history, label='training accuracy')\nplt.plot(val_running_corrects_history, label='validation accuracy')\nplt.legend()","execution_count":null,"outputs":[]},{"metadata":{"id":"yE0O8PXpYhWj"},"cell_type":"markdown","source":"Input new image from web to check our model's accuracy","execution_count":null},{"metadata":{"id":"M-kfhdeRUqAU","trusted":true},"cell_type":"code","source":"import PIL.ImageOps","execution_count":null,"outputs":[]},{"metadata":{"id":"i6OmihC2WREM","outputId":"571c3beb-cbe4-4f6d-af96-698eba8829da","trusted":true},"cell_type":"code","source":"import requests\nfrom PIL import Image\n\nurl = 'https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcT76mSMtKQWGstcqGi-0kPWJyVBqz8RCp8SuplMipkidRY0z9Mc&usqp=CAU'\nresponse = requests.get(url, stream = True)\nimg = Image.open(response.raw)\nplt.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"id":"nBAZGYCTW5hL","outputId":"209539d8-04bf-4dc6-da41-87ac7b89dd54","trusted":true},"cell_type":"code","source":"img = transform(img)  # applying the transformations on new image as our model has been trained on these transformations\nplt.imshow(im_convert(img)) # convert to numpy array for plt","execution_count":null,"outputs":[]},{"metadata":{"id":"VpThBSTAYFb8","outputId":"037d6082-5faf-4e59-ca02-b07f101780ea","trusted":true},"cell_type":"code","source":"image = img.to(device).unsqueeze(0) # put inputs in device as our model is running there\noutput = model(image)\n_, pred = torch.max(output, 1)\nprint(classes[pred.item()])","execution_count":null,"outputs":[]},{"metadata":{"id":"5bpivp0QYpKI"},"cell_type":"markdown","source":"Use the images from our validation dataset to check the precdictions","execution_count":null},{"metadata":{"id":"HazD4SzhYcTV","outputId":"206d94da-2b12-4687-a721-e962ed3ecc5c","trusted":true},"cell_type":"code","source":"\ndataiter = iter(validation_loader)\nimages, labels = dataiter.next()\nimages = images.to(device)\nlabels = labels.to(device)\noutput = model(images)\n_, preds = torch.max(output, 1)\n\nfig = plt.figure(figsize=(25, 4))\n\nfor idx in np.arange(20):\n  ax = fig.add_subplot(2, 10, idx+1, xticks=[], yticks=[])\n  plt.imshow(im_convert(images[idx]))\n  ax.set_title(\"{} ({})\".format(str(classes[preds[idx].item()]), str(classes[labels[idx].item()])), color=(\"green\" if preds[idx]==labels[idx] else \"red\"))","execution_count":null,"outputs":[]},{"metadata":{"id":"4V4Sf-B-YzP4"},"cell_type":"markdown","source":"**As seen the predictions on new images are really good. Maybe by running more epochs I can get better accuracy or some more hyper parameter tuning will help. Additionally I am sure that Transfer learning will yield much better accuracy, if I use Vgg16 for suppose. But for this case I wanted to code my own model. Even though LeNet is a old it is still a very good model to start with.**\n> I have build this on PyTorch but personally I like Tensorflow Keras more. Thats my opinion.\nLet me know the suggestions for hyper parameter tunning for the same model.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}