{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":16017,"databundleVersionId":708189,"sourceType":"competition"}],"dockerImageVersionId":30637,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-01-12T07:32:08.043931Z","iopub.execute_input":"2024-01-12T07:32:08.044806Z","iopub.status.idle":"2024-01-12T07:32:08.475948Z","shell.execute_reply.started":"2024-01-12T07:32:08.044772Z","shell.execute_reply":"2024-01-12T07:32:08.474872Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import DataLoader, Dataset\nimport torchvision\nimport torchvision.transforms as transforms\nfrom torch.utils.data.sampler import SubsetRandomSampler\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:08.477762Z","iopub.execute_input":"2024-01-12T07:32:08.478271Z","iopub.status.idle":"2024-01-12T07:32:10.741225Z","shell.execute_reply.started":"2024-01-12T07:32:08.478239Z","shell.execute_reply":"2024-01-12T07:32:10.740020Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Step 1: Check data","metadata":{}},{"cell_type":"code","source":"train_data = pd.read_csv(\"/kaggle/input/Kannada-MNIST/train.csv\")\ntrain_data.info()","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:10.743136Z","iopub.execute_input":"2024-01-12T07:32:10.744180Z","iopub.status.idle":"2024-01-12T07:32:16.599360Z","shell.execute_reply.started":"2024-01-12T07:32:10.744127Z","shell.execute_reply":"2024-01-12T07:32:16.598220Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 6000 entries -> each entry is a image 28x28 pixel -> flatten to 784 vectors ( first column is label)","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:16.602509Z","iopub.execute_input":"2024-01-12T07:32:16.603243Z","iopub.status.idle":"2024-01-12T07:32:16.608651Z","shell.execute_reply.started":"2024-01-12T07:32:16.603200Z","shell.execute_reply":"2024-01-12T07:32:16.607362Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data.head()","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:16.610051Z","iopub.execute_input":"2024-01-12T07:32:16.610372Z","iopub.status.idle":"2024-01-12T07:32:16.641954Z","shell.execute_reply.started":"2024-01-12T07:32:16.610344Z","shell.execute_reply":"2024-01-12T07:32:16.640832Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Assign data and labels\ntrain_images = train_data.iloc[:, 1:].values\ntrain_labels = train_data.iloc[:, 0].values","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:16.643591Z","iopub.execute_input":"2024-01-12T07:32:16.644081Z","iopub.status.idle":"2024-01-12T07:32:16.650091Z","shell.execute_reply.started":"2024-01-12T07:32:16.644038Z","shell.execute_reply":"2024-01-12T07:32:16.648987Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_images.shape","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:16.651840Z","iopub.execute_input":"2024-01-12T07:32:16.652470Z","iopub.status.idle":"2024-01-12T07:32:16.663155Z","shell.execute_reply.started":"2024-01-12T07:32:16.652435Z","shell.execute_reply":"2024-01-12T07:32:16.661853Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_images[0].dtype","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:16.664982Z","iopub.execute_input":"2024-01-12T07:32:16.665453Z","iopub.status.idle":"2024-01-12T07:32:16.678460Z","shell.execute_reply.started":"2024-01-12T07:32:16.665413Z","shell.execute_reply":"2024-01-12T07:32:16.677248Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# plot first image ( imshow need float type not int type and need to unflatten image to matrix)\ntrain_images = train_images.astype('float32')\ntrain_images_unflat = train_images.reshape(60000,28,28)\nimport matplotlib.pyplot as plt\nplt.imshow(train_images_unflat[0]) ","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:16.680045Z","iopub.execute_input":"2024-01-12T07:32:16.680666Z","iopub.status.idle":"2024-01-12T07:32:17.105900Z","shell.execute_reply.started":"2024-01-12T07:32:16.680630Z","shell.execute_reply":"2024-01-12T07:32:17.104703Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_labels[0]","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:17.110876Z","iopub.execute_input":"2024-01-12T07:32:17.111267Z","iopub.status.idle":"2024-01-12T07:32:17.118922Z","shell.execute_reply.started":"2024-01-12T07:32:17.111235Z","shell.execute_reply":"2024-01-12T07:32:17.117601Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Step 2: Custom load & preprocessing data","metadata":{}},{"cell_type":"markdown","source":"* Custom dataset class","metadata":{}},{"cell_type":"code","source":"class TrainMNIST(Dataset):\n    \n    def __init__(self, file_path, transform=None):\n        self.data = pd.read_csv(file_path)\n        self.transform = transform\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            \n        return image, label","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:17.120361Z","iopub.execute_input":"2024-01-12T07:32:17.120733Z","iopub.status.idle":"2024-01-12T07:32:17.130957Z","shell.execute_reply.started":"2024-01-12T07:32:17.120697Z","shell.execute_reply":"2024-01-12T07:32:17.129582Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class TestMNIST(Dataset):\n    \n    def __init__(self, file_path, transform=None):\n        self.data = pd.read_csv(file_path)\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.data)\n    \n    def __getitem__(self, index):\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\n        return image, torch.tensor(label, dtype=torch.long)  # Convert label to tensor","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:17.132330Z","iopub.execute_input":"2024-01-12T07:32:17.132668Z","iopub.status.idle":"2024-01-12T07:32:17.147035Z","shell.execute_reply.started":"2024-01-12T07:32:17.132638Z","shell.execute_reply":"2024-01-12T07:32:17.145602Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* Load data","metadata":{}},{"cell_type":"code","source":"train_data = TrainMNIST('/kaggle/input/Kannada-MNIST/train.csv', transform=transforms.ToTensor())\ntest_data = TestMNIST('/kaggle/input/Kannada-MNIST/test.csv', transform=transforms.ToTensor())\n#ToTenSor -> normalize value 0->255 to 0->1.0","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:17.148558Z","iopub.execute_input":"2024-01-12T07:32:17.149260Z","iopub.status.idle":"2024-01-12T07:32:22.224135Z","shell.execute_reply.started":"2024-01-12T07:32:17.149196Z","shell.execute_reply":"2024-01-12T07:32:22.222831Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# number of subprocesses to use for data loading\nnum_workers = 0\n# how many samples per batch to load\nbatch_size = 20\n\n# convert data to torch.FloatTensor\n#transform = transforms.ToTensor()\n\n\n# prepare data loaders\ntrain_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size,\n     num_workers=num_workers,)\ntest_loader = torch.utils.data.DataLoader(test_data, batch_size=batch_size, \n    num_workers=num_workers)","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:22.225680Z","iopub.execute_input":"2024-01-12T07:32:22.226063Z","iopub.status.idle":"2024-01-12T07:32:22.236194Z","shell.execute_reply.started":"2024-01-12T07:32:22.226031Z","shell.execute_reply":"2024-01-12T07:32:22.234224Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n%matplotlib inline\n    \n# obtain one batch of training images\ndataiter = iter(train_loader)\nimages, labels = next(dataiter)\nimages = images.numpy()\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n# Assuming you have 'images' and 'labels' arrays\n\n# plot the images in the batch, along with the corresponding labels\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    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()))\n\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:22.237496Z","iopub.execute_input":"2024-01-12T07:32:22.237925Z","iopub.status.idle":"2024-01-12T07:32:23.477501Z","shell.execute_reply.started":"2024-01-12T07:32:22.237890Z","shell.execute_reply":"2024-01-12T07:32:23.476298Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Step 3: Build Model","metadata":{}},{"cell_type":"markdown","source":"## 3.1 Build a Neuron Network","metadata":{}},{"cell_type":"code","source":"import torch.nn as nn\nimport torch.nn.functional as F\n\n# define the NN architecture\nclass Net(nn.Module):\n    def __init__(self):\n        super(Net, self).__init__()\n        # number of hidden nodes in each layer (512)\n        hidden_1 = 512\n        hidden_2 = 512\n        # linear layer (784 -> hidden_1)\n        self.fc1 = nn.Linear(28 * 28, hidden_1)\n        # linear layer (n_hidden -> hidden_2)\n        self.fc2 = nn.Linear(hidden_1, hidden_2)\n        # linear layer (n_hidden -> 10)\n        self.fc3 = nn.Linear(hidden_2, 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 output layer\n        x = self.fc3(x)\n        return x\n\n# initialize the NN\nmodel = Net()\nprint(model)","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:23.479197Z","iopub.execute_input":"2024-01-12T07:32:23.480170Z","iopub.status.idle":"2024-01-12T07:32:23.503542Z","shell.execute_reply.started":"2024-01-12T07:32:23.480126Z","shell.execute_reply":"2024-01-12T07:32:23.502374Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3.2 Chọn Loss Function và Optimizer","metadata":{}},{"cell_type":"code","source":"# specify loss function (categorical cross-entropy)\ncriterion = nn.CrossEntropyLoss()\n\n# specify optimizer (stochastic gradient descent) and learning rate = 0.01\noptimizer = torch.optim.SGD(model.parameters(), lr=0.01)","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:23.505047Z","iopub.execute_input":"2024-01-12T07:32:23.505467Z","iopub.status.idle":"2024-01-12T07:32:23.515107Z","shell.execute_reply.started":"2024-01-12T07:32:23.505426Z","shell.execute_reply":"2024-01-12T07:32:23.513781Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 3.2 Train the network","metadata":{}},{"cell_type":"code","source":"# number of epochs to train the model\nn_epochs = 20\n\n# initialize tracker for minimum validation loss\ntrain_loss_min = np.Inf # set initial \"min\" to infinity\ntrain_loss_values = []\n\nfor epoch in range(n_epochs):\n    # monitor training loss\n    train_loss = 0.0\n    \n    ###################\n    # train the model #\n    ###################\n    model.train() # prep model for training\n    for data, target in train_loader:\n        # clear the gradients of all optimized variables\n        optimizer.zero_grad()\n        # forward pass: compute predicted outputs by passing inputs to the model\n        output = model(data)\n        # calculate the loss\n        loss = criterion(output, target.long())\n        # backward pass: compute gradient of the loss with respect to model parameters\n        loss.backward()\n        # perform a single optimization step (parameter update)\n        optimizer.step()\n        # update running training loss\n        train_loss += loss.item()*data.size(0)\n\n    train_loss = train_loss/len(train_loader.dataset)\n    \n    train_loss_values.append(train_loss)\n    print('Epoch: {} \\tTraining Loss: {:.6f}'.format(\n        epoch+1, \n        train_loss,\n        ))\n    \n    # save model if validation loss has decreased\n    if train_loss <= train_loss_min:\n        print('Training loss decreased ({:.6f} --> {:.6f}).  Saving model ...'.format(\n        train_loss_min,\n        train_loss))\n        torch.save(model.state_dict(), 'model.pt')\n        train_loss_min = train_loss","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:32:23.516443Z","iopub.execute_input":"2024-01-12T07:32:23.516757Z","iopub.status.idle":"2024-01-12T07:39:45.683972Z","shell.execute_reply.started":"2024-01-12T07:32:23.516714Z","shell.execute_reply":"2024-01-12T07:39:45.682599Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot the training loss over epochs\nplt.plot(range(1, n_epochs + 1), train_loss_values, label='Training Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.title('Training Loss Over Epochs')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:39:45.686080Z","iopub.execute_input":"2024-01-12T07:39:45.686550Z","iopub.status.idle":"2024-01-12T07:39:46.031076Z","shell.execute_reply.started":"2024-01-12T07:39:45.686494Z","shell.execute_reply":"2024-01-12T07:39:46.029826Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 3.3 Predict digit dựa vào input ","metadata":{}},{"cell_type":"code","source":"model.load_state_dict(torch.load('model.pt'))","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:39:46.032326Z","iopub.execute_input":"2024-01-12T07:39:46.032642Z","iopub.status.idle":"2024-01-12T07:39:46.043937Z","shell.execute_reply.started":"2024-01-12T07:39:46.032614Z","shell.execute_reply":"2024-01-12T07:39:46.042984Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_loader = torch.utils.data.DataLoader(test_data, batch_size=batch_size, num_workers=num_workers)\n\n# Assuming you have a test_loader for your test dataset\n# and a trained model\n\nmodel.eval()  # Set the model to evaluation mode\n\npredictions = []\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nwith torch.no_grad():\n    for data in test_loader:\n        # Extract the tensor from the list\n        input_tensor = data[0]\n\n        # Make sure the tensor is on the same device as the model\n        input_tensor = input_tensor.to(device)\n\n        output = model(input_tensor)\n        _, predicted = torch.max(output, 1)  # Assuming it's a classification task\n        predictions.extend(predicted.cpu().numpy())\n","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:55:10.547004Z","iopub.execute_input":"2024-01-12T07:55:10.547546Z","iopub.status.idle":"2024-01-12T07:55:12.269505Z","shell.execute_reply.started":"2024-01-12T07:55:10.547498Z","shell.execute_reply":"2024-01-12T07:55:12.268094Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(predictions)","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:55:17.247181Z","iopub.execute_input":"2024-01-12T07:55:17.247615Z","iopub.status.idle":"2024-01-12T07:55:17.255339Z","shell.execute_reply.started":"2024-01-12T07:55:17.247579Z","shell.execute_reply":"2024-01-12T07:55:17.254166Z"},"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\n})\nsubmission.to_csv(\"submission.csv\", index=None)","metadata":{"execution":{"iopub.status.busy":"2024-01-12T07:55:20.354671Z","iopub.execute_input":"2024-01-12T07:55:20.355099Z","iopub.status.idle":"2024-01-12T07:55:20.810668Z","shell.execute_reply.started":"2024-01-12T07:55:20.355065Z","shell.execute_reply":"2024-01-12T07:55:20.809806Z"},"trusted":true},"execution_count":null,"outputs":[]}]}