{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Description\nThis kernel performs inference for [Grapheme fast.ai starter](https://www.kaggle.com/iafoss/grapheme-fast-ai-starter) kernel. Check it for more training details. The image preprocessing pipline is provided [here](https://www.kaggle.com/iafoss/image-preprocessing-128x128)."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"%reload_ext autoreload\n%autoreload 2\n%matplotlib inline\n\nimport cv2\nfrom tqdm import tqdm_notebook as tqdm\nimport fastai\nfrom fastai.vision import *\nimport os\nfrom mish_activation import *\nimport warnings\nwarnings.filterwarnings(\"ignore\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"HEIGHT = 137\nWIDTH = 236\nSIZE = 128\nbs = 128\nstats = (0.0692, 0.2051)\narch = models.densenet121\nMODEL = '../input/grapheme-fast-ai-starter/model_0.pth'\nnworkers = 2\n\nTEST = ['/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']\n\nLABELS = '../input/bengaliai-cv19/train.csv'\n\ndf = pd.read_csv(LABELS)\nnunique = list(df.nunique())[1:-1]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Model"},{"metadata":{"_kg_hide-output":false,"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"class Head(nn.Module):\n    def __init__(self, nc, n, ps=0.5):\n        super().__init__()\n        layers = [AdaptiveConcatPool2d(), Mish(), Flatten()] + \\\n            bn_drop_lin(nc*2, 512, True, ps, Mish()) + \\\n            bn_drop_lin(512, n, True, ps)\n        self.fc = nn.Sequential(*layers)\n        self._init_weight()\n        \n    def _init_weight(self):\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                torch.nn.init.kaiming_normal_(m.weight)\n            elif isinstance(m, nn.BatchNorm2d):\n                m.weight.data.fill_(1.0)\n                m.bias.data.zero_()\n        \n    def forward(self, x):\n        return self.fc(x)\n\n#change the first conv to accept 1 chanel input\nclass Dnet_1ch(nn.Module):\n    def __init__(self, arch=arch, n=nunique, pre=True, ps=0.5):\n        super().__init__()\n        m = arch(True) if pre else arch()\n        \n        conv = nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3, bias=False)\n        w = (m.features.conv0.weight.sum(1)).unsqueeze(1)\n        conv.weight = nn.Parameter(w)\n        \n        self.layer0 = nn.Sequential(conv, m.features.norm0, nn.ReLU(inplace=True))\n        self.layer1 = nn.Sequential(\n            nn.MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False),\n            m.features.denseblock1)\n        self.layer2 = nn.Sequential(m.features.transition1,m.features.denseblock2)\n        self.layer3 = nn.Sequential(m.features.transition2,m.features.denseblock3)\n        self.layer4 = nn.Sequential(m.features.transition3,m.features.denseblock4,\n                                    m.features.norm5)\n        \n        nc = self.layer4[-1].weight.shape[0]\n        self.head1 = Head(nc,n[0])\n        self.head2 = Head(nc,n[1])\n        self.head3 = Head(nc,n[2])\n        #to_Mish(self.layer0), to_Mish(self.layer1), to_Mish(self.layer2)\n        #to_Mish(self.layer3), to_Mish(self.layer4)\n        \n    def forward(self, x):    \n        x = self.layer0(x)\n        x = self.layer1(x)\n        x = self.layer2(x)\n        x = self.layer3(x)\n        x = self.layer4(x)\n        \n        x1 = self.head1(x)\n        x2 = self.head2(x)\n        x3 = self.head3(x)\n        \n        return x1,x2,x3","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = Dnet_1ch(pre=False).cuda()\nmodel.load_state_dict(torch.load(MODEL, map_location=torch.device('cpu')));\nmodel.eval();","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"#check https://www.kaggle.com/iafoss/image-preprocessing-128x128\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    #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    return cv2.resize(img,(size,size))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class GraphemeDataset(Dataset):\n    def __init__(self, fname):\n        self.df = pd.read_parquet(fname)\n        self.data = 255 - self.df.iloc[:, 1:].values.reshape(-1, HEIGHT, WIDTH).astype(np.uint8)\n\n    def __len__(self):\n        return len(self.data)\n\n    def __getitem__(self, idx):\n        name = self.df.iloc[idx,0]\n        #normalize each image by its max val\n        img = (self.data[idx]*(255.0/self.data[idx].max())).astype(np.uint8)\n        img = crop_resize(img)\n        img = (img.astype(np.float32)/255.0 - stats[0])/stats[1]\n        return img, name","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Prediction"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"row_id,target = [],[]\nfor fname in TEST:\n    ds = GraphemeDataset(fname)\n    dl = DataLoader(ds, batch_size=bs, num_workers=nworkers, shuffle=False)\n    with torch.no_grad():\n        for x,y in tqdm(dl):\n            x = x.unsqueeze(1).cuda()\n            p1,p2,p3 = model(x)\n            p1 = p1.argmax(-1).view(-1).cpu()\n            p2 = p2.argmax(-1).view(-1).cpu()\n            p3 = p3.argmax(-1).view(-1).cpu()\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 += [p1[idx].item(),p2[idx].item(),p3[idx].item()]\n                \nsub_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":1}