{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"## TODO :\n# Run for 80 epochs with no cut mix and mixup, acc after 60 epochs is .95","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install ../input/pretrainedmodels/pretrainedmodels-0.7.4/pretrainedmodels-0.7.4/ > /dev/null # no output\n\n!pip install /kaggle/input/efficientnet-pytorch/efficientnet_pytorch-0.6.1-py3-none-any.whl > /dev/null","execution_count":null,"outputs":[]},{"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\n\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\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 numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport gc\nimport cv2\nimport pandas as pd\nimport numpy as np\nimport pyarrow.parquet as pq\nimport sys\n\nsys.path.insert(0, \"/kaggle/input/ghostnetbengali\")\n\n\nimport albumentations\nimport torch\nfrom PIL import Image\nfrom torch.utils.data import DataLoader\nfrom torch.utils.data import Dataset\nfrom tqdm import tqdm\nfrom torch.utils.data.sampler import SequentialSampler\n# from torchvision import transforms as torchtransforms\nimport torch.nn as nn\nfrom tqdm import tqdm_notebook as tqdm\nimport torchvision\nimport torch.nn.functional as F\nimport time\nfrom efficientnet_pytorch import EfficientNet\nfrom ghost_net import ghost_net\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\n# ======================\n# Params\n\n# I did not optimize the batch size\n# If the GPU has more memory available\n# you can increase this for faster inference\nBATCH_SIZE = 96\nN_WORKERS = 4\n\nHEIGHT = 137\nWIDTH = 236\n\nINPUT_PATH=\"/kaggle/input/bengaliai-cv19\"\nMODEL_BASE_PATH_EFNET_B3  = '/kaggle/input/bengaliaieffnetb3'\nMODEL_BASE_PATH_DN161FULL  = '/kaggle/input/densenet161full'\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\n# These are from my experiments (I did not upload the weights)\n# For this demo I used equal weights, but feel free to modify them.\n# Make sure the sum of the labels are equals to 1 (per label)\nENSEMBLES = [\n    {\n        'model': 'efficientnet_b3',\n        'model_state_file': MODEL_BASE_PATH_EFNET_B3 + '/efficientNet_fold2.pth',\n    # LB: 0.9635\n    },\n    {\n        'model': 'efficientnet_b3',\n        'model_state_file': MODEL_BASE_PATH_EFNET_B3 + '/efficientNet_fold4.pth',\n        # 0.9626\n    },\n    {\n        'model': 'densenet161',\n        'model_state_file': MODEL_BASE_PATH_DN161FULL + '/densenet161_b30',\n    # with cutout and Scalerotate\n    #  LB 0.9607\n    },\n]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"NUM_ENSEMBLE = len(ENSEMBLES)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for ensemble in ENSEMBLES:\n    ensemble['w_grapheme'] = 1 / NUM_ENSEMBLE\n    ensemble['w_vowel'] = 1 / NUM_ENSEMBLE\n    ensemble['w_conso'] = 1 / NUM_ENSEMBLE","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df = pd.read_csv(INPUT_PATH + ('/test.csv'))\nsubmission_df = pd.read_csv(INPUT_PATH + '/sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import pretrainedmodels\nfrom torch.nn.parameter import Parameter\nimport torch\nimport torch.nn as nn\nfrom torch.nn import functional as F\nfrom torch.nn.parameter import Parameter\n\n\nclass Identity(nn.Module):\n    def __init__(self):\n        super(Identity, self).__init__()\n        \n    def forward(self, x):\n        return x\n\n\nclass EfficientNetWrapper(nn.Module):\n    def __init__(self):\n        super(EfficientNetWrapper, self).__init__()\n        \n        # Load imagenet pre-trained model \n        self.effNet = EfficientNet.from_name('efficientnet-b3')\n        \n        # Appdend output layers based on our date\n        self.fc_root = nn.Linear(in_features=1000, out_features=168)\n        self.fc_vowel = nn.Linear(in_features=1000, out_features=11)\n        self.fc_consonant = nn.Linear(in_features=1000, out_features=7)\n        \n    def forward(self, X):\n        output = self.effNet(X)\n        output_root = self.fc_root(output)\n        output_vowel = self.fc_vowel(output)\n        output_consonant = self.fc_consonant(output)\n        \n        return output_vowel, output_root, output_consonant\n    \n    \nclass DenseNet161(nn.Module):\n    def __init__(self, pretrained):\n        super(DenseNet161, self).__init__()\n        if pretrained == True:\n            self.model = pretrainedmodels.__dict__[\"densenet161\"](pretrained=\"imagenet\")\n        else:\n            self.model = pretrainedmodels.__dict__[\"densenet161\"](pretrained=None)\n\n        # self.model.features.gem = GeM()\n        self.model.last_linear = Identity()\n\n        self.l0 = nn.Linear(2208, 11)\n        self.l1 = nn.Linear(2208, 168)\n        self.l2 = nn.Linear(2208, 7)\n\n    def forward(self, x):\n        bs, _, _, _ = x.shape\n        x = self.model.features(x)\n        x = F.adaptive_avg_pool2d(x, 1).reshape(bs, -1)\n\n        l0 = self.l0(x)\n        l1 = self.l1(x)\n        l2 = self.l2(x)\n        return l0, l1, l2\n","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=128, pad=16):\n    HEIGHT = 137\n    WIDTH = 236\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))\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class ClsTestDataset(Dataset):\n\n    def __init__(self, num_samples=1, model='densenet161'):\n        \n        self.num_samples = num_samples\n        self.images = np.zeros([num_samples, WIDTH * HEIGHT], dtype=np.uint8)\n    \n        self.aug = albumentations.Compose([\n            albumentations.Resize(int(HEIGHT), int(WIDTH), always_apply=True),\n            albumentations.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225], always_apply=True)\n        ])\n    \n        img_id = 0\n\n        for i in tqdm(range(4)):\n            datafile = INPUT_PATH + '/test_image_data_{}.parquet'.format(i)\n            parq = pq.read_pandas(datafile, columns=[str(x) for x in range(32332)]).to_pandas()\n            parq =  parq.iloc[:, :].values.reshape(-1, HEIGHT, WIDTH).astype(np.uint8)\n            \n            for idx, image in enumerate(parq):\n                self.images[img_id, ...] = image.reshape(-1).astype(np.uint8)\n                img_id = img_id + 1\n                \n        del parq\n\n    def __len__(self):\n        return len(self.images)\n\n    def __getitem__(self, idx):\n        \n        img = self.images[idx]\n        img = img.reshape(HEIGHT, WIDTH).astype(float)\n        img = Image.fromarray(img).convert(\"RGB\")\n        img = self.aug(image=np.array(img))[\"image\"]\n        img = np.transpose(img, (2, 0, 1)).astype(np.float32)\n        img = torch.tensor(img, dtype=torch.float)\n\n        return {\n            \"image\": img,\n            \"image_id\": idx\n        }","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"bengali_dataset = ClsTestDataset(num_samples = test_df.shape[0] // 3)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# If you'd like to use different batch size for\n# different size models (tip #12)\ndata_loader_test = torch.utils.data.DataLoader(\n    bengali_dataset,\n    batch_size=BATCH_SIZE,\n    num_workers=N_WORKERS,\n    sampler=SequentialSampler(bengali_dataset),\n    shuffle=False\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Predictions\nsize = submission_df.shape[0] // 3\nresults = {\n    'grapheme_root': np.zeros((len(ENSEMBLES), size, 168), dtype=np.float),\n    'vowel_diacritic': np.zeros((len(ENSEMBLES), size, 11), dtype=np.float),\n    'consonant_diacritic': np.zeros((len(ENSEMBLES), size, 7), dtype=np.float),\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for model_idx, ensemble in enumerate(ENSEMBLES):\n        \n    if ensemble['model'].lower() == 'densenet161':\n        model = DenseNet161(pretrained=False)\n    elif ensemble['model'].lower() == 'efficientnet_b3':\n        model = EfficientNetWrapper()\n    else:\n        raise ValueError\n    \n\n    model_state = torch.load(ensemble['model_state_file'], map_location='cuda:0')\n    model.load_state_dict(model_state)\n    model.eval()\n    if torch.cuda.is_available():\n        model.cuda()\n    del model_state\n    \n    for batch_idx, data in enumerate(data_loader_test):\n        images = data['image']\n        image_idx = data['image_id']\n\n        if torch.cuda.is_available():\n            images = images.float().cuda()\n        else:\n            images = images.float()\n        \n        with torch.no_grad():\n            out_vowel, out_graph, out_conso = model(images)\n                              \n                    \n        out_graph = F.softmax(out_graph, dim=1).data.cpu().numpy() * ensemble['w_grapheme']\n        out_vowel = F.softmax(out_vowel, dim=1).data.cpu().numpy() * ensemble['w_vowel']\n        out_conso = F.softmax(out_conso, dim=1).data.cpu().numpy() * ensemble['w_conso']\n\n        start = batch_idx * BATCH_SIZE\n        end = min((batch_idx + 1) * BATCH_SIZE, submission_df.shape[0] // 3)\n\n        results['grapheme_root'][model_idx, start:end, :] = out_graph\n        results['vowel_diacritic'][model_idx, start:end, :] = out_vowel\n        results['consonant_diacritic'][model_idx, start:end, :] = out_conso\n        \n        del images\n        del out_graph, out_vowel, out_conso\n            \n    del model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Clean-up\ndel data_loader_test\ndel bengali_dataset\ndel test_df\n\ngc.collect()\n%reset -f out","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df = pd.read_csv(INPUT_PATH + '/sample_submission.csv')\nsubmission_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for l in ['grapheme_root', 'vowel_diacritic', 'consonant_diacritic']:\n    idx = submission_df[submission_df['row_id'].str.contains(l)].index\n    submission_df.iloc[idx, 1] = results[l].sum(axis=0).argmax(axis=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df.to_csv('./submission.csv', index=False)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df.head(100)","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}