{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":16017,"databundleVersionId":708189,"sourceType":"competition"}],"dockerImageVersionId":30787,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"**A. Global variables**","metadata":{}},{"cell_type":"code","source":"# Defining a global variable\npath_file_submission = '/kaggle/input/Kannada-MNIST/test.csv'\npath_file_train = '/kaggle/input/Kannada-MNIST/train.csv'\npath_file_dataset = '/kaggle/input/Kannada-MNIST/train.csv'\npath_output_model = 'vta_kanada_mnist.pt'\npath_output_submission = './submission.csv'\n\n# Label column\nlabel_column_train = 'label'\nlabel_column_test = 'id'\n\n# Input image information Height and Width\nimage_height = 28\nimage_width = 28\n\n# Number of subprocesses to use for data loading\nnum_workers = 0\n# How many samples per batch to load\nbatch_size = 64\n# number of epochs to train the model\nn_epochs = 30\n# learning rate\nlearning_rate = 0.0001","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-25T06:47:12.088644Z","iopub.execute_input":"2024-11-25T06:47:12.089027Z","iopub.status.idle":"2024-11-25T06:47:12.097788Z","shell.execute_reply.started":"2024-11-25T06:47:12.088995Z","shell.execute_reply":"2024-11-25T06:47:12.096749Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**B. Data loader**","metadata":{}},{"cell_type":"code","source":"# Import libraries\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torchvision\nimport torchvision.transforms as transforms\nfrom torch.utils.data import DataLoader, Dataset\nfrom sklearn.metrics import classification_report\nfrom torch.utils.data import DataLoader, random_split\nfrom torch.utils.data.sampler import SubsetRandomSampler\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\n# Define Custom dataset class\nclass custom_dataset_mnist(Dataset):\n\n    def __init__(self, csv_file, transform=None):\n        self.data = pd.read_csv(csv_file)\n        print(self.data.shape) # Print debug information (row, column)\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, i don't use ToTensor() method of torchvision.transforms\n        # so you can convert numpy ndarray shape to tensor in PyTorch (H, W, C) --> (C, H, W)\n        image = self.data.iloc[index, 1:].values.astype(np.uint8).reshape((1, image_height, image_width))\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\n\n# Split dataset into new train_dataset and test_dataset\nkanada_mnist_dataset = custom_dataset_mnist(csv_file=path_file_dataset, transform=transforms.ToTensor())\n# Define ratio\ntrain_size = int(0.8 * len(kanada_mnist_dataset))  # 80% for trainning\ntest_size = len(kanada_mnist_dataset) - train_size  # 20% for evaluation\n\n# Use random_split to split data\ntrain_dataset, test_dataset = random_split(kanada_mnist_dataset, [train_size, test_size])\n\n# Check size new dataset\nprint(f\"train_dataset size: {len(train_dataset)}\")\nprint(f\"test_dataset size: {len(test_dataset)}\")\n\n\ntrain_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, num_workers=num_workers, shuffle=True)\ntest_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=batch_size, num_workers=num_workers, shuffle=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-25T06:47:12.099684Z","iopub.execute_input":"2024-11-25T06:47:12.100041Z","iopub.status.idle":"2024-11-25T06:47:18.785207Z","shell.execute_reply.started":"2024-11-25T06:47:12.100003Z","shell.execute_reply":"2024-11-25T06:47:18.784262Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**C. Visualize the Data**","metadata":{}},{"cell_type":"code","source":"# # Get informaiton from input data\n# # test.csv file\n# file_cvs = np.genfromtxt(path_file_test, delimiter=',', skip_header=1)\n# num_columns = file_cvs.shape[1]\n# print(f\"Số lượng cột trong tệp CSV: {num_columns}\")\n# # train.csv file\n# file_cvs = np.genfromtxt(path_file_train, delimiter=',', skip_header=1)\n# num_columns = file_cvs.shape[1]\n# print(f\"Số lượng cột trong tệp CSV: {num_columns}\")\n\n\nimport 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\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":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-25T06:47:18.786471Z","iopub.execute_input":"2024-11-25T06:47:18.786827Z","iopub.status.idle":"2024-11-25T06:47:19.822743Z","shell.execute_reply.started":"2024-11-25T06:47:18.786790Z","shell.execute_reply":"2024-11-25T06:47:19.821899Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**D. Implement Neural Network architecture**","metadata":{}},{"cell_type":"code","source":"# Define NN architecture\nclass neural_network_arch(nn.Module):\n    def __init__(self):\n        super(neural_network_arch, self).__init__()\n        # number of hidden nodes in each layer (512)\n        hidden_node_1 = 512\n        hidden_node_2 = 512\n        hidden_node_3 = 10\n        self.flatten = nn.Flatten()\n        self.fc1 = nn.Linear(image_height * image_width, hidden_node_1)\n        self.fc2 = nn.Linear(hidden_node_1, hidden_node_2)\n        self.fc3 = nn.Linear(hidden_node_2, hidden_node_3)\n        self.relu = nn.ReLU()\n        self.dropout = nn.Dropout(0.2)\n\n    def forward(self, x):\n        x = self.flatten(x)\n        x = self.relu(self.fc1(x))\n        x = self.dropout(x)\n        x = self.relu(self.fc2(x))\n        x = self.fc3(x)\n        return x\n\n# Initialize the NN\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"Using device: {device}\")\nmodel = neural_network_arch().to(device)\nprint(model)\n\n# Define loss and optimaze\ncriterion = nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-25T06:47:19.824704Z","iopub.execute_input":"2024-11-25T06:47:19.825105Z","iopub.status.idle":"2024-11-25T06:47:20.056612Z","shell.execute_reply.started":"2024-11-25T06:47:19.825066Z","shell.execute_reply":"2024-11-25T06:47:20.055759Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**E. Train model**","metadata":{}},{"cell_type":"code","source":"# Train model\ndef train_model(model, loader, criterion, optimizer, num_epochs=5):\n    # initialize tracker for minimum validation loss\n    train_loss_min = np.Inf # set initial \"min\" to infinity\n    # prep model for training\n    model.train()\n    for epoch in range(num_epochs):\n        train_loss = 0.0\n        correct = 0\n        total = 0\n        for inputs, labels in loader:\n            # print(inputs.shape) # Print debug information\n            # print(labels) # Print debug information\n            # move the data to the device (CPU or GPU)\n            inputs, labels = inputs.to(device), labels.to(device)\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            outputs = model(inputs)\n            # calculate the loss\n            loss = criterion(outputs, labels)\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()  # Cộng dồn loss\n            _, predicted = torch.max(outputs, 1)  # Dự đoán nhãn có xác suất cao nhất\n            total += labels.size(0)\n            correct += (predicted == labels).sum().item()\n\n        epoch_loss = train_loss / len(loader)\n        epoch_accuracy = 100 * correct / total\n        print(f\"Epoch [{epoch+1}/{num_epochs}], Loss: {epoch_loss:.4f}, Accuracy: {epoch_accuracy:.2f}%\")\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(train_loss_min, train_loss))\n            torch.save(model.state_dict(), path_output_model)\n            train_loss_min = train_loss\n\ntrain_model(model, train_loader, criterion, optimizer, num_epochs=n_epochs)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-25T06:47:20.057959Z","iopub.execute_input":"2024-11-25T06:47:20.058326Z","iopub.status.idle":"2024-11-25T06:51:50.752469Z","shell.execute_reply.started":"2024-11-25T06:47:20.058288Z","shell.execute_reply":"2024-11-25T06:51:50.751469Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**F. Evaluate model**","metadata":{}},{"cell_type":"code","source":"# Evaluate model\ndef evaluate_model(model, loader):\n    model.eval()  # Chuyển mô hình sang chế độ đánh giá\n    true_labels = []\n    predictions = []\n    \n    with torch.no_grad():  # Tắt tính toán gradient\n        for inputs, labels in loader:\n            # print(inputs.shape) # Print debug information\n            # print(labels) # Print debug information\n            inputs, labels = inputs.to(device), labels.to(device)\n            outputs = model(inputs)  # Dự đoán\n            _, predicted = torch.max(outputs, 1)  # Lấy nhãn có xác suất cao nhất\n            # print(f\"True: {labels}, Predicted: {predicted}\") # Print debug information\n            \n            true_labels.extend(labels.cpu().numpy())  # Lưu nhãn thực tế\n            predictions.extend(predicted.cpu().numpy())  # Lưu dự đoán\n    \n    return true_labels, predictions\n\n# Load evaluation data\ntest_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=1, num_workers=num_workers, shuffle=False)\n# Load model\nmodel.load_state_dict(torch.load(path_output_model, weights_only=True))\n# Run evaluation model\ntrue_labels, predictions = evaluate_model(model, test_loader)\nprint(classification_report(true_labels, predictions))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-25T06:51:50.753642Z","iopub.execute_input":"2024-11-25T06:51:50.754015Z","iopub.status.idle":"2024-11-25T06:51:58.864530Z","shell.execute_reply.started":"2024-11-25T06:51:50.753976Z","shell.execute_reply":"2024-11-25T06:51:58.863708Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**G. Store data into submission csv file**","metadata":{}},{"cell_type":"code","source":"def save_predictions_to_csv(predictions, output_file):\n    df = pd.DataFrame({\n        'id': np.arange(0, len(predictions)),  # Giả sử ID là số từ 1 đến n\n        'label': predictions\n    })\n    df.to_csv(output_file, index=False)\n    print(f\"Results saved to {output_file}\")\n\n\n# Load evaluation data\nsubmission_dataset = custom_dataset_mnist(csv_file=path_file_submission, transform=transforms.ToTensor())\nsubmission_loader = torch.utils.data.DataLoader(dataset=submission_dataset, batch_size=1, num_workers=num_workers, shuffle=False)\n# Load model\nmodel.load_state_dict(torch.load(path_output_model, weights_only=True))\n# Run evaluation model\ntrue_labels, predictions = evaluate_model(model, submission_loader)\n# Write data into submission.csv file\nsave_predictions_to_csv(predictions, path_output_submission)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-25T06:51:58.865741Z","iopub.execute_input":"2024-11-25T06:51:58.866103Z","iopub.status.idle":"2024-11-25T06:52:02.526033Z","shell.execute_reply.started":"2024-11-25T06:51:58.866074Z","shell.execute_reply":"2024-11-25T06:52:02.525154Z"}},"outputs":[],"execution_count":null}]}