{"cells":[{"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\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import torch\nfrom torch import nn\nfrom torch.nn import functional as F\nfrom torchvision import models\n\n\nclass DenseNetHead(nn.Module):\n\n    def __init__(self, in_features: int, n_classes: int):\n        super().__init__()\n        self.classifier = nn.Linear(in_features, n_classes)\n\n    def forward(self, x):\n        return self.classifier(x)\n\n\nclass DenseNetBase(nn.Module):\n\n    def __init__(self, name: str, pretrained: bool = True):\n        super().__init__()\n        self.base = getattr(models, name)(pretrained=pretrained)\n        if name.endswith('121'):\n            self.out_features = 1024\n        elif name.endswith('161'):\n            self.out_features = 2208\n        elif name.endswith('169'):\n            self.out_features = 1664\n        else:\n            self.out_features = 1920\n\n    def forward(self, x):\n        base = self.base\n        features = base.features(x)\n        out = F.relu(features, inplace=True)\n        out = F.adaptive_avg_pool2d(out, (1, 1))\n        out = torch.flatten(out, 1)\n        return out\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nfrom torch import nn\nfrom torchvision import models\n\n\nclass ResNetHead(nn.Module):\n    def __init__(self, in_features: int, n_classes: int):\n        super().__init__()\n\n        self.pooling = nn.AdaptiveAvgPool2d((1, 1))\n        self.fc1 = nn.Linear(in_features, n_classes)\n\n    def forward(self, x):\n        x = self.pooling(x)\n        x = torch.flatten(x, start_dim=1)\n        x = self.apply_fc_out(x)\n        return x\n\n    def apply_fc_out(self, x):\n        return self.fc1(x)\n\n\nclass ResNetBase(nn.Module):\n    def __init__(self, name: str, pretrained: bool, frozen_start: bool = False):\n        super().__init__()\n\n        self.base = getattr(models, name)(pretrained=pretrained)\n\n        self.frozen_start = frozen_start\n\n        if name == 'resnet34' or name == 'resnet18':\n            self.out_features = 512\n        else:\n            self.out_features = 2048\n\n        self.frozen = []\n        if self.frozen_start:\n            self.frozen = [self.base.layer1, self.base.conv1, self.base.bn1]\n            for m in self.frozen:\n                self._freeze(m)\n\n    def forward(self, x):\n        base = self.base\n        x = base.conv1(x)\n        x = base.bn1(x)\n        x = base.relu(x)\n        x = base.maxpool(x)\n\n        x = base.layer1(x)\n        x = base.layer2(x)\n        x = base.layer3(x)\n        x = base.layer4(x)\n\n        return x\n\n    def train(self, mode=True):\n        super().train(mode=mode)\n        for m in self.frozen:\n            self._bn_to_eval(m)\n\n    def _freeze(self, module):\n        for p in module.parameters():\n            p.requires_grad = False\n\n    def _bn_to_eval(self, module):\n        for m in module.modules():\n            if isinstance(m, nn.BatchNorm2d):\n                m.eval()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# STN Network"},{"metadata":{"trusted":true},"cell_type":"code","source":"\"\"\"\nThe source code is from Clova.ai with no changes have been made.\nOnly factoring the code.\nhttps://github.com/clovaai/deep-text-recognition-benchmark/\n\"\"\"\n\nimport numpy as np\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n\nclass TPS_SpatialTransformerNetwork(nn.Module):\n    \"\"\" Rectification Network of RARE, namely TPS based STN \"\"\"\n\n    def __init__(self, F, I_size, I_r_size, I_channel_num=1):\n        \"\"\" Based on RARE TPS\n        input:\n            batch_I: Batch Input Image [batch_size x I_channel_num x I_height x I_width]\n            I_size : (height, width) of the input image I\n            I_r_size : (height, width) of the rectified image I_r\n            I_channel_num : the number of channels of the input image I\n        output:\n            batch_I_r: rectified image [batch_size x I_channel_num x I_r_height x I_r_width]\n        \"\"\"\n        super(TPS_SpatialTransformerNetwork, self).__init__()\n        self.F = F\n        self.I_size = I_size\n        self.I_r_size = I_r_size  # = (I_r_height, I_r_width)\n        self.I_channel_num = I_channel_num\n        self.LocalizationNetwork = LocalizationNetwork(self.F, self.I_channel_num)\n        self.GridGenerator = GridGenerator(self.F, self.I_r_size)\n\n    def forward(self, batch_I):\n        batch_C_prime = self.LocalizationNetwork(batch_I)  # batch_size x K x 2\n        build_P_prime = self.GridGenerator.build_P_prime(\n            batch_C_prime)  # batch_size x n (= I_r_width x I_r_height) x 2\n        build_P_prime_reshape = build_P_prime.reshape(\n            [build_P_prime.size(0), self.I_r_size[0], self.I_r_size[1], 2])\n\n        if torch.__version__ > \"1.2.0\":\n            batch_I_r = F.grid_sample(batch_I, build_P_prime_reshape,\n                                      padding_mode='border', align_corners=True)\n        else:\n            batch_I_r = F.grid_sample(batch_I, build_P_prime_reshape,\n                                      padding_mode='border')\n\n        return batch_I_r\n\n\nclass LocalizationNetwork(nn.Module):\n    \"\"\" Localization Network of RARE, which predicts C' (K x 2) from I (I_width x I_height) \"\"\"\n\n    def __init__(self, F, I_channel_num):\n        super(LocalizationNetwork, self).__init__()\n        self.F = F\n        self.I_channel_num = I_channel_num\n        self.conv = nn.Sequential(\n            nn.Conv2d(in_channels=self.I_channel_num, out_channels=64, kernel_size=3,\n                      stride=1, padding=1,\n                      bias=False), nn.BatchNorm2d(64), nn.ReLU(True),\n            nn.MaxPool2d(2, 2),  # batch_size x 64 x I_height/2 x I_width/2\n            nn.Conv2d(64, 128, 3, 1, 1, bias=False), nn.BatchNorm2d(128), nn.ReLU(True),\n            nn.MaxPool2d(2, 2),  # batch_size x 128 x I_height/4 x I_width/4\n            nn.Conv2d(128, 256, 3, 1, 1, bias=False), nn.BatchNorm2d(256), nn.ReLU(True),\n            nn.MaxPool2d(2, 2),  # batch_size x 256 x I_height/8 x I_width/8\n            nn.Conv2d(256, 512, 3, 1, 1, bias=False), nn.BatchNorm2d(512), nn.ReLU(True),\n            nn.AdaptiveAvgPool2d(1)  # batch_size x 512\n        )\n\n        self.localization_fc1 = nn.Sequential(nn.Linear(512, 256), nn.ReLU(True))\n        self.localization_fc2 = nn.Linear(256, self.F * 2)\n\n        # Init fc2 in LocalizationNetwork\n        self.localization_fc2.weight.data.fill_(0)\n        \"\"\" see RARE paper Fig. 6 (a) \"\"\"\n        ctrl_pts_x = np.linspace(-1.0, 1.0, int(F / 2))\n        ctrl_pts_y_top = np.linspace(0.0, -1.0, num=int(F / 2))\n        ctrl_pts_y_bottom = np.linspace(1.0, 0.0, num=int(F / 2))\n        ctrl_pts_top = np.stack([ctrl_pts_x, ctrl_pts_y_top], axis=1)\n        ctrl_pts_bottom = np.stack([ctrl_pts_x, ctrl_pts_y_bottom], axis=1)\n        initial_bias = np.concatenate([ctrl_pts_top, ctrl_pts_bottom], axis=0)\n        self.localization_fc2.bias.data = torch.from_numpy(initial_bias).float().view(-1)\n\n    def forward(self, batch_I):\n        \"\"\"\n        input:     batch_I : Batch Input Image [batch_size x I_channel_num x I_height x I_width]\n        output:    batch_C_prime : Predicted coordinates of fiducial points for input batch [batch_size x F x 2]\n        \"\"\"\n        batch_size = batch_I.size(0)\n        features = self.conv(batch_I).view(batch_size, -1)\n        batch_C_prime = self.localization_fc2(self.localization_fc1(features)).view(\n            batch_size, self.F, 2)\n        return batch_C_prime\n\n\nclass GridGenerator(nn.Module):\n    \"\"\" Grid Generator of RARE, which produces P_prime by multipling T with P \"\"\"\n\n    def __init__(self, F, I_r_size):\n        \"\"\" Generate P_hat and inv_delta_C for later \"\"\"\n        super(GridGenerator, self).__init__()\n        self.eps = 1e-6\n        self.I_r_height, self.I_r_width = I_r_size\n        self.F = F\n        self.C = self._build_C(self.F)  # F x 2\n        self.P = self._build_P(self.I_r_width, self.I_r_height)\n        ## for multi-gpu, you need register buffer\n        self.register_buffer(\"inv_delta_C\", torch.tensor(\n            self._build_inv_delta_C(self.F, self.C)).float())  # F+3 x F+3\n        self.register_buffer(\"P_hat\", torch.tensor(\n            self._build_P_hat(self.F, self.C, self.P)).float())  # n x F+3\n        ## for fine-tuning with different image width, you may use below instead of self.register_buffer\n        # self.inv_delta_C = torch.tensor(self._build_inv_delta_C(self.F, self.C)).float().cuda()  # F+3 x F+3\n        # self.P_hat = torch.tensor(self._build_P_hat(self.F, self.C, self.P)).float().cuda()  # n x F+3\n\n    def _build_C(self, F):\n        \"\"\" Return coordinates of fiducial points in I_r; C \"\"\"\n        ctrl_pts_x = np.linspace(-1.0, 1.0, int(F / 2))\n        ctrl_pts_y_top = -1 * np.ones(int(F / 2))\n        ctrl_pts_y_bottom = np.ones(int(F / 2))\n        ctrl_pts_top = np.stack([ctrl_pts_x, ctrl_pts_y_top], axis=1)\n        ctrl_pts_bottom = np.stack([ctrl_pts_x, ctrl_pts_y_bottom], axis=1)\n        C = np.concatenate([ctrl_pts_top, ctrl_pts_bottom], axis=0)\n        return C  # F x 2\n\n    def _build_inv_delta_C(self, F, C):\n        \"\"\" Return inv_delta_C which is needed to calculate T \"\"\"\n        hat_C = np.zeros((F, F), dtype=float)  # F x F\n        for i in range(0, F):\n            for j in range(i, F):\n                r = np.linalg.norm(C[i] - C[j])\n                hat_C[i, j] = r\n                hat_C[j, i] = r\n        np.fill_diagonal(hat_C, 1)\n        hat_C = (hat_C ** 2) * np.log(hat_C)\n        # print(C.shape, hat_C.shape)\n        delta_C = np.concatenate(  # F+3 x F+3\n            [\n                np.concatenate([np.ones((F, 1)), C, hat_C], axis=1),  # F x F+3\n                np.concatenate([np.zeros((2, 3)), np.transpose(C)], axis=1),  # 2 x F+3\n                np.concatenate([np.zeros((1, 3)), np.ones((1, F))], axis=1)  # 1 x F+3\n            ],\n            axis=0\n        )\n        inv_delta_C = np.linalg.inv(delta_C)\n        return inv_delta_C  # F+3 x F+3\n\n    def _build_P(self, I_r_width, I_r_height):\n        I_r_grid_x = (np.arange(-I_r_width, I_r_width,\n                                2) + 1.0) / I_r_width  # self.I_r_width\n        I_r_grid_y = (np.arange(-I_r_height, I_r_height,\n                                2) + 1.0) / I_r_height  # self.I_r_height\n        P = np.stack(  # self.I_r_width x self.I_r_height x 2\n            np.meshgrid(I_r_grid_x, I_r_grid_y),\n            axis=2\n        )\n        return P.reshape([-1, 2])  # n (= self.I_r_width x self.I_r_height) x 2\n\n    def _build_P_hat(self, F, C, P):\n        n = P.shape[0]  # n (= self.I_r_width x self.I_r_height)\n        P_tile = np.tile(np.expand_dims(P, axis=1),\n                         (1, F, 1))  # n x 2 -> n x 1 x 2 -> n x F x 2\n        C_tile = np.expand_dims(C, axis=0)  # 1 x F x 2\n        P_diff = P_tile - C_tile  # n x F x 2\n        rbf_norm = np.linalg.norm(P_diff, ord=2, axis=2, keepdims=False)  # n x F\n        rbf = np.multiply(np.square(rbf_norm), np.log(rbf_norm + self.eps))  # n x F\n        P_hat = np.concatenate([np.ones((n, 1)), P, rbf], axis=1)\n        return P_hat  # n x F+3\n\n    def build_P_prime(self, batch_C_prime):\n        \"\"\" Generate Grid from batch_C_prime [batch_size x F x 2] \"\"\"\n        batch_size = batch_C_prime.size(0)\n        batch_inv_delta_C = self.inv_delta_C.repeat(batch_size, 1, 1)\n        batch_P_hat = self.P_hat.repeat(batch_size, 1, 1)\n        batch_C_prime_with_zeros = torch.cat((batch_C_prime, torch.zeros(\n            batch_size, 3, 2).float().to(device)), dim=1)  # batch_size x F+3 x 2\n        batch_T = torch.bmm(batch_inv_delta_C,\n                            batch_C_prime_with_zeros)  # batch_size x F+3 x 2\n        batch_P_prime = torch.bmm(batch_P_hat, batch_T)  # batch_size x n x 2\n        return batch_P_prime  # batch_size x n x 2","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Grapheme Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_grapheme_model(base: str, n_classes: int, **kwargs) -> nn.Module:\n    return GraphemeModel(base=base, n_classes=n_classes, **kwargs)\n\n\nclass GraphemeModel(nn.Module):\n    def __init__(self, *, base: str, n_classes: int, **base_kwargs,):\n        super().__init__()\n\n        if base.startswith('resne'):\n\n            self.base = ResNetBase(base, **base_kwargs)\n            self.in_features = self.base.out_features\n            self.head = ResNetHead(\n                in_features=self.in_features,\n                n_classes=n_classes,\n            )\n        elif base.startswith('vgg'):\n            self.base = VGGBase(base, **base_kwargs)\n            self.head = VGGHead(\n                n_classes=n_classes,\n            )\n        else:\n            self.base = DenseNetBase(base, **base_kwargs)\n            self.in_features = self.base.out_features\n            self.head = DenseNetHead(\n                in_features=self.in_features,\n                n_classes=n_classes,\n            )\n\n    def forward(self, x):\n        x = self.base(x)\n        x = self.head(x)\n        return x","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Vowel + Consonant Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"from typing import List\n\ndef build_vc_model(base: str, n_classes: List[int], **kwargs) -> nn.Module:\n    return VCModel(base=base, n_classes=n_classes, **kwargs)\n\n\nclass VCModel(nn.Module):\n    def __init__(self, *, base: str, n_classes: List[int], **base_kwargs,):\n        super().__init__()\n        \n        self.transformation = TPS_SpatialTransformerNetwork(\n            F=20,\n            I_size=(224, 224),\n            I_r_size=(224, 224),\n            I_channel_num=3,\n        )\n\n        if base.startswith('resne'):\n\n            self.base = ResNetBase(base, **base_kwargs)\n            self.in_features = self.base.out_features\n            self.vowel_head = ResNetHead(\n                in_features=self.in_features,\n                n_classes=n_classes[0],\n            )\n            self.consonant_head = ResNetHead(\n                in_features=self.in_features,\n                n_classes=n_classes[1],\n            )\n        elif base.startswith('vgg'):\n            self.base = VGGBase(base, **base_kwargs)\n            self.vowel_head = VGGHead(\n                n_classes=n_classes[0],\n            )\n            self.consonant_head = VGGHead(\n                n_classes=n_classes[1],\n            )\n        else:\n            self.base = DenseNetBase(base, **base_kwargs)\n            self.in_features = self.base.out_features\n            self.vowel_head = DenseNetHead(\n                in_features=self.in_features,\n                n_classes=n_classes[0],\n            )\n            self.consonant_head = DenseNetHead(\n                in_features=self.in_features,\n                n_classes=n_classes[1],\n            )\n\n    def forward(self, x):\n        x = self.transformation(x)\n        x = self.base(x)\n        vowel = self.vowel_head(x)\n        consonant = self.consonant_head(x)\n\n        return vowel, consonant\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from pathlib import Path\n\nimport pandas as pd\nimport os\nimport torchvision\n\nimport torch\nfrom torch.nn import Module\nfrom PIL import Image\nimport cv2\nfrom torch.utils.data import Dataset, DataLoader\nimport numpy as np\n\nfrom tqdm import tqdm\nfrom albumentations import Compose\nfrom albumentations.pytorch import ToTensorV2\nimport albumentations as A","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch.nn.functional as F","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BASE = 'resnext50_32x4d'  # 'resnext101_32x8d'\nHEAD_DROPOUT = 0.5\nFROZEN_START = 0\nCLASSES = [168, 11, 7]\nFP16 = True\nHEAD = 'SimpleHead'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"GRAPHEME_BASE = 'resnext50_32x4d'\nVC_BASE = 'resnext50_32x4d'\n\nGRAPHEME_CLASSES = 168\nVC_CLASSES = [11, 7]\n\nSIZE=224\nHEIGHT = 137\nWIDTH = 236\n\n# GRAPHEME_WEIGHTS_FILE= os.path.abspath('/kaggle/input/vcmodels/bengali-experiments/model_best_0.pth')\nGRAPHEME_WEIGHTS_FILE = os.path.abspath('/kaggle/input/vcmodels/model_best_g_0.pth')\nVC_WEIGHTS_FILE = os.path.abspath('/kaggle/input/vcmodels/stn_resnext50/model_best_1.pth')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def 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    #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    #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    #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    #make sure that the aspect ratio is kept in rescaling\n    img = np.pad(img, [((l-ly)//2,), ((l-lx)//2,)], mode='constant')\n    img = cv2.resize(img,(size,size))\n    img = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)\n    return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class GraphemeDataset(Dataset):\n    def __init__(self, fname, transform=None):\n        self.df = pd.read_parquet(fname)\n        self.data = self.df.iloc[:, 1:].values\n        self.transform = transform\n        \n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        name = self.df.iloc[idx,0]\n        img = 255 - self.data[idx, :].reshape(HEIGHT, WIDTH).astype(np.uint8)\n        img = (img*(255.0/img.max())).astype(np.uint8)\n        img = crop_resize(img)\n        \n        if self.transform:\n            transformed = self.transform(image=img)\n            img = transformed['image']\n            \n        return img, name","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TEST = ['/kaggle/input/bengaliai-cv19/test_image_data_0.parquet',\n        '/kaggle/input/bengaliai-cv19/test_image_data_1.parquet',\n        '/kaggle/input/bengaliai-cv19/test_image_data_2.parquet',\n        '/kaggle/input/bengaliai-cv19/test_image_data_3.parquet']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"transform_test = Compose([\n    A.Normalize(mean=(0.0692, 0.0692, 0.0692),\n                std=(0.2052, 0.2052, 0.2052)),\n    ToTensorV2()\n])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\ngrapheme_model: Module = build_grapheme_model(\n    base=GRAPHEME_BASE,\n    pretrained=False,\n    n_classes=GRAPHEME_CLASSES,\n)\n\nprint('Creating model ...')\ngrapheme_model.load_state_dict(torch.load(GRAPHEME_WEIGHTS_FILE, map_location=device))\ngrapheme_model = grapheme_model.to(device)\ngrapheme_model.eval()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"vc_model: Module = build_vc_model(\n    base=VC_BASE,\n    pretrained=False,\n    n_classes=VC_CLASSES,\n)\nprint('Creating model ...')\nvc_model.load_state_dict(torch.load(VC_WEIGHTS_FILE, map_location=device))\nvc_model = vc_model.to(device)\nvc_model.eval()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"row_id,target = [],[]\nfor fname in TEST:\n    ds = GraphemeDataset(fname, transform=transform_test)\n    dl = DataLoader(ds, batch_size=256, shuffle=False)\n    with torch.no_grad():\n        for x, y in tqdm(dl):\n            x = x.to(device, dtype=torch.float)\n            pred_g = grapheme_model(x)\n            pred_v, pred_c = vc_model(x)\n            \n            pred_g = F.softmax(pred_g, dim=1).data.cpu().numpy().argmax(axis=1)\n            pred_v = F.softmax(pred_v, dim=1).data.cpu().numpy().argmax(axis=1)\n            pred_c = F.softmax(pred_c, dim=1).data.cpu().numpy().argmax(axis=1)\n            \n            for idx,name in enumerate(y):\n                row_id += [f'{name}_grapheme_root',f'{name}_vowel_diacritic',\n                           f'{name}_consonant_diacritic']\n                target += [pred_g[idx].item(),pred_v[idx].item(),pred_c[idx].item()]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub_df = pd.DataFrame({'row_id': row_id, 'target': target})\nsub_df.to_csv('submission.csv', index=False)\nsub_df.head()","execution_count":null,"outputs":[]},{"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":4}