{"cells":[{"metadata":{"trusted":true},"cell_type":"markdown","source":"This competition provides a lot of room for interresting experimentations. In this kernel I use a rather easy way to train a standard EfficientNet B3 model with a custom head layer and Generalized mean pool. I use only basic image preprocessing with a scaling factor.\n\nTo save on training time I use a different training set on each epoch. This gives a nice boost of about 0.005 to 0.008 compared to a fixed training set when using train/test split or cross-validation. The downside is that the validation has some less value.\n\nThis kernel contains the inference part where I use 3 models from the training. For the complete code to train it yourself you can download it from my [github](https://github.com/RobinSmits/KaggleBengaliAIHandwrittenGraphemeClassification). I trained it for 80 epochs on my 1070 Ti (roughly 1,5 days).\n\nI hope you like it and if you find this kernel helpfull..then please don't forget to upvote it."},{"metadata":{"trusted":true},"cell_type":"code","source":"import cv2\nimport os\nimport time, gc\nimport numpy as np\nimport pandas as pd\n\nimport tensorflow as tf\nimport keras\nfrom keras import backend as K\nfrom keras.models import Model, Input\nfrom keras.layers import Dense, Lambda\nfrom math import ceil\n\n# Install EfficientNet\n!pip install '../input/kerasefficientnetb3/efficientnet-1.0.0-py3-none-any.whl'\nimport efficientnet.keras as efn","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Constants\nHEIGHT = 137\nWIDTH = 236\nFACTOR = 0.70\nHEIGHT_NEW = int(HEIGHT * FACTOR)\nWIDTH_NEW = int(WIDTH * FACTOR)\nCHANNELS = 3\nBATCH_SIZE = 16\n\n# Dir\nDIR = '../input/bengaliai-cv19'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Image Preprocessing"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Image Size Summary\nprint(HEIGHT_NEW)\nprint(WIDTH_NEW)\n\n# Image Prep\ndef resize_image(img, WIDTH_NEW, HEIGHT_NEW):\n    # Invert\n    img = 255 - img\n\n    # Normalize\n    img = (img * (255.0 / img.max())).astype(np.uint8)\n\n    # Reshape\n    img = img.reshape(HEIGHT, WIDTH)\n    image_resized = cv2.resize(img, (WIDTH_NEW, HEIGHT_NEW), interpolation = cv2.INTER_AREA)\n\n    return image_resized.reshape(-1)   ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Create Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Generalized mean pool - GeM\ngm_exp = tf.Variable(3.0, dtype = tf.float32)\ndef generalized_mean_pool_2d(X):\n    pool = (tf.reduce_mean(tf.abs(X**(gm_exp)), \n                        axis = [1, 2], \n                        keepdims = False) + 1.e-7)**(1./gm_exp)\n    return pool","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create Model\ndef create_model(input_shape):\n    # Input Layer\n    input = Input(shape = input_shape)\n    \n    # Create and Compile Model and show Summary\n    x_model = efn.EfficientNetB3(weights = None, include_top = False, input_tensor = input, pooling = None, classes = None)\n    \n    # UnFreeze all layers\n    for layer in x_model.layers:\n        layer.trainable = True\n    \n    # GeM\n    lambda_layer = Lambda(generalized_mean_pool_2d)\n    lambda_layer.trainable_weights.extend([gm_exp])\n    x = lambda_layer(x_model.output)\n    \n    # multi output\n    grapheme_root = Dense(168, activation = 'softmax', name = 'root')(x)\n    vowel_diacritic = Dense(11, activation = 'softmax', name = 'vowel')(x)\n    consonant_diacritic = Dense(7, activation = 'softmax', name = 'consonant')(x)\n\n    # model\n    model = Model(inputs = x_model.input, outputs = [grapheme_root, vowel_diacritic, consonant_diacritic])\n\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create Model\nmodel1 = create_model(input_shape = (HEIGHT_NEW, WIDTH_NEW, CHANNELS))\nmodel2 = create_model(input_shape = (HEIGHT_NEW, WIDTH_NEW, CHANNELS))\nmodel3 = create_model(input_shape = (HEIGHT_NEW, WIDTH_NEW, CHANNELS))\nmodel4 = create_model(input_shape = (HEIGHT_NEW, WIDTH_NEW, CHANNELS))\nmodel5 = create_model(input_shape = (HEIGHT_NEW, WIDTH_NEW, CHANNELS))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Load Model Weights\nmodel1.load_weights('../input/kerasefficientnetb3/Train1_model_59.h5') # LB 0.9681\nmodel2.load_weights('../input/kerasefficientnetb3/Train1_model_64.h5') # LB 0.9679\n#model2.load_weights('../input/kerasefficientnetb3/Train1_model_66.h5') # LB 0.9685\nmodel3.load_weights('../input/kerasefficientnetb3/Train1_model_68.h5') # LB 0.9691\nmodel4.load_weights('../input/kerasefficientnetb3/Train1_model_57.h5') # LB ??\nmodel5.load_weights('../input/kerasefficientnetb3/Train1_model_70.h5') # LB ??","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Data Generator"},{"metadata":{"trusted":true},"cell_type":"code","source":"class TestDataGenerator(keras.utils.Sequence):\n    def __init__(self, X, batch_size = 16, img_size = (512, 512, 3), *args, **kwargs):\n        self.X = X\n        self.indices = np.arange(len(self.X))\n        self.batch_size = batch_size\n        self.img_size = img_size\n                    \n    def __len__(self):\n        return int(ceil(len(self.X) / self.batch_size))\n\n    def __getitem__(self, index):\n        indices = self.indices[index*self.batch_size:(index+1)*self.batch_size]\n        X = self.__data_generation(indices)\n        return X\n    \n    def __data_generation(self, indices):\n        X = np.empty((self.batch_size, *self.img_size))\n        \n        for i, index in enumerate(indices):\n            image = self.X[index]\n            image = np.stack((image,)*CHANNELS, axis=-1)\n            image = image.reshape(-1, HEIGHT_NEW, WIDTH_NEW, CHANNELS)\n            \n            X[i,] = image\n        \n        return X","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Predict and Submission"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create Submission File\ntgt_cols = ['grapheme_root','vowel_diacritic','consonant_diacritic']\n\n# Create Predictions\nrow_ids, targets = [], []\n\n# Loop through Test Parquet files (X)\nfor i in range(0, 4):\n    # Test Files Placeholder\n    test_files = []\n\n    # Read Parquet file\n    df = pd.read_parquet(os.path.join(DIR, 'test_image_data_'+str(i)+'.parquet'))\n    # Get Image Id values\n    image_ids = df['image_id'].values \n    # Drop Image_id column\n    df = df.drop(['image_id'], axis = 1)\n\n    # Loop over rows in Dataframe and generate images \n    X = []\n    for image_id, index in zip(image_ids, range(df.shape[0])):\n        test_files.append(image_id)\n        X.append(resize_image(df.loc[df.index[index]].values, WIDTH_NEW, HEIGHT_NEW))\n\n    # Data_Generator\n    data_generator_test = TestDataGenerator(X, batch_size = BATCH_SIZE, img_size = (HEIGHT_NEW, WIDTH_NEW, CHANNELS))\n        \n    # Predict with all 3 models\n    preds1 = model1.predict_generator(data_generator_test, verbose = 1)\n    preds2 = model2.predict_generator(data_generator_test, verbose = 1)\n    preds3 = model3.predict_generator(data_generator_test, verbose = 1)\n    preds4 = model4.predict_generator(data_generator_test, verbose = 1)\n    preds5 = model5.predict_generator(data_generator_test, verbose = 1)\n    \n    # Loop over Preds    \n    for i, image_id in zip(range(len(test_files)), test_files):\n        \n        for subi, col in zip(range(len(preds1)), tgt_cols):\n            sub_preds1 = preds1[subi]\n            sub_preds2 = preds2[subi]\n            sub_preds3 = preds3[subi]\n            sub_preds4 = preds4[subi]\n            sub_preds5 = preds5[subi]\n\n            # Set Prediction with average of 5 predictions\n            row_ids.append(str(image_id)+'_'+col)\n            sub_pred_value = np.argmax((sub_preds1[i] + sub_preds2[i] + sub_preds3[i] + sub_preds4[i] + sub_preds5[i]) / 5)\n            targets.append(sub_pred_value)\n    \n    # Cleanup\n    del df\n    gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create and Save Submission File\nsubmit_df = pd.DataFrame({'row_id':row_ids,'target':targets}, columns = ['row_id','target'])\nsubmit_df.to_csv('submission.csv', index = False)\nprint(submit_df.head(40))","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}