{"cells":[{"metadata":{"trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"!mkdir -p /tmp/pip/cache/\n!cp ../input/steel-my-models/efficientnet_pytorch-0.5.1.xyz /tmp/pip/cache/efficientnet_pytorch-0.5.1.tar.gz","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"!pip install --no-index --find-links /tmp/pip/cache/ efficientnet-pytorch","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true},"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)\n\nimport time\n\nimport albumentations as albu\nfrom albumentations.pytorch import ToTensor\nimport PIL\nimport cv2 as cv\n\nimport matplotlib.pyplot as plt\nfrom matplotlib.font_manager import FontProperties\n\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision\nimport torchvision.transforms.functional as TF\nfrom torch import nn\nimport torch.nn.functional as F\nfrom torch.utils.data import SubsetRandomSampler\nfrom torch.optim import Adam,lr_scheduler\n\nfrom efficientnet_pytorch import EfficientNet\nfrom efficientnet_pytorch.utils import Conv2dStaticSamePadding, get_model_params\n\nfrom tqdm import tqdm_notebook, tqdm","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"![image](https://github.com/Lexie88rus/Bengali_AI_Competition/raw/master/assets/samples.png)"},{"metadata":{},"cell_type":"markdown","source":"# Bengali.AI EfficientNet Starter Notebook"},{"metadata":{},"cell_type":"markdown","source":"Bengali.AI is a wonderful competition, especially for the beginners in deep learning. I created this simple pytorch notebook to demonstrate some code for Bengali hadwrittem symbols classification.\n\nI tried to add some data visualization tips throughout this notebook, which help to check the inputs and outputs of the model.\n\nUnfortunately, this code can't be used to make submissions for this competition on Kaggle, but it still can serve as a starting point to develop your own models for Bengali handwriting classification."},{"metadata":{},"cell_type":"markdown","source":"## Load Data"},{"metadata":{},"cell_type":"markdown","source":"Specify the path to data and load the csv files:"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"# setup the input data folder\nDATA_PATH = '../input/bengaliai-cv19/'\n\n# load the dataframes with labels\ntrain_labels = pd.read_csv(DATA_PATH + 'train.csv')\ntest_labels = pd.read_csv(DATA_PATH + 'test.csv')\nclass_map = pd.read_csv(DATA_PATH + 'class_map.csv')\nsample_submission = pd.read_csv(DATA_PATH + 'sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Load the test and train sets:"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def load_images():\n    '''\n    Helper function to load all train and test images\n    '''\n    train_list = []\n    for i in range(0,4):\n        train_list.append(pd.read_parquet(DATA_PATH + 'train_image_data_{}.parquet'.format(i)))\n    train = pd.concat(train_list, ignore_index=True)\n    \n    test_list = []\n    for i in range(0,4):\n        test_list.append(pd.read_parquet(DATA_PATH + 'test_image_data_{}.parquet'.format(i)))\n    test = pd.concat(test_list, ignore_index=True)\n    \n    return train, test","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train, test = load_images()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Image Preprocessing and Data Augmentation"},{"metadata":{},"cell_type":"markdown","source":"Preprocessing and data augmentation are exteremely important for the training of deep learning models. I use the adaptive thresholding to binarize the input images and a simple data augmentation pipeline consisting of random crop-resize and slight rotation of the input images."},{"metadata":{"trusted":true},"cell_type":"code","source":"# setup image hight and width\nHEIGHT = 137\nWIDTH = 236\n\ndef threshold_image(img):\n    '''\n    Helper function for thresholding the images\n    '''\n    gray = PIL.Image.fromarray(np.uint8(img), 'L')\n    ret,th = cv.threshold(np.array(gray),0,255,cv.THRESH_BINARY+cv.THRESH_OTSU)\n    return th\n\ndef train_transforms(p=.5):\n    '''\n    Function returns the training pipeline of augmentations\n    '''\n    return albu.Compose([\n        # compose the random cropping and random rotation\n        albu.RandomSizedCrop(min_max_height=(int(HEIGHT // 1.1), HEIGHT), height = HEIGHT, width = WIDTH, p=p),\n        albu.Rotate(limit=5, p=p),\n        albu.Resize(height = HEIGHT, width = WIDTH)\n    ], p=1.0)\n\ndef valid_transforms():\n    '''\n    Function returns the training pipeline of augmentations\n    '''\n    return albu.Compose([\n        # compose the random cropping and random rotation\n        albu.Resize(height = HEIGHT, width = WIDTH)\n    ], p=1.0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Define the Dataset"},{"metadata":{},"cell_type":"markdown","source":"The next step is to create a custom pytorch dataset, which will produce images and corresponding labels out of the traing dataset:"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"'''\nHelper functions to retrieve the images from the dataset in training and validation modes\n'''\n\ndef get_image(idx, df, labels):\n    '''\n    Helper function to get the image and label from the training set\n    '''\n    # get the image id by idx\n    image_id = df.iloc[idx].image_id\n    # get the image by id\n    img = df[df.image_id == image_id].values[:, 1:].reshape(HEIGHT, WIDTH).astype(float)\n    # get the labels\n    row = labels[labels.image_id == image_id]\n    \n    # return labels as tuple\n    labels = row['grapheme_root'].values[0], \\\n    row['vowel_diacritic'].values[0], \\\n    row['consonant_diacritic'].values[0]\n    \n    return img, labels\n\ndef get_validation(idx, df):\n    '''\n    Helper function to get the validation image and image_id from the test set\n    '''\n    # get the image id by idx\n    image_id = df.iloc[idx].image_id\n    # get the image by id\n    img = df[df.image_id == image_id].values[:, 1:].reshape(HEIGHT, WIDTH).astype(float)\n    return img, image_id","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class BengaliDataset(Dataset):\n    '''\n    Create a custom Bengali images dataset\n    '''\n    def __init__(self, df_images, transforms, df_labels = None, validation = False):\n        '''\n        Init function\n        INPUT:\n            df_images - dataframe with the images\n            transforms - data transforms\n            df_labels - datafrane containing the target labels\n            validation - flag indication if the dataset is for training or for validation\n        '''\n        self.df_images = df_images\n        self.df_labels = df_labels\n        self.transforms = transforms\n        self.validation = validation\n\n    def __len__(self):\n        return len(self.df_images)\n\n    def __getitem__(self, idx):\n        if not self.validation:\n            # get the image\n            img, label = get_image(idx, self.df_images, self.df_labels)\n            # threshold the image\n            img = threshold_image(img)\n            # transform the image\n            aug = self.transforms(image = img)\n            return TF.to_tensor(aug['image']), label\n        else:\n            # get the image\n            img, image_id = get_validation(idx, self.df_images)\n            # threshold the image\n            img = threshold_image(img)\n            # transform the image\n            aug = self.transforms(image = img)\n            # return transformed image and corresponding image_id (instead of label) to create submission\n            return TF.to_tensor(aug['image']), image_id","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Now let's check that everything is correct. Let's try to retrieve couple of images from the dataset:"},{"metadata":{"trusted":true},"cell_type":"code","source":"# initialize train dataset\ntrain_dataset = BengaliDataset(train, train_transforms(), train_labels)\n# create a sample trainloader\nsample_trainloader = DataLoader(train_dataset, batch_size=5, shuffle=True, num_workers=0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# plot sample train data\nfor img, labels in sample_trainloader:\n    \n    fig, axs = plt.subplots(1, img.shape[0], figsize=(15,10))\n    for i in range(0, img.shape[0]):\n        axs[i].imshow(TF.to_pil_image(img[i].reshape(HEIGHT, WIDTH)), cmap='gray')\n        \n        prop = FontProperties()\n        prop.set_file('../input/bengaliaiutils/kalpurush.ttf')\n        grapheme_root = class_map[(class_map.component_type == 'grapheme_root') \\\n                                  & (class_map.label == int(labels[0][i]))].component.values[0]\n        \n        vowel_diacritic = class_map[(class_map.component_type == 'vowel_diacritic') \\\n                                  & (class_map.label == int(labels[1][i]))].component.values[0]\n        \n        consonant_diacritic = class_map[(class_map.component_type == 'consonant_diacritic') \\\n                                  & (class_map.label == int(labels[2][i]))].component.values[0]\n        \n        axs[i].set_title('{}, {}, {}'.format(grapheme_root, vowel_diacritic, consonant_diacritic), \n                         fontproperties=prop, fontsize=20)\n    break;","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Define the Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"# initialize the efficent net model\nefficientnet_b0 = EfficientNet.from_name('efficientnet-b0')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"'''\nCustom model for the Bengali images\nbackbone model may be replaced with any other archirtecture :)\n'''\n\nclass BengaliModel(nn.Module):\n    '''\n    Custom model for Bengali classification\n    '''\n    def __init__(self, backbone_model):\n        super(BengaliModel, self).__init__()\n        # the initial layer to convolve into 3 channels\n        self.conv = nn.Conv2d(in_channels=1, out_channels=3, kernel_size=3)\n        # run through the efficientnet\n        self.backbone_model = backbone_model\n        # linear layers to produce the output labels\n        self.fc1 = nn.Linear(in_features=1000, out_features=168) # grapheme_root\n        self.fc2 = nn.Linear(in_features=1000, out_features=11) # vowel_diacritic\n        self.fc3 = nn.Linear(in_features=1000, out_features=7) # consonant_diacritic\n        \n    def forward(self, x):\n        # pass through the backbone model\n        y = self.conv(x)\n        y = self.backbone_model(y)\n        \n        # multi-output\n        grapheme_root = self.fc1(y)\n        vowel_diacritic = self.fc2(y)\n        consonant_diacritic = self.fc3(y)\n        \n        return grapheme_root, vowel_diacritic, consonant_diacritic","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# initialize the final model\nmodel = BengaliModel(efficientnet_b0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Train the Model"},{"metadata":{},"cell_type":"markdown","source":"Now we are all set for the modelling:\n\nFirst, let's start with defining the hyperparameters. In this notebook I won't be actually training the model, that is why the number of epochs is 0. I trained the model on my own machine and will just load the weights here."},{"metadata":{"trusted":true},"cell_type":"code","source":"test_split = 0.2\nbatch_size = 128\nepochs = 0 # change this value to actually train the model\nlearning_rate = 0.001\nnum_workers = 0","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Setup the dataset and samplers:"},{"metadata":{"trusted":true},"cell_type":"code","source":"dataset_size = len(train_dataset)\n\n# split the dataset into test and train\nindices = list(range(dataset_size))\nsplit = int(np.floor(test_split * dataset_size))\nnp.random.seed(42)\nnp.random.shuffle(indices)\ntrain_indices, test_indices = indices[split:], indices[:split]\n\ntrain_sampler = SubsetRandomSampler(train_indices)\ntest_sampler = SubsetRandomSampler(test_indices)\n\ntrainloader = DataLoader(train_dataset, batch_size=batch_size, sampler=train_sampler, num_workers=num_workers)\ntestloader = DataLoader(train_dataset, batch_size=32, sampler=test_sampler, num_workers=num_workers)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Optimizer and loss function:"},{"metadata":{"trusted":true},"cell_type":"code","source":"# set loss function\ncriterion = nn.CrossEntropyLoss()\n\n# set optimizer, only train the classifier parameters, feature parameters are frozen\noptimizer = Adam(model.parameters(), lr=learning_rate)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Define a training device:"},{"metadata":{"trusted":true},"cell_type":"code","source":"# setup training device\ndevice = torch.device('cuda:0' if torch.cuda.is_available() else \"cpu\")\ndevice","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Setup the logging. I will write the log into pandas DataFrame."},{"metadata":{"trusted":true},"cell_type":"code","source":"train_stats = pd.DataFrame(columns = ['Epoch', 'Time per epoch', 'Avg time per step', 'Train loss', 'Train accuracy'\n                                      ,'Test loss', 'Test accuracy'])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Now we are ready to train! "},{"metadata":{"trusted":true},"cell_type":"code","source":"# move the model to the training device\nmodel = model.to(device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# I'm just loading the weights instead of training\nstate = torch.load('../input/bengaliaiutils/efficientnet_b0_10.pth', map_location=lambda storage, loc: storage)\nmodel.load_state_dict(state[\"state_dict\"])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Training loop:"},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_accuracy(ps, labels):\n    '''\n    Helper function to calculate the accuracy given the labels and the output of the model\n    '''\n    ps = torch.exp(ps)\n    top_p, top_class = ps.topk(1, dim=1)\n    equals = top_class == labels.view(*top_class.shape)\n    accuracy = torch.mean(equals.type(torch.FloatTensor)).item()\n    return accuracy\n\nsteps = 0\nrunning_loss = 0\nfor epoch in range(epochs):\n    \n    since = time.time()\n    \n    train_accuracy = 0\n    top3_train_accuracy = 0 \n    for inputs, labels in tqdm(trainloader):\n        steps += 1\n        # move input and label tensors to the default device\n        inputs, labels = inputs.to(device), [label.to(device) for label in labels]\n        \n        optimizer.zero_grad()\n        \n        # forward pass\n        grapheme_root, vowel_diacritic, consonant_diacritic  = model.forward(inputs)\n        \n        # calculate the loss\n        loss = criterion(grapheme_root, labels[0]) + criterion(vowel_diacritic, labels[1]) + \\\n        criterion(consonant_diacritic, labels[2])\n        \n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item()\n        \n        # get the average accuracy\n        train_accuracy += (get_accuracy(grapheme_root, labels[0]) + get_accuracy(vowel_diacritic, labels[1]) + \\\n                           get_accuracy(consonant_diacritic, labels[2])) / 3.0\n\n        \n    time_elapsed = time.time() - since\n    \n    test_loss = 0\n    test_accuracy = 0\n    model.eval()\n    # run validation on the test set\n    with torch.no_grad():\n        for inputs, labels in testloader:\n            inputs, labels = inputs.to(device), [label.to(device) for label in labels]\n            \n            grapheme_root, vowel_diacritic, consonant_diacritic  = model.forward(inputs)\n            batch_loss = criterion(grapheme_root, labels[0]) + criterion(vowel_diacritic, labels[1]) + criterion(consonant_diacritic, labels[2])\n        \n            test_loss += batch_loss.item()\n\n            # Calculate test top-1 accuracy\n            test_accuracy += (get_accuracy(grapheme_root, labels[0]) + get_accuracy(vowel_diacritic, labels[1]) + \\\n                           get_accuracy(consonant_diacritic, labels[2])) / 3.0\n    \n    # print out the training stats\n    print(f\"Epoch {epoch+1}/{epochs}.. \"\n          f\"Time per epoch: {time_elapsed:.4f}.. \"\n          f\"Average time per step: {time_elapsed/len(trainloader):.4f}.. \"\n          f\"Train loss: {running_loss/len(trainloader):.4f}.. \"\n          f\"Train accuracy: {train_accuracy/len(trainloader):.4f}.. \"\n          f\"Test loss: {test_loss/len(testloader):.4f}.. \"\n          f\"Test accuracy: {test_accuracy/len(testloader):.4f}.. \")\n\n    # write to the training log\n    train_stats = train_stats.append({'Epoch': epoch, 'Time per epoch':time_elapsed, 'Avg time per step': time_elapsed/len(trainloader), 'Train loss' : running_loss/len(trainloader),\n                                      'Train accuracy': train_accuracy/len(trainloader),'Test loss' : test_loss/len(testloader),\n                                      'Test accuracy': test_accuracy/len(testloader)}, ignore_index=True)\n\n    running_loss = 0\n    steps = 0\n    model.train()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Analyze Results"},{"metadata":{},"cell_type":"markdown","source":"Now we can look at the training results:"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# I load my training stats from the local machine :)\ntrain_stats = pd.read_csv('../input/bengaliaiutils/efficientnet_b0_train_stats_10.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# plot the loss\nplt.plot(train_stats['Train loss'], label='train')\nplt.plot(train_stats['Test loss'], label='test')\nplt.title('Loss over epoch')\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# plot the accuracy\nplt.plot(train_stats['Train accuracy'], label='train')\nplt.plot(train_stats['Test accuracy'], label='test')\nplt.title('Accuracy over epoch')\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's also visualize some sample predictions from the train set:"},{"metadata":{"trusted":true},"cell_type":"code","source":"# plot sample train data\nmodel.eval()\nfor img, labels in testloader:\n    img, labels = img.to(device), [label.to(device) for label in labels]\n    grapheme_root, vowel_diacritic, consonant_diacritic  = model.forward(img)\n    \n    img = img.cpu()\n    grapheme_root = grapheme_root.cpu()\n    vowel_diacritic = vowel_diacritic.cpu()\n    consonant_diacritic = consonant_diacritic.cpu()\n    \n    # visualize the inputs\n    fig, axs = plt.subplots(4, 1, figsize=(10,15))\n    for i in range(0, img.shape[0]):\n        axs[0].imshow(TF.to_pil_image(img[i].reshape(HEIGHT, WIDTH)), cmap='gray')\n        \n        prop = FontProperties()\n        prop.set_file('../input/bengaliaiutils/kalpurush.ttf')\n        grapheme_root_str = class_map[(class_map.component_type == 'grapheme_root') \\\n                                  & (class_map.label == int(labels[0][i]))].component.values[0]\n        \n        vowel_diacritic_str = class_map[(class_map.component_type == 'vowel_diacritic') \\\n                                  & (class_map.label == int(labels[1][i]))].component.values[0]\n        \n        consonant_diacritic_str = class_map[(class_map.component_type == 'consonant_diacritic') \\\n                                  & (class_map.label == int(labels[2][i]))].component.values[0]\n        \n        axs[0].set_title('{}, {}, {}'.format(grapheme_root_str, vowel_diacritic_str, consonant_diacritic_str), \n                         fontproperties=prop, fontsize=20)\n        \n        # analyze grapheme root prediction\n        ps_root = F.softmax(grapheme_root[i])\n        top10_p, top10_class = ps_root.topk(10, dim=0)\n        \n        top10_p = top10_p.detach().numpy()\n        top10_class = top10_class.detach().numpy()\n        \n        axs[1].bar(range(len(top10_p)), top10_p)\n        axs[1].set_xticks(range(len(top10_p)))\n        axs[1].set_xticklabels(top10_class)\n        axs[1].set_title('grapheme_root: {}'.format(labels[0][i]))\n        \n        # analyze vowel prediction\n        ps_vowel = F.softmax(vowel_diacritic[i])\n        top11_p, top11_class = ps_vowel.topk(11, dim=0)\n        \n        top11_p = top11_p.detach().numpy()\n        top11_class = top11_class.detach().numpy()\n        \n        axs[2].bar(range(len(top11_p)), top11_p)\n        axs[2].set_xticks(range(len(top11_p)))\n        axs[2].set_xticklabels(top11_class)\n        axs[2].set_title('vowel_diacritic: {}'.format(labels[1][i]))\n        \n        # analyze consonant prediction\n        ps_cons = F.softmax(consonant_diacritic[i])\n        top7_p, top7_class = ps_cons.topk(7, dim=0)\n        \n        top7_p = top7_p.detach().numpy()\n        top7_class = top7_class.detach().numpy()\n        \n        axs[3].bar(range(len(top7_p)), top7_p)\n        axs[3].set_xticks(range(len(top7_p)))\n        axs[3].set_xticklabels(top7_class)\n        axs[3].set_title('consonant_diacritic: {}'.format(labels[2][i]))\n        \n        plt.show()\n        break;\n        \n    break;","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Create Submission"},{"metadata":{},"cell_type":"markdown","source":"The last step is to create a submission:"},{"metadata":{"trusted":true},"cell_type":"code","source":"# initialize train dataset\ntest_dataset = BengaliDataset(test, valid_transforms(), test_labels, validation = True)\nsample_validloader = DataLoader(test_dataset, batch_size=5, shuffle=True, num_workers=0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Take a look at the images from validation set:"},{"metadata":{"trusted":true},"cell_type":"code","source":"# plot sample train data\nfor img, image_ids in sample_validloader:\n    fig, axs = plt.subplots(1, img.shape[0], figsize=(15,10))\n    for i in range(0, img.shape[0]):\n        axs[i].imshow(TF.to_pil_image(img[i].reshape(HEIGHT, WIDTH)), cmap='gray')\n        axs[i].set_title(image_ids[i])\n    break;","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Create the submission:"},{"metadata":{"trusted":true},"cell_type":"code","source":"validloader = DataLoader(test_dataset, batch_size=1, shuffle=False, num_workers=0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_predicted_label(ps):\n    '''\n    Helper function to get the predicted label given the probabilities from the model output\n    '''\n    ps = F.softmax(ps)[0]\n    top_p, top_class = ps.topk(1, dim=0)\n        \n    top_p = top_p.detach().numpy()\n    top_class = top_class.detach().numpy()\n    \n    return top_class[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# create the submission\n# initialize the dataframe\nsubmission = pd.DataFrame(columns=['row_id', 'target'])\n\nfor imgs, image_ids in validloader:\n    img = imgs[0]\n    image_id = image_ids[0]\n    \n    imgs = imgs.to(device)\n    \n    # forward pass to get the output\n    grapheme_root, vowel_diacritic, consonant_diacritic  = model.forward(imgs)\n    \n    imgs = imgs.cpu()\n    grapheme_root = grapheme_root.cpu()\n    vowel_diacritic = vowel_diacritic.cpu()\n    consonant_diacritic = consonant_diacritic.cpu()\n    \n    # get the predicted labels\n    grapheme_root_label = get_predicted_label(grapheme_root)\n    vowel_diacritic_label = get_predicted_label(vowel_diacritic)\n    consonant_diacritic_label = get_predicted_label(consonant_diacritic)\n    \n    # add the results to the dataframe\n    submission = submission.append({'row_id':str(image_id)+'_grapheme_root', 'target':grapheme_root_label}, \n                                   ignore_index=True)\n    submission = submission.append({'row_id':str(image_id)+'_vowel_diacritic', 'target':vowel_diacritic_label}, \n                                   ignore_index=True)\n    submission = submission.append({'row_id':str(image_id)+'_consonant_diacritic', 'target':consonant_diacritic_label}, \n                                   ignore_index=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Look at the submission file:"},{"metadata":{"trusted":true},"cell_type":"code","source":"submission.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission.to_csv('submission.csv')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Conclusion\n\nIn this notebook I created and trained a sample model. This code can't be used for the actual predcitions for the competition. It requires a lot of optiomization, but you can use it as a sample for learning purposes.\n\n## References\n1. [EfficientNet paper](https://arxiv.org/pdf/1905.11946.pdf)\n2. [efficientnet-pytorch pacckage](https://pypi.org/project/efficientnet-pytorch/)\n3. [My EDA notebook for Bengali.AI](https://www.kaggle.com/aleksandradeis/bengali-ai-eda)"}],"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":4,"nbformat_minor":1}