{"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)\nimport matplotlib.pyplot as plt  # plotting \n#import matplotlib.font_manager as fm # to plot the font\nfrom tqdm.auto import tqdm # see progress bar\n#import PIL.Image as Image, PIL.ImageDraw as ImageDraw, PIL.ImageFont as ImageFont # Darw picture from font\nfrom skimage.transform import resize # Resizing of image\nfrom cv2 import resize as cv2_resize # resizng of image\nfrom keras.preprocessing.image import ImageDataGenerator  # image augmentation on training images ONLY\n#from sklearn.model_selection import train_test_split  # splitting the data\nimport keras.backend as K # for custom metrices implementations and other processes that we define\nfrom keras.layers import Dense,BatchNormalization,Input,Dropout,Conv2D,Flatten,MaxPool2D,LeakyReLU,Activation,Concatenate # keras layers\nfrom keras.models import Model #Model class\nfrom keras.optimizers import Adam #optimizer\nfrom keras.callbacks import ReduceLROnPlateau,ModelCheckpoint,EarlyStopping\nfrom keras import  models\nfrom keras import optimizers\nfrom keras import applications\nfrom keras.utils import to_categorical\n# Call backs acts like milestones and if/else while model is being trained\nimport gc # garbage collector\nimport sys\nimport cv2\nimport time\nfrom keras.preprocessing.image import ImageDataGenerator\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\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":{"trusted":true},"cell_type":"code","source":"BATCH_SIZE = 100\n\nTEST_IMG_PATH = '../input/bengaliai-cv19/test_image_data_'\nFILE_TYPE = '.parquet'\n\ntrain_classes = pd.read_csv(\"../input/bengaliai-cv19/train.csv\")\ntrain_classes['g_num'] = train_classes.apply(lambda row: row.grapheme_root + 168*row.vowel_diacritic+168*11*row.consonant_diacritic, axis=1)\n\ntrain_classes.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Grapheme Visualization"},{"metadata":{"trusted":true},"cell_type":"code","source":"testdata = np.load('/kaggle/input/data128c/dataC/data0.npy')\ntestdata = testdata.reshape(testdata.shape[0],128,128,1)\n\nindex = train_classes.loc[train_classes['g_num'] == 15].index.values.astype(int)[0:30]\n\nsubset = testdata[index,:,:]\nsubset = subset.reshape(subset.shape[0],128,128,1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"w=10\nh=10\nfig=plt.figure(figsize=(12, 12))\ncolumns = 5\nrows = 4\nfor i in range(0, columns*rows):\n    img = testdata[i,:,:,0]\n    fig.add_subplot(rows, columns, i+1)\n    plt.imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"w=10\nh=10\nfig=plt.figure(figsize=(12, 12))\ncolumns = 5\nrows = 4\nfor i in range(0, columns*rows):\n    img = subset[i,:,:,0]\n    fig.add_subplot(rows, columns, i+1)\n    plt.imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"del testdata\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class CustomDataGenerator(ImageDataGenerator):\n    '''\n    This class extends the ImageDataGenerator but as the parent class only map 1 class label to each image\n    For example it can only map if a picture of car is black or white but we are trying to map it to\n    N classes so that it can override the default flow() and provide a mapping of a car to color,model,\n    company etc. Specially useful if you have different losses for each class so you have to pass a dict\n    of y_labels\n    \n    This code's credit goes to - https://github.com/keras-team/keras/issues/12639\n    '''\n    \n    def flow(self,x,y=None,batch_size=BATCH_SIZE,shuffle=True,sample_weight=None,seed=None,save_to_dir=None,\n             save_prefix='',save_format='png',subset=None): \n        '''\n        Function takes data & label arrays, generates batches of augmented data (#official keras Documents)\n        Input:\n            x: Flow method looks for Rank-4 numpy array. i.e (number_of_images,width,height,channels)\n            y: dictonary which maps each picture to its ONE-HOT ENCODES respective classes such as  \n            if Image1 is associated to 3 classes in a way ->[0,1,2] and Image2 is associated as [3,4,5] so\n            the y will be as y={'y1':to_categorical([0,3]),'y2':to_categorical([1,4])...and so on} \n            others: default settings of parameters in the original flow() method\n        Output:\n            Just like the default flow(), it'll generate an instance of image array x  but instead of a \n            single y-label/class mapping it'll produce a a dictonary as label_dict that contains mapping \n            of all the classes for that image\n        '''\n\n        labels_array = None # all the labels array will be concatenated in this single array\n        key_lengths = {} \n        # define a dict which maps the 'key' (y1,y2 etc) to lengths of corresponding label_array\n        ordered_labels = [] # to store the ordering in which the labels Y were passed in this class\n        for key, label_value in y.items():\n            if labels_array is None:\n                labels_array = label_value \n                # for the first time loop, it's empty, so insert first element\n            else:\n                labels_array = np.concatenate((labels_array, label_value), axis=1) \n                # concat each array of y_labels \n                \n            key_lengths[key] = label_value.shape[1] \n            # key lengths will be different for different range of classes in each class due to_categorical \n            # ONE-HOT encodings. Ex- some have 2 classes (red,yellow) but other can have 4 \n            # (Audi,BMW,Ferrari,Toyota) so we have to keep track  due to inner working of super().flow()\n            ordered_labels.append(key)\n\n\n        for x_out, y_out in super().flow(x, labels_array, batch_size=batch_size):\n            label_dict = {} # final dictonary that'll be yielded\n            i = 0 # keeps count of the ordering of the labels and their lengths\n            for label in ordered_labels:\n                target_length = key_lengths[label]\n                label_dict[label] = y_out[:, i: i + target_length] \n                # Extract to-from the range of length of labels values. That is why we had ordered_labels\n                # and key_lengths It'll extract the elements ordering vise else there will be conflict\n                i += target_length\n\n            yield x_out, label_dict","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import sys\ndef sizeof_fmt(num, suffix='B'):\n    ''' by Fred Cirera,  https://stackoverflow.com/a/1094933/1870254, modified'''\n    for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:\n        if abs(num) < 1024.0:\n            return \"%3.1f %s%s\" % (num, unit, suffix)\n        num /= 1024.0\n    return \"%.1f %s%s\" % (num, 'Yi', suffix)\n\nfor name, size in sorted(((name, sys.getsizeof(value)) for name, value in locals().items()),\n                         key= lambda x: -x[1])[:10]:\n    print(\"{:>30}: {:>8}\".format(name, sizeof_fmt(size)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Using library backbone with pretrained weights"},{"metadata":{"trusted":true},"cell_type":"code","source":"img_input = Input(shape=(128,128,1))\nimg_conc = Concatenate()([img_input, img_input, img_input]) \n\ndensenet_base = applications.InceptionResNetV2(input_tensor=img_conc, include_top=False, weights='imagenet')\n\nx = densenet_base.output\nx = Flatten()(x)\nx = Dropout(0.35)(x)\n\nx = Dense(800,activation='relu')(x)\nx = BatchNormalization()(x)\nx = Dropout(rate = 0.35)(x)\n\nx = Dense(800,activation='relu')(x)\nx = BatchNormalization()(x)\nx = Dropout(rate = 0.35)(x)\n\nx = Dense(800,activation='relu')(x)\nx = BatchNormalization()(x)\nx = Dropout(rate = 0.35)(x)\n\nout1 = Dense(168, activation = 'softmax',name='out_1')(x) # names of output layers. We need these names\nout2 = Dense(11, activation = 'softmax',name='out_2')(x)  # as they act as the keys for mapping output\nout3 = Dense(7, activation = 'softmax',name='out_3')(x)   # to each later. See in the model.fit()\n\nmodel = models.Model(inputs=img_input, outputs=[out1,out2,out3])\n#model = models.Model(inputs=resnet_base.input, outputs=[out1,out2,out3])\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.load_weights('/kaggle/input/weights19/network6_128input_Augmentation5.h5')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Loading a quater of the training data"},{"metadata":{"trusted":true},"cell_type":"code","source":"from random import randrange\n\ndef create_training_batch(j):\n    #j = randrange(3)\n    print(j)\n    #train_X = pd.read_parquet(TRAIN_IMG_PATH+str(j)+FILE_TYPE) # import any random image file given in input\n    #train_X = pd.read_feather('train_data_'+str(j)+'.feather')\n    #train_X = train_X.iloc[:,1:].values.reshape(train_X.shape[0],64,64,1).astype('float32')/255.\n    train_X = np.load('/kaggle/input/data128c/dataC/data'+str(j)+'.npy')\n    train_X = train_X.reshape(train_X.shape[0],128,128,1).astype('float32')/255.\n    \n\n    \n    if j == 0:\n        train_Y = train_classes[['grapheme_root','vowel_diacritic','consonant_diacritic']].iloc[:50210]\n    if j == 1:\n        train_Y = train_classes[['grapheme_root','vowel_diacritic','consonant_diacritic']].iloc[50210:100420]\n    if j == 2:\n        train_Y = train_classes[['grapheme_root','vowel_diacritic','consonant_diacritic']].iloc[100420:150630]\n    if j == 3:\n        train_Y = train_classes[['grapheme_root','vowel_diacritic','consonant_diacritic']].iloc[150630:200840]\n    train_Y1 = to_categorical(train_Y['grapheme_root'],num_classes=168)\n    train_Y2 = to_categorical(train_Y['vowel_diacritic'],num_classes=11)\n    train_Y3 = to_categorical(train_Y['consonant_diacritic'],num_classes=7)\n    \n    \n    return train_X, train_Y1, train_Y2, train_Y3","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Training model with cross validation"},{"metadata":{"trusted":true},"cell_type":"code","source":"adam = optimizers.Adam(lr=0.00002)\nmodel.compile(adam, loss='categorical_crossentropy', metrics=[\"accuracy\"])\n\nfor i in range(0):\n    print(i)\n    train_X, train_Y1, train_Y2, train_Y3 = create_training_batch(i%4)\n    \n    datagen = CustomDataGenerator(\n        featurewise_center=False,\n        featurewise_std_normalization=False,\n        rotation_range=7,\n        width_shift_range=6,\n        height_shift_range=8,\n        #brightness_range= [0.1,1],\n        shear_range = 5,\n        zoom_range = 0.08,\n        horizontal_flip=False)\n\n    datagen.fit(train_X)\n    \n    if i%4 != 0:\n        history = model.fit_generator(datagen.flow(train_X, {'out_1': train_Y1,'out_2': train_Y2,'out_3': train_Y3},shuffle = False, batch_size=128),\n                    steps_per_epoch=train_X.shape[0] / 128, epochs=1)\n        \n    if i%4 == 0:\n        test_X = train_X[40000:50210,:,:,:]\n        test_Y1 = train_Y1[40000:50210,:]\n        test_Y2 = train_Y2[40000:50210,:]\n        test_Y3 = train_Y3[40000:50210,:]\n        \n        train_X = train_X[:40000,:,:,:]\n        train_Y1 = train_Y1[:40000,:]\n        train_Y2 = train_Y2[:40000,:]\n        train_Y3 = train_Y3[:40000,:]\n        \n        history = model.fit_generator(datagen.flow(train_X, {'out_1': train_Y1,'out_2': train_Y2,'out_3': train_Y3},shuffle = False, batch_size=128),\n                    steps_per_epoch=train_X.shape[0] / 128, epochs=1, validation_data = (test_X, [test_Y1, test_Y2, test_Y3]))\n        \n        del test_X\n        del test_Y1\n        del test_Y2\n        del test_Y3\n    \n    \n    del train_X\n    del train_Y1\n    del train_Y2\n    del train_Y3\n    gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"adam = optimizers.Adam(lr=0.0001)\nmodel.compile(adam, loss='categorical_crossentropy', metrics=[\"accuracy\"])\n\nfor i in range(0):\n    train_X, train_Y1, train_Y2, train_Y3 = create_training_batch(i%4)\n    print(i)\n    \n    datagen = CustomDataGenerator(\n        featurewise_center=False,\n        featurewise_std_normalization=False,\n        rotation_range=7,\n        width_shift_range=6,\n        height_shift_range=8,\n        #brightness_range= [0.1,1],\n        shear_range = 5,\n        zoom_range = 0.08,\n        horizontal_flip=False)\n\n    datagen.fit(train_X)\n    \n    history = model.fit_generator(datagen.flow(train_X, {'out_1': train_Y1,'out_2': train_Y2,'out_3': train_Y3},shuffle = False, batch_size=128),\n                    steps_per_epoch=train_X.shape[0] / 128, epochs=1)\n\n    \n    \n    del train_X\n    del train_Y1\n    del train_Y2\n    del train_Y3\n    gc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Saving model and weights"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Save the model\n#model.save('network6_128input_Augmentation5.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#from IPython.display import FileLink\n#FileLink('network6_128input_Augmentation5.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# serialize model to JSON\n#model_json = model.to_json()\n#with open(\"network6.json\", \"w\") as json_file:\n#    json_file.write(model_json)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#FileLink('network6.json')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Testing accuracy on validation test set"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_X, train_Y1, train_Y2, train_Y3 = create_training_batch(0)\n\ntest_X = train_X[40000:50210,:,:,:]\ntest_Y1 = train_Y1[40000:50210,:]\ntest_Y2 = train_Y2[40000:50210,:]\ntest_Y3 = train_Y3[40000:50210,:]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"datagen = ImageDataGenerator(\n    featurewise_center=False,\n    featurewise_std_normalization=False,\n    rotation_range=7,\n    width_shift_range=6,\n    height_shift_range=8,\n    #brightness_range= [0.1,1],\n    shear_range = 5,\n    zoom_range = 0.08,\n    horizontal_flip=False)\n\ndatagen.fit(test_X)\n\n#test_Xaug = datagen.flow(test_X,shuffle = False, batch_size=10210)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pred_Y1,pred_Y2,pred_Y3 = model.predict_generator(datagen.flow(test_X, shuffle = False,batch_size=128))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#pred_Y1_temp,pred_Y2_temp,pred_Y3_temp = model.predict(test_Xaug)\n#pred_Y1 += pred_Y1_temp\n#pred_Y2 += pred_Y2_temp\n#pred_Y3 += pred_Y3_temp\n\n#del pred_Y1_temp\n#del pred_Y2_temp\n#del pred_Y3_temp\n#gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.metrics import accuracy_score\np1 = accuracy_score(np.argmax(test_Y1,axis=1), np.argmax(pred_Y1,axis=1))\np2 = accuracy_score(np.argmax(test_Y2,axis=1), np.argmax(pred_Y2,axis=1))\np3 = accuracy_score(np.argmax(test_Y3,axis=1), np.argmax(pred_Y3,axis=1))\nprint(p1)\nprint(p2)\nprint(p3)\n\n0.5*p1 + 0.25*p2 + 0.25*p3","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"del test_X\ndel test_Y1\ndel test_Y2\ndel test_Y3\n        \ndel train_X\ndel train_Y1\ndel train_Y2\ndel train_Y3\n\ndel pred_Y1\ndel pred_Y2\ndel pred_Y3\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### net6 aug5 - val without augmentation pred = 0.9725269\n#### net6 aug5 - val with augmentation pred = 0.9709598\n#### net6 aug5 - stack 2 augmentations pred = 0.9729187\n#### net6 aug5 - stack 3 augmentations pred = 0.9729431\n#### net6 aug5 - stack 4 augmentations pred = 0.9733839\n#### net6 aug5 - stack 5 augmentations pred = 0.9735553\n#### net6 aug5 - stack 5 augmentations pred = 0.9736043"},{"metadata":{"trusted":true},"cell_type":"code","source":"HEIGHT = 137\nWIDTH = 236\nSIZE = 128","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    return cv2.resize(img,(size,size))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def Resize(df,size=128):\n    resized = {} \n    df = df.set_index('image_id')\n    for i in tqdm(range(df.shape[0])):\n        image = 255 - df.loc[df.index[i]].values.reshape(137,236)\n        image = (image*(255.0/image.max())).astype(np.uint8)\n        image = crop_resize(image)\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":{"trusted":true},"cell_type":"code","source":"target=[] # model predictions placeholder\nrow_id=[] # row_id place holder\n\ndatagen = ImageDataGenerator(\n    featurewise_center=False,\n    featurewise_std_normalization=False,\n    rotation_range=7,\n    width_shift_range=6,\n    height_shift_range=8,\n    #brightness_range= [0.1,1],\n    shear_range = 5,\n    zoom_range = 0.08,\n    horizontal_flip=False)\n\nfor j in range(4):\n    test_X = pd.read_parquet(TEST_IMG_PATH+str(j)+FILE_TYPE)\n    test_X = Resize(test_X)\n    index = test_X.iloc[:,0]\n    test_X = test_X.iloc[:,1:].values.reshape(test_X.shape[0],128,128,1).astype('float32')/255.\n    \n    datagen.fit(test_X)\n    grapheme_root_all,vowel_diacritic_all,consonant_diacritic_all = model.predict_generator(datagen.flow(test_X, shuffle = False))\n    \n    for a in range(9):\n\n        datagen.fit(test_X)\n\n        grapheme_root_all_temp,vowel_diacritic_all_temp,consonant_diacritic_all_temp = model.predict_generator(datagen.flow(test_X, shuffle = False))\n        grapheme_root_all += grapheme_root_all_temp\n        vowel_diacritic_all += vowel_diacritic_all_temp\n        consonant_diacritic_all += consonant_diacritic_all_temp\n        \n        del grapheme_root_all_temp\n        del vowel_diacritic_all_temp\n        del consonant_diacritic_all_temp\n        gc.collect()\n    \n    for i in range(consonant_diacritic_all.shape[0]):\n        #pred = np.argmax(preds[i])\n        #consonant_diacritic = int(pred/(11*168))\n        #vowel_diacritic = int((pred - consonant_diacritic*11*168)/168)\n        #grapheme_root = pred - consonant_diacritic*11*168 - vowel_diacritic*168\n        consonant_diacritic = np.argmax(consonant_diacritic_all[i])\n        vowel_diacritic = np.argmax(vowel_diacritic_all[i])\n        grapheme_root = np.argmax(grapheme_root_all[i])\n        row_id.append(index[i]+'_consonant_diacritic')\n        target.append(consonant_diacritic)\n        row_id.append(index[i]+'_vowel_diacritic')\n        target.append(vowel_diacritic)\n        row_id.append(index[i]+'_grapheme_root')\n        target.append(grapheme_root)\n\n    del test_X\n    del index\n    del consonant_diacritic\n    del vowel_diacritic\n    del grapheme_root\n    del consonant_diacritic_all\n    del vowel_diacritic_all\n    del grapheme_root_all\n    \n    gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = pd.DataFrame({'row_id': row_id,'target':target},columns = ['row_id','target'])\nsubmission.to_csv('submission.csv',index=False)\nsubmission.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}