{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<h1 style=\"text-align: center; font-family: Verdana; font-size: 32px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; font-variant: small-caps; letter-spacing: 3px; color: #FF1493; background-color: #ffffff;\">Bristol-Myers Squibb – Molecular Translation</h1>\n<h2 style=\"text-align: center; font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: underline; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">TFRecord Creation</h2>\n<h5 style=\"text-align: center; font-family: Verdana; font-size: 12px; font-style: normal; font-weight: bold; text-decoration: None; text-transform: none; letter-spacing: 1px; color: black; background-color: #ffffff;\">CREATED BY: DARIEN SCHETTLER</h5>\n\n---\n\n<br><br>\n\n<font color=\"purple\" style=\"font-weight: bold;\">CHANGE LOG</font>\n\n---\n\n* **v1 - `Working Draft`**\n* **v2 - `384x384x1 - No Rotation - Raw Labels - Preprocessing (inversion, pad2square)`**\n* **v3-9 - `Working Draft`**\n* **v10 - `384x384x1 - Yes Rotation - Tokenized - Preprocessing (inversion, pad2square) - Just Train/Val [CURRENT]`**\n* **v11 - `128x128x1 - Yes Rotation - Tokenized - Preprocessing (inversion, pad2square) - Train/Val/Test [CURRENT]`**\n\n---\n\n<br>","metadata":{}},{"cell_type":"markdown","source":"<p id=\"toc\"></p>\n\n<br><br>\n\n<h1 style=\"font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\">TABLE OF CONTENTS</h1>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#imports\">0&nbsp;&nbsp;&nbsp;&nbsp;IMPORTS</a></h3>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#background_information\">1&nbsp;&nbsp;&nbsp;&nbsp;BACKGROUND INFORMATION</a></h3>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#setup\">2&nbsp;&nbsp;&nbsp;&nbsp;SETUP</a></h3>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#helper_functions\">3&nbsp;&nbsp;&nbsp;&nbsp;HELPER FUNCTIONS</a></h3>\n\n---\n\n<h3 style=\"text-indent: 10vw; font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\"><a href=\"#dataset_preparation\">4&nbsp;&nbsp;&nbsp;&nbsp;PREPARE THE DATASET</a></h3>\n\n---","metadata":{}},{"cell_type":"markdown","source":"<h1 style=\"font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; background-color: #ffffff; color: navy;\" id=\"imports\">0&nbsp;&nbsp;IMPORTS&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"#toc\">&#10514;</a></h1>","metadata":{}},{"cell_type":"code","source":"print(\"\\n... IMPORTS STARTING ...\\n\")\nprint(\"\\n\\tVERSION INFORMATION\")\n\n# Machine Learning and Data Science Imports\nimport tensorflow as tf; print(f\"\\t\\t– TENSORFLOW VERSION: {tf.__version__}\");\nimport tensorflow_addons as tfa; print(f\"\\t\\t– TENSORFLOW ADDONS VERSION: {tfa.__version__}\");\nimport pandas as pd; pd.options.mode.chained_assignment = None;\nimport numpy as np; print(f\"\\t\\t– NUMPY VERSION: {np.__version__}\");\n\n# Built In Imports\nfrom kaggle_datasets import KaggleDatasets\nfrom collections import Counter\nfrom datetime import datetime\nimport multiprocessing as mp\nfrom glob import glob\nimport warnings\nimport requests\nimport imageio\nimport IPython\nimport urllib\nimport zipfile\nimport pickle\nimport random\nimport shutil\nimport string\nimport math\nimport time\nimport gzip\nimport ast\nimport io\nimport os\nimport gc\nimport re\n\n# Visualization Imports\nfrom matplotlib.colors import ListedColormap\nimport matplotlib.patches as patches\nimport plotly.graph_objects as go\nimport matplotlib.pyplot as plt\nfrom tqdm.notebook import tqdm; tqdm.pandas();\nimport plotly.express as px\nimport seaborn as sns\nfrom PIL import Image\nimport matplotlib; print(f\"\\t\\t– MATPLOTLIB VERSION: {matplotlib.__version__}\");\nimport plotly\nimport PIL\nimport cv2\n\n\nprint(\"\\n\\n... IMPORTS COMPLETE ...\\n\")\n\n\ngpus = tf.config.experimental.list_physical_devices('GPU')\nif gpus:\n    try:\n        # Currently, memory growth needs to be the same across GPUs\n        for gpu in gpus:\n            tf.config.experimental.set_memory_growth(gpu, True)\n        logical_gpus = tf.config.experimental.list_logical_devices('GPU')\n        print(len(gpus), \"... Physical GPUs,\", len(logical_gpus), \"Logical GPUs ...\\n\")\n    except RuntimeError as e:\n        # Memory growth must be set before GPUs have been initialized\n        print(e)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<h1 style=\"font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\" id=\"background_information\">1&nbsp;&nbsp;BACKGROUND INFORMATION&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"#toc\">&#10514;</a></h1>\n\n<br><b style=\"text-decoration: underline; font-family: Verdana; text-transform: uppercase;\">POSSIBLE FLAG EXPLORATION</b>\n\nTBD","metadata":{}},{"cell_type":"markdown","source":"<h1 style=\"font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\" id=\"setup\">2&nbsp;&nbsp;SETUP&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"#toc\">&#10514;</a></h1>","metadata":{}},{"cell_type":"markdown","source":"<h3 style=\"font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">2.1  BASIC SETUP</h3>","metadata":{}},{"cell_type":"code","source":"# Define the root and data directories\nROOT_DIR = \"/kaggle/input\"\nDATA_DIR = os.path.join(ROOT_DIR, \"bms-molecular-translation\")\n\nTRAIN_DIR = os.path.join(DATA_DIR, \"train\")\nTEST_DIR = os.path.join(DATA_DIR, \"test\")\n\nTRAIN_CSV_PATH = os.path.join(DATA_DIR, \"train_labels.csv\")\nSS_CSV_PATH = os.path.join(DATA_DIR, \"sample_submission.csv\")\n\ntrain_df = pd.read_csv(TRAIN_CSV_PATH)\ntrain_df[\"img_path\"] = train_df.image_id.progress_apply(lambda x: os.path.join(TRAIN_DIR, x[0], x[1], x[2], x+\".png\"))\n\nprint(\"\\n... TRAIN DATAFRAME W/ PATHS ...\\n\")\ndisplay(train_df)\n\nss_df = pd.read_csv(SS_CSV_PATH)\nss_df[\"img_path\"] = ss_df.image_id.progress_apply(lambda x: os.path.join(TEST_DIR, x[0], x[1], x[2], x+\".png\"))\n\nprint(\"\\n... SUBMISSION DATAFRAME ...\\n\")\ndisplay(ss_df)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<h3 style=\"font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">2.2 USER INPUT VARIABLES</h3>","metadata":{}},{"cell_type":"code","source":"# Proportion of data for training/validation\nN_TEST = len(ss_df) # 1616107\nN_VAL = 100_000 \nN_TRAIN = len(train_df)-N_VAL # 2424186-N_VAL\n\n# Whether to rotate images 90 degrees based on the w/h rule\nFIX_ROTATION = True\n\n# Whether to invert foreground/background\nDO_INVERT = True\n\n# Whether to repair images - not implemented yet\nDO_REPAIR = False\n\n# Desired image shape ... the last axis indicates whether to tile the single channel image to 3\nIMG_SHAPE = (128,128,1)\n\n# Whether or not to do tokenization to inchi strings\nAPPLY_TOKENIZATION = True\n\n# Only required if `APPLY_TOKENIZATION=True`\nTOKEN_LIST = [\"<PAD>\", \"InChI=1S/\", \"<END>\", \"/c\", \"/h\", \"/m\", \"/t\", \"/b\", \"/s\", \"/i\"] +\\\n             ['Si', 'Br', 'Cl', 'F', 'I', 'N', 'O', 'P', 'S', 'C', 'H', 'B', ] +\\\n             [str(i) for i in range(167,-1,-1)] +\\\n             [\"\\+\", \"\\(\", \"\\)\", \"\\-\", \",\", \"D\", \"T\"]\nSTART_TOKEN = tf.constant(TOKEN_LIST.index(\"InChI=1S/\"), dtype=tf.uint8)\nEND_TOKEN = tf.constant(TOKEN_LIST.index(\"<END>\"), dtype=tf.uint8)\nPAD_TOKEN = tf.constant(TOKEN_LIST.index(\"<PAD>\"), dtype=tf.uint8)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<h3 style=\"font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">2.3 AUTO DETECTED VARIABLES</h3>","metadata":{}},{"cell_type":"code","source":"# INCHI RELATED\nTOK2INT = {c.strip(\"\\\\\"):i for i,c in enumerate(TOKEN_LIST)}\nINT2TOK = {v:k for k,v in TOK2INT.items()}\n\n# MAX_LEN = train_df.InChI.progress_apply(lambda x: len(re.findall(\"|\".join(TOKEN_LIST), x))).max()+1\nMAX_LEN = 282\nVOCAB_LEN = len(INT2TOK)\n\n# IMG RELATED\nN_CHANNELS = IMG_SHAPE[-1]\nIMG_SIZE = IMG_SHAPE[:2]\n\n# Split dataframe\nval_df = train_df[:N_VAL].reset_index(drop=True)\ntrain_df = train_df[N_VAL:].reset_index(drop=True)\n\n# For TFRecord sharding - very rough estimations\nN_EX_PER_REC = (25000 if np.product(IMG_SHAPE)>400000 else 50000)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<h1 style=\"font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\" id=\"helper_functions\">3&nbsp;&nbsp;HELPER FUNCTIONS&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"#toc\">&#10514;</a></h1>","metadata":{}},{"cell_type":"code","source":"def flatten_l_o_l(nested_list):\n    \"\"\" Flatten a list of lists \"\"\"\n    return [item for sublist in nested_list for item in sublist]\n\n\ndef tf_load_image(path, img_size=(224,224), tile_to_3_channel=True, invert=False, rotate_trick=True, repair_images=False):\n    \"\"\" Load an image with the correct size and shape \"\"\"\n    img = decode_img(tf.io.read_file(path), img_size, n_channels=1, invert=invert, rotate_trick=rotate_trick, repair_images=repair_images)\n    \n    if tile_to_3_channel:\n        return tf.tile(img, tf.constant((1, 1, 3), dtype=tf.int32))\n    else:\n        return img\n    \ndef pad_to_square(a, rotate_trick=True, constant=255):\n    \"\"\" Pad a tensor array `a` evenly until it is a square \"\"\"\n    h_src = tf.shape(a)[0]\n    w_src = tf.shape(a)[1] \n            \n    if w_src>h_src: # pad height\n        n_to_add = w_src-h_src\n        top_pad = n_to_add//2\n        bottom_pad = n_to_add-top_pad\n        a = tf.pad(a, [(top_pad, bottom_pad), (0, 0), (0, 0)], mode='constant', constant_values=constant)\n    elif h_src>w_src: # pad width\n        n_to_add = h_src-w_src\n        left_pad = n_to_add//2\n        right_pad = n_to_add-left_pad\n        a = tf.pad(a, [(0, 0), (left_pad, right_pad), (0, 0)], mode='constant', constant_values=constant)\n        if rotate_trick:\n            a = tf.image.rot90(a)\n    else:\n        pass\n    return a\n    \n    \ndef decode_img(img, img_size=(224,224), n_channels=1, invert=False, rotate_trick=True, repair_images=False):\n    \"\"\" Decode the image by utilizing TF ... pad to square ... and resize \"\"\"\n    \n    # convert the compressed string to a 3D uint8 tensor\n    img = tf.image.decode_png(img, channels=n_channels)\n    \n    if invert:\n        img = tf.ones_like(img, dtype=tf.uint8)*255-img\n        constant_pad=0\n    else:\n        constant_pad=255\n        \n    if repair_images:\n        pass\n        \n    # resize the image to the desired size\n    img = pad_to_square(img, rotate_trick=rotate_trick, constant=constant_pad)\n    img = tf.image.resize(img, img_size)\n    \n    return tf.cast(img, tf.uint8)\n\n\ndef tokens_to_str(caption_tokens, discard_padding=True):\n    \"\"\" Should convert a string of token ids to an InChI string \"\"\"\n    if discard_padding:\n        return \"\".join([int_2_char_lex[x] for x in caption_tokens if x!=len(int_2_char_lex)])\n    else:\n        return \"\".join([int_2_char_lex[x] for x in caption_tokens])\n    \n    \ndef evaluate(image, from_np=False):\n    \"\"\" TBD \"\"\"\n    attention_plot = np.zeros((MAX_LEN, fixed_encoder.output_shape[1]))\n    hidden = tf.zeros((1, RNN_UNITS), tf.float32)\n\n    if not from_np:\n        temp_input = tf.expand_dims(tf_load_image(image, img_size=INPUT_SHAPE[:-1]), 0)\n        img_tensor_val = fixed_encoder(temp_input)\n    else:\n        img_tensor_val=image\n    \n    features = trainable_encoder(img_tensor_val)\n    dec = tf.ones((1, 1), tf.uint8)\n    result = [int_2_char_lex[1],]\n\n    for i in range(MAX_LEN-1):\n        predictions, hidden, attention_weights = dec_model([dec, hidden, features])\n        attention_plot[i] = tf.reshape(attention_weights, (-1, )).numpy()\n        predicted_id = tf.random.categorical(predictions, 1)[0][0].numpy()\n        result.append(int_2_char_lex[predicted_id])\n        if int_2_char_lex[predicted_id] == '<END>':\n            return result, attention_plot\n\n        dec = tf.expand_dims([predicted_id], 0)\n\n    attention_plot = attention_plot[:len(result), :]\n    return result, attention_plot\n\n\ndef plot_attention(image, result, attention_plot):\n    \"\"\" TBD \"\"\"    \n    temp_image = np.array(Image.open(image))\n\n    fig = plt.figure(figsize=(18, 14))\n\n    len_result = len(result)\n    for l in range(len_result):\n        temp_att = np.resize(attention_plot[l], (8, 8))\n        ax = fig.add_subplot(len_result//2, len_result//2, l+1)\n        ax.set_title(result[l])\n        img = ax.imshow(temp_image)\n        ax.imshow(temp_att, cmap='gray', alpha=0.4, extent=img.get_extent())\n\n    plt.tight_layout()\n    plt.show()\n    \ndef test_random_image(style=\"full\"):\n    \"\"\" TBD \"\"\"    \n    rid = np.random.randint(0, len(val_subset_df))\n    path = val_subset_df[\"img_path\"][rid]\n    \n    if style==\"full\":\n        real_caption = val_subset_df[\"InChI\"][rid][:-1]\n    else:\n        real_caption = val_subset_df[\"InChI_chem\"][rid][:-1]\n\n    result, attention_plot = evaluate(path)\n    result = ''.join(result[:-1])\n    print (f\"\\n\\tReal Caption       : {real_caption}\")\n    print (f\"\\tPrediction Caption   : {result}\")\n    print(f\"\\tLevenshtein Distance  : {Levenshtein.distance(real_caption, result)}\\n\")\n    plot_attention(path, result, attention_plot)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<h1 style=\"font-family: Verdana; font-size: 24px; font-style: normal; font-weight: bold; text-decoration: none; text-transform: none; letter-spacing: 3px; color: navy; background-color: #ffffff;\" id=\"dataset_preparation\">4&nbsp;&nbsp;PREPARE THE DATASET&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"#toc\">&#10514;</a></h1>\n\nIn this section we prepare a subset of the dataset for modelling","metadata":{}},{"cell_type":"markdown","source":"<h3 style=\"font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">4.0  VISUALIZE SOME EXAMPLES</h3>","metadata":{}},{"cell_type":"code","source":"row_text = []\nplt.figure(figsize=(18,18))\nfor i,(row_idx,row) in enumerate(train_df.sample(9).iterrows()):\n    row_text.append(f\"- {row.InChI}\")\n    plt.subplot(3,3,i+1)\n    plt.title(\"ROW #{}{}\".format(row_idx, '\\n'.join(row.InChI.replace(\"InChI=1S\", \"\").split(\"/\"))), fontweight='bold', fontsize=8)\n    plt.imshow(tf_load_image(row.img_path))\n\nplt.tight_layout()\nplt.show()\n\nprint(\"\\n... CAPTIONING FOR IMAGES ABOVE ...\\n\")\nfor x in row_text:\n    print(f\"{x}\")\nprint()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<h3 style=\"font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">4.1  PREPROCESS THE INCHI STRINGS (optional)</h3>","metadata":{}},{"cell_type":"code","source":"if APPLY_TOKENIZATION:\n    print(\"\\n... CREATING THE TOKENIZED INCHI LABEL ARRAYS STARTING ...\\n\")\n\n    print(\"\\n... STEP 1: ADDING STOP TOKEN `<END>` ...\")\n    train_df[\"InChI\"] = train_df[\"InChI\"]+\"<END>\"\n    val_df[\"InChI\"] = val_df[\"InChI\"]+\"<END>\"\n    \n    print(\"\\n... STEP 2: ITERATE OVER THE DATASET (OR LOAD FROM FILE) TO CONVERT STRING TO SPARSE TOKENIZED ENCODING ...\")\n    try:  \n        with open('/kaggle/input/bms-simple-tfrecord-creation/train_captions_darien_tokenized.pickle', 'rb') as handle:\n            train_captions = pickle.load(handle)\n        with open('/kaggle/input/bms-simple-tfrecord-creation/val_captions_darien_tokenized.pickle', 'rb') as handle:\n            val_captions = pickle.load(handle)\n        print(\"\\t--> LOADING CAPTIONS FROM FILE SUCCESSFUL\")\n    except:\n        print(\"\\t--> LOADING CAPTIONS FROM FILE UNSUCCESSFUL... GENERATING FROM SCRATCH\")\n        # Captions start as zeros because that is the padding token (by using zero we can mask it later)\n        \n        print(\"\\t--> WORKING ON TRAIN CAPTIONS\")\n        train_captions = np.zeros((N_TRAIN, MAX_LEN,), dtype=np.uint8)    \n        # Make the sparse, padded encodings for our captions \n        for i, inchi in tqdm(enumerate(train_df.InChI.values), total=N_TRAIN):\n            sparse_rep = [TOK2INT[c] for c in re.findall(\"|\".join(TOKEN_LIST), inchi)]\n            train_captions[i, :len(sparse_rep)] = sparse_rep  \n\n        print(\"\\t--> WORKING ON VAL CAPTIONS\")\n        val_captions = np.zeros((N_VAL, MAX_LEN,), dtype=np.uint8)    \n        # Make the sparse, padded encodings for our captions \n        for i, inchi in tqdm(enumerate(val_df.InChI.values), total=N_VAL):\n            sparse_rep = [TOK2INT[c] for c in re.findall(\"|\".join(TOKEN_LIST), inchi)]\n            val_captions[i, :len(sparse_rep)] = sparse_rep  \n\n    # print(\"\\n... STEP 3: SAVE ARRAYS FOR FUTURE USE TO SAVE TIME IF USING THIS ENCODING ...\")\n    # Save for next time\n    # with open('/kaggle/working/train_captions_darien_tokenized.pickle', 'wb') as handle:\n        # pickle.dump(train_captions, handle, protocol=pickle.HIGHEST_PROTOCOL)\n    # with open('/kaggle/working/val_captions_darien_tokenized.pickle', 'wb') as handle:\n        # pickle.dump(val_captions, handle, protocol=pickle.HIGHEST_PROTOCOL)\n    \n    train_captions = tf.cast(train_captions, tf.uint8)\n    val_captions = tf.cast(val_captions, tf.uint8)\n    \n    print(\"\\n\\n... CREATING THE TOKENIZED INCHI LABEL ARRAYS COMPLETED ...\\n\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<h3 style=\"font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">4.2  RECOVER DETAILS RELATED TO IMAGES (optional)</h3>","metadata":{}},{"cell_type":"code","source":"DO_BASIC_IMG_INVESTIGATION = False\n\nif DO_BASIC_IMG_INVESTIGATION:\n    # Average Shape (from random sample) Looks To Be Around 382 pixels wide by 221 pixles tall (grayscale)\n    img_shapes = train_df.sample(1000).img_path.progress_apply(lambda x: cv2.imread(x, 0).shape)\n    average_pix_value = train_df.sample(1000).img_path.progress_apply(lambda x: cv2.imread(x, 0).mean())\n\n    average_img_w = np.array([s[1] for s in img_shapes]).mean()\n    average_img_h = np.array([s[0] for s in img_shapes]).mean()\n    std_img_w = np.array([s[1] for s in img_shapes]).std()\n    std_img_h = np.array([s[0] for s in img_shapes]).std()\n\n    print(f\"\\n... AVERAGE FOR IMG WIDTH OVER 1000 IMAGES           : {average_img_w} ...\")\n    print(f\"... STANDARD DEVIATION IN IMG WIDTH OVER 1000 IMAGES : {std_img_w}     ...\\n\")\n\n    print(f\"\\n... AVERAGE FOR IMG HEIGHT OVER 1000 IMAGES          : {average_img_h} ...\")\n    print(f\"... STANDARD DEVIATION IN IMG HEIGHT OVER 1000 IMAGES: {std_img_h}     ...\\n\")\n\n    print(f\"\\n... AVERAGE PIXEL VALUE OVER 1000 IMAGES             : {average_pix_value.values.mean()} ...\\n\")\n\nelse:\n    print(\"\\n... USING PREVIOUSLY CALCULATED VALUES ...\\n\")\n    \n    print(f\"\\n... AVERAGE FOR IMG WIDTH OVER 1000 IMAGES           : {383.06} ...\")\n    print(f\"... STANDARD DEVIATION IN IMG WIDTH OVER 1000 IMAGES : {131.797}  ...\\n\")\n\n    print(f\"\\n... AVERAGE FOR IMG HEIGHT OVER 1000 IMAGES          : {221.438}  ...\")\n    print(f\"... STANDARD DEVIATION IN IMG HEIGHT OVER 1000 IMAGES: {70.177} ...\\n\")\n\n    print(f\"\\n... AVERAGE PIXEL VALUE OVER 1000 IMAGES             : {251.72}  ...\\n\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<h3 style=\"font-family: Verdana; font-size: 20px; font-style: normal; font-weight: normal; text-decoration: none; text-transform: none; letter-spacing: 2px; color: navy; background-color: #ffffff;\">4.3  UTILITY FUNCTIONS FOR TFRECORD CREATION</h3>","metadata":{}},{"cell_type":"code","source":"def _bytes_feature(value, is_list=False):\n    \"\"\"Returns a bytes_list from a string / byte.\"\"\"\n    if isinstance(value, type(tf.constant(0))):\n        value = value.numpy() # BytesList won't unpack a string from an EagerTensor.\n    \n    if not is_list:\n        value = [value]\n    \n    return tf.train.Feature(bytes_list=tf.train.BytesList(value=value))\n\ndef _float_feature(value, is_list=False):\n    \"\"\"Returns a float_list from a float / double.\"\"\"\n        \n    if not is_list:\n        value = [value]\n        \n    return tf.train.Feature(float_list=tf.train.FloatList(value=value))\n\ndef _int64_feature(value, is_list=False):\n    \"\"\"Returns an int64_list from a bool / enum / int / uint.\"\"\"\n        \n    if not is_list:\n        value = [value]\n        \n    return tf.train.Feature(int64_list=tf.train.Int64List(value=value))\n\n\ndef create_tf_dataset(df, is_test=False, tokenized_inchi=None):\n    ds = tf.data.Dataset.from_tensor_slices(df[\"img_path\"].values)\n    if is_test:\n        return ds\n    else:\n        if tokenized_inchi is not None:\n            target_ds = tf.data.Dataset.from_tensor_slices(tokenized_inchi)\n        else:\n            target_ds = tf.data.Dataset.from_tensor_slices(df[\"InChI\"].values)\n        return tf.data.Dataset.zip((ds, target_ds))       \n\ndef prep_tf_dataset_w_target(img_path, target, img_shape,\n                             invert=True, \n                             rotate_trick=True, \n                             repair_images=False):\n    \n    img_tensor = tf_load_image(img_path, \n                               img_size=img_shape[:2], \n                               tile_to_3_channel=img_shape[-1]==3, \n                               invert=invert, \n                               rotate_trick=rotate_trick, \n                               repair_images=repair_images)\n    return img_tensor, target\n\ndef prep_tf_dataset_wo_target(img_path, img_shape,\n                             invert=True, \n                             rotate_trick=True, \n                             repair_images=False):\n    img_tensor = tf_load_image(img_path, \n                               img_size=img_shape[:2], \n                               tile_to_3_channel=img_shape[-1]==3, \n                               invert=invert, \n                               rotate_trick=rotate_trick, \n                               repair_images=repair_images)\n    img_id = tf.strings.split(tf.strings.split(img_path, \".png\")[0], \"/\")[-1]\n    return img_tensor, img_id\n\ndef serialize_raw(image, image_id, target=None):\n    \"\"\"\n    Creates a tf.Example message ready to be written to a file from 4 features.\n\n    Args:\n        image (TBD): TBD\n        image_id (str): TBD\n        target (str): | delimited integers\n    \n    Returns:\n        A tf.Example Message ready to be written to file\n    \"\"\"\n    \n    # Create a dictionary mapping the feature name to the \n    # tf.Example-compatible data type.\n    feature = {'image': _bytes_feature(tf.io.encode_png(image), is_list=False)}\n    \n    if target is not None:\n        feature[\"inchi\"] = _bytes_feature(target, is_list=False)\n    else:\n        feature[\"image_id\"] = _bytes_feature(image_id, is_list=False)\n        \n    # Create a Features message using tf.train.Example.\n    example_proto = tf.train.Example(features=tf.train.Features(feature=feature))\n    return example_proto.SerializeToString()\n\ndef serialize_tokenized(image, other, is_test=False):\n    \"\"\"\n    Creates a tf.Example message ready to be written to a file from 4 features.\n\n    Args:\n        image (TBD): TBD\n        other: Either the image_id or the target inchi\n    \n    Returns:\n        A tf.Example Message ready to be written to file\n    \"\"\"\n    # Create a dictionary mapping the feature name to the \n    # tf.Example-compatible data type.\n    feature = {'image': _bytes_feature(tf.io.encode_png(image), is_list=False)}\n    if not is_test:\n        feature[\"inchi\"] = _int64_feature(other, is_list=True)\n    else:\n        feature[\"image_id\"] = _bytes_feature(other, is_list=False)\n    \n    # Create a Features message using tf.train.Example.\n    example_proto = tf.train.Example(features=tf.train.Features(feature=feature))\n    return example_proto.SerializeToString()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_ds = create_tf_dataset(train_df, tokenized_inchi=train_captions)\ntrain_ds = train_ds.map(lambda x,y: (prep_tf_dataset_w_target(x, y, IMG_SHAPE, \n                                                              invert=DO_INVERT, \n                                                              rotate_trick=FIX_ROTATION, \n                                                              repair_images=DO_REPAIR)), num_parallel_calls=tf.data.AUTOTUNE).prefetch(tf.data.AUTOTUNE)\n\nval_ds = create_tf_dataset(val_df, tokenized_inchi=val_captions)\nval_ds = val_ds.map(lambda x,y: (prep_tf_dataset_w_target(x, y, IMG_SHAPE, \n                                                          invert=DO_INVERT, \n                                                          rotate_trick=FIX_ROTATION, \n                                                          repair_images=DO_REPAIR)), \n                    num_parallel_calls=tf.data.AUTOTUNE).prefetch(tf.data.AUTOTUNE)\n\ntest_ds = create_tf_dataset(ss_df, is_test=True)\ntest_ds = test_ds.map(lambda x: (prep_tf_dataset_wo_target(x,IMG_SHAPE, \n                                                           invert=DO_INVERT, \n                                                           rotate_trick=FIX_ROTATION, \n                                                           repair_images=DO_REPAIR)),\n                      num_parallel_calls=tf.data.AUTOTUNE).prefetch(tf.data.AUTOTUNE)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def write_tfrecords(ds, n_ex, n_ex_per_rec=20000, serialize_fn=serialize_tokenized, out_dir=\"/kaggle/working/train_records\", is_test=False):\n    n_recs = int(np.ceil(n_ex/n_ex_per_rec))\n    \n    # Make dataset iterable\n    ds = ds.as_numpy_iterator()\n    \n    # Create folder\n    if not os.path.isdir(out_dir):\n        os.makedirs(out_dir, exist_ok=True)\n        \n    # Create tfrecords\n    for i in tqdm(range(n_recs), total=n_recs):\n        print(f\"\\n... Writing TFRecord {i+1} of {n_recs} ...\\n\")\n        tfrec_path = os.path.join(out_dir, f\"{out_dir.rsplit('_', 1)[1]}_{(i+1):02}_{n_recs:02}.tfrec\")\n        with tf.io.TFRecordWriter(tfrec_path) as writer:\n            for ex in tqdm(range(n_ex_per_rec), total=n_ex_per_rec):\n                try:\n                    example = serialize_fn(*next(ds), is_test=is_test)\n                    writer.write(example)\n                except:\n                    break\n                    \nprint(\"\\n... MAKING TRAINING TFRECORDS ...\\n\")\nwrite_tfrecords(train_ds, N_TRAIN, N_EX_PER_REC, serialize_fn=serialize_tokenized, out_dir=\"/kaggle/working/train_records\")\n\n                    \nprint(\"\\n... MAKING VALIDATION TFRECORDS ...\\n\")\nwrite_tfrecords(val_ds, N_VAL, N_EX_PER_REC, serialize_fn=serialize_tokenized, out_dir=\"/kaggle/working/val_records\")\n\nprint(\"\\n... MAKING TESTING TFRECORDS ...\\n\")\nwrite_tfrecords(test_ds, N_TEST, N_EX_PER_REC, serialize_fn=serialize_tokenized, out_dir=\"/kaggle/working/test_records\", is_test=True)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def decode_records(serialized_example, is_test=False, is_tokenized=True, img_shape=(384,384,3)):\n    \"\"\" Parses a set of features and label from the given `serialized_example`.\n        \n        It is used as a map function for `dataset.map`\n\n    Args:\n        serialized_example (tf.Example): A serialized example containing the\n            following features:\n                – sensor_feature_0 – [int64]\n                – sensor_feature_1 – [int64]\n                – sensor_feature_2 – [int64]\n        is_test (bool, optional): Whether to allow for the label feature\n        \n    Returns:\n        A decoded tf.data.Dataset object representing the tfrecord dataset\n    \"\"\"\n    # Defaults are not specified since both keys are required.\n    feature_dict = {\n        'image': tf.io.FixedLenFeature(shape=[], dtype=tf.string),\n    }\n    \n    if not is_test:\n        if is_tokenized:\n            feature_dict[\"inchi\"] = tf.io.FixedLenFeature(shape=[MAX_LEN], dtype=tf.int64, default_value=[0]*MAX_LEN)\n        else:\n            feature_dict[\"inchi\"] = tf.io.FixedLenFeature(shape=[], dtype=tf.string, default_value='')\n    else:\n        feature_dict['image_id'] = tf.io.FixedLenFeature(shape=[], dtype=tf.string, default_value='')\n    \n  \n    # Define a parser\n    features = tf.io.parse_single_example(serialized_example, features=feature_dict)\n    \n    image = decode_tf_ex_image(features['image'], resize_to=(*img_shape[:2], 3))\n    if not is_test:\n        target = features[\"inchi\"]\n        return image, target\n    else:\n        image_id = features[\"image_id\"]\n        return image, image_id\n    \ndef decode_tf_ex_image(image_data, resize_to=(384,384,3)):\n    image = tf.image.decode_png(image_data, channels=3)\n    image = tf.reshape(image, resize_to)\n    return tf.cast(image, tf.uint8)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"\\n... TRAIN CHECK ...\\n\")\nCHECK_TRAIN_TFREC_PATHS = sorted(tf.io.gfile.glob(f'/kaggle/working/train_records/*.tfrec'), key=lambda x: int(x[:-4].rsplit(\"_\", 2)[1]))[:1]\ncheck_train_ds = tf.data.TFRecordDataset(CHECK_TRAIN_TFREC_PATHS, num_parallel_reads=tf.data.AUTOTUNE)\ncheck_train_ds = check_train_ds.map(lambda x: (decode_records(x, img_shape=IMG_SHAPE)), num_parallel_calls=tf.data.AUTOTUNE)\nfor x,y in check_train_ds.take(1):\n    plt.figure(figsize=(12,12))\n    plt.imshow(x)\n    plt.title(\"\".join([INT2TOK[c] for c in  y.numpy() if c not in [0,1,2]]))\n    plt.show()\n\n    \nprint(\"\\n... VALIDATION CHECK ...\\n\")\nCHECK_VAL_TFREC_PATHS = sorted(tf.io.gfile.glob(f'/kaggle/working/val_records/*.tfrec'), key=lambda x: int(x[:-4].rsplit(\"_\", 2)[1]))[:1]\ncheck_val_ds = tf.data.TFRecordDataset(CHECK_VAL_TFREC_PATHS, num_parallel_reads=tf.data.AUTOTUNE)\ncheck_val_ds = check_val_ds.map(lambda x: (decode_records(x, img_shape=IMG_SHAPE)), num_parallel_calls=tf.data.AUTOTUNE)\nfor x,y in check_val_ds.take(1):\n    plt.figure(figsize=(12,12))\n    plt.imshow(x)\n    plt.title(\"\".join([INT2TOK[c] for c in  y.numpy() if c not in [0,1,2]]))\n    plt.show()\n\n\nprint(\"\\n... TEST CHECK ...\\n\")\nCHECK_TEST_TFREC_PATHS = sorted(tf.io.gfile.glob(f'/kaggle/working/test_records/*.tfrec'), key=lambda x: int(x[:-4].rsplit(\"_\", 2)[1]))[:1]\ncheck_test_ds = tf.data.TFRecordDataset(CHECK_TEST_TFREC_PATHS, num_parallel_reads=tf.data.AUTOTUNE)\ncheck_test_ds = check_test_ds.map(lambda x: (decode_records(x, img_shape=IMG_SHAPE, is_test=True)), num_parallel_calls=tf.data.AUTOTUNE)\nfor x,y in check_test_ds.take(1):\n    plt.figure(figsize=(12,12))\n    plt.imshow(x)\n    plt.title(str(y.numpy().decode()))\n    plt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}