{"cells":[{"metadata":{},"cell_type":"markdown","source":"# **Le mélanome de la peau**"},{"metadata":{},"cell_type":"markdown","source":"## Introduction\n   Un mélanome de la peau est une maladie des cellules de la peau appelées mélanocytes. Il se développe à partir d’une cellule initialement normale qui se transforme et se multiplie de façon anarchique pour former une lésion appelée tumeur maligne.\n\n   Il existe quatre principaux types de mélanome de la peau : le mélanome superficiel extensif, le mélanome de Dubreuilh, le mélanome nodulaire et le mélanome acrolentigineux. Le traitement de ces différents types de mélanome repose essentiellement sur la chirurgie qui sera adaptée à la topographie (c’est-à-dire à l’endroit où est situé le mélanome) et à la profondeur de la lésion."},{"metadata":{},"cell_type":"markdown","source":"<figure>\n<center>\n<img src='https://upload.wikimedia.org/wikipedia/commons/thumb/6/6c/Melanoma.jpg/260px-Melanoma.jpg' />\n<figcaption>La mélanome de la peau</figcaption></center>\n</figure>\n</center>"},{"metadata":{},"cell_type":"markdown","source":"## Formalisation des objectifs"},{"metadata":{},"cell_type":"markdown","source":"## Acquisition des données"},{"metadata":{},"cell_type":"markdown","source":"### Comprendre les données\nDans cette section, nous allons récupérer les données et étudier leurs distributions. Fait intéressant que les données contiennent à la fois des données tabulaires et des données d'image.\n\nNous continuerons en chargeant les métadonnées qui nous sont fournies. Les données de train ont 8 caractéristiques et 33126 observations,les données de test ont 5 caractéristiques et 10982 observations.\n\nL'ensemble de données de train se compose de:\n\n1. nom de l'image -> le nom de fichier de l'image spécifique pour la rame\n2. patient_id -> identifie le patient unique\n3. sexe -> sexe du patient\n4. age_approx -> âge approximatif du patient au moment de la numérisation\n5. anatom_site_general_challenge -> emplacement du site d'analyse\n6. diagnostic -> informations sur le diagnostic\n7. benign_malignant - indique le résultat du scan s'il est malin ou bénin\n8. target -> même que ci-dessus mais mieux pour la modélisation car c'est binaire"},{"metadata":{},"cell_type":"markdown","source":"### Analyser les données"},{"metadata":{"trusted":true},"cell_type":"code","source":"#Télécharger la bibliothèque nécessaire\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt\nimport pylab as pl\nfrom IPython import display\nimport seaborn as sns\nsns.set()\nimport re\nimport pydicom\nimport random\nimport torch\nimport torch.nn.functional as F\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models\nimport pandas as pd\n\n#import pytorch_lightning as pl\nfrom scipy.special import softmax\nfrom sklearn.model_selection import train_test_split, StratifiedKFold\nfrom sklearn.utils.class_weight import compute_class_weight\nfrom sklearn.metrics import roc_auc_score, auc\n\nfrom skimage.io import imread\nfrom PIL import Image\nimport plotly.express as px\n\nimport plotly.offline as py\npy.init_notebook_mode(connected=True)\nimport plotly.graph_objs as go\nfrom plotly.subplots import make_subplots\n\nimport os\nimport copy\n\nfrom albumentations import Compose, RandomCrop, Normalize,HorizontalFlip, Resize\nfrom albumentations import VerticalFlip, RGBShift, RandomBrightness\nfrom albumentations.core.transforms_interface import ImageOnlyTransform\nfrom albumentations.pytorch import ToTensor\n\nfrom tqdm.notebook import tqdm","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"os.listdir(\"../input/\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"basepath = '/kaggle/input/siim-isic-melanoma-classification/'\ntrain_img_path = '/kaggle/input/siim-isic-melanoma-classification/jpeg/train/'\ntest_img_path = '/kaggle/input/siim-isic-melanoma-classification/jpeg/test/'\nos.listdir(basepath)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Setting color palette.\ncolor_black = ['#333300','#666600','#999900','#CCCC00','#FFFF00','#FFFF33','#FFFF66','#FFFF99','#FFFFCC']\ncolor_black_2 = ['#FFD700','#BDB76B','#F0E68C','#EEE8AA','#FFEFD5','#FFDAB9','#330C73']\n\n# Setting color palette.\norange_black = ['#fdc029', '#df861d', '#FF6347', '#aa3d01', '#a30e15', '#800000', '#171820']\ncolor_black_3=['#318ce7','#ff66cc']\nB = ['#C3073F','#1A1A1D']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_info = pd.read_csv(basepath + \"train.csv\")\ntrain_info.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_info = pd.read_csv(basepath + \"test.csv\")\ntest_info.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Our test set misses three columns: diagnosis, benign_malignant & target."},{"metadata":{"trusted":true},"cell_type":"code","source":"train_info.shape[0] / test_info.shape[0]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Three times more entries in train than in test."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Image names\n\n# train: image_name count\ntrain_info.image_name.value_counts().max()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test: image_name count\ntest_info.image_name.value_counts().max()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Patient id counts\n\n# train: patient_id count\ntrain_info.patient_id.value_counts().max()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# test: patient_id count\ntest_info.patient_id.value_counts().max()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"patient_counts_train = train_info.patient_id.value_counts()\npatient_counts_test = test_info.patient_id.value_counts()\n\nfig, ax = plt.subplots(2,2,figsize=(20,12))\n\nsns.distplot(patient_counts_train, ax=ax[0,0], color=\"orangered\", kde=True);\nax[0,0].set_xlabel(\"Counts\")\nax[0,0].set_ylabel(\"Frequency\")\nax[0,0].set_title(\"Patient id value counts in train\");\n\nsns.distplot(patient_counts_test, ax=ax[0,1], color=\"lightseagreen\", kde=True);\nax[0,1].set_xlabel(\"Counts\")\nax[0,1].set_ylabel(\"Frequency\")\nax[0,1].set_title(\"Patient id value counts in test\");\n\nsns.boxplot(patient_counts_train, ax=ax[1,0], color=\"orangered\");\nax[1,0].set_xlim(0, 250)\nsns.boxplot(patient_counts_test, ax=ax[1,1], color=\"lightseagreen\");\nax[1,1].set_xlim(0, 250);","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Insights**\n* For most of the patients we only have a few images ranging fom 1 to roughly 20.\n* More than 45 images per patient is very seldom!\n* Nonetheless we have patients with more than 100 images.\n* There is one heavy outlier patient in the test set with close to 250 images.**"},{"metadata":{},"cell_type":"markdown","source":"**Data Visualization Age Vs. Gender**"},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(2,2,figsize=(20,15))\n\nsns.boxplot(train_info.sex, train_info.age_approx, ax=ax[0,0], palette=\"Reds_r\");\nax[0,0].set_title(\"Age per gender in train\");\n\nsns.boxplot(test_info.sex, test_info.age_approx, ax=ax[0,1], palette=\"Blues_r\");\nax[0,1].set_title(\"Age per gender in test\");\n\nsns.countplot(train_info.age_approx, hue=train_info.sex, ax=ax[1,0], palette=\"Reds_r\");\nsns.countplot(test_info.age_approx, hue=test_info.sex, ax=ax[1,1], palette=\"Blues_r\");","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Il s'agit d'un boxplot qui décrit en jaune les males et en gris les femmes , comme il est bien clair la catégorie d'age entre 40ans et 60 ans des males ont plus de chances de ne pas avoir la maladie ,par contre les femmes non atteinte ont une categorie entre 35ans jusqu'a 50 ans. En contre partie les males atteint varie leur age entre 60 ans et 70 ans et les femmes atteintes entre 40 et 60 ans."},{"metadata":{},"cell_type":"markdown","source":"**A General Look With Sunburst Chart**\n\nSunburst chart is pretty cool looking fella I'd say. It also giving lots of basic information to us. Let's see...\n\n* Only 2% of our targets are malignant\n* On malignant images males are dominant with 62%\n* Gender wise benign images are more balance 52-48% male female ratio\n* Malignant image scan locations differs based on the patients gender:\n* Meanwhile the torso is most common location in males it's almost half of the scans meanwhile in females it's 39%\n* Lower extremity is more common with female scans than males 18% males vs 26% females\n* Again upper extremity malignant scans is common with females than males (23- 17%)\n* Benign image scan locations more similar between male and female patients."},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"# Plotting interactive sunburst:\n\n\nfig = px.sunburst(data_frame=train_info,\n                  path=['benign_malignant', 'sex', 'anatom_site_general_challenge'],\n                  color='sex',\n                  color_discrete_sequence=orange_black,\n                  maxdepth=-1,\n                  title='Sunburst Chart Benign/Malignant > Sex > Location')\n\nfig.update_traces(textinfo='label+percent parent')\nfig.update_layout(margin=dict(t=0, l=0, r=0, b=0))\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Patient Information Management Individually**"},{"metadata":{"trusted":true},"cell_type":"code","source":"patient_gender_train = train_info.groupby(\"patient_id\").sex.unique().apply(lambda l: l[0])\npatient_gender_test = test_info.groupby(\"patient_id\").sex.unique().apply(lambda l: l[0])\n\ntrain_patients = pd.DataFrame(index=patient_gender_train.index.values, data=patient_gender_train.values, columns=[\"sex\"])\ntest_patients = pd.DataFrame(index=patient_gender_test.index.values, data=patient_gender_test.values, columns=[\"sex\"])\n\ntrain_patients.loc[:, \"num_images\"] = train_info.groupby(\"patient_id\").size()\ntest_patients.loc[:, \"num_images\"] = test_info.groupby(\"patient_id\").size()\n\ntrain_patients.loc[:, \"min_age\"] = train_info.groupby(\"patient_id\").age_approx.min()\ntrain_patients.loc[:, \"max_age\"] = train_info.groupby(\"patient_id\").age_approx.max()\ntest_patients.loc[:, \"min_age\"] = test_info.groupby(\"patient_id\").age_approx.min()\ntest_patients.loc[:, \"max_age\"] = test_info.groupby(\"patient_id\").age_approx.max()\n\ntrain_patients.loc[:, \"age_span\"] = train_patients[\"max_age\"] - train_patients[\"min_age\"]\ntest_patients.loc[:, \"age_span\"] = test_patients[\"max_age\"] - test_patients[\"min_age\"]\n\ntrain_patients.loc[:, \"benign_cases\"] = train_info.groupby([\"patient_id\", \"benign_malignant\"]).size().loc[:, \"benign\"]\ntrain_patients.loc[:, \"malignant_cases\"] = train_info.groupby([\"patient_id\", \"benign_malignant\"]).size().loc[:, \"malignant\"]\ntrain_patients[\"min_age_malignant\"] = train_info.groupby([\"patient_id\", \"benign_malignant\"]).age_approx.min().loc[:, \"malignant\"]\ntrain_patients[\"max_age_malignant\"] = train_info.groupby([\"patient_id\", \"benign_malignant\"]).age_approx.max().loc[:, \"malignant\"]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_patients.sort_values(by=\"malignant_cases\", ascending=False).head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Images show**"},{"metadata":{"trusted":true},"cell_type":"code","source":"import cv2, pandas as pd, matplotlib.pyplot as plt\ntrain = pd.read_csv('../input/siim-isic-melanoma-classification/train.csv')\nprint('Examples WITH Melanoma')\nimgs = train.loc[train.target==1].sample(10).image_name.values\nplt.figure(figsize=(20,8))\nfor i,k in enumerate(imgs):\n    img = cv2.imread(train_img_path+'/%s.jpg'%k)\n    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)\n    plt.subplot(2,5,i+1); plt.axis('off')\n    plt.imshow(img)\nplt.show()\nprint('Examples WITHOUT Melanoma')\nimgs = train.loc[train.target==0].sample(10).image_name.values\nplt.figure(figsize=(20,8))\nfor i,k in enumerate(imgs):\n    img = cv2.imread(train_img_path+'/%s.jpg'%k)\n    #img = cv2.imread('../input/siim-isic-melanoma-classification/jpeg/train/%s.jpg'%k)\n    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)\n    plt.subplot(2,5,i+1); plt.axis('off')\n    plt.imshow(img)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Préparation des données"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Massing values\n\n# Missing Train Values\nmissing_vals_train = train_info.isnull().sum() / train_info.shape[0]\nmissing_vals_train[missing_vals_train > 0].sort_values(ascending=False)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Missing Test Values\nmissing_vals_test = test_info.isnull().sum() / test_info.shape[0]\nmissing_vals_test[missing_vals_test > 0].sort_values(ascending=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"1. The anatomy shows most missing values.\n\n"},{"metadata":{},"cell_type":"markdown","source":"### Apprentissage (Modeling)"},{"metadata":{"trusted":true},"cell_type":"code","source":"sex_dummies = pd.get_dummies(train_info['sex'], prefix='sex')\ntrain = pd.concat([train_info, sex_dummies], axis=1)\n\n# getting dummy variables for gender on test set\n\nsex_dummies = pd.get_dummies(test_info['sex'], prefix='sex')\ntest = pd.concat([test_info, sex_dummies], axis=1)\n\n# dropping not useful columns\n\ntrain.drop(['sex','image_name','patient_id','diagnosis','benign_malignant'], axis=1, inplace=True)\ntest.drop(['sex','image_name','patient_id'], axis=1, inplace=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# getting dummy variables for location on train set\n\nanatom_dummies = pd.get_dummies(train['anatom_site_general_challenge'], prefix='anatom')\ntrain = pd.concat([train, anatom_dummies], axis=1)\n\n# getting dummy variables for location on test set\n\nanatom_dummies = pd.get_dummies(test['anatom_site_general_challenge'], prefix='anatom')\ntest = pd.concat([test, anatom_dummies], axis=1)\n\n# dropping not useful columns\n\ntrain.drop('anatom_site_general_challenge', axis=1, inplace=True)\ntest.drop('anatom_site_general_challenge', axis=1, inplace=True)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import xgboost as xgb\n\nfrom sklearn.model_selection import StratifiedKFold, train_test_split, cross_val_score, cross_validate\nfrom sklearn.metrics import roc_auc_score, roc_curve","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# dividing train set and labels for modelling\n\nX = train.drop('target', axis=1)\ny = train.target","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"X.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Setting Cross-Validation and Hold-out Set**\nCross validation might be enough but I wanted to test our model on data which it never seen before.\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"# taking holdout set for validating with stratified y\n\nX_train, X_test, y_train, y_test = train_test_split(X,\n                                                    y,\n                                                    test_size=0.2,\n                                                    stratify=y,\n                                                    random_state=42)\n\n# 5 fold stratify for cv\n\ncv = StratifiedKFold(5, shuffle=True, random_state=42)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# setting model hyperparameters, didn't include fine tuning here because of timing reasons...\n\nxg = xgb.XGBClassifier(\n    n_estimators=750,\n    min_child_weight=0.81,\n    learning_rate=0.025,\n    max_depth=2,\n    subsample=0.80,\n    colsample_bytree=0.42,\n    gamma=0.10,\n    random_state=42,\n    n_jobs=-1,\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"estimators = [xg]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# cross validation scheme\n\ndef model_check(X_train, y_train, estimators, cv):\n    model_table = pd.DataFrame()\n\n    row_index = 0\n    for est in estimators:\n\n        MLA_name = est.__class__.__name__\n        model_table.loc[row_index, 'Model Name'] = MLA_name\n\n        cv_results = cross_validate(est,\n                                    X_train,\n                                    y_train,\n                                    cv=cv,\n                                    scoring='roc_auc',\n                                    return_train_score=True,\n                                    n_jobs=-1)\n\n        model_table.loc[row_index,\n                        'Train roc Mean'] = cv_results['train_score'].mean()\n        model_table.loc[row_index,\n                        'Test roc Mean'] = cv_results['test_score'].mean()\n        model_table.loc[row_index, 'Test Std'] = cv_results['test_score'].std()\n        model_table.loc[row_index, 'Time'] = cv_results['fit_time'].mean()\n\n        row_index += 1\n\n    model_table.sort_values(by=['Test roc Mean'],\n                            ascending=False,\n                            inplace=True)\n\n    return model_table","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### **Model Results Based on Meta Features**\nResults are encouraging! It seems little bit overfitting but we might fix that in future by fine tuning. Let's leave it like that for now..."},{"metadata":{"trusted":true},"cell_type":"code","source":"# display cv results\n\nraw_models = model_check(X_train, y_train, estimators, cv)\nprint(raw_models)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# fitting train data\n\nxg.fit(X_train, y_train)\n\n# predicting on holdout set\nvalidation = xg.predict_proba(X_test)[:, 1]\n\n# checking results on validation set\nroc_auc_score(y_test, validation)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Creating Meta Submission\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"# predicting on test set\n\npredictions = xg.predict_proba(test)[:, 1]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample = pd.read_csv(os.path.join(base_path, 'sample_submission.csv'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# creating submission df\n\nmeta_df = pd.DataFrame(columns=['image_name', 'target'])\n\n# assigning predictions on submission df\n\nmeta_df['image_name'] = sample['image_name']\nmeta_df['target'] = predictions","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# creating submission csv file\n\nmeta_df.to_csv('meta_with_img_data.csv', header=True, index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"###  CNN Neural Networks"},{"metadata":{"trusted":true},"cell_type":"code","source":"pip install -U segmentation-models","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimport random\nimport re\nimport math\nimport time\n\nfrom tqdm import tqdm\nfrom tqdm.keras import TqdmCallback\n\n\nfrom pandas_summary import DataFrameSummary\n\nimport warnings\n\nwarnings.filterwarnings('ignore') # Disabling warnings for clearer outputs\n\n\n\nseed_val = 42\nrandom.seed(seed_val)\nnp.random.seed(seed_val)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Importing packages\n\nimport tensorflow as tf\nimport tensorflow.keras.backend as K\nimport efficientnet.tfkeras as efn\nfrom kaggle_datasets import KaggleDatasets\n\nseed_val = 42\n\ntf.random.set_seed(seed_val)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Loading image storage buckets\n\n# Data access\nGCS_PATH = KaggleDatasets().get_gcs_path('siim-isic-melanoma-classification')\nfilenames_train = np.array(tf.io.gfile.glob(GCS_PATH + '/tfrecords/train*.tfrec')\nfilenames_test = np.array(tf.io.gfile.glob(GCS_PATH + '/tfrecords/test*.tfrec')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Setting TPU as main device for training, if you get warnings while working with tpu's ignore them.\n\nDEVICE = 'TPU'\nif DEVICE == 'TPU':\n    print('connecting to TPU...')\n    try:        \n        tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n        print('Running on TPU ', tpu.master())\n    except ValueError:\n        print('Could not connect to TPU')\n        tpu = None\n\n    if tpu:\n        try:\n            print('Initializing  TPU...')\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('TPU initialized')\n        except _:\n            print('Failed to initialize TPU!')\n    else:\n        DEVICE = 'GPU'\n\nif DEVICE != 'TPU':\n    print('Using default strategy for CPU and single GPU')\n    strategy = tf.distribute.get_strategy()\n\nif DEVICE == 'GPU':\n    print('Num GPUs Available: ',\n          len(tf.config.experimental.list_physical_devices('GPU')))\n\nprint('REPLICAS: ', strategy.num_replicas_in_sync)\nAUTO = tf.data.experimental.AUTOTUNE","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#you can edit these settings.\n\ncfg = dict(\n           batch_size=32,\n           img_size=384,\n    \n           lr_start=0.000005,\n           lr_max=0.00000125,\n           lr_min=0.000001,\n           lr_rampup=5,\n           lr_sustain=0,\n           lr_decay=0.8,\n           epochs=12,\n    \n           transform_prob=1.0,\n           rot=180.0,\n           shr=2.0,\n           hzoom=8.0,\n           wzoom=8.0,\n           hshift=8.0,\n           wshift=8.0,\n    \n           optimizer='adam',\n           label_smooth_fac=0.05,\n           tta_steps=20\n            \n        )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_mat(rotation, shear, height_zoom, width_zoom, height_shift,\n            width_shift):\n    \n    ''' Settings for image preparations '''\n\n    # CONVERT DEGREES TO RADIANS\n    rotation = math.pi * rotation / 180.\n    shear = math.pi * shear / 180.\n\n    # ROTATION MATRIX\n    c1 = tf.math.cos(rotation)\n    s1 = tf.math.sin(rotation)\n    one = tf.constant([1], dtype='float32')\n    zero = tf.constant([0], dtype='float32')\n    rotation_matrix = tf.reshape(\n        tf.concat([c1, s1, zero, -s1, c1, zero, zero, zero, one], axis=0),\n        [3, 3])\n\n    # SHEAR MATRIX\n    c2 = tf.math.cos(shear)\n    s2 = tf.math.sin(shear)\n    shear_matrix = tf.reshape(\n        tf.concat([one, s2, zero, zero, c2, zero, zero, zero, one], axis=0),\n        [3, 3])\n\n    # ZOOM MATRIX\n    zoom_matrix = tf.reshape(\n        tf.concat([\n            one / height_zoom, zero, zero, zero, one / width_zoom, zero, zero,\n            zero, one\n        ],\n                  axis=0), [3, 3])\n\n    # SHIFT MATRIX\n    shift_matrix = tf.reshape(\n        tf.concat(\n            [one, zero, height_shift, zero, one, width_shift, zero, zero, one],\n            axis=0), [3, 3])\n\n    return K.dot(K.dot(rotation_matrix, shear_matrix),\n                 K.dot(zoom_matrix, shift_matrix))\n\n\ndef transform(image, cfg):\n    \n    ''' This function takes input images of [: , :, 3] sizes and returns them as randomly rotated, sheared, shifted and zoomed. '''\n\n    DIM = cfg['img_size']\n    XDIM = DIM % 2  # fix for size 331\n\n    rot = cfg['rot'] * tf.random.normal([1], dtype='float32')\n    shr = cfg['shr'] * tf.random.normal([1], dtype='float32')\n    h_zoom = 1.0 + tf.random.normal([1], dtype='float32') / cfg['hzoom']\n    w_zoom = 1.0 + tf.random.normal([1], dtype='float32') / cfg['wzoom']\n    h_shift = cfg['hshift'] * tf.random.normal([1], dtype='float32')\n    w_shift = cfg['wshift'] * tf.random.normal([1], dtype='float32')\n\n    # GET TRANSFORMATION MATRIX\n    m = get_mat(rot, shr, h_zoom, w_zoom, h_shift, w_shift)\n\n    # LIST DESTINATION PIXEL INDICES\n    x = tf.repeat(tf.range(DIM // 2, -DIM // 2, -1), DIM)\n    y = tf.tile(tf.range(-DIM // 2, DIM // 2), [DIM])\n    z = tf.ones([DIM * DIM], dtype='int32')\n    idx = tf.stack([x, y, z])\n\n    # ROTATE DESTINATION PIXELS ONTO ORIGIN PIXELS\n    idx2 = K.dot(m, tf.cast(idx, dtype='float32'))\n    idx2 = K.cast(idx2, dtype='int32')\n    idx2 = K.clip(idx2, -DIM // 2 + XDIM + 1, DIM // 2)\n\n    # FIND ORIGIN PIXEL VALUES\n    idx3 = tf.stack([DIM // 2 - idx2[0, ], DIM // 2 - 1 + idx2[1, ]])\n    d = tf.gather_nd(image, tf.transpose(idx3))\n\n    return tf.reshape(d, [DIM, DIM, 3])\n\ndef prepare_image(img, cfg=None, augment=True):\n    \n    ''' This function loads the image, resizes it, casts a tensor to a new type float32 in our case, transforms it using the function just above, then applies the augmentations.'''\n    \n    img = tf.image.decode_jpeg(img, channels=3)\n    img = tf.image.resize(img, [cfg['img_size'], cfg['img_size']],\n                          antialias=True)\n    img = tf.cast(img, tf.float32) / 255.0\n\n    if augment:\n        if cfg['transform_prob'] > tf.random.uniform([1], minval=0, maxval=1):\n            img = transform(img, cfg)\n\n        img = tf.image.random_flip_left_right(img)\n        img = tf.image.random_saturation(img, 0.7, 1.3)\n        img = tf.image.random_contrast(img, 0.8, 1.2)\n        img = tf.image.random_brightness(img, 0.1)\n\n    return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def read_labeled_tfrecord(example):\n    LABELED_TFREC_FORMAT = {\n        'image': tf.io.FixedLenFeature([], tf.string),\n        'image_name': tf.io.FixedLenFeature([], tf.string),\n        'patient_id': tf.io.FixedLenFeature([], tf.int64),\n        'sex': tf.io.FixedLenFeature([], tf.int64),\n        'age_approx': tf.io.FixedLenFeature([], tf.int64),\n        'anatom_site_general_challenge': tf.io.FixedLenFeature([], tf.int64),\n        'diagnosis': tf.io.FixedLenFeature([], tf.int64),\n        'target': tf.io.FixedLenFeature([], tf.int64),\n        #'width': tf.io.FixedLenFeature([], tf.int64),\n        #'height': tf.io.FixedLenFeature([], tf.int64)\n    }\n\n    example = tf.io.parse_single_example(example, LABELED_TFREC_FORMAT)\n    return example['image'], example['target']\n\n\ndef read_unlabeled_tfrecord(example):\n    UNLABELED_TFREC_FORMAT = {\n        'image': tf.io.FixedLenFeature([], tf.string),\n        'image_name': tf.io.FixedLenFeature([], tf.string),\n        'patient_id': tf.io.FixedLenFeature([], tf.int64),\n        'sex': tf.io.FixedLenFeature([], tf.int64),\n        'age_approx': tf.io.FixedLenFeature([], tf.int64),\n        'anatom_site_general_challenge': tf.io.FixedLenFeature([], tf.int64),\n    }\n    example = tf.io.parse_single_example(example, UNLABELED_TFREC_FORMAT)\n    return example['image'], example['image_name']\n\ndef count_data_items(filenames):\n    n = [\n        int(re.compile(r'-([0-9]*)\\.').search(filename).group(1))\n        for filename in filenames\n    ]\n    return np.sum(n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def getTrainDataset(files, cfg, augment=True, shuffle=True):\n    \n    ''' This function reads the tfrecord train images, shuffles them, apply augmentations to them and prepares the data for training. '''\n    \n    ds = tf.data.TFRecordDataset(files, num_parallel_reads=AUTO)\n    ds = ds.cache()\n\n    if shuffle:\n        opt = tf.data.Options()\n        opt.experimental_deterministic = False\n        ds = ds.with_options(opt)\n\n    ds = ds.map(read_labeled_tfrecord, num_parallel_calls=AUTO)\n    ds = ds.repeat()\n    if shuffle:\n        ds = ds.shuffle(2048)\n    ds = ds.map(lambda img, label:\n                (prepare_image(img, augment=augment, cfg=cfg), label),\n                num_parallel_calls=AUTO)\n    ds = ds.batch(cfg['batch_size'] * strategy.num_replicas_in_sync)\n    ds = ds.prefetch(AUTO)\n    return ds\n\ndef getTestDataset(files, cfg, augment=False, repeat=False):\n    \n    ''' This function reads the tfrecord test images and prepares the data for predicting. '''\n    \n    ds = tf.data.TFRecordDataset(files, num_parallel_reads=AUTO)\n    ds = ds.cache()\n    if repeat:\n        ds = ds.repeat()\n    ds = ds.map(read_unlabeled_tfrecord, num_parallel_calls=AUTO)\n    ds = ds.map(lambda img, idnum:\n                (prepare_image(img, augment=augment, cfg=cfg), idnum),\n                num_parallel_calls=AUTO)\n    ds = ds.batch(cfg['batch_size'] * strategy.num_replicas_in_sync)\n    ds = ds.prefetch(AUTO)\n    return ds\n\ndef get_model():\n    \n    ''' This function gets the layers inclunding efficientnet ones. '''\n    \n    model_input = tf.keras.Input(shape=(cfg['img_size'], cfg['img_size'], 3),\n                                 name='img_input')\n\n    dummy = tf.keras.layers.Lambda(lambda x: x)(model_input)\n\n    outputs = []\n\n    x = efn.EfficientNetB3(include_top=False,\n                           weights='noisy-student',\n                           input_shape=(cfg['img_size'], cfg['img_size'], 3),\n                           pooling='avg')(dummy)\n    x = tf.keras.layers.Dense(1, activation='sigmoid')(x)\n    outputs.append(x)\n\n    x = efn.EfficientNetB4(include_top=False,\n                           weights='noisy-student',\n                           input_shape=(cfg['img_size'], cfg['img_size'], 3),\n                           pooling='avg')(dummy)\n    x = tf.keras.layers.Dense(1, activation='sigmoid')(x)\n    outputs.append(x)\n\n    x = efn.EfficientNetB5(include_top=False,\n                           weights='noisy-student',\n                           input_shape=(cfg['img_size'], cfg['img_size'], 3),\n                           pooling='avg')(dummy)\n    x = tf.keras.layers.Dense(1, activation='sigmoid')(x)\n    outputs.append(x)\n\n    model = tf.keras.Model(model_input, outputs, name='aNetwork')\n    model.summary()\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def compileNewModel(cfg):\n    \n    ''' Configuring the model with losses and metrics. '''    \n    \n    with strategy.scope():\n        model = get_model()\n\n    with strategy.scope():\n        model.compile(optimizer=cfg['optimizer'],\n                      loss=[\n                          tf.keras.losses.BinaryCrossentropy(\n                              label_smoothing=cfg['label_smooth_fac']),\n                          tf.keras.losses.BinaryCrossentropy(\n                              label_smoothing=cfg['label_smooth_fac']),\n                          tf.keras.losses.BinaryCrossentropy(\n                              label_smoothing=cfg['label_smooth_fac'])\n                      ],\n                      metrics=[tf.keras.metrics.AUC(name='auc')])\n    return model\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def getLearnRateCallback(cfg):\n    \n    ''' Using callbacks for learning rate adjustments. '''\n    \n    lr_start = cfg['lr_start']\n    lr_max = cfg['lr_max'] * strategy.num_replicas_in_sync * cfg['batch_size']\n    lr_min = cfg['lr_min']\n    lr_rampup = cfg['lr_rampup']\n    lr_sustain = cfg['lr_sustain']\n    lr_decay = cfg['lr_decay']\n\n    def lrfn(epoch):\n        if epoch < lr_rampup:\n            lr = (lr_max - lr_start) / lr_rampup * epoch + lr_start\n        elif epoch < lr_rampup + lr_sustain:\n            lr = lr_max\n        else:\n            lr = (lr_max - lr_min) * lr_decay**(epoch - lr_rampup -\n                                                lr_sustain) + lr_min\n        return lr\n\n    lr_callback = tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=False)\n    return lr_callback\n\ndef learnModel(model, ds_train, stepsTrain, cfg, ds_val=None, stepsVal=0):\n    \n    ''' Fitting things together for training '''\n    \n    callbacks = [getLearnRateCallback(cfg)]\n\n    history = model.fit(ds_train,\n                        validation_data=ds_val,\n                        verbose=1,\n                        steps_per_epoch=stepsTrain,\n                        validation_steps=stepsVal,\n                        epochs=cfg['epochs'],\n                        callbacks=callbacks)\n\n    return history","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Here we train our model, takes a while but at the end we'll have strong model to make predictions!"},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install git+https://github.com/qubvel/efficientnet","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# getting train data\n\nds_train = getTrainDataset(\n    filenames_train, cfg).map(lambda img, label: (img, (label, label, label)))\nstepsTrain = count_data_items(filenames_train) / \\\n    (cfg['batch_size'] * strategy.num_replicas_in_sync)\n\n# compiling and training model\n\nmodel = compileNewModel(cfg)\nlearnModel(model, ds_train, stepsTrain, cfg)","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}