{"cells":[{"metadata":{},"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;\">Exploratory Data Analysis (EDA)</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>\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; 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=\"#baseline\">4&nbsp;&nbsp;&nbsp;&nbsp;BASELINE</a></h3>"},{"metadata":{},"cell_type":"markdown","source":"<h1 style=\"text-align: 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</h1>"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# FOR KERAS UTILS PLOT\n!pip install -q pydot\n!pip install -q pydotplus\n!apt-get install -q graphviz\nfrom Levenshtein import distance\n\nprint(\"\\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 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\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 tqdm\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\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\nprint(\"\\n\\n... TPU SETUP STARTING ...\\n\")\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver() # TPU detection\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\n    print(\"Running on TPU:\", tpu.master())\nexcept ValueError: # no TPU found, detect GPUs\n    strategy = tf.distribute.get_strategy() # for GPU or multi-GPU machines\n    print(\"\\n... USING GPU ...\\n\")\n    # Stop Tensorflow From Eating All The Memory\n    gpus = tf.config.experimental.list_physical_devices('GPU')\n    if 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)\n        \nN_REPLICAS = strategy.num_replicas_in_sync\nprint(f\"... Number Of Accelerators: {N_REPLICAS} ...\\n\")\nprint(\"\\n\\n... TPU SETUP COMPLETE COMPLETE ...\\n\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h1 style=\"text-align: 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</h1>"},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: 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;\">1.1  THE DATA</h3>\n\n---\n\n<b style=\"text-decoration: underline; font-family: Verdana;\">BACKGROUND INFORMATION</b>\n\nIn this competition, you are provided with images of chemicals, with the objective of predicting the corresponding [**International Chemical Identifier**](https://en.wikipedia.org/wiki/International_Chemical_Identifier) (**InChI**) text string of the image. The images provided (both in the training data as well as the test data) may be rotated to different angles, be at various resolutions, and have different noise levels.\n* Possibilities for augmentation via rotation and noise\n* There are about 4 Million total images in this dataset. Unzipping the downloaded data will take a non-trivial amount of time.\n\n<br>\n\n***Author's Take:***<br><br>\n&nbsp;&nbsp;&nbsp;&nbsp;**1. Identify Feature Vector From Image of Chemical Structure**<br>\n&nbsp;&nbsp;&nbsp;&nbsp;**2. Generate the Required String** \n\n<br>\n\nSubmissions are evaluated on the mean Levenshtein distance between the InChi strings you submit and the ground truth InChi values.\n\nFor each **image_id** in the test set, **you must predict the InChi string of the molecule in the corresponding image**. The file should contain a header and have the following format:\n\n```txt\nimage_id,InChI\n00000d2a601c,InChI=1S/H2O/h1H2\n00001f7fc849,InChI=1S/H2O/h1H2\n000037687605,InChI=1S/H2O/h1H2\netc.\n```\n\n<br><br>\n\n<b style=\"text-decoration: underline; font-family: Verdana;\">DATA FILES & DIRECTORIES</b>\n> **`train/`** - the training images, arranged in a 3-level folder structure by **image_id**<br>\n> **`test/`** - the test images, arranged in the same folder structure as **`train/`**<br>\n> **`train_labels.csv`** - ground truth **InChi** labels for the training images<br>\n> **`sample_submission.csv`** - a sample submission file in the correct format<br>"},{"metadata":{},"cell_type":"markdown","source":"<h3 style=\"text-align: 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;\">1.2  INTERNATIONAL CHEMICAL IDENTIFIER (InChi)</h3>\n\n<sub>[***Most information is from this wikipedia page***](https://en.wikipedia.org/wiki/International_Chemical_Identifier)</sub>\n\n---\n\nThe IUPAC International Chemical Identifier *(InChI /ˈɪntʃiː/ IN-chee or /ˈɪŋkiː/ ING-kee)* is a textual identifier for chemical substances, designed to provide a standard way to encode molecular information and to facilitate the search for such information in databases and on the web. Initially developed by **IUPAC** (**I**nternational **U**nion of **P**ure and **A**pplied **C**hemistry) and **NIST** (**N**ational **I**nstitute of **S**tandards and **T**echnology) from 2000 to 2005, the format and algorithms are non-proprietary.\n\nThe continuing development of the standard has been supported since 2010 by the not-for-profit **InChI Trust**, of which **IUPAC** is a member. The current software version is 1.06 and was released in December 2020.<br>\n&nbsp;&nbsp;&nbsp;&nbsp;– Prior to 1.04, the software was freely available under the open-source LGPL license.<br>\n&nbsp;&nbsp;&nbsp;&nbsp;– It now uses a custom license called IUPAC-InChI Trust License.\n\n<br><br>\n\n<b style=\"text-decoration: underline; font-family: Verdana;\">OVERVIEW</b>\n\nThe identifiers describe chemical substances in terms of **layers of information**<br>\n&nbsp;&nbsp;&nbsp;&nbsp;– The Atoms and Their Bond Connectivity<br>\n&nbsp;&nbsp;&nbsp;&nbsp;– [Tautomeric Information](https://en.wikipedia.org/wiki/Tautomer)<br>\n&nbsp;&nbsp;&nbsp;&nbsp;– [Isotope Information](https://en.wikipedia.org/wiki/Isotope)<br>\n&nbsp;&nbsp;&nbsp;&nbsp;– [Stereochemistry](https://en.wikipedia.org/wiki/Stereochemistry)<br>\n&nbsp;&nbsp;&nbsp;&nbsp;– [Electronic Charge Information](https://en.wikipedia.org/wiki/Electric_charge)<br>\n\n**NOTE:**\nNot all layers have to be provided; for instance, the tautomer layer can be omitted if that type of information is not relevant to the particular application.\n\n**InChIs** differ from the widely used CAS registry numbers in three respects:<br>\n&nbsp;&nbsp;&nbsp;&nbsp;– **firstly**, they are freely usable and non-proprietary;<br>\n&nbsp;&nbsp;&nbsp;&nbsp;– **secondly**, they can be computed from structural information and do not have to be assigned by some organization;<br>\n&nbsp;&nbsp;&nbsp;&nbsp;– **thirdly**, most of the information in an **InChI** is human readable (with practice).\n\nInChIs can thus be seen as akin to a general and extremely formalized version of IUPAC names. They can express more information than the simpler SMILES notation and differ in that every structure has a unique InChI string, which is important in database applications. Information about the 3-dimensional coordinates of atoms is not represented in InChI; for this purpose a format such as PDB can be used.\n\n<br>\n\n---\n\n<br>\n\nThe InChI algorithm converts input structural information into a unique InChI identifier in a three-step process: \n1. **Normalization** *(to remove redundant information)*\n2. **Canonicalization** *(to generate a unique number label for each atom)*\n3. **Serialization** *(to give a string of characters)*\n\n\n<br>\n\n---\n\n<br>\n\nIn order to avoid generating different InChIs for tautomeric structures, before generating the InChI, an input chemical structure is normalized to reduce it to its so-called core parent structure. This may involve changing bond orders, rearranging formal charges and possibly adding and removing protons. Different input structures may give the same result; \n- For example, acetic acid and acetate would both give the same core parent structure, that of acetic acid. \n- A core parent structure may be disconnected, consisting of more than one component, in which case the sublayers in the InChI usually consist of sublayers for each component, separated by semicolons (periods for the chemical formula sublayer.) One way this can happen is that all metal atoms are disconnected during normalization;\n    - So, for example, the InChI for tetraethyllead will have five components, one for lead and four for the ethyl groups.\n\nThe first, main, layer of the InChI refers to this core parent structure, giving its chemical formula, non-hydrogen connectivity without bond order (/c sublayer) and hydrogen connectivity (/h sublayer.) The /q portion of the charge layer gives its charge, and the /p portion of the charge layer tells how many protons (hydrogen ions) must be added to or removed from it to regenerate the original structure. If present, the stereochemical layer, with sublayers /b, /t, /m and /s, gives stereochemical information, and the isotopic layer /i (which may contain sublayers /h, /b, /t, /m and /s) gives isotopic information. These are the only layers which can occur in a standard InChI.[5]\n\nIf the user wants to specify an exact tautomer, a fixed hydrogen layer /f can be appended, which may contain various additional sublayers; this cannot be done in standard InChI though, so different tautomers will have the same standard InChI (for example, alanine will give the same standard InChI whether input in a neutral or a zwitterionic form.) Finally, a nonstandard reconnected /r layer can be added, which effectively gives a new InChI generated without breaking bonds to metal atoms. This may contain various sublayers, including /f.[5]\n\n<br>\n\n---\n\n<br>\n\n**STRUCTURE**\n\n- Every InChI starts with the string \"InChI=\" followed by the version number, currently 1. \n- If the InChI is standard, this is followed by the letter S for standard InChIs, which is a fully standardized InChI flavor maintaining the same level of attention to structure details and the same conventions for drawing perception. \n- The remaining information is structured as a sequence of layers and sub-layers, with each layer providing one specific type of information. \n    * The layers and sub-layers are separated by the delimiter \"/\" and start with a characteristic prefix letter (except for the chemical formula sub-layer of the main layer). \n    * The six layers with important sublayers are:\n\n<br>\n\n>**1. Main Layer**\n* Chemical formula (no prefix). This is the only sublayer that must occur in every InChI.\n* Atom connections (prefix: `\"c\"`). \n    * The atoms in the chemical formula (except for hydrogens) are numbered in sequence. This sublayer describes which atoms are connected by bonds to which other ones.\n* Hydrogen atoms (prefix: `\"h\"`). \n    * Describes how many hydrogen atoms are connected to each of the other atoms.\n\n<br>\n\n> **2. Charge Layer**\n* Charge Sublayer (prefix: `\"q\"`)\n* Proton Sublayer (prefix: `\"p\"`)\n\n<br>\n\n> **3. Stereochemical Layer**\n* Double Bonds and Cumulenes (prefix: `\"b\"`)\n* Tetrahedral Stereochemistry of Atoms and Allenes (prefixes: `\"t\"`, `\"m\"`)\n* Type of Stereochemistry Information (prefix: `\"s\"`)\n\n<br>\n\n> **4. Isotopic Layer**\n* prefixes: `\"i\"`, `\"h\"`, as well as `\"b\"`, `\"t\"`, `\"m\"`, `\"s\"` for isotopic stereochemistry\n\n<br>\n\n> **5. Fixed-H Layer**\n* prefix: `\"f\"`. **NEVER INCLUDED IN STANDARD INCHI**\n\n<br>\n\n> **6. Reconnected Layer**\n* prefix: `\"r\"`. **NEVER INCLUDED IN STANDARD INCHI**\n\n<br>\n\n---\n\n\n***UNRELATED ASIDE***\n\n---\n\nThe InChIKey, sometimes referred to as a hashed InChI, is a fixed length (27 character) condensed digital representation of the InChI that is not human-understandable. The InChIKey specification was released in September 2007 in order to facilitate web searches for chemical compounds, since these were problematic with the full-length InChI.[6] Unlike the InChI, the InChIKey is not unique: though collisions can be calculated to be very rare, they happen.[7]\n\n---"},{"metadata":{},"cell_type":"markdown","source":"<h1 style=\"text-align: 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</h1>"},{"metadata":{"trusted":true},"cell_type":"code","source":"ALL_ELEMENTS = [\"H\", \" He\", \" Li\", \" Be\", \" B\", \" C\", \" N\", \" O\", \" F\", \" Ne\", \" Na\", \" Mg\", \" Al\", \" Si\", \" P\", \n                \"S\", \" Cl\", \" Ar\", \" K\", \" Ca\", \" Sc\", \" Ti\", \" V\", \" Cr\", \" Mn\", \" Fe\", \" Co\", \" Ni\", \" Cu\", \" Zn\", \n                \"Ga\", \" Ge\", \" As\", \" Se\", \" Br\", \" Kr\", \" Rb\", \" Sr\", \" Y\", \" Zr\", \" Nb\", \" Mo\", \" Tc\", \" Ru\", \n                \"Rh\", \" Pd\", \" Ag\", \" Cd\", \" In\", \" Sn\", \" Sb\", \" Te\", \" I\", \" Xe\", \" Cs\", \" Ba\", \" La\", \" Ce\", \n                \"Pr\", \" Nd\", \" Pm\", \" Sm\", \" Eu\", \" Gd\", \" Tb\", \" Dy\", \" Ho\", \" Er\", \" Tm\", \" Yb\", \" Lu\", \" Hf\", \n                \"Ta\", \" W\", \" Re\", \" Os\", \" Ir\", \" Pt\", \" Au\", \" Hg\", \" Tl\", \" Pb\", \" Bi\", \" Po\", \" At\", \" Rn\", \n                \"Fr\", \" Ra\", \" Ac\", \" Th\", \" Pa\", \" U\", \" Np\", \" Pu\", \" Am\", \" Cm\", \" Bk\", \" Cf\", \" Es\", \" Fm\", \n                \"Md\", \" No\", \" Lr\", \" Rf\", \" Db\", \" Sg\", \" Bh\", \" Hs\", \" Mt\", \" Ds\", \" Rg\", \" Cn\", \" Uut\", \n                \"Fl\", \" Uup\", \" Lv\", \" Uus\", \" Uuo\"]\n\n# In Order\nTRAIN_ELEMENTS = ['C', 'H', 'B', 'Br', 'Cl', 'F', 'I', 'N', 'O', 'P', 'S', 'Si']\n\n# Prefixes and How Common\n#      -- ORDERING --> {c}{h/None}{b/None}{t/None}{m/None}{s/None}{i/None}{h/None}{t/None}{m/None}\nPREFIX_ORDERING = \"chbtmsihtm\"\n\n# Define the root and data directories\nROOT_DIR = \"/kaggle/input\"\n\n# try:\n#     DATA_DIR = KaggleDatasets().get_gcs_path(\"bms-molecular-translation\")\n#     print(\"USING TPU (GCS) DATA DIRECTORY\")\n# except:\n#     print(\"NOT USING TPU\")\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.apply(lambda x: os.path.join(TRAIN_DIR, x[0], x[1], x[2], x+\".png\"))\nprint(\"\\n... TRAIN DATAFRAME W/ PATHS ...\\n\")\ndisplay(train_df)\n\nss_df = pd.read_csv(SS_CSV_PATH)\nprint(\"\\n... SUBMISSION DATAFRAME ...\\n\")\ndisplay(ss_df)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h1 style=\"text-align: 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</h1>"},{"metadata":{"trusted":true},"cell_type":"code","source":"def flatten_l_o_l(nested_list):\n    return [item for sublist in nested_list for item in sublist]\n\ndef get_sublayer(split_list, prefix=\"c\"):\n    if not split_list:\n        return -1\n    else:\n        return split_list.pop(0)[1:]\n    \n    \ndef get_sublayer_hard(split_list, prefix=\"t\"):\n    if not split_list:\n        pass\n    else:\n        inchi_idx_list = [i for i,inchi_str in enumerate(split_list) if prefix in inchi_str]\n        if inchi_idx_list:\n            return split_list.pop(inchi_idx_list[0])[1:]\n        else:\n            pass\n        \n        \n# TRAIN --> ['S', 'Br', 'H', 'P', 'O', 'B', 'Si', 'N', 'C', 'I', 'F', 'Cl']\n# The chemical formula is represented according to Hill convention, that is, \n# - beginning with carbon atoms\n# - then hydrogens\n# - then all other elements in alphabetical order\ndef identify_relevant_elements(df):\n    tmp_chem_list = df[\"inchi_chemical_formula\"].apply(lambda x: re.split(r'(\\d+)', x))\n    tmp_chem_list = [x for x in flatten_l_o_l(tmp_chem_list) if (not x.isnumeric() and x!=\"\")]\n    return list(set(flatten_l_o_l([re.findall('[A-Z][^A-Z]*',x) for x in tmp_chem_list])))\n\n\ndef chem_form_list_to_columns(chem_form_list, all_possible_elem_list):\n    all_possible_elems = {e:0 for e in all_possible_elem_list}\n    for e, val in chem_form_list:\n        if val==\"\":\n            val=1\n        else:\n            val=int(val)\n        all_possible_elems[e]=val\n    return [all_possible_elems[e] for e in all_possible_elem_list]\n\n\ndef tf_load_image(path, img_size=(224,224), tile_to_3_channel=True, for_tpu=False):\n    img = decode_img(tf.io.read_file(path), img_size, n_channels=1, for_tpu=for_tpu)\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 decode_img(img, img_size=(224,224), n_channels=1, for_tpu=False):\n    \"\"\"TBD\"\"\"\n    \n    # convert the compressed string to a 3D uint8 tensor\n    img = tf.image.decode_png(img, channels=n_channels)\n\n    # resize the image to the desired size\n    if for_tpu:\n        return tf.cast(tf.image.resize(img, img_size), tf.float32)\n    else:\n        return tf.cast(tf.image.resize(img, img_size), tf.uint8)\n    \n    \ndef convert_pred_batch_to_str_batch(pred_batch):\n    return [\"\".join([\n        f\"{e}{int(c)}\" if c>1 else f\"{e}\" \\\n        for (c, e) in [\n            *zip(pred[np.nonzero(pred)], np.array(TRAIN_ELEMENTS)[np.nonzero(pred)])\n        ]]) for pred in np.round(pred_batch)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df[\"prefixes\"] = train_df.InChI.apply(lambda x: [xx[0] for xx in x.split(\"/\")[2:]])\nprefix_combos = train_df[\"prefixes\"].apply(lambda x: \"\".join(x)).value_counts()\ndisplay(prefix_combos)\n\n# Housekeeping Stuff\ntrain_df[\"inchi_split\"] = train_df.InChI.str.split(\"/\")\ntrain_df[\"inchi_root\"] = train_df.inchi_split.apply(lambda x: x.pop(0))\n\n# Guaranteed Stuff\ntrain_df[\"inchi_chemical_formula\"] = train_df.inchi_split.apply(lambda x: x.pop(0))\ntrain_df[\"inchi_atom_connections_/c\"] = train_df.inchi_split.apply(lambda x: get_sublayer(x, \"c\"))\n\n# Optional Stuff - Note there are no included /q and /p layers\ntrain_df[\"inchi_hydrogen_atoms_/h\"] = train_df.inchi_split.apply(lambda x: get_sublayer(x, \"h\"))\ntrain_df[\"inchi_charge_/q\"] = None\ntrain_df[\"inchi_proton_/p\"] = None\n\ntrain_df[\"inchi_db_and_c_/b\"] = train_df.inchi_split.apply(lambda x: get_sublayer_hard(x, \"b\"))\ntrain_df[\"inchi_tetrahedral_/t\"] = train_df.inchi_split.apply(lambda x: get_sublayer_hard(x, \"t\"))\ntrain_df[\"inchi_tetrahedral_/m\"] = train_df.inchi_split.apply(lambda x: get_sublayer_hard(x, \"m\"))\ntrain_df[\"inchi_stereo_info_/s\"] = train_df.inchi_split.apply(lambda x: get_sublayer_hard(x, \"s\"))\n\ntrain_df[\"inchi_isotopic_/s\"] = train_df.inchi_split.apply(lambda x: get_sublayer_hard(x, \"i\"))\ntrain_df[\"inchi_isotopic_/h\"] = train_df.inchi_split.apply(lambda x: get_sublayer_hard(x, \"h\"))\n\ntrain_df[\"inchi_tetrahedral_secondary_/t\"] = train_df.inchi_split.apply(lambda x: get_sublayer_hard(x, \"t\"))\ntrain_df[\"inchi_tetrahedral_secondary_/m\"] = train_df.inchi_split.apply(lambda x: get_sublayer_hard(x, \"m\"))\n\ntrain_df = train_df.drop(columns=[\"inchi_split\"])\n\n# Non NAN Counts\ndisplay(train_df.count())\ndisplay(train_df.head())\n\ntrain_df[\"chemical_formula_list\"] = train_df[\"inchi_chemical_formula\"].apply(lambda x: re.findall(r'([A-Z][a-z]*)([0-9]*)', x))\ntrain_df[\"chemical_formula_list\"] = train_df[\"chemical_formula_list\"].apply(lambda x: chem_form_list_to_columns(x, TRAIN_ELEMENTS))\ntrain_df = pd.concat([train_df, pd.DataFrame(train_df[\"chemical_formula_list\"].to_list(), columns=[f\"{e}_count\" for e in TRAIN_ELEMENTS])], axis=1)\ntrain_df = train_df.drop(columns=[\"chemical_formula_list\"])\n\ndisplay(train_df)\ndisplay(train_df[[\"inchi_chemical_formula\"]+[f\"{e}_count\" for e in TRAIN_ELEMENTS]])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h1 style=\"text-align: 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=\"examine_identicals\">4&nbsp;&nbsp;INVESTIGATION OF IDENTICAL MOLECULAR STRUCTURES</h1>"},{"metadata":{"trusted":true},"cell_type":"code","source":"repeated_formulas = [k for k,v in train_df[\"inchi_chemical_formula\"].value_counts().items() if v>1]\ndemo_idx = 0\ndemo_formula = repeated_formulas[demo_idx]\ntrain_df_formula_subset = train_df[train_df[\"inchi_chemical_formula\"]==demo_formula].reset_index(drop=True)\nrow_to_display = 4\n\nplt.figure(figsize=(18,5*row_to_display))\nplt.suptitle(f\"{demo_formula}\", fontsize=20)\nfor i in range(min(4*row_to_display, len(train_df_formula_subset))):\n    plt.subplot(5,4,i+1)\n    plt.imshow(np.asarray(Image.open(train_df_formula_subset.img_path[i])), cmap=\"gray\")\n    plt.title(\"{}\".format('\\n'.join(train_df_formula_subset.InChI[i].split('/')[2:])), fontweight=\"bold\", fontsize=9)\n    \nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"repeated_formulas = [k for k,v in train_df[\"inchi_chemical_formula\"].value_counts().items() if v>1]\ndemo_idx = 10\ndemo_formula = repeated_formulas[demo_idx]\ntrain_df_formula_subset = train_df[train_df[\"inchi_chemical_formula\"]==demo_formula].reset_index(drop=True)\nrow_to_display = 4\n\nplt.figure(figsize=(18,5*row_to_display))\nplt.suptitle(f\"{demo_formula}\", fontsize=20)\nfor i in range(min(4*row_to_display, len(train_df_formula_subset))):\n    plt.subplot(5,4,i+1)\n    plt.imshow(np.asarray(Image.open(train_df_formula_subset.img_path[i])), cmap=\"gray\")\n    plt.title(\"{}\".format('\\n'.join(train_df_formula_subset.InChI[i].split('/')[2:])), fontweight=\"bold\", fontsize=9)\n    \nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h1 style=\"text-align: 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=\"baseline_model\">5&nbsp;&nbsp;BASELINE MODEL</h1>"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df.image_id=train_df.image_id.astype(str)\ntrain_df.img_path=train_df.img_path.astype(str)\ntrain_df.inchi_chemical_formula=train_df.inchi_chemical_formula.astype(str)\n_ = [train_df[c].fillna(0, inplace=True) for c in train_df.columns if c.endswith(\"_count\")]\n\nVAL_SPLIT = 0.05\nVAL_INDICES = random.sample(range(len(train_df)), round(len(train_df)*VAL_SPLIT))\n\nval_df = train_df.iloc[VAL_INDICES]\ntrain_df = train_df[~train_df.index.isin(VAL_INDICES)]\n\nfeature_columns = [\"image_id\", \"img_path\"]\nlabel_columns = [f\"{e}_count\" for e in TRAIN_ELEMENTS]\n\ntrain_feature_ds = tf.data.Dataset.zip(tuple([tf.data.Dataset.from_tensor_slices(train_df[x].values) for x in feature_columns]))\ntrain_label_ds = tf.data.Dataset.from_tensor_slices(train_df[label_columns].values)\ntrain_ds = tf.data.Dataset.zip((train_feature_ds,train_label_ds))\n\nval_feature_ds = tf.data.Dataset.zip(tuple([tf.data.Dataset.from_tensor_slices(val_df[x].values) for x in feature_columns]))\nval_label_ds = tf.data.Dataset.from_tensor_slices(val_df[label_columns].values)\nval_ds = tf.data.Dataset.zip((val_feature_ds,val_label_ds))\n\ndel train_label_ds, train_feature_ds; gc.collect(); gc.collect();\ndel val_label_ds, val_feature_ds; gc.collect(); gc.collect();\n\nprint(train_ds)\nprint(val_ds)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"MODEL_CKPT_DIR = \"/kaggle/working/eb0_ckpts\"\n\n# BATCH_SIZE=BATCH_SIZE_PER_REPLICA*N_REPLICAS\nBATCH_SIZE = 64\nAUTOTUNE   = tf.data.AUTOTUNE\nSHUFF_BUFF = 256\n\n# Drop Image ID for now and open the image and stack y values as a single vector\n# if N_REPLICAS==8: # TPU PATH\n#     SAVE_LOCAL = tf.saved_model.SaveOptions(experimental_io_device='/job:localhost')\n#     train_ds = train_ds.map(lambda x,y: ((tf_load_image(x[1], for_tpu=True)), tf.cast(y, tf.float32)))\n#     val_ds = val_ds.map(lambda x,y: ((tf_load_image(x[1], for_tpu=True)), tf.cast(y, tf.float32)))\n# else:\n# TRAIN_CACHE_DIR = \"/kaggle/train_cache\"\n# VAL_CACHE_DIR = \"/kaggle/val_cache\"\n# if not os.path.isdir(TRAIN_CACHE_DIR):\n#     os.makedirs(TRAIN_CACHE_DIR, exist_ok=True)\n# if not os.path.isdir(VAL_CACHE_DIR):\n#     os.makedirs(VAL_CACHE_DIR, exist_ok=True)\nSAVE_LOCAL=None\ntrain_ds = train_ds.map(lambda x,y: ((tf_load_image(x[1])), tf.cast(y, tf.int32)))#.cache(TRAIN_CACHE_DIR)\nval_ds = val_ds.map(lambda x,y: ((tf_load_image(x[1])), tf.cast(y, tf.int32)))#.cache(VAL_CACHE_DIR)\n\ntrain_ds = train_ds.shuffle(SHUFF_BUFF) \\\n                   .batch(BATCH_SIZE) \\\n                   .prefetch(AUTOTUNE)\n\nval_ds = val_ds.batch(BATCH_SIZE) \\\n               .prefetch(AUTOTUNE)\n\nprint(train_ds, val_ds)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"N_EPOCHS=4\nLR_START = 0.0005\nLR_MAX = 0.0015\nLR_MIN = 0.00075\nLR_RAMPUP_EPOCHS = 2\nLR_SUSTAIN_EPOCHS = 1\nLR_EXP_DECAY = 0.4\n\ndef lrfn(epoch):\n    if epoch < LR_RAMPUP_EPOCHS:\n        lr = (LR_MAX - LR_START) / LR_RAMPUP_EPOCHS * epoch + LR_START\n    elif epoch < LR_RAMPUP_EPOCHS + LR_SUSTAIN_EPOCHS:\n        lr = LR_MAX\n    else:\n        lr = (LR_MAX - LR_MIN) * LR_EXP_DECAY**(epoch - LR_RAMPUP_EPOCHS - LR_SUSTAIN_EPOCHS) + LR_MIN\n    return lr\n\n# VIEW SCHEDULE\nrng = [i for i in range(N_EPOCHS)]\ny = [lrfn(x) for x in rng]\n\nplt.figure(figsize=(10,4))\nplt.plot(rng, y)\nplt.title(\"CUSTOM LR SCHEDULE\", fontweight=\"bold\")\nplt.show()\n\nprint(f\"Learning rate schedule: {y[0]:.3g} to {max(y):.3g} to {y[-1]:.3g}\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_backbone(efficientnet_name=\"efficientnet_b0\", input_shape=(224,224,3), include_top=False, weights=\"imagenet\", pooling=\"avg\"):\n    if \"b0\" in efficientnet_name:\n        eb = tf.keras.applications.EfficientNetB0(\n            include_top=include_top, weights=weights, pooling=pooling, input_shape=input_shape\n            )\n    elif \"b1\" in efficientnet_name:\n        eb = tf.keras.applications.EfficientNetB1(\n            include_top=include_top, weights=weights, pooling=pooling, input_shape=input_shape\n            )\n    elif \"b2\" in efficientnet_name:\n        eb = tf.keras.applications.EfficientNetB2(\n            include_top=include_top, weights=weights, pooling=pooling, input_shape=input_shape\n            )\n    elif \"b3\" in efficientnet_name:\n        eb = tf.keras.applications.EfficientNetB3(\n            include_top=include_top, weights=weights, pooling=pooling, input_shape=input_shape\n            )\n    elif \"b4\" in efficientnet_name:\n        eb = tf.keras.applications.EfficientNetB4(\n            include_top=include_top, weights=weights, pooling=pooling, input_shape=input_shape\n            )\n    elif \"b5\" in efficientnet_name:\n        eb = tf.keras.applications.EfficientNetB5(\n            include_top=include_top, weights=weights, pooling=pooling, input_shape=input_shape\n            )\n    elif \"b6\" in efficientnet_name:\n        eb = tf.keras.applications.EfficientNetB6(\n            include_top=include_top, weights=weights, pooling=pooling, input_shape=input_shape\n            )\n    elif \"b7\" in efficientnet_name:\n        eb = tf.keras.applications.EfficientNetB7(\n            include_top=include_top, weights=weights, pooling=pooling, input_shape=input_shape\n            )\n    else:\n        raise ValueError(\"Invalid EfficientNet Name!!!\")\n    return eb\n\n\ndef add_head_to_bb(bb, n_outputs=19, dropout=0.05, head_layer_nodes=(512,)):\n    x = tf.keras.layers.BatchNormalization()(bb.output)\n    x = tf.keras.layers.Dropout(dropout)(x)\n    \n    for n_nodes in head_layer_nodes:\n        x = tf.keras.layers.Dense(n_nodes, activation=\"relu\")(x)\n        x = tf.keras.layers.BatchNormalization()(x)\n        x = tf.keras.layers.Dropout(dropout/2)(x)\n    \n    output = tf.keras.layers.Dense(n_outputs, activation=\"linear\")(x)\n    return tf.keras.Model(inputs=bb.inputs, outputs=output)\n\n# tf.keras.backend.clear_session()\n# eb0 = get_backbone(\"b0\")\n# eb0 = add_head_to_bb(eb0, len(label_columns), dropout=0.4)\n# eb0.compile(optimizer=\"adam\", loss=tf.keras.losses.Huber(), metrics=[\"acc\", \"mae\"])\n\n# WE SKIP LOADING FRESH FOR DEMO PURPOSES\n# tf.keras.backend.clear_session()\neb0 = tf.keras.models.load_model(\"../input/moleculartranslationbaselinemodel/eb0_ckpts/ckpt-0001-2.0007.ckpt\")\ntf.keras.utils.plot_model(eb0, show_shapes=True, show_dtype=True, dpi=55)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# history = eb0.fit(\n#     train_ds.take(20), # DEMO PURPOSES\n#     validation_data=val_ds.take(10), # LEAVE AS FULL VAL SET FOR DEMO TO SEE PERFORMANCE ON VAL *~7-10 minutes~*\n#     callbacks=[\n#         tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=3, verbose=1, restore_best_weights=True),\n#         tf.keras.callbacks.ModelCheckpoint(filepath=os.path.join(MODEL_CKPT_DIR, \"ckpt-{epoch:04d}-{val_loss:.4f}.ckpt\"), verbose=1),\n#         tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=1)\n#     ], \n#     epochs=1, # DEMO PURPOSES\n# )\n# eb0.save(os.path.join(MODEL_CKPT_DIR, \"final\"))\n# eb0.evaluate(val_ds.take(10))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"repeated_formulas = [k for k,v in train_df[\"inchi_chemical_formula\"].value_counts().items() if v>1]\ndemo_idx = 2\ndemo_formula = repeated_formulas[demo_idx]\ntrain_df_formula_subset = train_df[train_df[\"inchi_chemical_formula\"]==demo_formula].reset_index(drop=True)\n\nbatch_to_infer_on = tf.stack([tf_load_image(path) for path in train_df_formula_subset.img_path.values[:16]])\nprint(f\"\\n... GT FORMULA = {demo_formula} ...\\n\")\npreds = eb0(batch_to_infer_on)\n\nprint(f\"\\nRANDOM STRING HAS A LEVENSHTEIN DISTANCE: {distance(demo_formula, 'C1H1O1N10Br4')}\\n\" \\\n      f\"\\t--> DEMO={demo_formula}\\n\" \\\n      f\"\\t--> RANDOM=C1HNO10Br4\\n\\n\")\nfor i, y_pred in enumerate(convert_pred_batch_to_str_batch(preds)):\n    print(f\"PRED {i+1} HAS A LEVENSHTEIN DISTANCE OF {distance(y_pred, demo_formula)}\\n\" \\\n          f\"\\t-->DEMO={demo_formula}\\n\\t-->PRED={y_pred}\\n\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Capture the image ids (paths) for the test set\nss_image_paths =  ss_df.image_id.apply(lambda x: os.path.join(TEST_DIR, x[0], x[1], x[2], x+\".png\")).values\n\ntest_feature_ds = tf.data.Dataset.from_tensor_slices(ss_image_paths)\ntest_ds = test_feature_ds.map(tf_load_image)\ntest_ds = test_ds.batch(BATCH_SIZE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Iterate over the submission dataframe and generate predictions for chemical formula\nchem_formula_preds = np.zeros((len(ss_image_paths,))).astype(str)\nfor i, tf_batch in enumerate(tqdm(test_ds)):\n    chem_formula_preds[i*BATCH_SIZE:(i+1)*BATCH_SIZE] = convert_pred_batch_to_str_batch(eb0(tf_batch))\n    if i%1000==0:\n        tf.keras.backend.clear_session()\n        eb0 = tf.keras.models.load_model(\"../input/moleculartranslationbaselinemodel/eb0_ckpts/ckpt-0001-2.0007.ckpt\")\n        gc.collect(); gc.collect();\n        \nwith open('/kaggle/working/preds.pickle', 'wb') as handle:\n    pickle.dump(chem_formula_preds, handle)\n\n\n# with open('../input/molecular-translation-eda-smart-baseline/preds.pickle', 'rb') as f:\n#     chem_formula_preds = pickle.load(f)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(chem_formula_preds)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"chem_formula_preds = [str(x) for x in chem_formula_preds]\nss_df[\"InChI_build\"] = [f\"InChI=1S/{x}/c\"+x+\"/c\" for x in chem_formula_preds]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ss_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"common_C = train_df[\"inchi_atom_connections_/c\"].value_counts(dropna=False).index[0]\ncommon_H = train_df[\"inchi_hydrogen_atoms_/h\"].value_counts(dropna=False).index[0]\ncommon_B = train_df[\"inchi_db_and_c_/b\"].value_counts(dropna=False).index[0]\ncommon_T1 = train_df[\"inchi_tetrahedral_/t\"].value_counts(dropna=False).index[0]\ncommon_M1 = train_df[\"inchi_tetrahedral_/m\"].value_counts(dropna=False).index[0]\ncommon_S1 = train_df[\"inchi_stereo_info_/s\"].value_counts(dropna=False).index[0]\ncommon_S2 = train_df[\"inchi_isotopic_/s\"].value_counts(dropna=False).index[0]\ncommon_H = train_df[\"inchi_isotopic_/h\"].value_counts(dropna=False).index[0]\ncommon_T2 = train_df[\"inchi_tetrahedral_secondary_/t\"].value_counts(dropna=False).index[0]\ncommon_M2 = train_df[\"inchi_tetrahedral_secondary_/m\"].value_counts(dropna=False).index[0]\n\nfor x in [common_C, common_H, common_B, common_T1, common_M1, common_S1, common_S2, common_H, common_T2, common_M2,]: print(x)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ss_df[\"InChI\"] = ss_df[\"InChI_build\"]+common_C\nss_df = ss_df.drop(columns=[\"InChI_build\"])\nss_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# inchi_submissions = []\n# for pred in chem_formula_preds:\n#     inchi_submissions.append(train_df.iloc[np.argmin((train_df[[f\"{e}_count\" for e in TRAIN_ELEMENTS]]-pred).abs().sum(axis=1))].InChI)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ss_df.to_csv(\"submission.csv\", index=False)","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}