{"cells":[{"metadata":{},"cell_type":"markdown","source":"## Contents\n\n1. [Background](#Background)\n1. [Goal](#GoalB)\n1. [Setup](#Setup)\n1. [Data](#Data)\n1. [Train](#Train)\n1. [Host](#Host)\n\n---\n\n## Background\n**Model**: Ever heard about *Generative Adversarial Model* (GAN) ? How about *DeepFakes*? Many generative models nowadays use typical GAN architecture to model data distribution, then reconstruct and generate a completely new data.\n\nWe have seen many application of GAN such as face generation and face morphing. The underlying model behind GAN architecture that enables its generative capability is called *Variational Autoencoder* (VAE). Today we are going to take a look on how to build a simple Conditional VAE model.\n\n**Dataset**: Kannada is a widely used dataset for handwritten digit classification. It consists of 70,000 labeled 28x28 pixel grayscale images of hand-written digits. The dataset is split into 60,000 training images and 10,000 test images. There are 10 classes (one for each of the 10 digits). TODO Kannada!!!!\n\n\n## Goal\nThis tutorial will show you how to build and deploy on Sagemaker using Pytorch. The dataset we are using is the MNIST dataset. To get the feel, we will first see how a simple neural network can be used to generate a new handwritten digit image without human intervension. Then we will train and deploy our Pytorch model in Sagemaker environment.\n\nFor more information about the PyTorch in SageMaker, please visit [sagemaker-pytorch-containers](https://github.com/aws/sagemaker-pytorch-containers) and [sagemaker-python-sdk](https://github.com/aws/sagemaker-python-sdk) github repositories.\n"},{"metadata":{},"cell_type":"markdown","source":"---\n\n## Setup \n_(Duration: 5 min)_\n\n_This notebook was created and tested on an ml.m4.xlarge notebook instance._\n\nLet's start by creating a SageMaker session and specifying:\n\n- The S3 bucket and prefix that you want to use for training and model data.  This should be within the same region as the Notebook Instance, training, and hosting.\n- The IAM role arn used to give training and hosting access to your data. See the documentation for how to create these.  Note, if more than one role is required for notebook instances, training, and/or hosting, please replace the `sagemaker.get_execution_role()` with a the appropriate full IAM role arn string(s).\n\n---\n"},{"metadata":{},"cell_type":"markdown","source":"### Packages\n\nWe will also setup our project by specifying libraries and modules that we need"},{"metadata":{"trusted":false},"cell_type":"code","source":"%%bash\npip3 install --quiet torchsummary","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"import os, sys\nimport math\nimport json, logging, argparse\n\nimport numpy as np\nimport pandas as pd\n\nimport torch\nimport torch.nn as nn\nimport torch.distributed as dist\nimport torch.optim as optim\nimport torch.nn.functional as F\n\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.utils.data.distributed import DistributedSampler\n\nfrom torchvision import datasets, transforms\nfrom torchsummary import summary\n\n# visualisation\nimport matplotlib.pyplot as plt\n\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Meet and Greet Data: Kaggle Kannada MNIST\n_(Duration: 20 min)_\n\n\nDownloading may take a few moments, and you should see your progress as the data is loading. You may also choose to change the `batch_size` if you want to load more data at a time."},{"metadata":{"trusted":false},"cell_type":"code","source":"data_dir = 'data/Kannada'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Downloading Kaggle Dataset"},{"metadata":{"trusted":false},"cell_type":"code","source":"%%bash\n# Install Kaggle CLI using Python Pip\npip3 install --quiet kaggle\nmkdir -p ~/.kaggle\n\n# Copy API key file to where Kaggle expects it\n# Make sure to upload kaggle key file next to this notebook\ncp kaggle.json ~/.kaggle/kaggle.json && chmod 600 ~/.kaggle/kaggle.json","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"%%bash\n\n# Download Kannada MNIST from Kaggle\nkaggle competitions download -c Kannada-MNIST\n\n# Create our data directory\nmkdir -p data/Kannada/raw\nmkdir -p data/Kannada/processed\n\n# Unzip to data/Kannada directory\nunzip Kannada-MNIST.zip -d data/Kannada/raw","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"for dirname, _, filenames in os.walk(data_dir):\n    for filename in filenames:\n        print('data at: ' + os.path.join(dirname, filename))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Loading Dataset with Pandas"},{"metadata":{"trusted":false},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\n# # Load Data\ntrain = pd.read_csv(os.path.join(data_dir, 'raw/train.csv'))\ntest = pd.read_csv(os.path.join(data_dir, 'raw/Dig-MNIST.csv'))\nsubmission_set = pd.read_csv(os.path.join(data_dir, 'raw/test.csv')).iloc[:,1:]\n\n# # Seperate train data and labels\ntrain_data = train.drop('label',axis=1)\ntrain_targets = train['label']\n\n# # Seperate test data and labels\ntest_images=test.drop('label',axis=1)\ntest_labels=test['label']","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"# Train Test Split for validation\ntrain_images, val_images, train_labels, val_labels = train_test_split(train_data, \n                                                                     train_targets, \n                                                                     test_size=0.15)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"# Reset Index\ntrain_images.reset_index(drop=True, inplace=True)\ntrain_labels.reset_index(drop=True, inplace=True)\n\nval_images.reset_index(drop=True, inplace=True)\nval_labels.reset_index(drop=True, inplace=True)\n\ntest_images.reset_index(drop=True, inplace=True)\ntest_labels.reset_index(drop=True, inplace=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### The shape of Kannada dataset\n\nI'm in lop with the shape op u"},{"metadata":{"trusted":false},"cell_type":"code","source":"train_images.iloc[20000:20005, 200:320]","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"print(\"Train Set: \\n\" + '-'*20)\nprint(train_images.shape)\nprint(train_labels.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"val_images.iloc[8000:8005, 200:320]","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"print(\"\\nValidation Set: \\n\"  + '-'*20)\nprint(val_images.shape)\nprint(val_labels.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"test_images.iloc[5000:5005, 200:320]","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"print(\"\\nTest Set: \\n\"  + '-'*20)\nprint(test_images.shape)\nprint(test_labels.shape)\n\nprint(\"\\nSubmission: \")\nprint(submission_set.shape)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Visualising Dataset Distribution"},{"metadata":{},"cell_type":"markdown","source":"Let's look at the distribution of Kannada dataset across different classes."},{"metadata":{"trusted":false},"cell_type":"code","source":"train_dist = train_labels.value_counts(normalize = True)\ntest_dist = test_labels.value_counts(normalize = True)\nsubmission_dist = train_labels.value_counts(normalize = True)\n\n# display table for visualising dataset distribution\npd.DataFrame({\n    'Trainset Distribution': train_dist,\n    'Testset Distribution': test_dist,\n    'Submissionset Distribution': submission_dist\n})","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Pytorch Dataset Object & Data Loader"},{"metadata":{},"cell_type":"markdown","source":"### Define Pytorch Dataset Object"},{"metadata":{"trusted":false},"cell_type":"code","source":"class KannadaDataSet(Dataset):\n    def __init__(self, images, labels, transforms=None):\n        self.X = images\n        self.y = labels\n        self.transforms = transforms\n         \n    def __len__(self):\n        return (len(self.X))\n    \n    def __getitem__(self, i):\n        data = self.X.iloc[i,:]\n        data = np.array(data).astype(np.uint8).reshape(IMGSIZE,IMGSIZE,1)\n        \n        if self.transforms:\n            data = self.transforms(data)\n            \n        if self.y is not None:\n            # for train set, val set, and test set\n            return (data, self.y[i])\n        else:\n            # for kaggle submission\n            # since submission set will not have labels\n            return data","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Define Image Transformation Pipeline"},{"metadata":{"trusted":false},"cell_type":"code","source":"IMGSIZE = 28\n\n# Transformations for the train\ntrain_trans = transforms.Compose(([\n    transforms.ToPILImage(),\n    transforms.RandomCrop(IMGSIZE),\n    transforms.RandomAffine(degrees=5, translate=(0.1, 0.1)),\n    transforms.ToTensor(), # automatically divide pixels by 255\n]))\n\n# Transformations for the validation & test sets\nval_trans = transforms.Compose(([\n    transforms.ToPILImage(),\n    transforms.ToTensor(), # automatically divide pixels by 255\n]))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Define Pytorch Data Loader"},{"metadata":{"trusted":false},"cell_type":"code","source":"batch_size = 64\n\n# Initialise dataset object for each set\ntrain_data = KannadaDataSet(train_images, train_labels, train_trans)\nval_data   = KannadaDataSet(val_images, val_labels, val_trans)\ntest_data  = KannadaDataSet(test_images, test_labels, val_trans)\nsubmission_data = KannadaDataSet(submission_set, None, val_trans)\n\n# Define Dataloader for each set\ntrain_loader = DataLoader(train_data,\n                          batch_size=batch_size,\n                          shuffle=True)\n\nval_loader = DataLoader(val_data, \n                        batch_size=batch_size, # batch_size=1000\n                        shuffle=False)\n\ntest_loader = DataLoader(test_data,\n                         batch_size=batch_size, # batch_size=1000\n                         shuffle=False)\n\n# for kaggle submission\nsubmission_loader = DataLoader(submission_data,\n                               batch_size=batch_size,\n                               shuffle=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Visualise Image in Training Batch\n\nThe first step in a classification task is to take a look at the data, make sure it is loaded in correctly, then make any initial observations about patterns in that data."},{"metadata":{"trusted":false},"cell_type":"code","source":"# obtain one batch of training images\ndataiter = iter(train_loader)\nimages, labels = dataiter.next()\nimages = images.numpy()\n\n# plot the images in the batch, along with the corresponding labels\nfig = plt.figure(figsize=(25, 6))\nfor idx in np.arange(16):\n    ax = fig.add_subplot(2, 16/2, idx+1, xticks=[], yticks=[])\n    ax.imshow(np.squeeze(images[idx]), cmap='gray')\n    ax.set_title('digit ' + str(labels[idx].item()), fontsize=16)  # .item() gets single value in scalar tensor","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Visualise Image in Detail\n\nNow let's see an image from MNIST dataset in detail. Notice how our image pixels only ranges from $(0, 1)$. This means that no further normalisation is required in the preprocessing step."},{"metadata":{"trusted":false},"cell_type":"code","source":"img = np.squeeze(images[1])\n\nfig = plt.figure(figsize = (12,12)) \nax = fig.add_subplot(111)\nax.imshow(img, cmap='gray')\nwidth, height = img.shape\nthresh = img.max()/2.5\n\nfor x in range(width):\n    for y in range(height):\n        val = round(img[x][y],2) if img[x][y] !=0 else 0\n        ax.annotate(str(val), xy=(y,x),\n                    horizontalalignment='center',\n                    verticalalignment='center',\n                    color='white' if img[x][y]<thresh else 'black')\n\nax.set_title('Kannada Digit in detail: label %d' % labels[1].item());","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"# Save data to local folder first\ntrain_images.to_csv(os.path.join(data_dir, 'processed/train.csv'), index=False, header=False)\ntrain_labels.to_csv(os.path.join(data_dir, 'processed/train_labels.csv'), index=False, header=False)\n\nval_images.to_csv(os.path.join(data_dir, 'processed/validation.csv'), index=False, header=False)\nval_labels.to_csv(os.path.join(data_dir, 'processed/validation_labels.csv'), index=False, header=False)\n\ntest_images.to_csv(os.path.join(data_dir, 'processed/test.csv'), index=False, header=False)\ntest_labels.to_csv(os.path.join(data_dir, 'processed/test_labels.csv'), index=False, header=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Building and Training CNN Model\n_(Duration: 25 min)_\n\n"},{"metadata":{"trusted":false},"cell_type":"code","source":"class KannadaCNN(nn.Module):\n    \"\"\" Convolutional Neural Network\n    \"\"\"\n    def __init__(self, drop_p=0.4):\n        super().__init__()\n        \n        # First hidden layer\n        self.conv2d_0 = nn.Conv2d(1, 64, kernel_size=5, padding=2)\n        self.convbn_0 = nn.BatchNorm2d(num_features=64)\n\n        self.conv2d_1 = nn.Conv2d(64, 64, kernel_size=5, padding=2)\n        self.convbn_1 = nn.BatchNorm2d(num_features=64)\n\n        self.pool_1 = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)\n        self.drop_1 = nn.Dropout2d(p=drop_p)\n\n        # Second hidden layer\n        self.conv2d_2 = nn.Conv2d(64, 128, kernel_size=3, padding=1)\n        self.convbn_2 = nn.BatchNorm2d(num_features=128)\n\n        self.conv2d_3 = nn.Conv2d(128, 128, kernel_size=3, padding=1)\n        self.convbn_3 = nn.BatchNorm2d(num_features=128)\n\n        self.pool_2 = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)\n        self.drop_2 = nn.Dropout2d(p=drop_p)\n\n        # Third hidden layer\n        self.conv2d_4 = nn.Conv2d(128, 256, kernel_size=3, padding=1)\n        self.convbn_4 = nn.BatchNorm2d(num_features=256)\n        \n        self.conv2d_5 = nn.Conv2d(256, 256, kernel_size=3, padding=1)\n        self.convbn_5 = nn.BatchNorm2d(num_features=256)\n\n        self.pool_3 = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)\n        self.drop_3 = nn.Dropout(p=drop_p)\n\n        # Dense fully connected layer\n        self.dense_linear_1 = nn.Linear(256*3*3, 512)\n        self.drop_4 = nn.Dropout(p=drop_p)\n\n        self.dense_linear_2 = nn.Linear(512, 256)\n        self.drop_5 = nn.Dropout(p=drop_p)\n\n        self.dense_linear_3 = nn.Linear(256, 128)\n        self.out_layer = nn.Linear(128, 10)\n\n    def forward(self, x):\n        x = self.conv2d_0(x)\n        x = self.convbn_0(x)\n        x = F.leaky_relu(x)\n        \n        x = self.conv2d_1(x)\n        x = self.convbn_1(x)\n        x = F.leaky_relu(x)\n\n        x = self.pool_1(x)\n        x = self.drop_1(x)\n\n        x = self.conv2d_2(x)\n        x = self.convbn_2(x)\n        x = F.leaky_relu(x)\n\n        x = self.conv2d_3(x)\n        x = self.convbn_3(x)\n        x = F.leaky_relu(x)\n\n        x = self.pool_2(x)\n        x = self.drop_2(x)\n\n        x = self.conv2d_4(x)\n        x = self.convbn_4(x)\n        x = F.leaky_relu(x)\n        \n        x = self.conv2d_5(x)\n        x = self.convbn_5(x)\n        x = F.leaky_relu(x)\n        \n        x = self.pool_3(x)\n        x = self.drop_3(x)\n\n        x = x.view(-1, 256*3*3)\n        x = self.dense_linear_1(x)\n        x = F.relu(x)\n        x = self.drop_4(x)\n        \n        x = self.dense_linear_2(x)\n        x = F.relu(x)\n        x = self.drop_5(x)\n        \n        x = self.dense_linear_3(x)\n        x = F.relu(x)\n\n        out = self.out_layer(x)\n        return out","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Construct Neural Network from Pytorch Model Class\n\nNow that we have defined our CNN model class using `nn.Module` and added the all layers, let's make it come to life by constructing the object so that we can use it to make prediction."},{"metadata":{"trusted":false},"cell_type":"code","source":"# Constructing our CNN module\nmodel = KannadaCNN().to(device)\n# initialise network\nnet = KannadaCNN().to(device)\n\n# optimiser\noptimiser = optim.Adam(net.parameters(), lr=5e-4)\ncriterion = nn.CrossEntropyLoss()\n\n# display model summary\nsummary(model, input_size=(1,IMGSIZE,IMGSIZE))  # IMGSIZE = 28","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"def accuracy(output, target, topk=(1,)):\n    \"\"\"Computes the precision@k for the specified values of k\"\"\"\n    maxk = max(topk)\n    batch_size = target.size(0)\n\n    _, pred = output.topk(maxk, 1, True, True)\n    pred = pred.t()\n    correct = pred.eq(target.view(1, -1).expand_as(pred))\n\n    res = []\n    for k in topk:\n        correct_k = correct[:k].view(-1).float().sum(0)\n        res.append(correct_k.mul_(100.0 / batch_size))\n    return res","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"def train_helper(train_loader, model, optimizer, criterion,\n                  epoch, device='cpu', log_interval=25):\n    # set to training mode\n    model.train()\n\n    # training result to record\n    train_loss = 0.0\n    train_top1 = 0.0\n    train_top5 = 0.0\n\n    for batch_idx, (data, target) in enumerate(train_loader, start=1):\n        # convert tensor for current runtime device\n        data, target = data.to(device), target.to(device)\n\n        # reset optimiser gradient to zero\n        optimizer.zero_grad()\n\n        # feed forward\n        out = model(data)\n        \n        # calculate loss and optimise network params\n        loss = criterion(out, target)\n        loss.backward()\n        \n        # optimize weight to account for loss/gradient\n        optimizer.step()\n\n        # calculate training accuracy for top1 and top5\n        top1, top5 = accuracy(out, target, topk=(1,5))\n\n        # update result records\n        train_top1 += top1.item()\n        train_top5 += top5.item()\n        train_loss += loss.item()\n\n        # logging loss output to stdout\n        if batch_idx % log_interval == 0:\n            print('Train Epoch: {:03d} [{:05d}/{:05d} ({:2.0f}%)] | '\n                  'Top1 g Acc: {:4.1f} \\t| Top5 Acc: {:4.1f} \\t| Loss: {:.4f}'\n                  .format(epoch, batch_idx * len(data), len(train_loader.sampler),\n                      100 * batch_idx / len(train_loader),\n                      top1, top5, loss.item()))\n\n    # display training result\n    train_loss /= len(train_loader.dataset)\n    train_top1 /= len(train_loader) # average loss over mini-batches\n    train_top5 /= len(train_loader) # average loss over mini-batches\n\n    print('Training Summary Epoch: {:03d} | '\n          'Average Top1 Acc: {:.2f}  | Average Top5 Acc: {:.2f} | Loss: {:.4f}'\n          .format(epoch, train_top1, train_top5, train_loss))\n    \n    return train_loss, train_top1, train_top5 ","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"def test_helper(test_loader, model, criterion, \n                 epoch, device='cpu'):\n    # set to validation mode\n    model.eval()\n    \n    test_loss = 0.0  # record testing loss\n    test_top1 = 0.0\n    test_top5 = 0.0\n    for batch_idx, (data, target) in enumerate(test_loader, start=1):\n\n        # convert tensor for current runtime device\n        data, target = data.to(device), target.to(device)\n\n        # generate image x\n        out = model(data)\n\n        # calculate loss and optimise network params\n        loss = criterion(out, target)\n        \n        # calculate testing accuracy for top1 and top5\n        top1, top5 = accuracy(out, target, topk=(1,5))\n\n        # update test loss\n        test_top1 += top1.item()\n        test_top5 += top5.item()\n        test_loss += loss.item()\n\n    # display validation/testing result\n    test_loss /= len(test_loader.dataset)  # average loss over all images\n    test_top1 /= len(test_loader)\n    test_top5 /= len(test_loader)\n\n    print('Val/Test Summary Epoch: {:03d} | '\n          'Average Top1 Acc: {:.2f}  | Average Top5 Acc: {:.2f} | Loss: {:.4f}'\n          .format(epoch, test_top1, test_top5, test_loss))\n    \n    return test_loss, test_top1, test_top5","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"# ----------------------------\n# TRAINING SESSION\n# ----------------------------\n\ntrain_losses = []\nval_losses = []\n\ntrain_accuracies = []\nval_accuracies = []\n\nfor epoch in range(1, 5 + 1):\n    print('\\n' + '-' * 100)\n    # run session on training set\n    train_loss, train_acc, _  = train_helper(train_loader, net, optimiser, criterion,\n                                              epoch=epoch, device=device, log_interval=100)\n    train_losses.append(train_loss)\n    train_accuracies.append(train_acc)\n\n    # run session on validation set\n    val_loss, val_acc, _ = test_helper(val_loader, net, criterion, epoch, device=device)\n    val_losses.append(val_loss)\n    val_accuracies.append(val_acc)\n\n# finally, run session testing set\nprint('\\n' + 'Final Test Set Result:\\n'+ '*' * 80)\ntest_helper(test_loader, net, criterion, epoch, device=device);","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Visualise Training Session results"},{"metadata":{"trusted":false},"cell_type":"code","source":"fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(14, 5))\n\n# Plot Error training vs validation\naxes[0].plot(train_losses);\naxes[0].plot(val_losses);\n\naxes[0].set_ylabel('Error');\naxes[0].set_xlabel('Epochs');\n\naxes[0].set_ylim(0, 0.005);\naxes[0].legend(labels=['train error', 'validation error']);\n\n# Plot Accuracy training vs validation\naxes[1].plot(train_accuracies);\naxes[1].plot(val_accuracies);\n\naxes[1].set_ylabel('Accuracy (%)');\naxes[1].set_xlabel('Epochs');\n\naxes[1].set_ylim(80, 100);\naxes[1].legend(labels=['train acc', 'validation acc']);","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Writing Submission to Kaggle Competition"},{"metadata":{"trusted":false},"cell_type":"code","source":"# Time to get the network's predictions on the test set\n# Put the test set in a DataLoader\n\nnet.eval() # Safety first\npredictions = torch.LongTensor().to(device) # Tensor for all predictions\n\n# Go through the test set, saving the predictions in... 'predictions'\nfor images in submission_loader:\n    images = images.to(device)\n    preds = net(images)\n    predictions = torch.cat((predictions, preds.argmax(dim=1)), dim=0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"submission_pred_df = pd.DataFrame(predictions.cpu().detach().numpy())","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"submission_pred_df.to_csv(os.path.join(data_dir, 'kannada_sub_baseline.csv'), \n                          index=True, index_label='id', header=['label'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":false},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.7"}},"nbformat":4,"nbformat_minor":1}