{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import cv2\nimport os\nimport math\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport keras","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.applications import DenseNet121\nfrom keras.models import Sequential, Model\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras import layers, optimizers\nfrom sklearn.model_selection import train_test_split","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_parent_url = '/kaggle/input/bengaliai/256_train/256/'\nmetadata_url     = '/kaggle/input/bengaliai-cv19/train.csv'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_metadata(url):\n    ## for loading all the image data select the dir\n    train = pd.read_csv(url)\n    return train","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## importing the metadata of the image\ntrain = load_metadata(metadata_url)\ntrain.head()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## adding corresponding image path to the metadata\n## 3gb imag parquet image data create memory allocation problem\n## so we are usin ghr png version\ntrain['filename'] = train.image_id.apply(lambda filename: image_parent_url + filename + '.png')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(train.head()['grapheme'][2])\nimg1 = cv2.imread(train.head()['filename'][2])\nplt.imshow(img1)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### before preprocessing and cropping","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"print(train.head()['grapheme'][3])\nimg = cv2.imread(train.head()['filename'][3])\nplt.imshow(img)\n## so all the image is mapped perfectly","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img.shape ## wso we can see that the image is rgb \n### it is ont necessary in this typ of image\n### have to convert to gray scale","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## there is more space and random size with random padding \n## we need to change the padding in a fixed size and we need to do some image \n## processing so changing the padding\ndef get_pad_width(im, new_shape, is_rgb=True):\n    ## reduicing the padding\n    ## subract and then make this half od the shape\n    pad_diff = new_shape - im.shape[0], new_shape - im.shape[1]\n    t, b = math.floor(pad_diff[0]/2), math.ceil(pad_diff[0]/2)\n    l, r = math.floor(pad_diff[1]/2), math.ceil(pad_diff[1]/2)\n    if is_rgb:\n        ## if 3 dim then make onde dim 0\n        pad_width = ((t,b), (l,r), (0, 0))\n    else:\n        ## if not settinh up the same\n        pad_width = ((t,b), (l,r))\n    return pad_width","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## testing\nget_pad_width(img,220)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## the image have unnecessary space even the padding is reduced \n## and position in a different way\n## need to get the area of only then image\n## the reference should be the biggest image \n## in the data set\n## and we consider the image as a square","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def test_image(img, thresh=220, maxval=255, square=True):\n    ## frayscale conversion\n    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # convert to grayscale\n    ### invert the image\n    ### now color is not necessary in this image\n    retval, thresh_gray = cv2.threshold(gray, thresh=thresh, maxval=maxval, type=cv2.THRESH_BINARY_INV)\n    ## finding the countour position in the image\n    contours, hierarchy = cv2.findContours(thresh_gray,cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)\n    \n    \n    ## set with the initial image\n    # Find object with the biggest bounding box\n    mx = (0,0,0,0)      # biggest bounding box so far\n    mx_area = 0\n    for cont in contours:\n        ## get the co ordinates\n        x,y,w,h = cv2.boundingRect(cont)\n        ## calculate the area\n        area = w*h\n        ## if the area is max\n        if area > mx_area:\n            ## then set the area\n            mx = x,y,w,h\n            mx_area = area\n    x,y,w,h = mx\n    \n    ## then find the subset of the image\n    crop = img[y:y+h, x:x+w]\n    \n    ## all image are square \n    ## so it will not cause a problem\n    if square:\n        pad_width = get_pad_width(crop, max(crop.shape))\n        crop = np.pad(crop, pad_width=pad_width, mode='constant', constant_values=255)\n    \n    return crop","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# grayscale with opencv for further compatability","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# GrayScale","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(gray,cmap=\"gray\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"retval, thresh_gray = cv2.threshold(gray, thresh=220, maxval=225, type=cv2.THRESH_BINARY_INV)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(thresh_gray,cmap=\"binary\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## making binary image","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":" contours, hierarchy = cv2.findContours(thresh_gray,cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"mx = (0,0,0,0)      # biggest bounding box so far\nmx_area = 0\nfor cont in contours:\n    ## get the co ordinates\n    x,y,w,h = cv2.boundingRect(cont)\n    ## calculate the area\n    area = w*h\n    ## if the area is max\n    if area > mx_area:\n        ## then set the area\n        mx = x,y,w,h\n        mx_area = area\nx,y,w,h = mx\n\n## then find the subset of the image\ncrop = img[y:y+h, x:x+w]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(crop)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# NOT SQUARE MAKE IT SQUARE","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"pad_width = get_pad_width(crop, max(crop.shape))\ncrop = np.pad(crop, pad_width=pad_width, mode='constant', constant_values=255)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(crop)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## after preprocessing and cropping","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(test_image(img))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.imshow(test_image(img)/255)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\nplt.imshow(test_image(img1))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def crop_object(img, thresh=220, maxval=255, square=True):\n    ## frayscale conversion\n    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # convert to grayscale\n    ### invert the image\n    ### now color is not necessary in this image\n    retval, thresh_gray = cv2.threshold(gray, thresh=thresh, maxval=maxval, type=cv2.THRESH_BINARY_INV)\n    ## finding the countour position in the image\n    contours, hierarchy = cv2.findContours(thresh_gray,cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)\n    \n    \n    ## set with the initial image\n    # Find object with the biggest bounding box\n    mx = (0,0,0,0)      # biggest bounding box so far\n    mx_area = 0\n    for cont in contours:\n        ## get the co ordinates\n        x,y,w,h = cv2.boundingRect(cont)\n        ## calculate the area\n        area = w*h\n        ## if the area is max\n        if area > mx_area:\n            ## then set the area\n            mx = x,y,w,h\n            mx_area = area\n    x,y,w,h = mx\n    \n    ## then find the subset of the image\n    crop = img[y:y+h, x:x+w]\n    ## all image are square \n    ## so it will not cause a problem\n    if square:\n        pad_width = get_pad_width(crop, max(crop.shape))\n        crop = np.pad(crop, pad_width=pad_width, mode='constant', constant_values=255)\n    \n    return crop","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## image shuffling\n## and resizing\n## converting\n## and giving batch for neural net\n## this gives a ran\n\ndef data_generator(filenames, y, batch_size=64, shape=(128, 128, 1), random_state=2019):\n    y = y.copy()\n    np.random.seed(random_state)\n    indices = np.arange(len(filenames))\n    \n    while True:\n        np.random.shuffle(indices)\n        \n        for i in range(0, len(indices), batch_size):\n            batch_idx = indices[i:i+batch_size]\n            size = len(batch_idx)\n            \n            batch_files = filenames[batch_idx]\n            X_batch = np.zeros((size, *shape))\n            y_batch = y[batch_idx]\n            \n            for i, file in enumerate(batch_files):\n                img = cv2.imread(file)\n                img = crop_object(img, thresh=220)\n                img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n                img = cv2.resize(img, shape[:2])\n                ## replacing \n                X_batch[i, :, :, 0] = img / 255.\n            ## this will create a generator\n            ## return but not ending loop\n            yield X_batch, [y_batch[:, i] for i in range(y_batch.shape[1])]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Modelling","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_model(densenet):\n    x_in = layers.Input(shape=(128, 128, 1))\n    x = layers.Conv2D(3, (3, 3), padding='same')(x_in)\n    x = densenet(x)\n    \n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.5)(x)\n    x = layers.Dense(256, activation='relu')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.5)(x)\n    \n    \n    ## there are three prediction head\n    ## thats why using model api\n    ## not sequentiual\n    out_grapheme = layers.Dense(168, activation='softmax', name='grapheme')(x)\n    out_vowel = layers.Dense(11, activation='softmax', name='vowel')(x)\n    out_consonant = layers.Dense(7, activation='softmax', name='consonant')(x)\n    \n    model = Model(inputs=x_in, outputs=[out_grapheme, out_vowel, out_consonant])\n    \n    model.compile(\n        optimizers.Adam(lr=0.0001), \n        metrics=['accuracy'], \n        loss='sparse_categorical_crossentropy'\n    )\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\ndensenet = DenseNet121(include_top=False, input_shape=(128, 128, 3))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = build_model(densenet)\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_files, valid_files, y_train, y_valid = train_test_split(\n    train.filename.values, \n    train[['grapheme_root','vowel_diacritic', 'consonant_diacritic']].values, \n    test_size=0.25, \n    random_state=2019\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_size = 128\n\ntrain_gen = data_generator(train_files, y_train)\nvalid_gen = data_generator(valid_files, y_valid)\n\ntrain_steps = round(len(train_files) / batch_size) + 1\nvalid_steps = round(len(valid_files) / batch_size) + 1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## do not run this in your computer\n## if you dont have any GPU\n## keras model will save it untill the early stopping hit\ncallbacks = [keras.callbacks.ModelCheckpoint('model.h5', save_best_only=True)]\n\ntrain_history = model.fit_generator(\n    train_gen,\n    steps_per_epoch=train_steps,\n    epochs=20,\n    validation_data=valid_gen,\n    validation_steps=valid_steps,\n    callbacks=callbacks\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Save history","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(train_history.history['val_grapheme_loss'])\nplt.plot(train_history.history['val_vowel_loss'])\nplt.plot(train_history.history['val_consonant_loss'])\nplt.title('model loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(train_history.history['val_grapheme_accuracy'])\n\nplt.plot(train_history.history['val_vowel_accuracy'])\nplt.plot(train_history.history['val_consonant_accuracy'])\nplt.title('model accuracy')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_model1(densenet):\n    x_in = layers.Input(shape=(128, 128, 1))\n    x = layers.Conv2D(3, (3, 3), padding='same')(x_in)\n    x = densenet(x)\n    \n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.5)(x)\n    x = layers.Dense(256, activation='relu')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.5)(x)\n    \n    \n    ## there are three prediction head\n    ## thats why using model api\n    ## not sequentiual\n    out_grapheme = layers.Dense(168, activation='softmax', name='grapheme')(x)\n    out_vowel = layers.Dense(11, activation='softmax', name='vowel')(x)\n    out_consonant = layers.Dense(7, activation='softmax', name='consonant')(x)\n    \n    model = Model(inputs=x_in, outputs=[out_grapheme, out_vowel, out_consonant])\n    \n    model.compile(\n        optimizers.SGD(lr=0.0001), \n        metrics=['accuracy'], \n        loss='sparse_categorical_crossentropy'\n    )\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model1 = build_model1(densenet)\nmodel1.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_model2(densenet):\n    x_in = layers.Input(shape=(128, 128, 1))\n    x = layers.Conv2D(3, (3, 3), padding='same')(x_in)\n    x = densenet(x)\n    \n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.5)(x)\n    x = layers.Dense(256, activation='relu')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.5)(x)\n    \n    \n    ## there are three prediction head\n    ## thats why using model api\n    ## not sequentiual\n    out_grapheme = layers.Dense(168, activation='softmax', name='grapheme')(x)\n    out_vowel = layers.Dense(11, activation='softmax', name='vowel')(x)\n    out_consonant = layers.Dense(7, activation='softmax', name='consonant')(x)\n    \n    model = Model(inputs=x_in, outputs=[out_grapheme, out_vowel, out_consonant])\n    \n    model.compile(\n        optimizers.RMSprop(lr=0.0001), \n        metrics=['accuracy'], \n        loss='sparse_categorical_crossentropy'\n    )\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model2 = build_model2(densenet)\nmodel2.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_files, valid_files, y_train, y_valid = train_test_split(\n    train.filename.values, \n    train[['grapheme_root','vowel_diacritic', 'consonant_diacritic']].values, \n    test_size=0.25, \n    random_state=2019\n)\nbatch_size = 128\n\ntrain_gen = data_generator(train_files, y_train)\nvalid_gen = data_generator(valid_files, y_valid)\n\ntrain_steps = round(len(train_files) / batch_size) + 1\nvalid_steps = round(len(valid_files) / batch_size) + 1\n\n\ncallbacks = [keras.callbacks.ModelCheckpoint('model.h5', save_best_only=True)]\n\ntrain_history = model1.fit_generator(\n    train_gen,\n    steps_per_epoch=train_steps,\n    epochs=20,\n    validation_data=valid_gen,\n    validation_steps=valid_steps,\n    callbacks=callbacks\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(train_history.history['val_grapheme_accuracy'])\n\nplt.plot(train_history.history['val_vowel_accuracy'])\nplt.plot(train_history.history['val_consonant_accuracy'])\nplt.title('model accuracy')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(train_history.history['val_grapheme_loss'])\nplt.plot(train_history.history['val_vowel_loss'])\nplt.plot(train_history.history['val_consonant_loss'])\nplt.title('model loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"del model\ndel model1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_files, valid_files, y_train, y_valid = train_test_split(\n    train.filename.values, \n    train[['grapheme_root','vowel_diacritic', 'consonant_diacritic']].values, \n    test_size=0.25, \n    random_state=2019\n)\nbatch_size = 128\n\ntrain_gen = data_generator(train_files, y_train)\nvalid_gen = data_generator(valid_files, y_valid)\n\ntrain_steps = round(len(train_files) / batch_size) + 1\nvalid_steps = round(len(valid_files) / batch_size) + 1\n\n\ncallbacks = [keras.callbacks.ModelCheckpoint('model.h5', save_best_only=True)]\n\ntrain_history = model2.fit_generator(\n    train_gen,\n    steps_per_epoch=train_steps,\n    epochs=20,\n    validation_data=valid_gen,\n    validation_steps=valid_steps,\n    callbacks=callbacks\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(train_history.history['val_grapheme_accuracy'])\n\nplt.plot(train_history.history['val_vowel_accuracy'])\nplt.plot(train_history.history['val_consonant_accuracy'])\nplt.title('model accuracy')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(train_history.history['val_grapheme_loss'])\nplt.plot(train_history.history['val_vowel_loss'])\nplt.plot(train_history.history['val_consonant_loss'])\nplt.title('model loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# RMS PROP OPTIMIZER DOES THE BEST","execution_count":null}],"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}