{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Importing Libraries"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom tqdm.auto import tqdm\ntqdm.pandas()\nimport matplotlib.pyplot as plt\nimport cv2\nimport keras \nimport keras.layers as L\nimport keras.models as M\nimport tensorflow as tf\nfrom keras.utils import Sequence\nimport os\nimport skimage.io as io\nimport gc\nfrom IPython.display import clear_output","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Importing Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"train=pd.read_csv('../input/bms-molecular-translation/train_labels.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Making Required changes and adding columns"},{"metadata":{"trusted":true},"cell_type":"code","source":"train['path'] = train['image_id'].progress_apply(\n    lambda x: \"../input/bms-molecular-translation/train/{}/{}/{}/{}.png\".format(\n        x[0], x[1], x[2], x))\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"im=cv2.imread(train['path'][1])\n# Showing highlighted image\nimage=(cv2.erode(im,np.ones((2,2))))\nplt.imshow(image)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Let's Get the First Part Of The Inchi and try to predict it in this file \ntrain['part 1'] = train['InChI'].progress_apply(\n    lambda x:  x.split('/')[1]  )\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train['Length']=train['part 1'].progress_apply(lambda x : len(x))\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Getting to know Part 1 column better :)"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Getting the highest length \nmax_len=train['Length'].max()\nmax_len","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Let's get the character set of the usual characters in part 1\ncharacters=set()\nfor i in train['part 1'].values:\n    for j in i :\n        if j not in characters :\n            characters.add(j)\ncharacters=sorted(characters)\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Having a look at the characters\ncharacters","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Making Dictionary for labelling\nchar_to_label={i:j for j,i in enumerate(characters) }\nlabel_to_char={j:i for j,i in enumerate(characters)}\n# Adding another label 100 which will show no character which means there is nothing there\nlabel_to_char[100]=''","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Making Custom DataGenerator For Our Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Making a Custom DataGenerator to get the data from the DataFrame and changing it into custom output required for out CTC LAyer\nclass DataGenerator(Sequence):\n    def __init__(self,dataframe,char_map,batch_size=16,width=200,height=50,downsample_factor=4,max_length=20,shuffle=True):\n        self.dataframe=dataframe\n        self.char_map=char_map\n        self.batch_size=batch_size\n        self.width=width\n        self.height=height\n        self.downsample_factor=downsample_factor\n        self.max_length=max_length\n        self.shuffle=shuffle\n        self.indices = np.arange(len(dataframe))\n        self.on_epoch_end()\n    def __len__(self):\n        return len(self.dataframe)//self.batch_size\n    def __getitem__(self,idx):\n        curr_batch_idx=self.indices[idx*self.batch_size:(idx+1)*self.batch_size]\n        \n        # Get the batch images\n        batch_images=np.ones((self.batch_size,self.width,self.height,1),dtype=np.float32)\n        batch_labels=np.ones((self.batch_size,self.max_length),dtype=np.float32)\n        input_length=np.ones((self.batch_size,1),dtype=np.float32)*(self.width//self.downsample_factor-2)\n        label_length=np.zeros((self.batch_size,1),dtype=np.int64)\n        \n        # Starting the loop to get the data\n        for i,idx in enumerate(curr_batch_idx):\n            img=cv2.imread(self.dataframe['path'].values[idx])\n            img=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n            img=cv2.erode(img,((2,2)))\n            img=cv2.resize(img,(self.width,self.height))\n            img=img/255   # Normalizing the image\n            img=img.T\n            img=np.expand_dims(img,axis=-1)\n            text=self.dataframe['part 1'].values[idx]\n            label=[]\n            for j in text: \n                label.append(self.char_map[j])\n            label.extend([100]*(20-len(label)))            \n            batch_images[i]=img\n            batch_labels[i]=label\n            label_length[i]=len(label)\n            \n        batch_inputs= {\n                'input_data':batch_images,\n                'input_label':batch_labels,\n                'input_length':input_length,\n                'label_length':label_length\n                \n            }\n        return batch_inputs,np.zeros((self.batch_size),dtype=np.float32)\n    def on_epoch_end(self):\n        if self.shuffle == True :\n            np.random.shuffle(self.indices)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_datagenerator=DataGenerator(train[:150000],char_to_label)\nvalidation_datagenerator=DataGenerator(train[150000:160000],char_to_label)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Making CTC Layer"},{"metadata":{"trusted":true},"cell_type":"code","source":"class CTCLayer(L.Layer):\n    def __init__(self, name=None):\n        super().__init__(name=name)\n        self.loss_fn = keras.backend.ctc_batch_cost\n\n    def call(self, y_true, y_pred, input_length, label_length):\n        # Compute the training-time loss value and add it\n        # to the layer using `self.add_loss()`.\n        loss = self.loss_fn(y_true, y_pred, input_length, label_length)\n        self.add_loss(loss)\n        \n        # On test time, just return the computed loss\n        return loss","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Making the model"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Making the Model now\ndef make_model():\n    inp=L.Input(shape=(200,50,1),dtype=np.float32,name='input_data')\n    labels=L.Input(shape=[5],dtype=np.float32,name='input_label')\n    input_length=L.Input(shape=[1],dtype=np.int64,name='input_length')\n    label_length=L.Input(shape=[1],dtype=np.int64,name='label_length')\n    x=L.Conv2D(32,(3,3),activation='relu',padding='same',kernel_initializer='he_normal')(inp)\n    x=L.MaxPooling2D(pool_size=(2,2))(x)\n    x=L.Conv2D(64,(3,3),activation='relu',padding='same',kernel_initializer='he_normal')(x)\n    x=L.MaxPooling2D(pool_size=(2,2))(x)\n    new_shape=((200//4),(50//4)*64)\n    x=L.Reshape(new_shape)(x)\n    x=L.Dense(64,activation='relu')(x)\n    x=L.Dropout(0.4)(x)\n    x=L.Bidirectional(L.LSTM(128,return_sequences=True,dropout=0.2))(x)\n    x=L.Bidirectional(L.LSTM(64,return_sequences=True,dropout=0.25))(x)\n    x=L.Dense(len(characters)+1,activation='softmax',kernel_initializer='he_normal',name='Dense_output')(x)\n    output=CTCLayer(name='outputs')(labels,x,input_length,label_length)\n    model=M.Model([inp,labels,input_length,label_length],output)\n    # Optimizer\n    sgd = keras.optimizers.SGD(learning_rate=0.0015,\n                               decay=1e-6,\n                               momentum=0.9,\n                               nesterov=True,\n                               clipnorm=5)\n    model.compile(optimizer=sgd)\n    return model\n    \n    \n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model=make_model()\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training The Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"# ### Add early stopping\n# es = keras.callbacks.EarlyStopping(monitor='val_loss',\n#                                    patience=2,\n#                                    restore_best_weights=True)\n\n# ### Train the model\n# if 'prediction_model.h5' not in os.listdir('./'):\n#     history = model.fit(train_datagenerator,\n#                         validation_data=validation_datagenerator,\n#                         steps_per_epoch=1500,\n#                         epochs=8,\n#                         callbacks=[es])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Making Predictions Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"# prediction_model = keras.models.Model(model.get_layer(name='input_data').input,\n#                                         model.get_layer(name='Dense_output').output)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Load Model If Not Training Again"},{"metadata":{"trusted":true},"cell_type":"code","source":"prediction_model=M.load_model('../input/prediction-model-competition/prediction_model_ocr (1).h5')\nprediction_model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"label_to_char[100]=''\n# A utility to decode the output of the network\ndef decode_batch_predictions(pred):\n    pred = pred[:, :-2]\n    input_len = np.ones(pred.shape[0])*pred.shape[1]\n    \n    # Use greedy search. For complex tasks, you can use beam search\n    results = keras.backend.ctc_decode(pred, \n                                        input_length=input_len,\n                                        greedy=True)[0][0]\n    \n    # Iterate over the results and get back the text\n    output_text = []\n    for res in results.numpy():\n        outstr = ''\n        for c in res:\n            if c < len(characters) and c >=0:\n                outstr += label_to_char[c]\n        output_text.append(outstr)\n    \n    # return final text results\n    return output_text","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for p, (inp_value, _) in enumerate(validation_datagenerator):\n    bs = inp_value['input_data'].shape[0]\n    X_data = inp_value['input_data']\n    labels = inp_value['input_label']\n    preds = prediction_model.predict(X_data)\n    pred_texts = decode_batch_predictions(preds)\n    \n    \n    orig_texts = []\n    for label in labels:\n        text = ''.join([label_to_char[int(x)] for x in label])\n        orig_texts.append(text)\n        \n    for i in range(bs):\n        print(f'Ground truth: {orig_texts[i]} \\t Predicted: {pred_texts[i]}')\n    break","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# The Results Are Not too similar to baseline for sure :)"},{"metadata":{"trusted":true},"cell_type":"code","source":"sample=pd.read_csv('../input/bms-molecular-translation/sample_submission.csv')\nsample['path'] = sample['image_id'].progress_apply(\n    lambda x: \"../input/bms-molecular-translation/test/{}/{}/{}/{}.png\".format(\n        x[0], x[1], x[2], x))\nsample.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Releasing Some RAM"},{"metadata":{"trusted":true},"cell_type":"code","source":"del train\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Transform Function"},{"metadata":{"trusted":true},"cell_type":"code","source":"def transform(path):\n    img=cv2.imread(path)\n    img=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    img=cv2.erode(img,((2,2)))\n    img=cv2.resize(img,(200,50))\n    img=img/255   # Normalizing the image\n    img=img.T\n    img=np.expand_dims(img,axis=-1)\n    return img","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Since there is a lot of testing data this might take some time have a snack :)"},{"metadata":{},"cell_type":"markdown","source":"# One Prediction :)"},{"metadata":{"trusted":true},"cell_type":"code","source":"im=transform('../input/bms-molecular-translation/train/2/0/1/201013c95288.png')\nbatch_images=np.ones((128,200,50,1),dtype=np.float32)\nbatch_images[0]=im\n\nx=prediction_model.predict(batch_images)\npred_texts = decode_batch_predictions(x)\nplt.imshow(cv2.imread('../input/bms-molecular-translation/train/2/0/1/201013c95288.png'))\nprint(pred_texts[0])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## You can use this code to make predictions and save it"},{"metadata":{"trusted":true},"cell_type":"code","source":"# predictions=[]\n# for i in range(len(sample)//500):\n#     print(i*100/3232)\n#     clear_output(wait=True)\n#     dd=sample[i*500:(i+1)*500]\n#     batch_images=np.ones((500,200,50,1),dtype=np.float32)\n#     for i in range(500):\n#         batch_images[i]=transform(dd['path'].values[i])\n#     x=prediction_model.predict(batch_images)\n#     pred_texts = decode_batch_predictions(x)\n#     predictions.extend(pred_texts)\n# dd=sample[3232*500:]\n# pt=len(sample)%500\n# batch_images=np.ones((pt,200,50,1),dtype=np.float32)\n# for j,k in enumerate(dd['path'].values) :\n#     im=transform(k)\n#     batch_images[j]=im\n# x=prediction_model.predict(batch_images)\n# pred_texts = decode_batch_predictions(x)\n# predictions.extend(pred_texts)\n# predictions=np.array(predictions)\n# np.save('Text Predicted.npy',predictions)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Loading Predictions"},{"metadata":{"trusted":true},"cell_type":"code","source":"predictions=np.load('../input/predicted-text/Text Predicted.npy')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Saving the results"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Baseline answers : label='InChI=1S/'+'C15H22N2O2/'+'c1-1-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18(19)20/'+'h1-1,1111,1,,,,,'\nlabel=['InChI=1S/'+i+'/c1-1-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18(19)20/'+'h1-1,1111,1,,,,,' for i in predictions]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission=pd.read_csv('../input/bms-molecular-translation/sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission['InChI']=label","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":{"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}