{"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":"# Project Description\n- Name: Simple MNIST in Pytorch\n- Description: \n  - MNIST (\"Modified National Institute of Standards and Technology\") is the de facto “hello world” dataset of computer vision. Since its release in 1999, this classic dataset of handwritten images has served as the basis for benchmarking classification algorithms. As new machine learning techniques emerge, MNIST remains a reliable resource for researchers and learners alike. Goal is to correctly identify digits from a dataset of tens of thousands of handwritten images.\n- Accelerator: Tesla P100-PCIE-16GB\n","metadata":{}},{"cell_type":"code","source":"import os\nimport sys\nimport numpy as np\nimport pandas as pd\nfrom time import time\nimport matplotlib.pyplot as plt\n\nimport torch\nfrom torch import nn, optim\nimport torch.nn.functional as F\nfrom torch.utils.data import DataLoader\nfrom torch.utils.data import random_split\nfrom torch.utils.data import TensorDataset\nfrom torchvision import datasets, transforms","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-11-09T12:52:10.924033Z","iopub.execute_input":"2021-11-09T12:52:10.924403Z","iopub.status.idle":"2021-11-09T12:52:10.935205Z","shell.execute_reply.started":"2021-11-09T12:52:10.924368Z","shell.execute_reply":"2021-11-09T12:52:10.934385Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"CUDA Available:\",torch.cuda.is_available())\nprint(\"CUDA Device Name:\",torch.cuda.get_device_name(0))","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:10.947662Z","iopub.execute_input":"2021-11-09T12:52:10.950017Z","iopub.status.idle":"2021-11-09T12:52:10.960716Z","shell.execute_reply.started":"2021-11-09T12:52:10.949973Z","shell.execute_reply":"2021-11-09T12:52:10.959410Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# let's load our train dataframe\ntrain_df = pd.read_csv(\"../input/digit-recognizer/train.csv\")\n\n# let's load our test dataframe\ntest_df = pd.read_csv(\"../input/digit-recognizer/test.csv\")","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:10.963032Z","iopub.execute_input":"2021-11-09T12:52:10.963781Z","iopub.status.idle":"2021-11-09T12:52:14.788680Z","shell.execute_reply.started":"2021-11-09T12:52:10.963738Z","shell.execute_reply":"2021-11-09T12:52:14.787904Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df.head() # shows first 5 rows of train_df, label column holds label for the images whose data is in pixel0 to pixel 783 of size(28*28)","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:14.790145Z","iopub.execute_input":"2021-11-09T12:52:14.790382Z","iopub.status.idle":"2021-11-09T12:52:14.809672Z","shell.execute_reply.started":"2021-11-09T12:52:14.790349Z","shell.execute_reply":"2021-11-09T12:52:14.808497Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df.head() # shows first 5 rows of test_df, notice how test doesn't have label column as this dataframe will be used for testing our model","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:14.811676Z","iopub.execute_input":"2021-11-09T12:52:14.812020Z","iopub.status.idle":"2021-11-09T12:52:14.827480Z","shell.execute_reply.started":"2021-11-09T12:52:14.811984Z","shell.execute_reply":"2021-11-09T12:52:14.826739Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = train_df.label # labels for images in train_df, we'll drop this column to create a dataset that's ready to train\nlabels","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:14.828821Z","iopub.execute_input":"2021-11-09T12:52:14.829752Z","iopub.status.idle":"2021-11-09T12:52:14.842629Z","shell.execute_reply.started":"2021-11-09T12:52:14.829714Z","shell.execute_reply":"2021-11-09T12:52:14.841868Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = train_df.drop(\"label\", axis = 1) # dropping column \"label\" from the dataframe","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:14.844377Z","iopub.execute_input":"2021-11-09T12:52:14.844699Z","iopub.status.idle":"2021-11-09T12:52:14.927492Z","shell.execute_reply.started":"2021-11-09T12:52:14.844664Z","shell.execute_reply":"2021-11-09T12:52:14.926588Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# shows the first image, first the data of row is converted to array and then resahped to 28*28\nplt.imshow(train_df[0:1].values.reshape((28,28)))","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:14.928778Z","iopub.execute_input":"2021-11-09T12:52:14.929071Z","iopub.status.idle":"2021-11-09T12:52:15.133397Z","shell.execute_reply.started":"2021-11-09T12:52:14.929033Z","shell.execute_reply":"2021-11-09T12:52:15.132697Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Convert the data into model feedable dataset where 1st half is the image data and \n# 2nd is its label(pandas series needs to be converted to numpy array first before converted to torch tensor)\ntrain_ds = TensorDataset( torch.tensor( train_df.values), torch.tensor( labels.to_numpy()))\n# This one will be dataframe we'll test our model on\ntest_ds = torch.tensor(test_df.values)","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:15.134696Z","iopub.execute_input":"2021-11-09T12:52:15.135169Z","iopub.status.idle":"2021-11-09T12:52:15.384733Z","shell.execute_reply.started":"2021-11-09T12:52:15.135133Z","shell.execute_reply":"2021-11-09T12:52:15.383904Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(train_ds)","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:15.386052Z","iopub.execute_input":"2021-11-09T12:52:15.386372Z","iopub.status.idle":"2021-11-09T12:52:15.392590Z","shell.execute_reply.started":"2021-11-09T12:52:15.386335Z","shell.execute_reply":"2021-11-09T12:52:15.391818Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Now we wish to divide our 42000 elements strong dataset into 32000(training)+10000(validation)\n# A validation dataset is a sample of data held back from training your model that is used \n# to give an estimate of model skill while tuning model’s hyperparameters.\n\n# The validation dataset is different from the test dataset that is also held back from \n# the training of the model, but is instead used to give an unbiased estimate of the skill\n# of the final tuned model when comparing or selecting between final models.\n\n# Simple, right?\ntrain_ds , val_ds = random_split( train_ds,(32000,10000)) ","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:15.395529Z","iopub.execute_input":"2021-11-09T12:52:15.396038Z","iopub.status.idle":"2021-11-09T12:52:15.405069Z","shell.execute_reply.started":"2021-11-09T12:52:15.396004Z","shell.execute_reply":"2021-11-09T12:52:15.404393Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define hyperparameters\nbatch_size = 150 # Batch size refers to the number of training examples utilized in one iteration. \n\n# learning rate is a configurable hyperparameter used in the training of neural networks\n# controls how quickly the model is adapted to the problem.\nlearning_rate = 0.01 \n\n# for each image\ninput_size = 784 # no of datapoints in a image\nnum_class = 10 # no of classes that image can be classified into","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:15.406344Z","iopub.execute_input":"2021-11-09T12:52:15.406761Z","iopub.status.idle":"2021-11-09T12:52:15.415450Z","shell.execute_reply.started":"2021-11-09T12:52:15.406726Z","shell.execute_reply":"2021-11-09T12:52:15.414667Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# loads the train_ds, valid_ds and test_ds, we divide datas according to batch_size but notice how we only \n# shuffle train_ds and not valid_ds or test_ds cuz they are for checking not training\ntrain_loader = DataLoader(train_ds , batch_size, shuffle = True)\nval_loader = DataLoader(val_ds , batch_size, shuffle = False)\ntest_loader = DataLoader(test_ds , batch_size, shuffle = False)","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:15.418150Z","iopub.execute_input":"2021-11-09T12:52:15.418672Z","iopub.status.idle":"2021-11-09T12:52:15.432612Z","shell.execute_reply.started":"2021-11-09T12:52:15.418640Z","shell.execute_reply":"2021-11-09T12:52:15.431705Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataiter = iter(train_loader) # We can iterate over train_loader to get batches of data\nimages, labels = dataiter.next()\n\nprint(images.shape) # batch of 150 with 784 (28x28) data points\nprint(labels.shape) # corresponding 150 labels ","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:15.434081Z","iopub.execute_input":"2021-11-09T12:52:15.434455Z","iopub.status.idle":"2021-11-09T12:52:15.453632Z","shell.execute_reply.started":"2021-11-09T12:52:15.434421Z","shell.execute_reply":"2021-11-09T12:52:15.452917Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# means CNN inherits from nn.Module\nclass CNN(nn.Module):\n    def __init__(self, in_channels=1, num_classes=10):\n        super(CNN, self).__init__() #  super is so that child classes that may be using cooperative multiple inheritance will call the correct next parent class function in the Method Resolution Order (MRO).\n        # Idk what this super does honestly, after this is a bunch of shady stuff that I can't explain, sorry dear\n        self.conv1 = nn.Conv2d(in_channels=in_channels,out_channels=8,kernel_size=(3,3),stride=(1,1),padding=(1,1))\n        self.pool = nn.MaxPool2d(kernel_size=(2,2), stride=(2,2))\n        self.conv2 = nn.Conv2d(in_channels=8, out_channels=16, kernel_size=(3,3), stride=(1,1),padding=(1,1),)\n        self.fc1 = nn.Linear(16 * 7 * 7, num_classes)\n    def forward(self, x):\n        x = F.relu(self.conv1(x))\n        x = self.pool(x)\n        x = F.relu(self.conv2(x))\n        x = self.pool(x)\n        x = x.reshape(x.shape[0], -1)\n        x = self.fc1(x)\n        return x\nmodel = CNN().to('cuda')\n\n#optimizer\nopt = torch.optim.Adam # our optimiser is adam, combination of rms prop and momentum\nopt = opt(model.parameters(),lr=learning_rate) # adam needs learning rate parameter","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:15.455785Z","iopub.execute_input":"2021-11-09T12:52:15.456236Z","iopub.status.idle":"2021-11-09T12:52:15.467118Z","shell.execute_reply.started":"2021-11-09T12:52:15.456200Z","shell.execute_reply":"2021-11-09T12:52:15.466379Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Function that's used to fit on the data\ndef fit(epochs,model,data):\n    loss_fun = F.cross_entropy # our loss function is cross_entropy\n    hist = [] # we want to store loss here\n    # for each epoch\n    for Epoch in range(epochs):\n        # for each batch in that data\n        for img,label in data:\n            img = img.reshape(img.shape[0],1,28,28) # \n            # print(img.shape) # torch.Size([150, 1, 28, 28]) this is full batch right here\n            img = img.to('cuda') \n            label = label.to('cuda')\n            out = model(img/255) # forward propagation\n            loss = loss_fun(out,label) # loss calculation by comparision with prediction and accurate one \n            loss.backward() # backward propagation\n            opt.step() # function can be called once the gradients are computed using e.g. backward(). and it updates parameters\n            opt.zero_grad() # zero out of calculated gradients before rinse repeat\n            hist.append(loss)\n        \n        # print loss after every 10 epochs till epoch no 200\n        if (Epoch+1)%10 ==0:\n            print(f\"Epoch:[{Epoch+1}/{epochs}] ; Loss : {loss}\")\n    return hist","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:15.468228Z","iopub.execute_input":"2021-11-09T12:52:15.470039Z","iopub.status.idle":"2021-11-09T12:52:15.480264Z","shell.execute_reply.started":"2021-11-09T12:52:15.469993Z","shell.execute_reply":"2021-11-09T12:52:15.479583Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"hist=fit(200,model,train_loader)","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:52:15.481905Z","iopub.execute_input":"2021-11-09T12:52:15.482369Z","iopub.status.idle":"2021-11-09T12:56:57.529613Z","shell.execute_reply.started":"2021-11-09T12:52:15.482334Z","shell.execute_reply":"2021-11-09T12:56:57.528928Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# This is where we check our accuracy for the validation data\ndef acc(data):\n    accy=[] # we will run this on multiple batches from validation, those will be stored in this\n    for img ,label in data:\n        img = img.reshape(img.shape[0],1,28,28) # batch reshape\n        img, label = img.to('cuda'), label.to('cuda')\n        out = model(img/255) #forward propagation\n        # print(\"Forward:\",out[0])\n        out = F.softmax(out) # To get the prediction and confidence in different classes\n        # print(\"Softmax:\",out[0])\n        _,pred_index = torch.max(out, dim=1) # find max in those confidences\n        # print(\"Pred_ Index:\",pred_index)\n        x=torch.sum(pred_index == label)/len(pred_index) # calculate no of true positives diveded by total cases\n        # print(x)\n        x = x*100 # conversion to 1-100\n        x = x.to(\"cpu\").numpy()\n        accy.append(x) # append the accuracies\n        break\n    return np.mean(accy)  \n\nprint(\"Final Mean Accuracy:\",acc(val_loader))","metadata":{"execution":{"iopub.status.busy":"2021-11-09T13:12:41.015633Z","iopub.execute_input":"2021-11-09T13:12:41.015903Z","iopub.status.idle":"2021-11-09T13:12:41.034220Z","shell.execute_reply.started":"2021-11-09T13:12:41.015867Z","shell.execute_reply":"2021-11-09T13:12:41.033358Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.imshow(test_ds[2].reshape(28,28).numpy()) # this no 2 image is 9 right, let's predict it in next cell","metadata":{"execution":{"iopub.status.busy":"2021-11-09T13:20:52.421550Z","iopub.execute_input":"2021-11-09T13:20:52.421813Z","iopub.status.idle":"2021-11-09T13:20:52.627394Z","shell.execute_reply.started":"2021-11-09T13:20:52.421785Z","shell.execute_reply":"2021-11-09T13:20:52.626710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Test Dataset shape:\",test_ds.shape)\nprint(\"Single Input fit:\",test_ds[0].reshape(1,784).shape)\n\ndef prediction(data):\n    LABEL=[] # output label\n    data = data.reshape(data.shape[0],1,28,28) # input reshape\n    data = data.to('cuda')\n    out = model(data/255) # forward propagation\n    out = F.softmax(out) # softmax on forward prop\n    _,pred_index =torch.max(out,dim=1) # find max confidence one label\n    LABEL.append(pred_index) # save that label\n    return LABEL\n\nx = prediction(test_ds[2].reshape(1,784)) # input to get predictions \nx # our prediction as we can see is correct. # x=x[0].to('cpu').numpy() can be useful when using full arrays","metadata":{"execution":{"iopub.status.busy":"2021-11-09T12:56:57.904600Z","iopub.execute_input":"2021-11-09T12:56:57.904993Z","iopub.status.idle":"2021-11-09T12:56:57.911001Z","shell.execute_reply.started":"2021-11-09T12:56:57.904957Z","shell.execute_reply":"2021-11-09T12:56:57.910155Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}