{"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-21T09:32:14.511584Z","iopub.execute_input":"2023-10-21T09:32:14.511844Z","iopub.status.idle":"2023-10-21T09:32:14.870751Z","shell.execute_reply.started":"2023-10-21T09:32:14.511819Z","shell.execute_reply":"2023-10-21T09:32:14.869723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 1. Importing library","metadata":{}},{"cell_type":"code","source":"# import libraries\nimport 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":"2023-10-21T09:32:14.872638Z","iopub.execute_input":"2023-10-21T09:32:14.873243Z","iopub.status.idle":"2023-10-21T09:32:21.333567Z","shell.execute_reply.started":"2023-10-21T09:32:14.873208Z","shell.execute_reply":"2023-10-21T09:32:21.332632Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 2. Define Custom dataset class","metadata":{}},{"cell_type":"markdown","source":"Nguyên tắc: 1 custom dataset class bao gồm cũng có 3 methods:\n\n1. __getitem__ : trả về data (và/hoặc label) khi biết số index\n2. __len__ : trả về số mẫu trong dataset\n3. __init__: thực hiện các thao tác trong khi khởi tạo 1 object dataset","metadata":{}},{"cell_type":"code","source":"class TrainMNIST(Dataset): # chuyển đổi input đầu vào về dạng tensor\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) # trả về số dòng/mẫu trong dataframe 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)) # lấy từ cột thứ 2 trở đi vì cột đầu là label, chuyển về dạng np array và đổi lại định dạng\n        label = self.data.iloc[index, 0] # lấy cột label\n\n        if self.transform is not None:\n            image = self.transform(image) # biến np array đc store ở biến image về dạng tensor\n\n        return image, label","metadata":{"execution":{"iopub.status.busy":"2023-10-21T09:33:09.539316Z","iopub.execute_input":"2023-10-21T09:33:09.539856Z","iopub.status.idle":"2023-10-21T09:33:09.547890Z","shell.execute_reply.started":"2023-10-21T09:33:09.539821Z","shell.execute_reply":"2023-10-21T09:33:09.546715Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class TestMNIST(Dataset): # copy code class TrainMNIST, bỏ biến label\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, 0:].values.astype(np.uint8).reshape((28, 28, 1))\n\n\n        if self.transform is not None:\n            image = self.transform(image)\n\n        return image # tập test k có label nên k trả về label","metadata":{"execution":{"iopub.status.busy":"2023-10-21T09:33:31.859307Z","iopub.execute_input":"2023-10-21T09:33:31.859703Z","iopub.status.idle":"2023-10-21T09:33:31.866679Z","shell.execute_reply.started":"2023-10-21T09:33:31.859670Z","shell.execute_reply":"2023-10-21T09:33:31.865733Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 3. Load and Visualize the [Data](http://pytorch.org/docs/stable/torchvision/datasets.html)","metadata":{}},{"cell_type":"code","source":"train_data = TrainMNIST('../input/Kannada-MNIST/train.csv', transform=transforms.ToTensor()) # hàm chuyển từ np array về dạng tensor => dataset class\ntest_data = TestMNIST('../input/Kannada-MNIST/test.csv', transform=transforms.ToTensor())","metadata":{"execution":{"iopub.status.busy":"2023-10-21T09:34:57.767552Z","iopub.execute_input":"2023-10-21T09:34:57.767937Z","iopub.status.idle":"2023-10-21T09:35:03.004988Z","shell.execute_reply.started":"2023-10-21T09:34:57.767908Z","shell.execute_reply":"2023-10-21T09:35:03.004143Z"},"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, num_workers=num_workers,)\ntest_loader = torch.utils.data.DataLoader(test_data, batch_size=batch_size, num_workers=num_workers)\n# chia tập data thành nhiều khúc, mỗi khúc chứa 20 mẫu","metadata":{"execution":{"iopub.status.busy":"2023-10-21T09:35:17.496185Z","iopub.execute_input":"2023-10-21T09:35:17.496906Z","iopub.status.idle":"2023-10-21T09:35:17.502394Z","shell.execute_reply.started":"2023-10-21T09:35:17.496875Z","shell.execute_reply":"2023-10-21T09:35:17.501499Z"},"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\nimages, labels = next(iter(train_loader))\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-21T09:35:29.480848Z","iopub.execute_input":"2023-10-21T09:35:29.481554Z","iopub.status.idle":"2023-10-21T09:35:30.975163Z","shell.execute_reply.started":"2023-10-21T09:35:29.481522Z","shell.execute_reply":"2023-10-21T09:35:30.974101Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 4. Thiết kế 1 mạng NN đơn giản [Architecture](http://pytorch.org/docs/stable/nn.html)","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): # chứa cấu trúc của model\n        super(Net, self).__init__()\n        # number of hidden nodes in each layer (512)\n        hidden_1 = 512\n        hidden_2 = 512\n        hidden_3 = 512\n        hidden_4 = 512\n        # linear layer (784 -> hidden_1)\n        self.fc1 = nn.Linear(28 * 28, hidden_1) # nn.Linear(số kênh/chiều đầu vào, số lượng neurons ở lớp đó)\n        # linear layer (n_hidden -> hidden_2)\n        self.fc2 = nn.Linear(hidden_1, hidden_2)\n        self.fc3 = nn.Linear(hidden_2, hidden_3)\n        self.fc4 = nn.Linear(hidden_3, hidden_4)\n        # linear layer (n_hidden -> 10)\n        self.fc5 = nn.Linear(hidden_4, 10) # lớp output\n\n\n    def forward(self, x): # biểu diễn forward propagation, từ input tính toán ra output\n        # flatten image input\n        x = x.view(-1, 28 * 28) # method duỗi thẳng về vector, -1 tượng trưng cho số mẫu đưa vào cùng 1 lúc. Vd cò 20 mẫu thì -1 là 20x28^2\n        # add hidden layer, with relu activation function\n        x = F.relu(self.fc1(x)) # self.fc1(x) là đang tính net, có kqua net thì đưa vào hàm activation function relu, x lưu trữ kqua output của biến thứ 1\n        # add hidden layer, with relu activation function\n        x = F.relu(self.fc2(x)) # output của lớp thứ 1 đưa vào fully connected layer của lớp thứ 2\n        x = F.relu(self.fc3(x))\n        x = F.relu(self.fc4(x))\n        # add output layer\n        x = self.fc5(x)\n        return x\n\n# initialize the NN\nmodel = Net()\nprint(model)","metadata":{"execution":{"iopub.status.busy":"2023-10-21T09:36:17.948133Z","iopub.execute_input":"2023-10-21T09:36:17.948592Z","iopub.status.idle":"2023-10-21T09:36:17.975280Z","shell.execute_reply.started":"2023-10-21T09:36:17.948549Z","shell.execute_reply":"2023-10-21T09:36:17.974285Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"###  5. Chọn [Loss Function](http://pytorch.org/docs/stable/nn.html#loss-functions) và [Optimizer](http://pytorch.org/docs/stable/optim.html)","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) # update tất cả trọng số ở mô hình hiện tại","metadata":{"execution":{"iopub.status.busy":"2023-10-21T09:36:49.833911Z","iopub.execute_input":"2023-10-21T09:36:49.834391Z","iopub.status.idle":"2023-10-21T09:36:49.841501Z","shell.execute_reply.started":"2023-10-21T09:36:49.834333Z","shell.execute_reply":"2023-10-21T09:36:49.840052Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 6. Train the Network","metadata":{}},{"cell_type":"code","source":"# number of epochs to train the model\nn_epochs = 30\n\n# initialize tracker for minimum validation loss\ntrain_loss_min = np.Inf # set initial \"min\" to infinity\n\nfor epoch in range(n_epochs): # loop cho từng epoch\n    # monitor training loss\n    train_loss = 0.0\n\n    ###################\n    # train the model #\n    ###################\n    model.train() # prep model for training # với mỗi epoch trong 20 epochs nó sẽ chuyển model về dạng training mode. Khi predict sẽ chuyển về dạng valuation mode\n    for data, target in train_loader: # load từng batch ở tập train, 1 batch 20 hình\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) # lấy kqua output đi so sánh với target/label\n        # backward pass: compute gradient of the loss with respect to model parameters\n        loss.backward() # lấy đạo hàm vừa tính đc đem vào optimizer để optimizer.step hiểu rằng dùng toàn bộ đạo hàm phía trên để update vào trọng số của mô hình\n        # perform a single optimization step (parameter update)\n        optimizer.step() # update lại trọng số\n        # update running training loss\n        train_loss += loss.item()*data.size(0) # VD có 20 mẫu thì tính loss của 20 mẫu đó rồi TB lại với nhau. Lấy con số TB đó nhân với số mẫu ở trong batch hiện tại để ra đc tổng loss\n        # ghi tổng loss lại trên biến train_loss\n\n\n    train_loss = train_loss/len(train_loader.dataset) # tính tổng loss cho toàn bộ epoch rồi chia cho tổng số mẫu ở trong dataset hiện tại\n\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ếu loss hiện tại giảm so với loss trc thì model tốt hơn => save model mới\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') # model.state_dict(): trả về weights của model\n        train_loss_min = train_loss","metadata":{"execution":{"iopub.status.busy":"2023-10-21T09:37:29.479748Z","iopub.execute_input":"2023-10-21T09:37:29.480111Z","iopub.status.idle":"2023-10-21T09:49:33.422876Z","shell.execute_reply.started":"2023-10-21T09:37:29.480084Z","shell.execute_reply":"2023-10-21T09:49:33.421911Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"###  Load the Model with the Lowest Validation Loss","metadata":{}},{"cell_type":"code","source":"model.load_state_dict(torch.load('model.pt'))","metadata":{"execution":{"iopub.status.busy":"2023-10-21T09:49:39.246143Z","iopub.execute_input":"2023-10-21T09:49:39.246860Z","iopub.status.idle":"2023-10-21T09:49:39.257312Z","shell.execute_reply.started":"2023-10-21T09:49:39.246826Z","shell.execute_reply":"2023-10-21T09:49:39.256472Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Visualize Sample Test Results","metadata":{}},{"cell_type":"code","source":"# obtain one batch of test images\n# lấy ra 1 batch bất kì trên test_loader\nimages, labels = next(iter(train_loader))\n\n# get sample outputs\noutput = model(images) # cho batch đó đi qua model\n# convert output probabilities to predicted class\n_, preds = torch.max(output, 1) # softmax trả về kqua lớn nhất khi đi qua nó, giá trị nào lớn nhất trong vector output thì đi qua softmax cũng lớn nhất. Chọn giá trị lớn nhất vì nó cũng là label\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-21T09:49:44.716276Z","iopub.execute_input":"2023-10-21T09:49:44.716693Z","iopub.status.idle":"2023-10-21T09:49:45.861046Z","shell.execute_reply.started":"2023-10-21T09:49:44.716657Z","shell.execute_reply":"2023-10-21T09:49:45.860137Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 7. Predict digit dựa vào input >> tạo file submission trong kaggle","metadata":{}},{"cell_type":"code","source":"test_loader = torch.utils.data.DataLoader(test_data, batch_size=1, num_workers=num_workers)","metadata":{"execution":{"iopub.status.busy":"2023-10-21T09:49:50.025947Z","iopub.execute_input":"2023-10-21T09:49:50.026918Z","iopub.status.idle":"2023-10-21T09:49:50.031631Z","shell.execute_reply.started":"2023-10-21T09:49:50.026882Z","shell.execute_reply":"2023-10-21T09:49:50.030277Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict(model, dataloader):\n    prediction_list = []\n    for i, batch in enumerate(dataloader):\n        outputs = model(batch)\n        _, predicted = torch.max(outputs.data, 1) \n        prediction_list.append(predicted.cpu().item())\n    return prediction_list","metadata":{"execution":{"iopub.status.busy":"2023-10-21T09:49:53.138557Z","iopub.execute_input":"2023-10-21T09:49:53.139294Z","iopub.status.idle":"2023-10-21T09:49:53.144516Z","shell.execute_reply.started":"2023-10-21T09:49:53.139259Z","shell.execute_reply":"2023-10-21T09:49:53.143575Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions = predict(model, test_loader)\npredictions = [x.item() for x in predictions]","metadata":{"execution":{"iopub.status.busy":"2023-10-21T10:02:21.434059Z","iopub.execute_input":"2023-10-21T10:02:21.434901Z","iopub.status.idle":"2023-10-21T10:02:21.534325Z","shell.execute_reply.started":"2023-10-21T10:02:21.434865Z","shell.execute_reply":"2023-10-21T10:02:21.532865Z"},"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    \"ImageId\": test_data.index + 1,\n    \"Label\": predictions\n})\nsubmission.to_csv(\"submission.csv\", index=None)","metadata":{"execution":{"iopub.status.busy":"2023-10-21T09:56:00.608114Z","iopub.execute_input":"2023-10-21T09:56:00.608480Z","iopub.status.idle":"2023-10-21T09:56:00.957802Z","shell.execute_reply.started":"2023-10-21T09:56:00.608450Z","shell.execute_reply":"2023-10-21T09:56:00.956485Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Kết quả thực tế trên Kaggle ~ %","metadata":{}},{"cell_type":"code","source":"sample_submission","metadata":{},"execution_count":null,"outputs":[]}]}