{"cells":[{"metadata":{},"cell_type":"markdown","source":"### This is a starter kernel written in Pytorch using Resnet-34.\n### Only basic data pre-processing is used.\n### This kernel is not a high score kernel and only aims to help in starting.\n### If you want to know about data, do visit my EDA kernel:\n[https://www.kaggle.com/bitthal/bengali-dataset-eda](https://www.kaggle.com/bitthal/bengali-dataset-eda)\n\n### Do upvote if you find this kernel useful."},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","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 matplotlib.pyplot as plt\nimport os\nimport gc\n\n\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, utils\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\n\n\nfrom sklearn.model_selection import train_test_split\n\nBASE_DIR = '/kaggle/input/bengaliai-cv19'\n\n# os.listdir(BASE_DIR)","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"train_df = pd.read_csv(os.path.join(BASE_DIR, 'train.csv'))\nprint('Shape of train_df: ', train_df.shape)\n\ntest_df = pd.read_csv(os.path.join(BASE_DIR, 'test.csv'))\nprint('Shape of test_df: ', test_df.shape)\n\nclass_map = pd.read_csv(os.path.join(BASE_DIR, 'class_map.csv'))\nprint('Shape of class_map: ', class_map.shape)\n\nsample_submission_df = pd.read_csv(os.path.join(BASE_DIR, 'sample_submission.csv'))\nprint('Shape of sample submission: ', sample_submission_df.shape)\n\ntrain_image_files = [x for x in os.listdir(BASE_DIR) if 'train_ima' in x]\nprint(\"Number of Train Image files: \", len(train_image_files))\n\ntest_image_files = [x for x in os.listdir(BASE_DIR) if 'test_ima' in x]\nprint(\"Number of Train Image files: \", len(test_image_files))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Preprocessing Utils"},{"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 < ORIGINAL_WIDTH - 13) else ORIGINAL_WIDTH\n    ymax = ymax + 10 if (ymax < ORIGINAL_HEIGHT - 10) else ORIGINAL_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    \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":{},"cell_type":"markdown","source":"#### Code for generating feather files"},{"metadata":{"trusted":true},"cell_type":"code","source":"ORIGINAL_HEIGHT = 137\nORIGINAL_WIDTH = 236\n\ndef load_npa(file):\n    df = pd.read_parquet(file)\n    return df.iloc[:, 1:].values.reshape(-1, ORIGINAL_HEIGHT, ORIGINAL_WIDTH)\n\ndef load_pa_df(file):\n    df = pd.read_parquet(file)\n    df = df.set_index('image_id')\n    return df\n\n# loading one of the parquest file for analysis\ntrain_image_files.sort()\ntrain_images = [load_pa_df(os.path.join(BASE_DIR, x)) for x in train_image_files]\nfor images in train_images:\n    print(\"Number of images in loaded files: \", images.shape[0])\n    \nprint(\"Number of columns in image df: \", images.shape[1])\nfor i, images in enumerate(train_images):\n    print(\"Number of images in loaded file {}: {}\\n\".format(i, images.shape[0]))\n    print(\"Images in loaded file {}: {}\\n\\n\".format(i, images.index.values))\n#     print(images.head())\n\ndef get_image_from_dfrow(df, row_id):\n    df_row = df.iloc[row_id]\n    pixel_values = df_row.values[1:]\n    return df_row.values.reshape(ORIGINAL_HEIGHT, ORIGINAL_WIDTH).astype('int')\n\nf, ax = plt.subplots(5, 5, figsize=(16, 8))\nfor i in range(5):\n    for j in range(5):\n        ax[i][j].imshow(get_image_from_dfrow(train_images[0], i*5+j), cmap='Greys')\n\nfrom tqdm import tqdm\nimport cv2\nfor i, images in enumerate(train_images):\n    train_images[i] = Resize(train_images[i])\n    \n# save for faster training\ntrain_images[0].to_feather('train-images0.feather')\ntrain_images[1].to_feather('train-images1.feather')\ntrain_images[2].to_feather('train-images2.feather')\ntrain_images[3].to_feather('train-images3.feather')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# train_images = [None, None, None, None]\n# train_images[0] = pd.read_feather('train-images0.feather')\n# train_images[1] = pd.read_feather('train-images1.feather')\n# train_images[2] = pd.read_feather('train-images2.feather')\n# train_images[3] = pd.read_feather('train-images3.feather')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_image_from_dfrow(df, row_id):\n    df_row = df.iloc[row_id]\n    pixel_values = df_row.values[1:]\n    return pixel_values.reshape(128, 128).astype('int')\n\nf, ax = plt.subplots(5, 5, figsize=(16, 8))\nfor i in range(5):\n    for j in range(5):\n        ax[i][j].imshow(get_image_from_dfrow(train_images[0], i*5+j), cmap='Greys')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data_full = pd.concat(train_images,ignore_index=True)\n\ndel train_images\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class GraphemeDataset(Dataset):\n    def __init__(self,df,label=None,_type='train',transform =True,aug=None):\n        df = df.set_index('image_id')\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.label)\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            name = self.label.image_id.values[idx]\n            image = self.df.loc[name].values.reshape(SIZE,SIZE).astype(np.float)\n                        \n#             augment = self.aug(image =image)\n#             image = augment['image']\n            img_ = image.reshape(1, 128, 128)\n\n            return img_, [label1, label2, label3]\n        else:\n            image = self.df.loc[name].values.reshape(SIZE,SIZE).astype(np.float)\n            return image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"HEIGHT = 128\nWIDTH = 128\n\nBATCH = 16\n\ntrain, test = train_test_split(train_df, test_size=0.1)\n\ntrain.reset_index(inplace=True)\ntest.reset_index(inplace=True)\n\nsplit_train_df = train\nsplit_val_df = test\n\nprint(\"Train Data Shape: \", split_train_df.shape)\nprint(\"Test Data Shape: \", split_val_df.shape)\n\ntrain_dataset = GraphemeDataset(data_full ,split_train_df,transform = False)\nval_dataset = GraphemeDataset(data_full , split_val_df,transform = False)\n\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH, shuffle=True, num_workers=2)\nval_loader = DataLoader(val_dataset, batch_size=BATCH, shuffle=False, num_workers=2)","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":"class ResidualBlock(nn.Module):\n    def __init__(self,in_channels,out_channels,stride=1,kernel_size=3,padding=1,bias=False):\n        super(ResidualBlock,self).__init__()\n        self.cnn1 =nn.Sequential(\n            nn.Conv2d(in_channels,out_channels,kernel_size,stride,padding,bias=False),\n            nn.BatchNorm2d(out_channels),\n            nn.ReLU(True)\n        )\n        self.cnn2 = nn.Sequential(\n            nn.Conv2d(out_channels,out_channels,kernel_size,1,padding,bias=False),\n            nn.BatchNorm2d(out_channels)\n        )\n        if stride != 1 or in_channels != out_channels:\n            self.shortcut = nn.Sequential(\n                nn.Conv2d(in_channels,out_channels,kernel_size=1,stride=stride,bias=False),\n                nn.BatchNorm2d(out_channels)\n            )\n        else:\n            self.shortcut = nn.Sequential()\n            \n    def forward(self,x):\n        residual = x\n        x = self.cnn1(x)\n        x = self.cnn2(x)\n        x += self.shortcut(residual)\n        x = nn.ReLU(True)(x)\n        return x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class ResNet34(nn.Module):\n    def __init__(self):\n        super(ResNet34,self).__init__()\n        \n        self.block1 = nn.Sequential(\n            nn.Conv2d(1,64,kernel_size=2,stride=2,padding=3,bias=False),\n            nn.BatchNorm2d(64),\n            nn.ReLU(True)\n        )\n        \n        self.block2 = nn.Sequential(\n            nn.MaxPool2d(1,1),\n            ResidualBlock(64,64),\n            ResidualBlock(64,64,2)\n        )\n        \n        self.block3 = nn.Sequential(\n            ResidualBlock(64,128),\n            ResidualBlock(128,128,2)\n        )\n        \n        self.block4 = nn.Sequential(\n            ResidualBlock(128,256),\n            ResidualBlock(256,256,2)\n        )\n        self.block5 = nn.Sequential(\n            ResidualBlock(256,512),\n            ResidualBlock(512,512,2)\n        )\n        \n        self.avgpool = nn.AvgPool2d(2)\n        # vowel_diacritic\n        self.fc = nn.Linear(2048,512)\n        self.fc1 = nn.Linear(512,11)\n        # grapheme_root\n        self.fc2 = nn.Linear(512,168)\n        # consonant_diacritic\n        self.fc3 = nn.Linear(512,7)\n        \n    def forward(self,x):\n        x = self.block1(x)\n        x = self.block2(x)\n        x = self.block3(x)\n        x = self.block4(x)\n        x = self.block5(x)\n        x = self.avgpool(x)\n        x = x.view(x.size(0),-1)\n        x = self.fc(x)\n        x1 = self.fc1(x)\n        x2 = self.fc2(x)\n        x3 = self.fc3(x)\n        return x1,x2,x3\n    \nmodel = ResNet34()\nmodel.to(device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Model Test\n# model(torch.Tensor(np.zeros((2, 1, 128, 128))).to(device))\n# os.listdir('/kaggle/input/')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## Loaidng on CPU\n# model.load_state_dict(torch.load('/kaggle/input/v5-data/model_v3_39.pth', map_location=torch.device('cpu')))\n## Loading on GPU\nmodel.load_state_dict(torch.load('/kaggle/input/v5-data/model_v3_39.pth'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.0001)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def accuracy(prediction, labels):\n    ans = 0\n    for pred, y in zip(prediction, labels):\n        _, pred = torch.max(pred.data, 1)\n        ans += (pred == y).sum().item()\n    return ans","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## Running for 1 Epoch for demonstration\nfor epoch in range(1):  # loop over the dataset multiple times\n\n    train_loss = 0\n    test_loss = 0\n    train_accuracy = 0\n    test_accuracy = 0\n    count = 0\n    model.train()\n    for i, data in enumerate(train_loader, 0):\n        inputs, labels = data\n        \n        inputs = inputs.to(device, dtype=torch.float)\n        labels = [x.to(device) for x in labels]\n        \n        optimizer.zero_grad()\n        outputs = model(inputs)\n        \n        loss = criterion(outputs[0], labels[0]) + criterion(outputs[1], labels[1]) + criterion(outputs[2], labels[2])\n        loss.backward()\n        optimizer.step()\n        \n        train_accuracy += accuracy(outputs, labels)\n        train_loss += loss.item()\n\n        if (i+1) % 2000 == 0:\n            print(\"Step: {}, TrainAccuracy: {}, TrainLoss: {}\".format(i+1, train_accuracy/((i+1)*BATCH*3), train_loss))\n            \n\n\n    \n    model.eval()\n    with torch.no_grad():\n        for i, data in enumerate(val_loader, 0):\n            inputs, labels = data\n\n            inputs = inputs.to(device, dtype=torch.float)\n            labels = [x.to(device) for x in labels]\n            outputs = model(inputs)\n            \n            loss = criterion(outputs[0], labels[0]) + criterion(outputs[1], labels[1]) + criterion(outputs[2], labels[2])\n\n            test_accuracy += accuracy(outputs, labels)\n            test_loss += loss.item()\n\n        \n        \n        \n        \n        \n        \n    print('Epoch: {}, TrainLoss: {}, TestLoss: {}, TrainAccuracy: {}, TestAccuracy: {}'.format(epoch + 1,\n                                                                                        train_loss, test_loss, \n                                                                                        train_accuracy/(len(train_dataset)*3), test_accuracy/(len(val_dataset)*3)))\n    if (epoch) % 2 == 0:\n        torch.save(model.state_dict(), 'model_v3_{}.pth'.format(epoch+35))\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"del data_full, train_loader, val_loader\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df = pd.read_csv(os.path.join(BASE_DIR, 'test.csv'))\nprint('Shape of test_df: ', test_df.shape)\nprint(test_df.head(), '\\n\\n')\n\nsample_submission_df = pd.read_csv(os.path.join(BASE_DIR, 'sample_submission.csv'))\nprint('Shape of sample submission: ', sample_submission_df.shape)\nprint(sample_submission_df.head())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# loading one of the parquest file for analysis\ntest_image_files.sort()\ntest_images = [load_pa_df(os.path.join(BASE_DIR, x)) for x in test_image_files]\nfor images in test_images:\n    print(\"Number of images in loaded files: \", images.shape[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from tqdm import tqdm\nimport cv2\nfor i, images in enumerate(test_images):\n    test_images[i] = Resize(test_images[i])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_data_full = pd.concat(test_images,ignore_index=True)\n\nclass GraphemeDatasetTest(Dataset):\n    def __init__(self,df,label=None,_type='train',transform =True,aug=None):\n        df = df.set_index('image_id')\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.label)\n    \n    def __getitem__(self,idx):\n        name = self.label[idx]\n        image = self.df.loc[name].values.reshape(SIZE,SIZE).astype(np.float)\n\n        img_ = image.reshape(1, 128, 128)\n\n        return img_, name\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\ntest_dataset = GraphemeDatasetTest(test_data_full , test_df.image_id.unique(),transform = False)\ntest_loader = DataLoader(test_dataset, batch_size=1, shuffle=False, num_workers=2)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.eval()\npredictions = []\nbatch_size=1\nnames = []\nwith torch.no_grad():\n    for idx, (inputs, tag) in enumerate(test_loader):\n        inputs = inputs.to(device, dtype=torch.float)\n\n        outputs1,outputs2,outputs3 = model(inputs)\n        \n        predictions.append(outputs3.argmax(1).cpu().detach().numpy()[0])\n        predictions.append(outputs2.argmax(1).cpu().detach().numpy()[0])\n        predictions.append(outputs1.argmax(1).cpu().detach().numpy()[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.read_csv('/kaggle/input/bengaliai-cv19/sample_submission.csv')\nsubmission.target = np.hstack(predictions)\nsubmission.head(10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission.to_csv('submission.csv',index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.8"}},"nbformat":4,"nbformat_minor":1}