{"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-10-03T14:50:36.874137Z","iopub.execute_input":"2023-10-03T14:50:36.874466Z","iopub.status.idle":"2023-10-03T14:50:37.180854Z","shell.execute_reply.started":"2023-10-03T14:50:36.874440Z","shell.execute_reply":"2023-10-03T14:50:37.179861Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Import Library","metadata":{}},{"cell_type":"code","source":"# import libraries\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import DataLoader, Dataset\n\nimport torchvision\nimport torchvision.transforms as transforms\nfrom torch.utils.data.sampler import SubsetRandomSampler\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom tqdm import tqdm\n\n# Evaluate model\nfrom sklearn.metrics import confusion_matrix, accuracy_score","metadata":{"execution":{"iopub.status.busy":"2023-10-03T14:50:37.182607Z","iopub.execute_input":"2023-10-03T14:50:37.183064Z","iopub.status.idle":"2023-10-03T14:50:38.951677Z","shell.execute_reply.started":"2023-10-03T14:50:37.183028Z","shell.execute_reply":"2023-10-03T14:50:38.950732Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define Custom dataset class ","metadata":{}},{"cell_type":"code","source":"kanada_root = '/kaggle/input/Kannada-MNIST'\n[os.path.join(kanada_root, f) for f in os.listdir(kanada_root) if 'test' in f][0]","metadata":{"execution":{"iopub.status.busy":"2023-10-03T14:50:38.952984Z","iopub.execute_input":"2023-10-03T14:50:38.953485Z","iopub.status.idle":"2023-10-03T14:50:38.963198Z","shell.execute_reply.started":"2023-10-03T14:50:38.953449Z","shell.execute_reply":"2023-10-03T14:50:38.962180Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Kanada_MNIST_Datset(Dataset):\n    def __init__(self, root_dataset,train=True, transform=None):\n        self.train = train\n        self.transform = transform\n        \n        kanada_path = os.path.join(root_dataset)\n\n        if self.train:\n            file_url = [os.path.join(kanada_path, f) for f in os.listdir(kanada_path) if 'train' in f][0]\n        else:\n            file_url = [os.path.join(kanada_path, f) for f in os.listdir(kanada_path) if 'test' in f][0]\n        \n        self.data = pd.read_csv(file_url)\n        \n    def __len__(self):\n        return len(self.data)\n    \n    def __getitem__(self, index):\n        # load image as ndarray type (Height * Width * Channels)\n        # be carefull for converting dtype to np.uint8 [Unsigned integer (0 to 255)]\n        # in this example, we use ToTensor(), so we define the numpy array like (H, W, C)\n        image = self.data.iloc[index, 1:].values.astype(np.uint8).reshape((28, 28, 1))\n        label = self.data.iloc[index, 0]\n        \n        if self.transform is not None:\n            image = self.transform(image)\n        if self.train:\n            return image, label\n        else:\n            return image","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:17:07.835428Z","iopub.execute_input":"2023-10-03T15:17:07.835860Z","iopub.status.idle":"2023-10-03T15:17:07.843914Z","shell.execute_reply.started":"2023-10-03T15:17:07.835792Z","shell.execute_reply":"2023-10-03T15:17:07.842922Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def make_loader(dataset, train_batch_size, validation_split=0.2):\n    \"\"\"make dataloader for pytorch training\n\n    Args:\n        dataset ([object]): [the dataset object]\n        train_batch_size ([int]): [training batch size]\n        validation_split (float, optional): [validation ratio]. Defaults to 0.2.\n\n    Returns:\n        [type]: [description]\n    \"\"\"\n    # number of samples in train and test set\n    train_len = int(len(dataset) * (1 - validation_split))\n    test_len = len(dataset) - train_len\n    train_set, test_set = torch.utils.data.random_split(dataset, [train_len, test_len])\n    # create train_loader\n    train_loader = torch.utils.data.DataLoader(\n        train_set, batch_size=train_batch_size, shuffle=True,\n    )\n    # create test_loader\n    test_loader = torch.utils.data.DataLoader(test_set, batch_size=1, shuffle=False,)\n    return train_loader, test_loader","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:18:39.010530Z","iopub.execute_input":"2023-10-03T15:18:39.010916Z","iopub.status.idle":"2023-10-03T15:18:39.017573Z","shell.execute_reply.started":"2023-10-03T15:18:39.010884Z","shell.execute_reply":"2023-10-03T15:18:39.016391Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Build the Train and Val loader\ndataset = Kanada_MNIST_Datset(root_dataset=kanada_root,train=True,transform=transforms.ToTensor())\ntrain_loader, val_loader = make_loader(dataset, train_batch_size=20, validation_split=0.2)\n\n\n# Build the test loader\ntest_dataset = Kanada_MNIST_Datset(root_dataset=kanada_root,train=False,transform=transforms.ToTensor())\ntest_loader = DataLoader(dataset=test_dataset, \n                         batch_size=20, \n                         num_workers=1)","metadata":{"execution":{"iopub.status.busy":"2023-10-03T16:06:27.152862Z","iopub.execute_input":"2023-10-03T16:06:27.153622Z","iopub.status.idle":"2023-10-03T16:06:30.686821Z","shell.execute_reply.started":"2023-10-03T16:06:27.153587Z","shell.execute_reply":"2023-10-03T16:06:30.685872Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- test","metadata":{}},{"cell_type":"code","source":"image_1, label_1 = dataset.__getitem__(index=102)\nprint(image_1.shape, label_1)","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:21:57.688910Z","iopub.execute_input":"2023-10-03T15:21:57.689254Z","iopub.status.idle":"2023-10-03T15:21:57.695150Z","shell.execute_reply.started":"2023-10-03T15:21:57.689226Z","shell.execute_reply":"2023-10-03T15:21:57.694183Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_1 = test_dataset.__getitem__(index=1)\nprint(image_1.shape)","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:17:35.246517Z","iopub.execute_input":"2023-10-03T15:17:35.247252Z","iopub.status.idle":"2023-10-03T15:17:35.253672Z","shell.execute_reply.started":"2023-10-03T15:17:35.247214Z","shell.execute_reply":"2023-10-03T15:17:35.252745Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_dataset.data.head(12)","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:24:02.447281Z","iopub.execute_input":"2023-10-03T15:24:02.447997Z","iopub.status.idle":"2023-10-03T15:24:02.463356Z","shell.execute_reply.started":"2023-10-03T15:24:02.447962Z","shell.execute_reply":"2023-10-03T15:24:02.462439Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Load and Visual the data ","metadata":{}},{"cell_type":"code","source":"# obtain one batch of training images\ndataiter = iter(train_loader)\nimages, labels = next(dataiter)\nimages = images.numpy()\n\n# plot the images in the batch, along with the corresponding labels\nfig = plt.figure(figsize=(25, 4))\nfor idx in np.arange(20):\n    ax = fig.add_subplot(2, 10, idx+1, xticks=[], yticks=[])\n    ax.imshow(np.squeeze(images[idx]), cmap='gray')\n    # print out the correct label for each image\n    # .item() gets the value contained in a Tensor\n    ax.set_title(str(labels[idx].item()))","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:24:09.249825Z","iopub.execute_input":"2023-10-03T15:24:09.250188Z","iopub.status.idle":"2023-10-03T15:24:10.327316Z","shell.execute_reply.started":"2023-10-03T15:24:09.250159Z","shell.execute_reply":"2023-10-03T15:24:10.325531Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Design the Architecture of Simple Neuron network ","metadata":{}},{"cell_type":"code","source":"# define the NN architecture\nclass SimpleNet(nn.Module):\n    def __init__(self):\n        super(SimpleNet, self).__init__()\n\n        # linear layer (784 -> hidden_1)\n        self.fc1 = nn.Linear(28 * 28, 1024)\n        # linear layer (n_hidden -> hidden_2)\n        self.fc2 = nn.Linear(1024, 512)\n        # linear layer (n_hidden -> hidden_3)\n        self.fc3 = nn.Linear(512, 256)\n        # linear layer (n_hidden -> 10)\n        self.fc4 = nn.Linear(256, 10)\n\n\n    def forward(self, x):\n        # flatten image input\n        x = x.view(-1, 28 * 28)\n        # add hidden layer, with relu activation function\n        x = F.relu(self.fc1(x))\n        # add hidden layer, with relu activation function\n        x = F.relu(self.fc2(x))\n        # add hidden layer, with relu activation function\n        x = F.relu(self.fc3(x))\n        # add output layer\n        x = self.fc4(x)\n        return x\n\n","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:24:15.494964Z","iopub.execute_input":"2023-10-03T15:24:15.495307Z","iopub.status.idle":"2023-10-03T15:24:15.502007Z","shell.execute_reply.started":"2023-10-03T15:24:15.495279Z","shell.execute_reply":"2023-10-03T15:24:15.500940Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Select Loss Function and Optimizer","metadata":{}},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\ndevice","metadata":{"execution":{"iopub.status.busy":"2023-10-03T14:51:08.208107Z","iopub.execute_input":"2023-10-03T14:51:08.208774Z","iopub.status.idle":"2023-10-03T14:51:08.236273Z","shell.execute_reply.started":"2023-10-03T14:51:08.208742Z","shell.execute_reply":"2023-10-03T14:51:08.235269Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train model ","metadata":{}},{"cell_type":"code","source":"for image, label in train_loader:\n    print(image.shape)\n    test = image\n    print(image.shape)\n    break","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:24:26.342672Z","iopub.execute_input":"2023-10-03T15:24:26.343670Z","iopub.status.idle":"2023-10-03T15:24:26.357916Z","shell.execute_reply.started":"2023-10-03T15:24:26.343622Z","shell.execute_reply":"2023-10-03T15:24:26.356783Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Set up cuda \ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# Model \n# initialize the NN\nmodel = SimpleNet().to(device=device)\nprint(model)\n\n# specify loss function (categorical cross-entropy)\ncriterion = nn.CrossEntropyLoss()\n# specify optimizer (stochastic gradient descent) and learning rate = 0.01\noptimizer = torch.optim.Adam(model.parameters(), lr=0.01)\n# number of epochs to train the model\nn_epochs = 8\n# initialize tracker for minimum validation loss\ntest_loss_min = np.Inf # set initial \"min\" to infinity\n\nfor epoch in range(n_epochs):\n    \n    ###################\n    # train the model #\n    ###################\n    model.train() # prep model for training\n    # monitor training loss\n    train_loss = []\n    process_bar = tqdm(train_loader, colour='blue')\n    for i, (image, label) in enumerate(process_bar):\n        \n        # Run GPU\n        image = image.to(device)\n        #print(images.shape)\n        label = label.to(device)\n        # forward pass: compute predicted outputs by passing inputs to the model\n        output = model(image)\n        # calculate the loss\n        loss = criterion(output, label)\n        # clear the gradients of all optimized variables\n        optimizer.zero_grad()\n        # backward pass: compute gradient of the loss with respect to model parameters\n        loss.backward()\n        \n        # update running training loss\n        train_loss.append(loss.item())\n        \n        # perform a single optimization step (parameter update)\n        optimizer.step()\n        \n        process_bar.set_description(\n                \"Epoch {}. Iteration {}/{} Loss {}\".format(epoch + 1,\n                                                           i + 1,\n                                                           len(train_loader),\n                                                           np.mean(train_loss)\n                                                           )\n            )\n   \n    ###################\n    # Evaluate the model #\n    ###################\n    all_predictions = []\n    all_labels = []\n    test_loss = []\n    \n    # set model in the evaluation mode\n    model.eval()\n    \n    # Stop the optimal process:\n    with torch.inference_mode():\n        for i, (image, label) in enumerate(val_loader):\n            image = image.to(device)\n            label= label.to(device)\n            \n            # Predict the testset\n            output = model(image)\n            # Calcualte the loss value\n            loss_value = criterion(output,label)\n            test_loss.append(loss_value.item())\n            \n            # Get the prediction\n            predictions = torch.argmax(output,dim=1)\n            all_predictions.extend(predictions.tolist())\n            all_labels.extend(label.tolist())\n            \n        # Create the confusion matrix\n        conf_matrix = confusion_matrix(all_labels, all_predictions)\n        \n        # calcualte the accuracy score\n        acc_score = accuracy_score(y_true=all_labels, y_pred=all_predictions)\n        print(\"=== epoch {}. Accuracy {} ===\".format(epoch + 1, acc_score))\n    \n    # save model if validation loss has decreased\n    if np.mean(test_loss) <= test_loss_min:\n        print('Test loss decreased ({:.5f} --> {:.5f}).  Saving model ...'.format(test_loss_min,np.mean(test_loss)))\n        torch.save(model.state_dict(), 'model.pt')\n        test_loss_min = np.mean(test_loss)","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:39:20.054332Z","iopub.execute_input":"2023-10-03T15:39:20.054689Z","iopub.status.idle":"2023-10-03T15:42:54.880052Z","shell.execute_reply.started":"2023-10-03T15:39:20.054658Z","shell.execute_reply":"2023-10-03T15:42:54.879173Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"---\n## Test the Trained Network\n\nFinally, we test our best model on previously unseen **test data** and evaluate it's performance. Testing on unseen data is a good way to check that our model generalizes well. It may also be useful to be granular in this analysis and take a look at how this model performs on each class as well as looking at its overall loss and accuracy.","metadata":{}},{"cell_type":"markdown","source":"### Visualize Sample Test Results\n\nThis cell displays test images and their labels in this format: `predicted (ground-truth)`. The text will be green for accurately classified examples and red for incorrect predictions.","metadata":{}},{"cell_type":"code","source":"model.load_state_dict(torch.load('model.pt'))","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:44:47.433538Z","iopub.execute_input":"2023-10-03T15:44:47.433922Z","iopub.status.idle":"2023-10-03T15:44:47.447031Z","shell.execute_reply.started":"2023-10-03T15:44:47.433893Z","shell.execute_reply":"2023-10-03T15:44:47.445993Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"next(model.parameters()).is_cuda","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:47:33.995287Z","iopub.execute_input":"2023-10-03T15:47:33.996351Z","iopub.status.idle":"2023-10-03T15:47:34.003193Z","shell.execute_reply.started":"2023-10-03T15:47:33.996295Z","shell.execute_reply":"2023-10-03T15:47:34.002291Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.to('cpu')","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:48:24.693245Z","iopub.execute_input":"2023-10-03T15:48:24.693590Z","iopub.status.idle":"2023-10-03T15:48:24.704729Z","shell.execute_reply.started":"2023-10-03T15:48:24.693564Z","shell.execute_reply":"2023-10-03T15:48:24.703594Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"next(model.parameters()).is_cuda","metadata":{"execution":{"iopub.status.busy":"2023-10-03T15:48:32.835789Z","iopub.execute_input":"2023-10-03T15:48:32.836168Z","iopub.status.idle":"2023-10-03T15:48:32.842167Z","shell.execute_reply.started":"2023-10-03T15:48:32.836141Z","shell.execute_reply":"2023-10-03T15:48:32.841159Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# obtain one batch of test images\ndataiter = iter(test_loader)\nimages = next(dataiter)\n\n# get sample outputs\noutput = model(images)\n# convert output probabilities to predicted class\n_, preds = torch.max(output, 1)\n# prep images for display\nimages = images.numpy()\n\n# plot the images in the batch, along with predicted and true labels\nfig = plt.figure(figsize=(25, 4))\nfor idx in np.arange(20):\n    ax = fig.add_subplot(2, 10, idx+1, xticks=[], yticks=[])\n    ax.imshow(np.squeeze(images[idx]), cmap='gray')\n    ax.set_title(\"{}\".format(str(preds[idx].item())))","metadata":{"execution":{"iopub.status.busy":"2023-10-03T16:06:37.609915Z","iopub.execute_input":"2023-10-03T16:06:37.610256Z","iopub.status.idle":"2023-10-03T16:06:38.447883Z","shell.execute_reply.started":"2023-10-03T16:06:37.610230Z","shell.execute_reply":"2023-10-03T16:06:38.446978Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Predict digit according to Test data ","metadata":{}},{"cell_type":"code","source":"test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=1, num_workers=1)","metadata":{"execution":{"iopub.status.busy":"2023-10-03T16:06:45.183190Z","iopub.execute_input":"2023-10-03T16:06:45.184318Z","iopub.status.idle":"2023-10-03T16:06:45.189398Z","shell.execute_reply.started":"2023-10-03T16:06:45.184275Z","shell.execute_reply":"2023-10-03T16:06:45.188441Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict(model, dataloader):\n    model.eval()\n    test_pred = torch.LongTensor()\n    \n    for i, data in enumerate(dataloader):\n#         data = data.cuda()\n        output = model(data)\n        \n        pred = output.cpu().data.max(1, keepdim=True)[1]\n        test_pred = torch.cat((test_pred, pred), dim=0)\n        \n    return test_pred","metadata":{"execution":{"iopub.status.busy":"2023-10-03T16:57:06.628508Z","iopub.execute_input":"2023-10-03T16:57:06.629039Z","iopub.status.idle":"2023-10-03T16:57:06.635289Z","shell.execute_reply.started":"2023-10-03T16:57:06.629004Z","shell.execute_reply":"2023-10-03T16:57:06.634058Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions = predict(model,test_loader)\npredictions = np.array(predictions)","metadata":{"execution":{"iopub.status.busy":"2023-10-03T16:57:10.126103Z","iopub.execute_input":"2023-10-03T16:57:10.127112Z","iopub.status.idle":"2023-10-03T16:57:14.285220Z","shell.execute_reply.started":"2023-10-03T16:57:10.127076Z","shell.execute_reply":"2023-10-03T16:57:14.284238Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions","metadata":{"execution":{"iopub.status.busy":"2023-10-03T16:57:17.524467Z","iopub.execute_input":"2023-10-03T16:57:17.525048Z","iopub.status.idle":"2023-10-03T16:57:17.531236Z","shell.execute_reply.started":"2023-10-03T16:57:17.525014Z","shell.execute_reply":"2023-10-03T16:57:17.530197Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_data = pd.read_csv(\"/kaggle/input/Kannada-MNIST/test.csv\")\n\n\nsubmission = pd.DataFrame(data={\n    \"id\": test_data.index,\n    \"label\": predictions.flatten()\n})\nsubmission.to_csv(\"submission.csv\", index=None)","metadata":{"execution":{"iopub.status.busy":"2023-10-03T16:57:28.948750Z","iopub.execute_input":"2023-10-03T16:57:28.949473Z","iopub.status.idle":"2023-10-03T16:57:29.181286Z","shell.execute_reply.started":"2023-10-03T16:57:28.949439Z","shell.execute_reply":"2023-10-03T16:57:29.180385Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"submission","metadata":{"execution":{"iopub.status.busy":"2023-10-03T16:57:33.844559Z","iopub.execute_input":"2023-10-03T16:57:33.845279Z","iopub.status.idle":"2023-10-03T16:57:33.855451Z","shell.execute_reply.started":"2023-10-03T16:57:33.845236Z","shell.execute_reply":"2023-10-03T16:57:33.854405Z"},"trusted":true},"execution_count":null,"outputs":[]}]}