{"cells":[{"metadata":{"papermill":{"duration":0.045074,"end_time":"2021-01-13T22:39:03.980489","exception":false,"start_time":"2021-01-13T22:39:03.935415","status":"completed"},"tags":[]},"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: #74d5dd; background-color: #ffffff;\">Human Protein Atlas - Single Cell Classification</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"},{"metadata":{"papermill":{"duration":0.042275,"end_time":"2021-01-13T22:39:04.065992","exception":false,"start_time":"2021-01-13T22:39:04.023717","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<h2 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</h2>\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=\"#helper_functions\">4&nbsp;&nbsp;&nbsp;&nbsp;DATASET PREPERATION</a></h3>\n\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\">5&nbsp;&nbsp;&nbsp;&nbsp;MODEL BUILDING</a></h3>\n\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\">6&nbsp;&nbsp;&nbsp;&nbsp;MODEL TRAINING</a></h3>\n\n---"},{"metadata":{"papermill":{"duration":0.043617,"end_time":"2021-01-13T22:39:04.153416","exception":false,"start_time":"2021-01-13T22:39:04.109799","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<a 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</a>"},{"metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.execute_input":"2021-01-13T22:39:04.243237Z","iopub.status.busy":"2021-01-13T22:39:04.242538Z","iopub.status.idle":"2021-01-13T22:39:39.252127Z","shell.execute_reply":"2021-01-13T22:39:39.252932Z"},"papermill":{"duration":35.05654,"end_time":"2021-01-13T22:39:39.253146","exception":false,"start_time":"2021-01-13T22:39:04.196606","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"print(\"\\n... IMPORTS STARTING ...\\n\")\nprint(\"\\n\\tVERSION INFORMATION\")\n\n# For later visualization\n!pip install -q visualkeras\nimport visualkeras\n\n# Machine Learning and Data Science Imports\nimport tensorflow_addons as tfa; print(f\"\\t\\t– TENSORFLOW ADDONS VERSION: {tfa.__version__}\");\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__}\");\nimport scipy; print(f\"\\t\\t– SCIPY VERSION: {scipy.__version__}\");\n\n# Built In Imports\nfrom kaggle_datasets import KaggleDatasets\nfrom collections import Counter\nfrom datetime import datetime\nimport multiprocessing\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 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# PRESETS\nLBL_NAMES = [\"Nucleoplasm\", \"Nuclear Membrane\", \"Nucleoli\", \"Nucleoli Fibrillar Center\", \"Nuclear Speckles\", \"Nuclear Bodies\", \"Endoplasmic Reticulum\", \"Golgi Apparatus\", \"Intermediate Filaments\", \"Actin Filaments\", \"Microtubules\", \"Mitotic Spindle\", \"Centrosome\", \"Plasma Membrane\", \"Mitochondria\", \"Aggresome\", \"Cytosol\", \"Vesicles\", \"Negative\"]\nINT_2_STR = {x:LBL_NAMES[x] for x in np.arange(19)}\nINT_2_STR_LOWER = {k:v.lower().replace(\" \", \"_\") for k,v in INT_2_STR.items()}\nSTR_2_INT_LOWER = {v:k for k,v in INT_2_STR_LOWER.items()}\nSTR_2_INT = {v:k for k,v in INT_2_STR.items()}\nFIG_FONT = dict(family=\"Helvetica, Arial\", size=14, color=\"#7f7f7f\")\nLABEL_COLORS = [px.colors.label_rgb(px.colors.convert_to_RGB_255(x)) for x in sns.color_palette(\"Spectral\", len(LBL_NAMES))]\nLABEL_COL_MAP = {str(i):x for i,x in enumerate(LABEL_COLORS)}\n\nprint(\"\\n\\n... IMPORTS COMPLETE ...\\n\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"try:\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    \nN_REPLICAS = strategy.num_replicas_in_sync\nprint(f\"... Number Of Accelerators: {N_REPLICAS} ...\\n\")","execution_count":null,"outputs":[]},{"metadata":{"papermill":{"duration":0.042806,"end_time":"2021-01-13T22:39:39.338543","exception":false,"start_time":"2021-01-13T22:39:39.295737","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<a 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;XAI BACKGROUND INFORMATION</a>"},{"metadata":{"papermill":{"duration":0.042139,"end_time":"2021-01-13T22:39:39.423376","exception":false,"start_time":"2021-01-13T22:39:39.381237","status":"completed"},"tags":[]},"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  WHAT IS EXPLAINABLE AI - GENERAL INFO</h3>\n\n---\n\nFor the purposes of this notebook and my explanation, I will be logically seperating explainable AI into two seperate branches. \n\n[**See this excerpt/paper that explains the branches in more detail.**](https://arxiv.org/pdf/1907.07374.pdf)\n\n<i>\n    \n> \"The two major categories presented here, namely perceptive interpretability and interpretability by mathematical structures, appear to present different polarities within the notion of interpretability. \n> \n> As an example for the difficulty with perceptive interpretability, when a visual evidence is given erroneously, the underlying mathematical structure may not seem to provide useful clues on the mistakes. \n> \n> On the other hand, a mathematical analysis of patterns may provide\ninformation in high dimensions. They can only be easily perceived once the pattern is brought into lower dimensions, abstracting some fine-grained information we could not yet prove is not discriminative with measurable certainty.\"\n\n[**<sup><sub>Tjoa, E., & Guan, C. (2019). A survey on explainable artificial intelligence (XAI): Towards\nmedical XAI. arXiv preprint arXiv:1907.0737</sub></sup>**](https://arxiv.org/pdf/1907.07374.pdf)\n\n</i>\n    \n<br><br>\n\n<b style=\"text-decoration: underline; font-family: Verdana;\">PERCEPTIVE</b>\n    \nIn short this is interpretability that can be observed by humans. Often the explanations arising through this branch are obvious to humans or already known.\n\n\n<br>\n\n<b style=\"text-decoration: underline; font-family: Verdana;\">MATHEMATICAL</b>\n    \nIn short this is interpretability that can only be observed by first applying mathematical manipulations to the data. An example technique that most are familiar with is clustering **[`(t-SNE)`](https://en.wikipedia.org/wiki/T-distributed_stochastic_neighbor_embedding)**\n"},{"metadata":{"papermill":{"duration":0.042063,"end_time":"2021-01-13T22:39:39.507915","exception":false,"start_time":"2021-01-13T22:39:39.465852","status":"completed"},"tags":[]},"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  THE GOAL</h3>\n\n---\n\n**Pull the veil back on black-box machine learning models and help users understand how/why a model makes the decisions that it does. This can inform on how to improve the model as well as being useful for identifying things like bias and overfitting**\n\n<center>\n\n![XAI APPROACHES](https://i.ibb.co/ZXdBQ4D/Screen-Shot-2020-07-07-at-10-24-16-AM.png)\n\n</center>"},{"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.3  CURRENT XAI APPROACHES</h3>\n\n---\n\n| Algorithm                     \t| Type         \t| Description                                                                                                                                                                                                                                                                                                                                                                                                  \t|\n|:-------------------------------\t|:--------------\t|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\t|\n| Integrated Gradients        \t| Gradient     \t| Approximates the integral of gradients along the path (straight line from baseline to input) sand multiplies with (input - baseline)                                                                                                                                                                                                                                                                         \t|\n| DeepLift                    \t| Application  \t| Explains differences in the non-linear activations' outputs in terms of the differences of the input from its corresponding reference.                                                                                                                                                                                                   \t|\n| DeepLiftSHAP                \t| Gradient     \t| An extension of DeepLift that approximates SHAP values.<br>For each input example it considers a distribution of baselines and computes the expected value of the attributions based on DeepLift algorithm across all input-baseline pairs.                                                                 \t|\n| GradientSHAP                \t| Gradient     \t| Approximates SHAP values based on the expected gradients.<br>It adds gaussian noise to each input example #samples times, selects a random point between each sample and randomly drawn baseline from baselines' distribution, computes the gradient for it and multiples it with (input - baseline).<br>Final SHAP values represent the expected values of gradients * (input - baseline) for each input example. \t|\n| Input * Gradient              \t| Gradient     \t| Multiplies model inputs with the gradients of the model outputs w.r.t. those inputs.                                                                                                                                                                                                                                                                                                                         \t|\n| Saliency                     \t| Gradient     \t| The gradients of the output w.r.t. inputs.                                                                                                                                                                                                                                                                                                                                                                   \t|\n| Guided BackProp / DeconvNet \t| Gradient     \t| Computes the gradients of the model outputs w.r.t. its inputs.<br>If there are any RELUs present in the model, their gradients will be overridden so that only positive gradients of the inputs (in case of Guided BackProp) and outputs (in case of deconvnet) are back-propagated.                                                                                                                            \t|\n| Guided GradCam                \t| Gradient     \t| Computes the element-wise product of Guided BackProp and up-sampled positive GradCam attributions.                                                                                                                                                                                                                                                                                                           \t|\n| LayerGradCam                  \t| Gradient     \t| Computes the gradients of model outputs w.r.t. selected input layer, averages them for each output channel and multiplies with the layer activations.                                                                                                                                                                                                                                                        \t|\n| Layer Internal Influence      \t| Gradient     \t| Approximates the integral of gradients along the path from baseline to inputs for selected input layer.                                                                                                                                                                                                                                                                                                      \t|\n| Layer Conductance            \t| Gradient     \t| Decomposes integrated gradients via chain rule.<br>It approximates the integral of gradients defined by a chain rule, described as the gradients of the output w.r.t. to the neurons multiplied by the gradients of the neurons w.r.t. the inputs, along the path from baseline to inputs.<br>Finally, the latter is multiplied by (input - baseline).                                                             \t|\n| Layer Gradient * Activation   \t| Gradient     \t| Computes element-wise product of layer activations and the gradient of the output w.r.t. that layer.                                                                                                                                                                                                                                                                                                         \t|\n| Layer Activation              \t| -            \t| Computes the inputs or outputs of selected layer.                                                                                                                                                                                                                                                                                                                                                            \t|\n| Feature Ablation            \t| Perturbation \t| Assigns an importance score to each input feature based on the magnitude changes in model output or loss when those features are replaced by a baseline (usually zeros) based on an input feature mask.                                                                                                                                                                                                      \t|\n| Feature Permutation           \t| Perturbation \t| Assigns an importance score to each input feature based on the magnitude changes in model output or loss when those features are permuted based on input feature mask.                                                                                                                                                                                                                                       \t|\n| Occlusion                     \t| Perturbation \t| Assigns an importance score to each input feature based on the magnitude changes in model output when those features are replaced by a baseline (usually zeros) using rectangular sliding windows and sliding strides.<br>If a features is located in multiple hyper-rectangles the importance scores are averaged across those hyper-rectangles.                                                               \t|\n| Shapely Value                 \t| Perturbation \t| Computes feature importances based on all permutations of all input features.<br>It adds each feature for each permutation one-by-one to the baseline and computes the magnitudes of output changes for each feature which are ultimately being averaged across all permutations to estimate final attribution score.                                                                                           \t|\n| Shapely Value Sampling        \t| Perturbation \t| Similar to Shapely value, but instead of considering all feature permutations it considers only #samples random permutations.                                                                                                                                                                                                                                                                                \t|\n| NoiseTunnel                   \t| -            \t| Depends on the choice of above mentioned attribution algorithm                                                                                                                                                                                                                                                                                                                                                                        \t|\n"},{"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.4  WHERE IS XAI NEEDED?</h3>\n\n---\n\n***Obviously things like the weakly-supervised tasks in this competition may require XAI***\n\nXAI can be used for a wide range of things that we won't get into here (protecting against bias, protecting against overfitting, detecting features, etc.)\n\nOne other place XAI can be used is when working with black-box models. To understand what that is we will see the definitions and examples of the terms: **Transparent and Black-Box Models**:\n\n<br>\n\n<b style=\"text-decoration: underline; font-family: Verdana;\">TRANSPARENT MODELS</b>\n\nThese are models/algorithms that are easily interpretable and **DO NOT** (generally) requre XAI. \n\n*Although occasionally post-hoc analysis is required or basic explainability tools.*\n\n<b>\n\n* Linear/Logistic Regression\n* Decision Trees\n* K-Nearest Neighbors\n* Rule Based Learners\n* General Additive Models\n* Bayesian Models\n\n</b><br>\n\n<b style=\"text-decoration: underline; font-family: Verdana;\">BLACK-BOX MODELS</b>\n\nThese are models/algorithms that are NOT easily interpretable and **DO** requre XAI. \n\n*This is not an exhaustive list of black-box models. It is simply the more common black-box models.*\n\n\n<b>\n\n* Tree Ensembles\n* Support Vector Machines\n* Multi-Layer Neural Network (MLPNN)\n* Convolutional Neural Network (CNN)\n* Recurrent Neural Network (RNN)\n\n</b>"},{"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: uppercase; letter-spacing: 2px; color: navy; background-color: #ffffff;\">1.4  Why Is XAI Needed: The Case For Growing Global AI Regulation</h3>\n\n---\n\nMany regulatory bodies have begun to encourage or enforce explainability in predictive algorithms used in the public domain.<br><br>**See below!**<br><sub>*(list was created roughly a year ago)*</sub>\n\n- GDPR: Article 22 empowers individuals with the right to demand an explanation of how an\nautomated system made a decision that affects them.\n- Algorithmic Accountability Act 2019: Requires companies to provide an assessment of the risks posed by\nthe automated decision system to the privacy or security and the risks that contribute to inaccurate, unfair,\nbiased, or discriminatory decisions impacting consumers\n- California Consumer Privacy Act: Requires companies to rethink their approach to capturing,\nstoring, and sharing personal data to align with the new requirements by January 1, 2020.\n- Washington Bill 1655: Establishes guidelines for the use of automated decision systems to protect\nconsumers, improve transparency, and create more market predictability.\n- Massachusetts Bill H.2701: Establishes a commission on automated decision-making,\ntransparency, fairness, and individual rights.\n- Illinois House Bill 3415: States predictive data analytics determining creditworthiness or hiring\ndecisions may not include information that correlates with the applicant race or zip code."},{"metadata":{},"cell_type":"markdown","source":"<a 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;INTEGRATED GRADIENTS BACKGROUND INFORMATION</a>"},{"metadata":{},"cell_type":"markdown","source":"This notebook will show how to implement Integrated Gradients (IG) for this competition**. IG is an Explainable AI (XAI) technique introduced in the paper **[Axiomatic Attribution for Deep Networks](https://arxiv.org/abs/1703.01365)\n\n\n***LINKS***\n\n&nbsp;&nbsp;&nbsp;&nbsp;- **[Tensorflow - Google Colab This Is Heavily Based Off Of](https://colab.research.google.com/github/tensorflow/docs/blob/master/site/en/tutorials/interpretability/integrated_gradients.ipynb)**<br>\n\n---\n\n**I**ntegrated **G**radients (**IG**) aims to explain the relationship between a model's predictions in terms of its features. It has many use cases including understanding feature importances, identifying data skew, and debugging model performance.\n\n**IG** has become a popular interpretability technique due to its broad applicability to any differentiable model (e.g. images, text, structured data), ease of implementation, theoretical justifications, and computational efficiency relative to alternative approaches that allows it to scale to large networks and feature spaces such as images.\n\nGo to this notebook to see the implementation of IG. In it, we will walk through an implementation of **IG** step-by-step to understand the pixel feature importances of an image classifier. \n\n---\n\nAs an example, consider this **[image](https://commons.wikimedia.org/wiki/File:San_Francisco_fireboat_showing_off.jpg)** of a fireboat spraying jets of water. \n\nYou would classify this image as a **fireboat** and might highlight the pixels making up the **boat** and **water cannons** as being important to your decision. \n\nYour model will also classify this image as a fireboat later on in this tutorial; however, does it highlight the same pixels as important when explaining its decision?\n\nIn the images below titled \"**IG** Attribution Mask\" and \"Original + **IG** Mask Overlay\" you can see that your model instead highlights (in purple) the pixels comprising the boat's **water cannons** and **jets of water** as being ***more important than the boat itself*** to its decision. \n\nHow will your model generalize to new fireboats? What about fireboats without water jets? \n\nRead on to learn more about how **IG** works and how to apply **IG** to your models to better understand the relationship between their predictions and underlying features.\n\n![IG Example](https://www.tensorflow.org/tutorials/interpretability/images/IG_fireboat.png)"},{"metadata":{"papermill":{"duration":0.042459,"end_time":"2021-01-13T22:39:39.678624","exception":false,"start_time":"2021-01-13T22:39:39.636165","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<a 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\">3&nbsp;&nbsp;NOTEBOOK SETUP</a>"},{"metadata":{"execution":{"iopub.execute_input":"2021-01-13T22:39:39.779248Z","iopub.status.busy":"2021-01-13T22:39:39.778548Z","iopub.status.idle":"2021-01-13T22:39:40.76653Z","shell.execute_reply":"2021-01-13T22:39:40.767021Z"},"papermill":{"duration":1.046203,"end_time":"2021-01-13T22:39:40.767178","exception":false,"start_time":"2021-01-13T22:39:39.720975","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# Define the root data directory\nGCS_DATA_DIR = KaggleDatasets().get_gcs_path(\"hpa-single-cell-image-classification\")\nLOCAL_DATA_DIR = \"/kaggle/input/hpa-single-cell-image-classification\"\n\n# Define the paths to the training and testing tfrecord and image folders respectively\nTRAIN_IMG_DIR = os.path.join(GCS_DATA_DIR, \"train\")\nTRAIN_TFREC_DIR = os.path.join(GCS_DATA_DIR, \"train_tfrecords\")\nTEST_IMG_DIR = os.path.join(GCS_DATA_DIR, \"test\")\nTEST_TFREC_DIR = os.path.join(GCS_DATA_DIR, \"test_tfrecords\")\n\n# Capture all the relevant full image paths\nTRAIN_IMG_PATHS = tf.io.gfile.glob(os.path.join(TRAIN_IMG_DIR, '*.png'))\nTEST_IMG_PATHS = tf.io.gfile.glob(os.path.join(TEST_IMG_DIR, '*.png'))\nprint(f\"\\n... Recall that 4 training images compose one example (R,G,B,Y) ...\")\nprint(f\"... \\t– i.e. The first 4 training files are:\")\nfor path in [x.rsplit('/',1)[1] for x in TRAIN_IMG_PATHS[:4]]: print(f\"... \\t\\t– {path}\")\nprint(f\"\\n... The number of training images is {len(TRAIN_IMG_PATHS)} i.e. {len(TRAIN_IMG_PATHS)//4} 4-channel images ...\")\nprint(f\"... The number of testing images is {len(TEST_IMG_PATHS)} i.e. {len(TEST_IMG_PATHS)//4} 4-channel images ...\")\n\n# Capture all the relevant full tfrec paths\nTRAIN_TFREC_PATHS = tf.io.gfile.glob(os.path.join(TRAIN_TFREC_DIR, '*.tfrec'))\nTEST_TFREC_PATHS = tf.io.gfile.glob(os.path.join(TEST_TFREC_DIR, '*.tfrec'))\nprint(f\"\\n... The number of training tfrecord files is {len(TRAIN_TFREC_PATHS)} ...\")\nprint(f\"... The number of testing tfrecord files is {len(TEST_TFREC_PATHS)} ...\\n\")\n\n# Define paths to the relevant csv files\nTRAIN_CSV = os.path.join(LOCAL_DATA_DIR, \"train.csv\")\nSS_CSV = os.path.join(LOCAL_DATA_DIR, \"sample_submission.csv\")\n\n# Create the relevant dataframe objects\ntrain_df = pd.read_csv(TRAIN_CSV)\nss_df = pd.read_csv(SS_CSV)\n\nprint(\"\\n\\nTRAIN DATAFRAME\\n\\n\")\ndisplay(train_df.head(3))\n\nprint(\"\\n\\nSAMPLE SUBMISSION DATAFRAME\\n\\n\")\ndisplay(ss_df.head(3))","execution_count":null,"outputs":[]},{"metadata":{"papermill":{"duration":0.043842,"end_time":"2021-01-13T22:39:40.85577","exception":false,"start_time":"2021-01-13T22:39:40.811928","status":"completed"},"tags":[]},"cell_type":"markdown","source":"<a 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\">4&nbsp;&nbsp;HELPER FUNCTIONS</a>"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","execution":{"iopub.execute_input":"2021-01-13T22:39:40.980411Z","iopub.status.busy":"2021-01-13T22:39:40.979421Z","iopub.status.idle":"2021-01-13T22:39:40.982404Z","shell.execute_reply":"2021-01-13T22:39:40.981881Z"},"papermill":{"duration":0.080032,"end_time":"2021-01-13T22:39:40.982523","exception":false,"start_time":"2021-01-13T22:39:40.902491","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"def load_image(img_id, img_dir):\n    \"\"\" Load An Image Using ID and Directory Path - Composes 4 Individual Images \"\"\"\n    rgby = [\n        np.asarray(Image.open(os.path.join(img_dir, img_id+f\"_{c}.png\")), np.uint8) \\\n        for c in [\"red\", \"green\", \"blue\", \"yellow\"]\n    ]\n    return np.stack(rgby, axis=-1)\n\n\ndef plot_rgb(arr, figsize=(12,12)):\n    \"\"\" Plot 3 Channel Microscopy Image \"\"\"\n    plt.figure(figsize=figsize)\n    plt.title(f\"RGB Composite Image\", fontweight=\"bold\")\n    plt.imshow(arr)\n    plt.axis(False)\n    plt.show()\n    \n    \ndef convert_rgby_to_rgb(arr):\n    \"\"\" Convert a 4 channel (RGBY) image to a 3 channel RGB image.\n    \n    Advice From Competition Host/User: lnhtrang\n\n    For annotation (by experts) and for the model, I guess we agree that individual \n    channels with full range px values are better. \n    In annotation, we toggled the channels. \n    For visualization purpose only, you can try blending the channels. \n    For example, \n        - red = red + yellow\n        - green = green + yellow/2\n        - blue=blue.\n        \n    Args:\n        arr (numpy array): The RGBY, 4 channel numpy array for a given image\n    \n    Returns:\n        RGB Image\n    \"\"\"\n    \n    rgb_arr = np.zeros_like(arr[..., :-1])\n    rgb_arr[..., 0] = arr[..., 0]\n    rgb_arr[..., 1] = arr[..., 1]+arr[..., 3]/2\n    rgb_arr[..., 2] = arr[..., 2]\n    \n    return rgb_arr\n\ndef tif_gzip_to_png(tif_path):\n    \"\"\"Function to convert .tif.gz to .png and put it in the same folder\n    \n    Eg. for working in local work station\n    \n    Args:\n        tif_path (str): Path to the tif zip file to be converted to png\n        \n    Returns:\n        None; Zip will be unzipped to same directory as .tif zip exists    \n    \"\"\"\n    \n    png_path = pathlib.Path(tif_path.replace('.tif.gz','.png'))\n    tf = gzip.open(tif_path).read()\n    img = imageio.imread(tf, 'tiff')\n    imageio.imwrite(png_path, img)\n    \n    \ndef download_and_convert_tifgzip_to_png(url, target_path):    \n    \"\"\"Function to convert .tif.gz to .png and put it in the same folder\n    \n    Args:\n        url (str): Path to the url containing the tifgzip file\n        target_path (str): Path to directory to unzip to\n        \n    Returns:\n        None; Images are downloaded and unzipped    \n    \"\"\"\n    \n    r = requests.get(url)\n    f = io.BytesIO(r.content)\n    tf = gzip.open(f).read()\n    img = imageio.imread(tf, 'tiff')\n    imageio.imwrite(target_path, img)\n\n    \ndef get_new_data():\n    public_hpa_df = pd.read_csv('../input/publichpa/kaggle_2021.tsv',sep='\\t',header=None)\n    public_hpa_df.columns = ['Image', 'Label']\n    colors = ['blue', 'red', 'green', 'yellow']\n    save_dir = os.path.join(os.getcwd(),'publichpa')\n    if not os.path.exists(save_dir):\n        os.makedirs(save_dir)\n\n    for i, row in public_hpa_df.iterrows():\n        try:\n            img = row.Image\n            for color in colors:\n                img_url = f'{img}_{color}.tif.gz'\n                save_path = os.path.join(save_dir,  f'{os.path.basename(img)}_{color}.png')\n                download_and_convert_tifgzip_to_png(img_url, save_path)\n                print(f'Downloaded {img_url} as {save_path}')    \n        except:\n            print(f'failed to download: {img}')\n    return save_dir\n\n\ndef plot_ex(arr, figsize=(20,6), title=None, plot_merged=True, rgb_only=False):\n    \"\"\" Plot 4 Channels Side by Side \"\"\"\n    if plot_merged and not rgb_only:\n        n_images=5 \n    elif plot_merged and rgb_only:\n        n_images=4\n    elif not plot_merged and rgb_only:\n        n_images=4\n    else:\n        n_images=3\n    plt.figure(figsize=figsize)\n    if type(title) == str:\n        plt.suptitle(title, fontsize=20, fontweight=\"bold\")\n\n    for i, c in enumerate([\"Red Channel – Microtubles\", \"Green Channel – Protein of Interest\", \"Blue - Nucleus\", \"Yellow – Endoplasmic Reticulum\"]):\n        if not rgb_only:\n            ch_arr = np.zeros_like(arr[..., :-1])        \n        else:\n            ch_arr = np.zeros_like(arr)\n        if c in [\"Red Channel – Microtubles\", \"Green Channel – Protein of Interest\", \"Blue - Nucleus\"]:\n            ch_arr[..., i] = arr[..., i]\n        else:\n            if rgb_only:\n                continue\n            ch_arr[..., 0] = arr[..., i]\n            ch_arr[..., 1] = arr[..., i]\n        plt.subplot(1,n_images,i+1)\n        plt.title(f\"{c.title()}\", fontweight=\"bold\")\n        plt.imshow(ch_arr)\n        plt.axis(False)\n        \n    if plot_merged:\n        plt.subplot(1,n_images,n_images)\n        \n        if rgb_only:\n            plt.title(f\"Merged RGB\", fontweight=\"bold\")\n            plt.imshow(arr)\n        else:\n            plt.title(f\"Merged RGBY into RGB\", fontweight=\"bold\")\n            plt.imshow(convert_rgby_to_rgb(arr))\n        plt.axis(False)\n        \n    plt.tight_layout(rect=[0, 0.2, 1, 0.97])\n    plt.show()\n    \n    \ndef flatten_list_of_lists(l_o_l):\n    return [item for sublist in l_o_l for item in sublist]\n\ndef get_class_wts(df, low_idx=4):\n    label_counts = Counter([c for sublist in df.Label.str.split(\"|\").to_list() for c in sublist])\n    low_val = sorted(label_counts.values())[low_idx-1] # Not the lowest as it is very underrepresented\n    class_wts ={int(k):min(1.0, low_val/v) for k,v in label_counts.items()}\n    return {i:class_wts[i] for i in sorted(class_wts)}\n\ndef decode_image(image_data, resize_to=(512,512)):\n    image = tf.image.decode_png(image_data, channels=1)\n    # explicit size needed for TPU\n    image = tf.image.resize(image, resize_to) \n    return tf.cast(image, tf.float32)\n\ndef str_2_multi_hot_encoding(tfstring, n_classes=19):\n    ragged_indices = tf.strings.to_number(tf.strings.split(tfstring, sep=\"|\"), out_type=tf.int32)\n    one_hot_stack = tf.one_hot(ragged_indices, depth=n_classes)\n    return tf.reduce_max(one_hot_stack, axis=-2)\n\n\ndef decode(serialized_example):\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        'image_name': tf.io.FixedLenFeature(shape=(), dtype=tf.string),\n        'target': tf.io.FixedLenFeature(shape=(), dtype=tf.string),\n    }\n    \n  \n    # Define a parser\n    features = tf.io.parse_single_example(serialized_example, features=feature_dict)\n        \n    image = decode_image(features['image'])\n    image_name = features[\"image_name\"]\n    label = str_2_multi_hot_encoding(features[\"target\"])\n    return image, image_name, label\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a 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=\"dataset_preperation\">5&nbsp;&nbsp;DATASET PREPERATION</a>"},{"metadata":{"trusted":true},"cell_type":"code","source":"N_EPOCHS=5\nN_EX = len(train_df)\nINPUT_SHAPE = (512,512,3)\nBATCH_SIZE=16*strategy.num_replicas_in_sync\nSHUFF_BUFF=200\nLOAD_MODEL_PATH = \"/kaggle/input/hpa-xai-ig-tfrecords-tpu-training/model\" # None or 'path/to/model'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# num_parallel_reads=None forces the order to be preserved\nraw_train_ds = tf.data.TFRecordDataset(TRAIN_TFREC_PATHS, num_parallel_reads=None)\n\n# See an example\nfor raw in raw_train_ds.take(1):\n    example = tf.train.Example()\n    example.ParseFromString(raw.numpy())\n    for k,v in example.features.feature.items():\n        print(k)\n        if k!=\"image\":\n            print(f\"\\t--> {v.bytes_list.value[0]}\")\n        else:\n            print(f\"\\t-->{str(v.bytes_list.value[0][:25])+' ... '}\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_ds = raw_train_ds.map(decode)\n\n# See examples\nprint(\"\\n ... NOTICE THE IMAGES ARE CLUMPED TOGETHER BY CHANNEL FOR A GIVEN ID ...\\n\")\nfor i, (img, image_name, lbl) in enumerate(train_ds.take(4)):\n    print(f\"IMAGE SHAPE : {img.shape}\")\n    print(f\"IMG #{i//4} -- IMAGE NAME  : {image_name.numpy().decode()}\")\n    print(f\"IMAGE LABEL : {lbl}\\n\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#####################################################\n# ANNOYINGLY THIS DOES NOT WORK AS THE QUADRUPLE OF #\n# CHANNEL IMAGES IS IN A DIFFERENT ORDER EVERY TIME #\n#####################################################\n# red_train_ds = train_ds.shard(4, index=3)\n# green_train_ds = train_ds.shard(4, index=2)\n# blue_train_ds = train_ds.shard(4, index=0)\n# yellow_train_ds = train_ds.shard(4, index=1)\n#####################################################\n\nred_train_ds = train_ds.filter(lambda x,y,z: tf.strings.regex_full_match(y, \".*red.*\"))\ngreen_train_ds = train_ds.filter(lambda x,y,z: tf.strings.regex_full_match(y, \".*green.*\"))\nblue_train_ds = train_ds.filter(lambda x,y,z: tf.strings.regex_full_match(y, \".*blue.*\"))\nyellow_train_ds = train_ds.filter(lambda x,y,z: tf.strings.regex_full_match(y, \".*yellow.*\"))\n\nprint(\"\\n ... NOTICE THE IMAGES ARE NOW IN THEIR OWN DATASET BY COLOR ...\\n\")\nfor (img_r, image_name_r, lbl_r), (img_b, image_name_b, lbl_b) in zip(red_train_ds.take(4), blue_train_ds.take(4)):\n    print(f\"IMAGE SHAPE : R={img_r.shape} - B={img_b.shape}\")\n    print(f\"IMAGE NAME  : R={image_name_r.numpy().decode()} - B={image_name_b.numpy().decode()}\")\n    print(f\"IMAGE LABEL : R={lbl_r} - B={lbl_b}\")\n    print()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def preprocess_tfrec_ds(red, green, blue, yellow, drop_yellow=True, return_id=False):\n    (ri, rn, rl), (gi, gn, gl), (bi, bn, bl), (yi, yn, yl) = red, green, blue, yellow\n    if drop_yellow:\n        combo_img = tf.stack([ri[..., 0], gi[..., 0], bi[..., 0]], axis=-1)\n    else:\n        combo_img = tf.stack([ri[..., 0], gi[..., 0], bi[..., 0], yi[..., 0]], axis=-1)\n    \n    if return_id:\n        img_id = tf.strings.substr(rn, pos=0, len=36) # 36 is length of id (always)\n        return combo_img, img_id, rl\n    else:\n        return combo_img, rl\n    \ntrain_ds = tf.data.Dataset.zip((red_train_ds, green_train_ds, blue_train_ds, yellow_train_ds)).map(preprocess_tfrec_ds)\n\nprint(\"\\n\\t\\t... TRAIN EXAMPLES ...\\n\")\nfor x,y in train_ds.take(3):\n    plot_ex(x.numpy().astype(np.uint8), title=f\"LABELS = {[INT_2_STR[lbl] for lbl in np.where(y.numpy()==1)[0]]}\", rgb_only=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def augment(img_batch, lbl_batch):\n    # SEEDING & KERNEL INIT\n    SEED = tf.random.uniform((2,), minval=0, maxval=100, dtype=tf.dtypes.int32)\n    K = tf.random.uniform((1,), minval=0, maxval=4, dtype=tf.dtypes.int32)[0]\n\n    img_batch = tf.image.stateless_random_flip_left_right(img_batch, SEED)\n    img_batch = tf.image.stateless_random_flip_up_down(img_batch, SEED)\n    img_batch = tf.image.rot90(img_batch, K)\n    \n    img_batch = tf.image.stateless_random_saturation(img_batch, 0.9, 1.1, SEED)\n    img_batch = tf.image.stateless_random_brightness(img_batch, 0.075, SEED)\n    img_batch = tf.image.stateless_random_contrast(img_batch, 0.9, 1.1, SEED)    \n\n    return img_batch, lbl_batch\n\n# ############################################################################ #\n# #### NOT IMPLEMENTING CURRENTLY AS ITS VERY SLOW AND TFRECORDS CONTAIN #### #\n# #### OVERLAPPING IMAGE_IDS MAKE IT IMPOSSIBLE TO SPLIT OFF ONE OR TWO  #### #\n# ####                      I WILL FIX THAT LATER                        #### #\n# ############################################################################ #\n# val_ds = train_ds.take(N_VAL)\n# train_ds = train_ds.skip(N_VAL)\n# val_ds = val_ds.batch(BATCH_SIZE) \\\n#                .prefetch(tf.data.AUTOTUNE)\n# ############################################################################ #\n\ntrain_ds = train_ds.repeat() \\\n                   .shuffle(SHUFF_BUFF) \\\n                   .batch(BATCH_SIZE) \\\n                   .map(augment, num_parallel_calls=tf.data.AUTOTUNE) \\\n                   .prefetch(tf.data.AUTOTUNE)\ntrain_ds","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a 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=\"model_creation\">6&nbsp;&nbsp;MODEL CREATION</a>"},{"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: uppercase; letter-spacing: 2px; color: navy; background-color: #ffffff;\">6.1 Model Paramaters/Configuration</h3>\n\n---"},{"metadata":{"trusted":true},"cell_type":"code","source":"DROPOUT = 0.25\nLR = 0.00075\nOUTPUT_ACTIVIATION = \"sigmoid\"\nLOSS_FN = \"binary_crossentropy\"\n\nclass_wts = get_class_wts(train_df)\nprint(\"\\n ... CLASS WEIGHTING ...\\n\")\nfor k,v in class_wts.items(): print(f\"\\tCLASS #{k:<2} --> {v:.4f}\")","execution_count":null,"outputs":[]},{"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: uppercase; letter-spacing: 2px; color: navy; background-color: #ffffff;\">6.2 Model Creation & Initialization</h3>\n\n---\n\nThe original image size from the EfficientNet paper for EfficientNetB2 is 260x260x3. We are however not bound by this and can use a smaller/larger size if we want. The original image sizes used for every version of EfficientNet are:\n\n- EfficientNetB0 - (224, 224, 3)\n- EfficientNetB1 - (240, 240, 3)\n- EfficientNetB2 - (260, 260, 3)\n- EfficientNetB3 - (300, 300, 3)\n- EfficientNetB4 - (380, 380, 3)\n- EfficientNetB5 - (456, 456, 3)\n- EfficientNetB6 - (528, 528, 3)\n- EfficientNetB7 - (600, 600, 3)\n\n<br>\n\n**I'LL BE USING `EfficientNetB2` WITH (512x512x3) INPUT**"},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_backbone(efficientnet_name=\"efficientnet_b0\", input_shape=(512,512,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_classes=19, dropout=0.3):\n    x = tf.keras.layers.Dropout(dropout)(bb.output)\n    output = tf.keras.layers.Dense(n_classes, activation=\"softmax\")(x)\n    return tf.keras.Model(inputs=bb.inputs, outputs=output)\n\n\nif LOAD_MODEL_PATH is None:\n    with strategy.scope():\n        eb = get_backbone(\"b5\")\n        eb = add_head_to_bb(eb)\n        eb.compile(optimizer=tf.keras.optimizers.Adam(lr=LR), \n                   loss=tf.keras.losses.BinaryCrossentropy(label_smoothing=0.0), \n                   metrics=[\"acc\", tf.keras.metrics.AUC(name=\"auc\", multi_label=True)])\nelse:\n    with strategy.scope():\n        load_locally = tf.saved_model.LoadOptions(experimental_io_device='/job:localhost')\n        eb = tf.keras.models.load_model(LOAD_MODEL_PATH, options=load_locally) # loading in Tensorflow's \"SavedModel\" format\n\nprint(\"\\n... VISUAL OF MODEL ...\\n\")\ndisplay(visualkeras.layered_view(eb))\n\nprint(\"\\n... MODEL SUMMARY ...\\n\")\neb.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a 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=\"model_training\">7&nbsp;&nbsp;MODEL TRAINING</a>"},{"metadata":{"trusted":true},"cell_type":"code","source":"### POTENTIAL LOSS FN ###\n# def macro_double_soft_f1(y, y_hat):\n#     \"\"\"Compute the macro soft F1-score as a cost (average 1 - soft-F1 across all labels).\n#     Use probability values instead of binary predictions.\n#     This version uses the computation of soft-F1 for both positive and negative class for each label.\n    \n#     Args:\n#         y (int32 Tensor): targets array of shape (BATCH_SIZE, N_LABELS)\n#         y_hat (float32 Tensor): probability matrix from forward propagation of shape (BATCH_SIZE, N_LABELS)\n        \n#     Returns:\n#         cost (scalar Tensor): value of the cost function for the batch\n#     \"\"\"\n#     y = tf.cast(y, tf.float32)\n#     y_hat = tf.cast(y_hat, tf.float32)\n#     tp = tf.reduce_sum(y_hat * y, axis=0)\n#     fp = tf.reduce_sum(y_hat * (1 - y), axis=0)\n#     fn = tf.reduce_sum((1 - y_hat) * y, axis=0)\n#     tn = tf.reduce_sum((1 - y_hat) * (1 - y), axis=0)\n#     soft_f1_class1 = 2*tp / (2*tp + fn + fp + 1e-16)\n#     soft_f1_class0 = 2*tn / (2*tn + fn + fp + 1e-16)\n#     cost_class1 = 1 - soft_f1_class1 # reduce 1 - soft-f1_class1 in order to increase soft-f1 on class 1\n#     cost_class0 = 1 - soft_f1_class0 # reduce 1 - soft-f1_class0 in order to increase soft-f1 on class 0\n#     cost = 0.5 * (cost_class1 + cost_class0) # take into account both class 1 and class 0\n#     macro_cost = tf.reduce_mean(cost) # average on all labels\n#     return macro_cost\n\n### POTENTIAL LOSS FN ###\n# def macro_soft_f1(y, y_hat):\n#     \"\"\"Compute the macro soft F1-score as a cost.\n#     Average (1 - soft-F1) across all labels.\n#     Use probability values instead of binary predictions.\n    \n#     Args:\n#         y (int32 Tensor): targets array of shape (BATCH_SIZE, N_LABELS)\n#         y_hat (float32 Tensor): probability matrix of shape (BATCH_SIZE, N_LABELS)\n        \n#     Returns:\n#         cost (scalar Tensor): value of the cost function for the batch\n#     \"\"\"\n    \n#     y = tf.cast(y, tf.float32)\n#     y_hat = tf.cast(y_hat, tf.float32)\n#     tp = tf.reduce_sum(y_hat * y, axis=0)\n#     fp = tf.reduce_sum(y_hat * (1 - y), axis=0)\n#     fn = tf.reduce_sum((1 - y_hat) * y, axis=0)\n#     soft_f1 = 2*tp / (2*tp + fn + fp + 1e-16)\n#     cost = 1 - soft_f1 # reduce 1 - soft-f1 in order to increase soft-f1\n#     macro_cost = tf.reduce_mean(cost) # average on all labels\n    \n#     return macro_cost\n\n### POTENTIAL METRIC ###\n# def macro_f1(y, y_hat, thresh=0.5):\n#     \"\"\"Compute the macro F1-score on a batch of observations (average F1 across labels)\n    \n#     Args:\n#         y (int32 Tensor): labels array of shape (BATCH_SIZE, N_LABELS)\n#         y_hat (float32 Tensor): probability matrix from forward propagation of shape (BATCH_SIZE, N_LABELS)\n#         thresh: probability value above which we predict positive\n        \n#     Returns:\n#         macro_f1 (scalar Tensor): value of macro F1 for the batch\n#     \"\"\"\n#     y_pred = tf.cast(tf.greater(y_hat, thresh), tf.float32)\n#     tp = tf.cast(tf.math.count_nonzero(y_pred * y, axis=0), tf.float32)\n#     fp = tf.cast(tf.math.count_nonzero(y_pred * (1 - y), axis=0), tf.float32)\n#     fn = tf.cast(tf.math.count_nonzero((1 - y_pred) * y, axis=0), tf.float32)\n#     f1 = 2*tp / (2*tp + fn + fp + 1e-16)\n#     macro_f1 = tf.reduce_mean(f1)\n#     return macro_f1\n\n\n# Using an LR ramp up because fine-tuning a pre-trained model.\n# Starting with a high LR would break the pre-trained weights.\nLR_START = 0.00001\nLR_MAX = 0.000125*strategy.num_replicas_in_sync\nLR_MIN = 0.00001\nLR_RAMPUP_EPOCHS = 3\nLR_SUSTAIN_EPOCHS = 0\nLR_EXP_DECAY = 0.875\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(10 if N_EPOCHS<10 else 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":"history = eb.fit(\n    train_ds, \n    callbacks=[\n        tf.keras.callbacks.ReduceLROnPlateau(monitor='loss', patience=1),\n        tf.keras.callbacks.EarlyStopping(monitor='loss', patience=2, verbose=1, restore_best_weights=True),\n        tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=1)\n    ], \n    class_weight=class_wts, \n    epochs=N_EPOCHS,\n    steps_per_epoch=N_EX//BATCH_SIZE,\n)\nsave_locally = tf.saved_model.SaveOptions(experimental_io_device='/job:localhost')\neb.save('./model', options=save_locally) # saving in Tensorflow's \"SavedModel\" format","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize = (12, 12))\n\nplt.subplot(2,1,1)\nplt.xlabel(\"Epochs\", fontweight=\"bold\")\nplt.ylabel(\"Loss\", fontweight=\"bold\")\nplt.plot(history.history[\"loss\"], label = \"Training Loss\" , marker='o')\nplt.grid(True)\nplt.legend()\n\nplt.subplot(2,1,2)\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"AUC\")\nplt.plot(history.history[\"auc\"], label = \"Training AUC\" , marker='o')\nplt.grid(True)\nplt.legend()\n\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a 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=\"appendix\">8&nbsp;&nbsp;APPENDIX - HOW TO RECREATE THE TFRECORDS</a>"},{"metadata":{"trusted":true},"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 serialize(image, image_name, target):\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_name (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 = {\n        'image': _bytes_feature(image, is_list=False),\n        'image_name': _bytes_feature(image_name, is_list=False),\n        'target': _bytes_feature(target, is_list=False),\n    }\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()","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}