{"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":"import pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv(\"../input/digit-recognizer/train.csv\")\ntrain_df.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data overview","metadata":{}},{"cell_type":"markdown","source":"### Dataset contains images of digits from 0 to 9. Every digit is handwritten. We can use this dataset to develop a digit recogniser and use it for future research.\n### In this dataset, the count of each class is near 4000. ","metadata":{}},{"cell_type":"code","source":"label_counts = train_df[\"label\"].value_counts()\n\nplt.figure(figsize=(10, 10))\nplt.xticks(label_counts.index)\nplt.bar(label_counts.index, label_counts.values)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### We must reshape the dataset to split rows to image matrix.","metadata":{}},{"cell_type":"code","source":"# Get all rows and all column without 'label' column.\nX = train_df.iloc[:, 1:].values\n\n# Reshaping\nX = X.reshape(-1, 28, 28)\n\n# Getting labels\nY = train_df.iloc[:, 0].values","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Now we can show a sample image.\n### Image has size 28x28 pixels and only 1 colour channel (grey scale).","metadata":{}},{"cell_type":"code","source":"fig, axs = plt.subplots(3, 3, figsize=(10, 10))\nfig.suptitle('Vertically stacked subplots')\naxs[0, 0].imshow(X[0], cmap='gray')\naxs[0, 0].set_title(\"Label: {}\".format(Y[0]))\naxs[0, 1].imshow(X[1], cmap='gray')\naxs[0, 1].set_title(\"Label: {}\".format(Y[1]))\naxs[0, 2].imshow(X[2], cmap='gray')\naxs[0, 2].set_title(\"Label: {}\".format(Y[2]))\n\naxs[1, 0].imshow(X[3], cmap='gray')\naxs[1, 0].set_title(\"Label: {}\".format(Y[3]))\naxs[1, 1].imshow(X[4], cmap='gray')\naxs[1, 1].set_title(\"Label: {}\".format(Y[4]))\naxs[1, 2].imshow(X[5], cmap='gray')\naxs[1, 2].set_title(\"Label: {}\".format(Y[5]))\n\naxs[2, 0].imshow(X[6], cmap='gray')\naxs[2, 0].set_title(\"Label: {}\".format(Y[6]))\naxs[2, 1].imshow(X[7], cmap='gray')\naxs[2, 1].set_title(\"Label: {}\".format(Y[7]))\naxs[2, 2].imshow(X[8], cmap='gray')\naxs[2, 2].set_title(\"Label: {}\".format(Y[8]))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Preparing dataset\n\n### Now we will prepare the train and validation dataset. We must also load validation data.","metadata":{}},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\n\nfrom torch.utils.data import Dataset, DataLoader\n\nimport torchvision.transforms as transforms","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class TrainDataset(Dataset):\n    \n    def __init__(self, X_data, Y_data):\n        self.x_data = X_data\n        self.y_data = Y_data\n        \n        self.transforms = transforms.Compose([\n            transforms.ToTensor()\n        ])\n    \n    def __len__(self):\n        return len(self.y_data)\n    \n    def __getitem__(self, idx):\n        x_data = self.x_data[idx]/255\n        y_data = self.y_data[idx]\n        \n        image = torch.tensor([x_data], dtype=torch.float32)\n        target = torch.tensor([y_data])\n        \n        output = {\n            \"image\": image,\n            \"target\": target\n        }\n        \n        return output","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Loading validation data\ntest_df = pd.read_csv(\"../input/digit-recognizer/test.csv\")\ntest_X = test_df.iloc[:].values.reshape(-1, 28, 28)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class TestDataset(Dataset):\n    \n    def __init__(self, X_data):\n        self.x_data = X_data\n        \n        self.transforms = transforms.Compose([\n            transforms.ToTensor()\n        ])\n    \n    def __len__(self):\n        return len(self.x_data)\n    \n    def __getitem__(self, idx):\n        x_data = self.x_data[idx]/255\n        \n        image = torch.tensor([x_data], dtype=torch.float32)\n        \n        output = {\n            \"image\": image\n        }\n        \n        return output","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Creating a model\n\n### The time has come for prepare the model.","metadata":{}},{"cell_type":"code","source":"class MNISTModel(nn.Module):\n    \n    def __init__(self):\n        super(MNISTModel, self).__init__()\n        \n        self.conv1 = nn.Conv2d(in_channels=1, out_channels=64, kernel_size=(3, 3))\n        self.conv2 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=(3, 3))\n        \n        self.max_pool = nn.MaxPool2d(kernel_size=(2, 2))\n        \n        \n        self.fc1 = nn.Linear(in_features=self.count_neurons((1, 28, 28)), out_features=512)\n        self.fc2 = nn.Linear(in_features=512, out_features=10)\n        \n        \n        self.optimizer = optim.Adam(self.parameters(), lr=0.001)\n        self.criterion = nn.CrossEntropyLoss()\n    \n    def count_neurons(self, img_dim):\n        img = torch.rand(1, *img_dim)\n        \n        img = F.relu(self.conv1(img))\n        img = self.max_pool(img)\n        img = F.relu(self.conv2(img))\n        img = self.max_pool(img)\n        \n        return img.shape[1] * img.shape[2] * img.shape[3]\n        \n    def forward(self, img):\n        img = F.relu(self.conv1(img))\n        img = self.max_pool(img)\n        img = F.relu(self.conv2(img))\n        img = self.max_pool(img)\n        \n        img = torch.flatten(img, start_dim=1)\n        \n        img = F.relu(self.fc1(img))\n        img = self.fc2(img)\n        \n        return torch.softmax(img, dim=1)\n    \nmnistModel = MNISTModel()\ndevice = \"cuda:0\" if torch.cuda.is_available() else \"cpu\"\nmnistModel.to(device)","metadata":{"_kg_hide-output":false,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training model\n\n### Now we must train our model on the training data.","metadata":{}},{"cell_type":"code","source":"train_dataset = TrainDataset(X, Y)\ntrain_dataloader = DataLoader(train_dataset, 128)\n\nEPOCHS = 40\n\nfor epoch in range(EPOCHS):\n    running_loss = 0.0\n    \n    for i, data in enumerate(train_dataloader):\n    \n        images, targets = data[\"image\"].to(device), data[\"target\"].to(device).squeeze(1)\n    \n        output = mnistModel(images)\n        loss = mnistModel.criterion(output, targets)\n    \n        mnistModel.optimizer.zero_grad()\n        loss.backward()\n        mnistModel.optimizer.step()\n    \n        running_loss += loss.item()\n        \n        if i % 2000 == 1999:\n            print(\"[{epoch}  {iteration}] Loss: {loss}\".format(epoch=epoch+1, iteration=i+1, loss=running_loss/2000))\n            running_loss = 0.0\n        ","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Creating a submission","metadata":{}},{"cell_type":"code","source":"test_dataset = TestDataset(test_X)\ntest_dataloader = DataLoader(test_dataset, 1)\n\nids = []\npredicted = []\n\nfor i, data in enumerate(test_dataloader):\n    image = data[\"image\"].to(device)\n\n    outputs = mnistModel(image)\n    predicted.append(int(outputs.detach().cpu().numpy().argmax(axis=1)))\n    ids.append(i+1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"output_dataframe = pd.DataFrame({\n    \"ImageId\": ids,\n    \"Label\": predicted\n})","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Saving the submission and the model","metadata":{}},{"cell_type":"code","source":"output_dataframe.to_csv(\"./submission.csv\", sep=\",\", index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"torch.save(mnistModel.state_dict(), \"./mnist-model.pth\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Summary\n\n### This model has 98.77% of accuracy on the submission test. In the output folder is the saved model.","metadata":{}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}