{"cells":[{"metadata":{},"cell_type":"markdown","source":"![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1095143%2Fa9a48686e3f385d9456b59bf2035594c%2Fdesc.png?generation=1576531903599785&alt=media)\n"},{"metadata":{},"cell_type":"markdown","source":"Optical character recognition is particularly challenging for Bengali. While Bengali has 49 letters (to be more specific 11 vowels and 38 consonants) in its alphabet, there are also 18 potential diacritics, or accents. This means that there are many more graphemes, or the smallest units in a written language. The added complexity results in ~13,000 different grapheme variations (compared to English’s 250 graphemic units).\n\nBangladesh-based non-profit Bengali.AI is focused on helping to solve this problem. They build and release crowdsourced, metadata-rich datasets and open source them through research competitions."},{"metadata":{},"cell_type":"markdown","source":"**Objective:**\n\nFor this competition, you’re given the image of a handwritten Bengali grapheme and are challenged to separately classify three constituent elements in the image: grapheme root, vowel diacritics, and consonant diacritics."},{"metadata":{},"cell_type":"markdown","source":"**About data:**\n\nThere are roughly 10,000 possible graphemes, of which roughly 1,000 are represented in the training set. The test set includes some graphemes that do not exist in train but has no new grapheme components. Focusing the problem on the grapheme components rather than on recognizing whole graphemes should make it possible to assemble a Bengali OCR system without handwriting samples for all 10,000 graphemes."},{"metadata":{},"cell_type":"markdown","source":"> Change status:\n* Update_V2: Image augmentation,resizing and eval metric added\n* Update_V1: Densenet from feather data with 50 epochs"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","collapsed":true,"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":false},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Kindly upvote the kernel if you like it! Thanks</font><br>"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"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 in \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 \"../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\nimport glob\nimport dask.dataframe as dd\nimport gc\nimport matplotlib.pyplot as plt\nimport plotly.express as px\nfrom IPython.core.display import display, HTML\nimport cv2\nimport torch.nn as nn\nfrom torch.utils.data import Dataset,DataLoader\nimport torch,torchvision\nfrom torchvision import transforms,models\nfrom torchvision.models import DenseNet\nfrom tqdm import tqdm_notebook as tqdm\nfrom collections import OrderedDict\nimport seaborn as sns\nimport sklearn.metrics\nimport warnings\n\n## This library is for augmentations .\nfrom albumentations import (\n    PadIfNeeded,\n    HorizontalFlip,\n    VerticalFlip,    \n    CenterCrop,    \n    Crop,\n    Compose,\n    Transpose,\n    RandomRotate90,\n    ElasticTransform,\n    GridDistortion, \n    OpticalDistortion,\n    RandomSizedCrop,\n    OneOf,\n    CLAHE,\n    RandomBrightnessContrast,    \n    RandomGamma,\n    ShiftScaleRotate    \n)\n\nwarnings.filterwarnings('ignore')\n%matplotlib inline\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Data glimpse</font><br>"},{"metadata":{"_cell_guid":"","_uuid":"","trusted":true},"cell_type":"code","source":"PATH='../input/bengaliai-cv19/'\nfeathers='../input/bengaliaicv19feather/'\n\nclass_map = pd.read_csv(PATH+\"class_map.csv\")\nsample_submission = pd.read_csv(PATH+\"sample_submission.csv\")\ntest = pd.read_csv(PATH+\"test.csv\")\ntrain = pd.read_csv(PATH+\"train.csv\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_parquet_files=glob.glob(PATH+'train_image_data*.parquet')\ntest_parquet_files=glob.glob(PATH+'test_image_data*.parquet')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"So, What are **parquet** files?\n\nApache Parquet is a free and open-source column-oriented data storage format of the Apache Hadoop ecosystem. It provides efficient data compression and encoding schemes with enhanced performance to handle complex data in bulk."},{"metadata":{},"cell_type":"markdown","source":"*Here the entire image pixels are stored in columnar format for easy compression and loading. The paraquet files has 137x236 grayscale images, hence adding upto 32332 columns for an image*"},{"metadata":{},"cell_type":"markdown","source":"As mentioned by @amrrs I tried manipulating the data using dask but the dask compute functions takes quite a lot of time to output compared to pandas"},{"metadata":{},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Bench marking Dask vs Pandas</font><br>"},{"metadata":{"trusted":true},"cell_type":"code","source":"HEIGHT=137\nWIDTH=236","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\ndf = dd.read_parquet(train_parquet_files[0])\n\ndef load_as_npa(df):\n    imageid=df.iloc[:, 0]\n    \n    return imageid.compute,df[list(df.columns[1:])].to_dask_array(lengths=True).reshape(-1,HEIGHT, WIDTH)\n\nimage_ids0, images0 = load_as_npa(df)\n\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\ndf = pd.read_parquet(train_parquet_files[0])\n\ndef load_as_npa(df):\n    return df.iloc[:, 0], df.iloc[:, 1:].values.reshape(-1, HEIGHT, WIDTH)\n\nimage_ids0, images0 = load_as_npa(df)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's visualize the sample image"},{"metadata":{"trusted":true},"cell_type":"code","source":"# preview the images first\nplt.figure(figsize=(16,10))\nx, y = 5,5\n\nfor i in range(10):  \n    plt.subplot(y, x, i+1)\n    plt.imshow(images0[i])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Data exploration</font><br>"},{"metadata":{},"cell_type":"markdown","source":"**Train data**"},{"metadata":{"trusted":true},"cell_type":"code","source":"nrows = train.shape[0]\nroot = train[\"grapheme_root\"].nunique()\nvowel = train[\"vowel_diacritic\"].nunique()\nconsonant = train[\"consonant_diacritic\"].nunique()\n\nmax_root = train[\"grapheme_root\"].value_counts().index[0]\nmax_vowel = train[\"vowel_diacritic\"].value_counts().index[0]\nmax_consonant=train[\"consonant_diacritic\"].value_counts().index[0]\n\ndisplay(HTML(f\"\"\"<br>Number of rows in the dataset: {nrows:,}</br>\n             <br>Number of unique grapheme root in the dataset: {root:,}</br>\n             <br>Number of unique vowels in the dataset: {vowel:,}</br>\n             <br>Number of unique consonants in the dataset: {consonant:,}</br>\n             <br>Most occuring grapheme root id {max_root}</br>\n             <br>Most occuring vowel_diacritic id {max_vowel}</br>\n             <br>Most occuring consonant_diacritic id {max_consonant}</br>\n             \"\"\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plots = train['grapheme_root'].value_counts().reset_index()\nplots.columns=['Grapheme roots','Counts']\nfig = px.scatter(plots, x=\"Grapheme roots\", y=\"Counts\",size='Counts', hover_data=['Grapheme roots'])\n\nfig.update_traces(marker=dict(line=dict(width=2,\n                                        color='MediumPurple')),\n                  selector=dict(mode='markers'))\n\nfig.show()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plots = train['vowel_diacritic'].value_counts().reset_index()\nplots.columns=['Vowels','Counts']\nfig = px.scatter(plots, x=\"Vowels\", y=\"Counts\",size='Counts', hover_data=['Vowels'])\n\nfig.update_traces(marker=dict(line=dict(width=2,\n                                        color='DarkSlateGrey')),\n                  selector=dict(mode='markers'))\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plots = train['consonant_diacritic'].value_counts().reset_index()\nplots.columns=['Consonants','Counts']\nfig = px.scatter(plots, x=\"Consonants\", y=\"Counts\",size='Counts', hover_data=['Consonants'])\n\nfig.update_traces(marker=dict(line=dict(width=2,\n                                        color='#bcbd22')),\n                  selector=dict(mode='markers'))\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Using feathers</font><br>"},{"metadata":{"trusted":true},"cell_type":"code","source":"del train_parquet_files,test_parquet_files\n\nimages0 = pd.read_feather(feathers+'train_image_data_0.feather')\nimages1 = pd.read_feather(feathers+'train_image_data_1.feather')\nimages2 = pd.read_feather(feathers+'train_image_data_2.feather')\nimages3 = pd.read_feather(feathers+'train_image_data_3.feather')\n\ntest_0=pd.read_feather(feathers+'test_image_data_0.feather')\ntest_1=pd.read_feather(feathers+'test_image_data_1.feather')\ntest_2=pd.read_feather(feathers+'test_image_data_2.feather')\ntest_3=pd.read_feather(feathers+'test_image_data_3.feather')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Resizing the images withe 128*128 size</font><br>"},{"metadata":{"trusted":true},"cell_type":"code","source":"#Credits: https://www.kaggle.com/phoenix9032/pytorch-efficientnet-starter-code/data\n\nSIZE = 128\n\ndef bbox(img):\n    rows = np.any(img, axis=1)\n    cols = np.any(img, axis=0)\n    rmin, rmax = np.where(rows)[0][[0, -1]]\n    cmin, cmax = np.where(cols)[0][[0, -1]]\n    return rmin, rmax, cmin, cmax\n\ndef crop_resize(img0, size=SIZE, pad=16):\n    \n    #crop a box around pixels large than the threshold \n    #some images contain line at the sides\n    ymin,ymax,xmin,xmax = bbox(img0[5:-5,5:-5] > 80)\n    \n    #cropping may cut too much, so we need to add it back\n    xmin = xmin - 13 if (xmin > 13) else 0\n    ymin = ymin - 10 if (ymin > 10) else 0\n    xmax = xmax + 13 if (xmax < WIDTH - 13) else WIDTH\n    ymax = ymax + 10 if (ymax < HEIGHT - 10) else HEIGHT\n    img = img0[ymin:ymax,xmin:xmax]\n    \n    #remove lo intensity pixels as noise\n    img[img < 28] = 0\n    lx, ly = xmax-xmin,ymax-ymin\n    l = max(lx,ly) + pad\n    \n    #make sure that the aspect ratio is kept in rescaling\n    img = np.pad(img, [((l-ly)//2,), ((l-lx)//2,)], mode='constant')\n    return cv2.resize(img,(size,size))\n\ndef Resize(df,size=128):\n    resized = {} \n    df = df.set_index('image_id')\n    \n    for i in tqdm(range(df.shape[0])): \n        image0 = 255 - df.loc[df.index[i]].values.reshape(137,236).astype(np.uint8)\n        \n        #normalize each image by its max val\n        img = (image0*(255.0/image0.max())).astype(np.uint8)\n        image = crop_resize(img)\n        resized[df.index[i]] = image.reshape(-1)\n    resized = pd.DataFrame(resized).T.reset_index()\n    resized.columns = resized.columns.astype(str)\n    resized.rename(columns={'index':'image_id'},inplace=True)\n    return resized","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"images0=Resize(images0)\nimages1=Resize(images1)\nimages2=Resize(images2)\nimages3=Resize(images3)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data_full = pd.concat([images0,images1,images2,images3],ignore_index=True)\n\ndel images0,images1,images2,images3\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"nrow, ncol = 5, 5\n\nfig, axes = plt.subplots(nrow, ncol, figsize=(15, 7))\naxes = axes.flatten()\nfor i, ax in enumerate(axes):\n    img0 = data_full.iloc[i, 1:].values.reshape(SIZE, SIZE).astype(np.uint8)\n    ax.imshow(img0)\n\nplt.tight_layout()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The following work is inspired from this kernel [here](https://www.kaggle.com/khoongweihao/resnet-34-pytorch-starter-kit/data)"},{"metadata":{},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Data Augmentation</font><br>"},{"metadata":{"trusted":true},"cell_type":"code","source":"## Add Augmentations as suited from Albumentations library\ntrain_aug = Compose([ \n    HorizontalFlip(p=0.1),              \n    ShiftScaleRotate(p=1),\n    RandomGamma(p=0.8)])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Data definition</font><br>"},{"metadata":{"trusted":true},"cell_type":"code","source":"class GraphemeDataset(Dataset):\n    def __init__(self,df,label=None,_type='train',transform =True,aug=train_aug):\n        self.df = df\n        self.label = label\n        self.aug = aug\n        self.transform = transform\n        self.type=_type\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self,idx):\n        \n        if self.type=='train':\n            label1 = self.label.vowel_diacritic.values[idx]\n            label2 = self.label.grapheme_root.values[idx]\n            label3 = self.label.consonant_diacritic.values[idx]\n            image = self.df.iloc[idx][1:].values.reshape(SIZE,SIZE).astype(np.float)\n            \n            augment = self.aug(image =image)\n            image = augment['image']\n\n            return image,label1,label2,label3\n        else:\n            image = self.df.iloc[idx][1:].values.reshape(SIZE,SIZE).astype(np.float)\n            return image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dataset = GraphemeDataset(data_full ,train[['grapheme_root', 'vowel_diacritic', 'consonant_diacritic']],transform = True) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"##Visulization function for checking Original and augmented image\n\n#Credits: https://www.kaggle.com/phoenix9032/pytorch-efficientnet-starter-code/data\n\n\ndef visualize(original_image,aug_image):\n    fontsize = 18\n    \n    f, ax = plt.subplots(1, 2, figsize=(8, 8))\n\n    ax[0].imshow(original_image, cmap='gray')\n    ax[0].set_title('Original image', fontsize=fontsize)\n    ax[1].imshow(aug_image,cmap='gray')\n    ax[1].set_title('Augmented image', fontsize=fontsize)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## One image taken from raw dataframe another from dataset \n\norig_image = data_full.iloc[0, 1:].values.reshape(128,128).astype(np.float)\naug_image = train_dataset[0][0]\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## Check it \nvisualize (orig_image,aug_image)\n\ndel aug_image,orig_image,train_dataset\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Dense net</font><br>"},{"metadata":{"trusted":true},"cell_type":"code","source":"class Dense_Block(nn.Module):\n\n    def __init__(self, in_channels):\n        super(Dense_Block, self).__init__()\n\n        self.relu = nn.ReLU(inplace = True)\n        self.bn = nn.BatchNorm2d(num_features = in_channels)\n\n        self.conv1 = nn.Conv2d(in_channels = in_channels, out_channels = 32, kernel_size = 3, stride = 1, padding = 1)\n        self.conv2 = nn.Conv2d(in_channels = 32, out_channels = 32, kernel_size = 3, stride = 1, padding = 1)\n        self.conv3 = nn.Conv2d(in_channels = 64, out_channels = 32, kernel_size = 3, stride = 1, padding = 1)\n        self.conv4 = nn.Conv2d(in_channels = 96, out_channels = 32, kernel_size = 3, stride = 1, padding = 1)\n        self.conv5 = nn.Conv2d(in_channels = 128, out_channels = 32, kernel_size = 3, stride = 1, padding = 1)\n\n    \n    def forward(self, x):\n\n        bn = self.bn(x)\n        conv1 = self.relu(self.conv1(bn))\n\n        conv2 = self.relu(self.conv2(conv1))\n        c2_dense = self.relu(torch.cat([conv1, conv2], 1))\n\n        conv3 = self.relu(self.conv3(c2_dense))\n        c3_dense = self.relu(torch.cat([conv1, conv2, conv3], 1))\n\n        conv4 = self.relu(self.conv4(c3_dense))\n        c4_dense = self.relu(torch.cat([conv1, conv2, conv3, conv4], 1))\n\n        conv5 = self.relu(self.conv5(c4_dense))\n        c5_dense = self.relu(torch.cat([conv1, conv2, conv3, conv4, conv5], 1))\n\n        return c5_dense\n\n\nclass Transition_Layer(nn.Module):\n    def __init__(self, in_channels, out_channels):\n        super(Transition_Layer, self).__init__()\n\n        self.relu = nn.ReLU(inplace = True)\n        self.bn = nn.BatchNorm2d(num_features = out_channels)\n        self.conv = nn.Conv2d(in_channels = in_channels, out_channels = out_channels, kernel_size = 1, bias = False)\n        self.avg_pool = nn.AvgPool2d(kernel_size = 2, stride = 2, padding = 0)\n\n    def forward(self, x):\n\n        bn = self.bn(self.relu(self.conv(x)))\n        out = self.avg_pool(bn)\n\n        return out\n\n\nclass DenseNet(nn.Module):\n    def __init__(self):\n        super(DenseNet, self).__init__()\n\n        self.lowconv = nn.Conv2d(in_channels = 1, out_channels = 64, kernel_size = 7, padding = 3, bias = False)\n        self.relu = nn.ReLU()\n\n        # Make Dense Blocks\n        self.denseblock1 = self._make_dense_block(Dense_Block, 64)\n        self.denseblock2 = self._make_dense_block(Dense_Block, 128)\n        self.denseblock3 = self._make_dense_block(Dense_Block, 128)\n\n        # Make transition Layers\n        self.transitionLayer1 = self._make_transition_layer(Transition_Layer, in_channels = 160, out_channels = 128)\n        self.transitionLayer2 = self._make_transition_layer(Transition_Layer, in_channels = 160, out_channels = 128)\n        self.transitionLayer3 = self._make_transition_layer(Transition_Layer, in_channels = 160, out_channels = 64)\n\n        # Classifier\n        self.bn = nn.BatchNorm2d(num_features = 64)\n        \n        self.pre_classifier = nn.Linear(16384, 256)\n        \n         # vowel_diacritic\n        self.fc1 = nn.Linear(256,11)\n        \n        # grapheme_root\n        self.fc2 = nn.Linear(256,168)\n        \n        # consonant_diacritic\n        self.fc3 = nn.Linear(256,7)\n        \n    def _make_dense_block(self, block, in_channels):\n        layers = []\n        layers.append(block(in_channels))\n        return nn.Sequential(*layers)\n\n    def _make_transition_layer(self, layer, in_channels, out_channels):\n        modules = []\n        modules.append(layer(in_channels, out_channels))\n        return nn.Sequential(*modules)\n\n    def forward(self, x):\n        out = self.relu(self.lowconv(x))\n\n        out = self.denseblock1(out)\n        out = self.transitionLayer1(out)\n\n        out = self.denseblock2(out)\n        out = self.transitionLayer2(out)\n\n        out = self.denseblock3(out)\n        out = self.transitionLayer3(out)\n    \n        out = self.bn(out)\n        out = out.view(out.size(0),-1)\n\n        out = self.pre_classifier(out)\n        \n        x1 = self.fc1(out)\n        x2 = self.fc2(out)\n        x3 = self.fc3(out)\n\n        return x1,x2,x3","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Competition metric</font><br>"},{"metadata":{"trusted":true},"cell_type":"code","source":"def macro_recall_multi(pred_graphemes, true_graphemes,pred_vowels,true_vowels,pred_consonants,true_consonants, n_grapheme=168, n_vowel=11, n_consonant=7):\n    \n    pred_label_graphemes = torch.argmax(pred_graphemes, dim=1).cpu().numpy()\n\n    true_label_graphemes = true_graphemes.cpu().numpy()\n    \n    pred_label_vowels = torch.argmax(pred_vowels, dim=1).cpu().numpy()\n\n    true_label_vowels = true_vowels.cpu().numpy()\n    \n    pred_label_consonants = torch.argmax(pred_consonants, dim=1).cpu().numpy()\n\n    true_label_consonants = true_consonants.cpu().numpy()    \n\n    recall_grapheme = sklearn.metrics.recall_score(pred_label_graphemes, true_label_graphemes, average='macro')\n    recall_vowel = sklearn.metrics.recall_score(pred_label_vowels, true_label_vowels, average='macro')\n    recall_consonant = sklearn.metrics.recall_score(pred_label_consonants, true_label_consonants, average='macro')\n    scores = [recall_grapheme, recall_vowel, recall_consonant]\n    final_score = np.average(scores, weights=[2, 1, 1])\n\n    return final_score","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = DenseNet().to(device)\n\noptimizer = torch.optim.Adam(model.parameters(), lr=4e-4)\n\ncriterion = nn.CrossEntropyLoss()\n\nbatch_size=64","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"epochs = 80\nmodel.train()\nlosses = []\naccs = []\nrecall=[]\n\nfor epoch in range(epochs):\n    reduced_index =train.groupby(['grapheme_root', 'vowel_diacritic', 'consonant_diacritic']).apply(lambda x: x.sample(5)).image_id.values\n    \n    reduced_train = train.loc[train.image_id.isin(reduced_index)]\n    reduced_data = data_full.loc[data_full.image_id.isin(reduced_index)]\n    \n    train_image = GraphemeDataset(reduced_data,reduced_train,transform = True)\n    train_loader = torch.utils.data.DataLoader(train_image,batch_size=batch_size,shuffle=True)\n    \n    print('epochs {}/{} '.format(epoch+1,epochs))\n    running_loss = 0.0\n    running_acc = 0.0\n    running_recall=0.0\n    \n    for idx, (inputs,labels1,labels2,labels3) in tqdm(enumerate(train_loader),total=len(train_loader)):\n        \n        inputs = inputs.to(device)\n        labels1 = labels1.to(device)\n        labels2 = labels2.to(device)\n        labels3 = labels3.to(device)\n        \n        optimizer.zero_grad()\n        \n        outputs1,outputs2,outputs3 = model(inputs.unsqueeze(1).float())\n        \n        loss1 = criterion(outputs1,labels1)\n        loss2 = criterion(outputs2,labels2)\n        loss3 = criterion(outputs3,labels3)\n        running_loss += loss1+loss2+loss3\n        \n        running_acc += (outputs1.argmax(1)==labels1).float().mean()\n        running_acc += (outputs2.argmax(1)==labels2).float().mean()\n        running_acc += (outputs3.argmax(1)==labels3).float().mean()\n        \n        running_recall+= macro_recall_multi(outputs2,labels2,outputs1,labels1,outputs3,labels3)\n        \n        (loss1+loss2+loss3).backward()\n        optimizer.step()\n    \n    recall.append(running_recall/len(train_loader))\n    losses.append(running_loss/len(train_loader))\n    accs.append(running_acc/(len(train_loader)*3))\n    \n    print('recall: {:.4f}'.format(running_recall/len(train_loader)))\n    print('acc : {:.2f}%'.format(running_acc/(len(train_loader)*3)))\n    print('loss : {:.4f}'.format(running_loss/len(train_loader)))\n    \ntorch.save(model.state_dict(), 'densenet_epochs_saved_weights.pth')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig,ax = plt.subplots(1,3,figsize=(15,5))\nax[0].plot(losses)\nax[0].set_title('loss')\nax[1].plot(accs)\nax[1].set_title('acc')\nax[2].plot(recall)\nax[2].set_title('Recall')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Inference</font><br>"},{"metadata":{"trusted":true},"cell_type":"code","source":"torch.cuda.empty_cache()\ngc.collect()\n\nmodel = DenseNet().to(device)\nmodel.load_state_dict(torch.load('densenet_epochs_saved_weights.pth'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.eval()\ntest_data = ['test_image_data_0.parquet','test_image_data_1.parquet','test_image_data_2.parquet','test_image_data_3.parquet']\npredictions = []\nbatch_size=1\n\nfor fname in test_data:\n    data = pd.read_parquet(f'/kaggle/input/bengaliai-cv19/{fname}')\n    data = Resize(data)\n    test_image = GraphemeDataset(data,_type='test')\n    test_loader = torch.utils.data.DataLoader(test_image,batch_size=1,shuffle=False)\n    \n    with torch.no_grad():\n        for idx, (inputs) in tqdm(enumerate(test_loader),total=len(test_loader)):\n            inputs.to(device)\n            \n            outputs1,outputs2,outputs3 = model(inputs.unsqueeze(1).float().cuda())\n            predictions.append(outputs3.argmax(1).cpu().detach().numpy())\n            predictions.append(outputs2.argmax(1).cpu().detach().numpy())\n            predictions.append(outputs1.argmax(1).cpu().detach().numpy())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Submission</font><br>"},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_submission.target = np.hstack(predictions)\nsample_submission.to_csv('submission.csv',index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_submission['alphabet_part']=sample_submission['row_id'].apply(lambda x: x.split('_')[-2])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample = sample_submission.groupby(['alphabet_part','target']).count().reset_index()\n\nplt.figure(figsize=(20,7))\nax = sns.barplot(x=\"row_id\", y=\"target\", hue=\"alphabet_part\", data=sample)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"<font color='#088a5a' size=3>Work in progress...!</font><br>"},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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}