{"cells":[{"metadata":{"_uuid":"e0017918-a3ff-4cd3-a7cd-8ee4a5959a42","_cell_guid":"21851e2c-be60-450d-902b-225f4b8bce95","trusted":true},"cell_type":"markdown","source":"# Import Module"},{"metadata":{"_uuid":"9ce118af-cab4-44a1-acea-50bf4ae9c925","_cell_guid":"7dd8995b-9829-4c25-83e1-ab9e2274d8e3","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)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b306a55b-4c8a-49de-a4a8-f1fa852cd3a2","_cell_guid":"2a69cca2-c9e6-43b0-ae88-d831bb5e7c7f","trusted":true},"cell_type":"code","source":"# Pytorch\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport torch.nn.functional as F\nfrom torchvision import transforms, models\nfrom torch.utils.data import Dataset","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"83ba2dfb-ca9a-45e5-980a-69285d892c3d","_cell_guid":"15dc7666-583d-4e6b-89d7-965796f23d89","trusted":true},"cell_type":"code","source":"# utils\nimport cv2\nimport time\nimport random\nimport os\nimport matplotlib.pyplot as plt\nimport joblib\nimport gc\nfrom tqdm import tqdm\nimport warnings\nwarnings.filterwarnings('ignore')","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"fc31ff23-7830-4442-a106-0d409164b119","_cell_guid":"b606de3e-dfcd-4fcf-88c9-0d3707b804e7","trusted":true},"cell_type":"markdown","source":"# Preprocessing"},{"metadata":{"_uuid":"c1c1d71b-de75-4a14-b6b7-05d363ea1178","_cell_guid":"c3860d33-2a55-4c8e-a9db-49a206bbcab0","trusted":true},"cell_type":"code","source":"def seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = True","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"fc22d000-f7d8-41d6-b706-f24779229938","_cell_guid":"fcf2dca3-bf5a-4c56-834e-cbcf308877c5","trusted":true},"cell_type":"code","source":"# Setting\n\nHEIGHT = 137\nWIDTH = 236\nSIZE = 128\n\nSEED = 314\nBATCH_SIZE = 128\ndevice = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\nseed_everything(SEED)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"2b92f40c-8ad8-416e-9e31-22cad6fcdecf","_cell_guid":"8ea91f69-57a5-46cc-ab4c-493589e62e2b","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] > 60)\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    return cv2.resize(img,(size,size))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"85075074-43dc-40b9-bf75-8e22f9cbe71e","_cell_guid":"33e25596-86cd-47ab-bf9b-d63b36f86f91","trusted":true},"cell_type":"markdown","source":"# Make DataLoader"},{"metadata":{"_uuid":"41f039fa-8f40-4175-82b9-30ef151c07ae","_cell_guid":"fbc94a70-5aed-474c-873f-f6a143b675da","trusted":true},"cell_type":"code","source":"class BengaliDataset(Dataset):\n    def __init__(self, df, img_height, img_width, transform=None):\n        self.df = df\n        self.img_height = img_height\n        self.img_width = img_width\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        img = self.df.iloc[idx][0:].values.astype(np.uint8)\n        img = img.reshape(self.img_height, self.img_width)\n        img = 255 - img\n        img = (img*(255.0/img.max())).astype(np.uint8)\n        img = crop_resize(img)\n        img = img[:, :, np.newaxis]\n        img = np.repeat(img, 3, 2)\n\n        if self.transform is not None:\n            img = self.transform(image=img)['image']\n        else:\n            img = img\n\n        return (self.df.index[idx], img)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"33e3ffb5-dd6c-4610-91f9-db0979e5e226","_cell_guid":"26ce4b61-5445-4856-abf1-7e7c15805da8","trusted":true},"cell_type":"markdown","source":"# Model"},{"metadata":{"_uuid":"55e6e4e1-88e1-4025-9fa1-a09e68b5aa5c","_cell_guid":"8bd8ad56-1f7a-4084-903c-bbd5ea10521d","trusted":true},"cell_type":"code","source":"import math\n\nclass Selayer(nn.Module):\n    def __init__(self, inplanes):\n        super(Selayer, self).__init__()\n        self.global_avgpool = nn.AdaptiveAvgPool2d(1)\n        self.conv1 = nn.Conv2d(inplanes, int(inplanes / 16), kernel_size=1, stride=1)\n        self.conv2 = nn.Conv2d(int(inplanes / 16), inplanes, kernel_size=1, stride=1)\n        self.relu = nn.ReLU(inplace=True)\n        self.sigmoid = nn.Sigmoid()\n\n    def forward(self, x):\n\n        out = self.global_avgpool(x)\n\n        out = self.conv1(out)\n        out = self.relu(out)\n\n        out = self.conv2(out)\n        out = self.sigmoid(out)\n\n        return x * out\n\n\nclass Bottleneck(nn.Module):\n    expansion = 4\n\n    def __init__(self, inplanes, planes, cardinality, stride=1, downsample=None):\n        super(Bottleneck, self).__init__()\n        self.conv1 = nn.Conv2d(inplanes, planes * 2, kernel_size=1, bias=False)\n        self.bn1 = nn.BatchNorm2d(planes * 2)\n\n        self.conv2 = nn.Conv2d(planes * 2, planes * 2, kernel_size=3, stride=stride,\n                               padding=1, groups=cardinality, bias=False)\n        self.bn2 = nn.BatchNorm2d(planes * 2)\n\n        self.conv3 = nn.Conv2d(planes * 2, planes * 4, kernel_size=1, bias=False)\n        self.bn3 = nn.BatchNorm2d(planes * 4)\n\n        self.selayer = Selayer(planes * 4)\n\n        self.relu = nn.ReLU(inplace=True)\n        self.downsample = downsample\n        self.stride = stride\n\n    def forward(self, x):\n        residual = x\n\n        out = self.conv1(x)\n        out = self.bn1(out)\n        out = self.relu(out)\n\n        out = self.conv2(out)\n        out = self.bn2(out)\n        out = self.relu(out)\n\n        out = self.conv3(out)\n        out = self.bn3(out)\n\n        out = self.selayer(out)\n\n        if self.downsample is not None:\n            residual = self.downsample(x)\n\n        out += residual\n        out = self.relu(out)\n\n        return out\n\n\nclass SeResNeXt(nn.Module):\n\n    def __init__(self, block, layers, cardinality=32):\n        super(SeResNeXt, self).__init__()\n        self.cardinality = cardinality\n        self.inplanes = 64\n\n        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,\n                               bias=False)\n        self.bn1 = nn.BatchNorm2d(64)\n        self.relu = nn.ReLU(inplace=True)\n        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)\n\n        self.layer1 = self._make_layer(block, 64, layers[0])\n        self.layer2 = self._make_layer(block, 128, layers[1], stride=2)\n        self.layer3 = self._make_layer(block, 256, layers[2], stride=2)\n        self.layer4 = self._make_layer(block, 512, layers[3], stride=2)\n\n        self.avgpool = nn.AdaptiveAvgPool2d(1)\n\n        # grapheme_root\n        self.fc1 = nn.Linear(2048, 168)\n        # vowel_diacritic\n        self.fc2 = nn.Linear(2048, 11)\n        # consonant_diacritic\n        self.fc3 = nn.Linear(2048, 7)\n\n\n\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels\n                m.weight.data.normal_(0, math.sqrt(2. / n))\n                if m.bias is not None:\n                    m.bias.data.zero_()\n            elif isinstance(m, nn.BatchNorm2d):\n                m.weight.data.fill_(1)\n                m.bias.data.zero_()\n\n    def _make_layer(self, block, planes, blocks, stride=1):\n        downsample = None\n        if stride != 1 or self.inplanes != planes * block.expansion:\n            downsample = nn.Sequential(\n                nn.Conv2d(self.inplanes, planes * block.expansion,\n                          kernel_size=1, stride=stride, bias=False),\n                nn.BatchNorm2d(planes * block.expansion),\n            )\n\n        layers = []\n        layers.append(block(self.inplanes, planes, self.cardinality, stride, downsample))\n        self.inplanes = planes * block.expansion\n        for i in range(1, blocks):\n            layers.append(block(self.inplanes, planes, self.cardinality))\n                             \n        return nn.Sequential(*layers)\n        \n\n    def forward(self, x):\n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.relu(x)\n        x = self.maxpool(x)\n\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.layer4(x)\n\n        x = self.avgpool(x)\n        x = x.view(x.size(0), -1)\n        \n        x1 = self.fc1(x)\n        x2 = self.fc2(x)\n        x3 = self.fc3(x)\n        \n        return x1,x2,x3\n    \ndef se_resnext50(**kwargs):\n    return SeResNeXt(Bottleneck, [3, 4, 6, 3], **kwargs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class mymodel(nn.Module):\n    def __init__(self, original_model, num_ftrs):\n        super(mymodel,self).__init__()\n        self.num_ftrs = num_ftrs\n        \n        self.features = nn.Sequential(*(list(original_model.children())[:-1]))\n\n        self.fc1 = nn.Linear(self.num_ftrs, 168)\n        # vowel_diacritic\n        self.fc2 = nn.Linear(self.num_ftrs, 11)\n        # consonant_diacritic\n        self.fc3 = nn.Linear(self.num_ftrs, 7)\n        \n    def forward(self, x):\n        x = self.features(x)\n        bs, ch, height, width = x.shape\n        x = x.view(bs, ch*height*width)\n        x1 = self.fc1(x)\n        x2 = self.fc2(x)\n        x3 = self.fc3(x)\n        \n        return x1,x2,x3\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8ac80f7a-7521-4389-acfe-e1fbcf206c85","_cell_guid":"63a8c2a2-79ae-45f0-ad45-09ec5ce1483c","trusted":true},"cell_type":"code","source":"# import torchvision\n\n# # model = se_resnext50().to(device)\n# original_model = torchvision.models.resnext50_32x4d(pretrained=True)\n# num_ftrs = original_model.fc.in_features\n# model = mymodel(original_model, num_ftrs).to(device)\n# model.load_state_dict(torch.load('../input/augmix-resnet/final_resnext_saved_weights.pth'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Bottleneck(nn.Module):\n    # Bottleneck in torchvision places the stride for downsampling at 3x3 convolution(self.conv2)\n    # while original implementation places the stride at the first 1x1 convolution(self.conv1)\n    # according to \"Deep residual learning for image recognition\"https://arxiv.org/abs/1512.03385.\n    # This variant is also known as ResNet V1.5 and improves accuracy according to\n    # https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch.\n\n    expansion = 4\n\n    def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1,\n                 base_width=64, dilation=1, norm_layer=None):\n        super(Bottleneck, self).__init__()\n        if norm_layer is None:\n            norm_layer = nn.BatchNorm2d\n        width = int(planes * (base_width / 64.)) * groups\n        # Both self.conv2 and self.downsample layers downsample the input when stride != 1\n        self.conv1 = conv1x1(inplanes, width)\n        self.bn1 = norm_layer(width)\n        self.conv2 = conv3x3(width, width, stride, groups, dilation)\n        self.bn2 = norm_layer(width)\n        self.conv3 = conv1x1(width, planes * self.expansion)\n        self.bn3 = norm_layer(planes * self.expansion)\n        self.relu = nn.ReLU(inplace=True)\n        self.downsample = downsample\n        self.stride = stride\n\n    def forward(self, x):\n        identity = x\n\n        out = self.conv1(x)\n        out = self.bn1(out)\n        out = self.relu(out)\n\n        out = self.conv2(out)\n        out = self.bn2(out)\n        out = self.relu(out)\n\n        out = self.conv3(out)\n        out = self.bn3(out)\n\n        if self.downsample is not None:\n            identity = self.downsample(x)\n\n        out += identity\n        out = self.relu(out)\n\n        return out\n\nclass ResNet(nn.Module):\n\n    def __init__(self, block, layers, num_classes=1000, zero_init_residual=False,\n                 groups=1, width_per_group=64, replace_stride_with_dilation=None,\n                 norm_layer=None):\n        super(ResNet, self).__init__()\n        if norm_layer is None:\n            norm_layer = nn.BatchNorm2d\n        self._norm_layer = norm_layer\n\n        self.inplanes = 64\n        self.dilation = 1\n        if replace_stride_with_dilation is None:\n            # each element in the tuple indicates if we should replace\n            # the 2x2 stride with a dilated convolution instead\n            replace_stride_with_dilation = [False, False, False]\n        if len(replace_stride_with_dilation) != 3:\n            raise ValueError(\"replace_stride_with_dilation should be None \"\n                             \"or a 3-element tuple, got {}\".format(replace_stride_with_dilation))\n        self.groups = groups\n        self.base_width = width_per_group\n        self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, padding=3,\n                               bias=False)\n        self.bn1 = norm_layer(self.inplanes)\n        self.relu = nn.ReLU(inplace=True)\n        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)\n        self.layer1 = self._make_layer(block, 64, layers[0])\n        self.layer2 = self._make_layer(block, 128, layers[1], stride=2,\n                                       dilate=replace_stride_with_dilation[0])\n        self.layer3 = self._make_layer(block, 256, layers[2], stride=2,\n                                       dilate=replace_stride_with_dilation[1])\n        self.layer4 = self._make_layer(block, 512, layers[3], stride=2,\n                                       dilate=replace_stride_with_dilation[2])\n        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))\n        self.fc = nn.Linear(512 * block.expansion, num_classes)\n\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')\n            elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):\n                nn.init.constant_(m.weight, 1)\n                nn.init.constant_(m.bias, 0)\n\n        # Zero-initialize the last BN in each residual branch,\n        # so that the residual branch starts with zeros, and each residual block behaves like an identity.\n        # This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677\n        if zero_init_residual:\n            for m in self.modules():\n                if isinstance(m, Bottleneck):\n                    nn.init.constant_(m.bn3.weight, 0)\n                elif isinstance(m, BasicBlock):\n                    nn.init.constant_(m.bn2.weight, 0)\n\n    def _make_layer(self, block, planes, blocks, stride=1, dilate=False):\n        norm_layer = self._norm_layer\n        downsample = None\n        previous_dilation = self.dilation\n        if dilate:\n            self.dilation *= stride\n            stride = 1\n        if stride != 1 or self.inplanes != planes * block.expansion:\n            downsample = nn.Sequential(\n                conv1x1(self.inplanes, planes * block.expansion, stride),\n                norm_layer(planes * block.expansion),\n            )\n\n        layers = []\n        layers.append(block(self.inplanes, planes, stride, downsample, self.groups,\n                            self.base_width, previous_dilation, norm_layer))\n        self.inplanes = planes * block.expansion\n        for _ in range(1, blocks):\n            layers.append(block(self.inplanes, planes, groups=self.groups,\n                                base_width=self.base_width, dilation=self.dilation,\n                                norm_layer=norm_layer))\n\n        return nn.Sequential(*layers)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def _resnet(arch, block, layers, pretrained, progress, **kwargs):\n    model = ResNet(block, layers, **kwargs)\n    if pretrained:\n        state_dict = load_state_dict_from_url(model_urls[arch],\n                                              progress=progress)\n        model.load_state_dict(state_dict)\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):\n    \"\"\"3x3 convolution with padding\"\"\"\n    return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,\n                     padding=dilation, groups=groups, bias=False, dilation=dilation)\n\n\ndef conv1x1(in_planes, out_planes, stride=1):\n    \"\"\"1x1 convolution\"\"\"\n    return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)\n\n\n\ndef resnext50_32x4d(pretrained=False, progress=True, **kwargs):\n    r\"\"\"ResNeXt-50 32x4d model from\n    `\"Aggregated Residual Transformation for Deep Neural Networks\" <https://arxiv.org/pdf/1611.05431.pdf>`_\n    Args:\n        pretrained (bool): If True, returns a model pre-trained on ImageNet\n        progress (bool): If True, displays a progress bar of the download to stderr\n    \"\"\"\n    kwargs['groups'] = 32\n    kwargs['width_per_group'] = 4\n    return _resnet('resnext50_32x4d', Bottleneck, [3, 4, 6, 3],\n                   pretrained, progress, **kwargs)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"original_model = resnext50_32x4d()\nnum_ftrs = original_model.fc.in_features\nmodel = mymodel(original_model, num_ftrs).to(device)\nmodel.load_state_dict(torch.load('../input/augmix-resnet/final_resnext_saved_weights.pth'))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b0aedef0-1eb0-4dfe-9a6c-144b16af3896","_cell_guid":"ecbc6dc8-b9cf-4af6-b25a-02b1cc9ae850","trusted":true},"cell_type":"markdown","source":"# Inference"},{"metadata":{"trusted":true},"cell_type":"code","source":"data_dir = '../input/bengaliai-cv19'\nfiles_train =[f'test_image_data_{fid}.parquet' for fid in range(4)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import albumentations\nfrom albumentations.pytorch.transforms import ToTensor\n\ntransforms_test = albumentations.Compose([\n    ToTensor()],\n    p=1.0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\n\nmodel.eval()\nrow_id = []\ntarget = []\n\nfor fname in files_train:\n    F = os.path.join(data_dir, fname)\n    df_test = pd.read_parquet(F)\n    df_test.set_index('image_id', inplace=True)\n    test_image = BengaliDataset(df_test, img_height=HEIGHT, img_width=WIDTH, transform=transforms_test)\n    test_loader = torch.utils.data.DataLoader(dataset = test_image, batch_size = BATCH_SIZE, num_workers=4, shuffle = False)\n    with torch.no_grad():\n        for idx, (img_ids, img) in enumerate(test_loader):\n            img = img.to(device)\n            pred_graphemes, pred_vowels, pred_consonants = model(img)\n            for img_id, pred_grapheme, pred_vowel, pred_consonant in zip(img_ids, pred_graphemes, pred_vowels, pred_consonants):\n                row_id.append(img_id + '_consonant_diacritic')\n                target.append(pred_consonant.argmax(0).cpu().detach().numpy())\n                row_id.append(img_id + '_grapheme_root')\n                target.append(pred_grapheme.argmax(0).cpu().detach().numpy())\n                row_id.append(img_id + '_vowel_diacritic')\n                target.append(pred_vowel.argmax(0).cpu().detach().numpy())\n                \n    del(df_test, test_image, test_loader)\n    gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_submission = pd.DataFrame(\n    {\n        'row_id': row_id,\n        'target': np.array(target)\n    },\n    columns=['row_id','target']\n)\n\n\ndf_submission.to_csv('submission.csv', index=False)\n\ndf_submission.head(10)","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}