{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"    \n<center><img src=https://hubmapconsortium.org/wp-content/uploads/2019/01/HuBMAP-Retina-Logo-Color.png></center>","metadata":{}},{"cell_type":"markdown","source":"Starter notebook - training the UNet from `segmentation_models` library using pre-saved 512x512 Numpy arrays.<br>\nInference notebook: https://www.kaggle.com/vmuzhichenko/hubmap-hpa-tf-unet-infer \n\n#### Credits:\n* https://www.kaggle.com/code/wrrosa/hubmap-tf-with-tpu-efficientunet-512x512-train\n* https://www.kaggle.com/code/susnato/hubmap-hpa-fpn-starter-tf\n* https://www.kaggle.com/code/awsaf49/uwmgi-2-5d-train-pytorch","metadata":{}},{"cell_type":"code","source":"import os\nimport gc\nimport cv2\nimport glob\nfrom tqdm import notebook\nimport tifffile as tiff \nimport numpy as np \nimport pandas as pd \nimport tensorflow as tf\nfrom tensorflow.keras import backend as K\nfrom tensorflow.keras.utils import get_custom_objects\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import KFold\nimport albumentations as A\n\n!pip install segmentation_models -q\nimport segmentation_models as sm\nsm.set_framework('tf.keras')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-07-15T09:50:52.550815Z","iopub.execute_input":"2022-07-15T09:50:52.551210Z","iopub.status.idle":"2022-07-15T09:51:12.468771Z","shell.execute_reply.started":"2022-07-15T09:50:52.551128Z","shell.execute_reply":"2022-07-15T09:51:12.467549Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"config = {\n'backbone':'efficientnetb4',\n'img_size': 512,\n'n_folds' : 3,\n'seed' : 142,\n'epochs': 30,\n'LR_MAX': 0.0015,\n'LR_MIN':1e-5,\n'LR_DECAY': 0.9, \n'plot_history': True\n}\n\nDEBUG = False\nTRAIN_ONE_EPOCH = False\n\ntrain = pd.read_csv(\"../input/hubmap-organ-segmentation/train.csv\")\ntest = pd.read_csv(\"../input/hubmap-organ-segmentation/test.csv\")\nsample_submission = pd.read_csv(\"../input/hubmap-organ-segmentation/sample_submission.csv\")\n\nimages_dir = '../input/hubmap-hpa-numpy-512x512/train_images/'\nmasks_dir = '../input/hubmap-hpa-numpy-512x512/train_masks/'\n\nif DEBUG:\n    train = train.head(50)","metadata":{"execution":{"iopub.status.busy":"2022-07-15T09:51:12.472422Z","iopub.execute_input":"2022-07-15T09:51:12.473382Z","iopub.status.idle":"2022-07-15T09:51:12.757807Z","shell.execute_reply.started":"2022-07-15T09:51:12.473353Z","shell.execute_reply":"2022-07-15T09:51:12.756867Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# https://tensorlayer.readthedocs.io/en/latest/_modules/tensorlayer/cost.html#dice_coe\ndef dice_coe(output, target, axis = (1,2), smooth=1e-10):\n    output = tf.dtypes.cast( tf.math.greater(output, 0.5), tf. float32 )\n    target = tf.dtypes.cast( tf.math.greater(target, 0.5), tf. float32 )\n    inse = tf.reduce_sum(output * target, axis=axis)\n    l = tf.reduce_sum(output, axis=axis)\n    r = tf.reduce_sum(target, axis=axis)\n\n    dice = (2. * inse + smooth) / (l + r + smooth)\n    dice = tf.reduce_mean(dice, name='dice_coe')\n    return dice","metadata":{"execution":{"iopub.status.busy":"2022-07-15T09:51:12.759342Z","iopub.execute_input":"2022-07-15T09:51:12.759932Z","iopub.status.idle":"2022-07-15T09:51:12.767932Z","shell.execute_reply.started":"2022-07-15T09:51:12.759886Z","shell.execute_reply":"2022-07-15T09:51:12.766962Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def list_arrays(inds: list):\n    imgs = [images_dir + f'{x}.npy' for x in inds]\n    masks = [masks_dir + f'{x}.npy' for x in inds]\n    \n    return imgs, masks\n\ndef load_img(path):\n    img = np.load(path)\n    img = img.astype('float32') \n    mx = np.max(img)\n    if mx:\n        img/=mx \n    return img\n\ndef load_msk(path):\n    msk = np.load(path)\n    msk = msk.astype('float32')\n    return np.expand_dims(msk, axis=-1)","metadata":{"execution":{"iopub.status.busy":"2022-07-15T09:51:12.771464Z","iopub.execute_input":"2022-07-15T09:51:12.771890Z","iopub.status.idle":"2022-07-15T09:51:12.779805Z","shell.execute_reply.started":"2022-07-15T09:51:12.771862Z","shell.execute_reply":"2022-07-15T09:51:12.778676Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class sample_generator(tf.keras.utils.Sequence):\n    def __init__(self, img_paths: list, msk_paths:list, batch_size, labels=True,transforms=None, shuffle=True):\n        self.label      = labels\n        self.img_paths  = img_paths\n        self.msk_paths  = msk_paths\n        self.transforms = transforms\n        self.batch_size = batch_size\n        self.length = len(img_paths)\n        self.shuffle = shuffle\n        \n    def __len__(self):\n        return  int(np.ceil(self.length / float(self.batch_size)))\n    \n    def __getitem__(self, index):\n        \n        batch_x=[]\n        batch_y=[]\n            \n        if self.label: \n            \n            for i in range(self.batch_size):\n                cur_ind = self.batch_size*index + i\n                if cur_ind < self.length:\n                    \n                    if self.shuffle:\n                        cur_ind = np.random.randint(self.length)\n                    \n                    img_path  = self.img_paths[cur_ind]\n                    img = load_img(img_path)\n\n                    msk_path = self.msk_paths[cur_ind]\n                    msk = load_msk(msk_path)\n                    if self.transforms:\n                        data = self.transforms(image=img, mask=msk)\n                        img  = data['image']\n                        msk  = data['mask']\n\n                    batch_x.append(img), batch_y.append(msk)\n            return np.array(batch_x), np.array(batch_y)\n        \n        else:\n            \n            for i in range(self.batch_size):\n                cur_ind = self.batch_size*index + i\n                if cur_ind < self.length:\n            \n                    img_path  = self.img_paths[cur_ind]\n                    img = load_img(img_path)\n                    if self.transforms:\n                        data = self.transforms(image=img)\n                        img  = data['image']\n                        \n                    batch_x.append(img),\n            return np.array(batch_x)","metadata":{"execution":{"iopub.status.busy":"2022-07-15T09:51:12.781059Z","iopub.execute_input":"2022-07-15T09:51:12.781698Z","iopub.status.idle":"2022-07-15T09:51:12.798800Z","shell.execute_reply.started":"2022-07-15T09:51:12.781661Z","shell.execute_reply":"2022-07-15T09:51:12.797876Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_transforms = {\n    \"train\": A.Compose([\n\nA.HorizontalFlip(p=0.5),\nA.VerticalFlip(p=0.5),\nA.RandomBrightnessContrast(p=0.25),\nA.ShiftScaleRotate(shift_limit=0.0625, scale_limit=0.05, rotate_limit=10, p=0.5),\nA.OneOf([A.GridDistortion(num_steps=5, distort_limit=0.05, p=1.0),\n         A.ElasticTransform(alpha=1, sigma=20, alpha_affine=20, p=1.0)\n        ], p=0.25),\nA.CoarseDropout(max_holes=16, max_height=config['img_size']//50, max_width=config['img_size']//50, min_holes=5, fill_value=0, mask_fill_value=0, p=0.5),\n        ], p=1.0),\n    \"valid\": A.Compose([], p=1.0)\n}","metadata":{"execution":{"iopub.status.busy":"2022-07-15T09:51:12.801508Z","iopub.execute_input":"2022-07-15T09:51:12.802275Z","iopub.status.idle":"2022-07-15T09:51:12.814899Z","shell.execute_reply.started":"2022-07-15T09:51:12.802239Z","shell.execute_reply":"2022-07-15T09:51:12.813843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_lr_sin(plot=False):\n    # Learning Rate Scheduler function\n    def lr_fnc(epoch):   \n        sin_base = (np.sin(epoch/1.5)+1)/2\n        lr = sin_base * config['LR_MAX'] * config['LR_DECAY']**epoch + config['LR_MIN']/(epoch+1)\n        \n        return lr\n    \n    # Display the Learning Rate Scheduler\n    if plot:\n        lr_list = [lr_fnc(x) for x in list(range(config['epochs']))]\n        plt.scatter(range(config['epochs']), lr_list)\n        plt.show()\n    lr_callback = tf.keras.callbacks.LearningRateScheduler(lr_fnc, verbose=False)\n    return lr_callback\n\nget_lr_sin(plot=True)\n\nlrs = get_lr_sin(plot=False)","metadata":{"execution":{"iopub.status.busy":"2022-07-15T09:51:12.817777Z","iopub.execute_input":"2022-07-15T09:51:12.818428Z","iopub.status.idle":"2022-07-15T09:51:13.035307Z","shell.execute_reply.started":"2022-07-15T09:51:12.818392Z","shell.execute_reply":"2022-07-15T09:51:13.034412Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"estop = tf.keras.callbacks.EarlyStopping(monitor='val_dice_coe', patience=5, verbose=0, mode='max',restore_best_weights=True)\n\n\nkf_split = KFold(n_splits=config['n_folds'], shuffle=True, random_state=config['seed'])\nfor fold,(tr_idx, val_idx) in enumerate(kf_split.split(train.id)):\n    \n    train_imgs, train_masks = list_arrays(train.iloc[tr_idx].id.to_list())\n    val_imgs, val_masks = list_arrays(train.iloc[val_idx].id.to_list())\n    \n    train_dataset = sample_generator(train_imgs, train_masks, 4 ,transforms=data_transforms['train'])\n    val_dataset = sample_generator(val_imgs, val_masks, 4 ,transforms=data_transforms['valid'], shuffle=False)\n    \n    K.clear_session()\n    \n    model = sm.Unet(config['backbone'], encoder_weights='imagenet')\n    model.compile(optimizer = tf.keras.optimizers.Adam(lr = 0.001),\n                      loss = tf.keras.losses.BinaryCrossentropy(),\n                      metrics=[dice_coe, tf.keras.metrics.AUC(from_logits=True)])\n    \n      \n    path_to_checkpoints = f\"{config['backbone']}_UNet_{config['img_size']}x{config['img_size']}_best_fold_{fold+1}.hdf5\"\n    checkpointer = tf.keras.callbacks.ModelCheckpoint(filepath=path_to_checkpoints, monitor=\"val_dice_coe\", mode='max', verbose=1, save_best_only=True)\n\n    \n    print(f'Training Model Fold {fold+1}...')\n    history = model.fit(\n        train_dataset,\n        epochs = config['epochs'],\n        callbacks = [estop, lrs, checkpointer],\n        validation_data = val_dataset,\n    )   \n    \n    \n    if config['plot_history']:     \n        plt.figure(figsize=(15,5))\n        n_e = np.arange(len(history.history['dice_coe']))\n        plt.plot(n_e,history.history['dice_coe'],'-o',label='Train dice_coe',color='#ff7f0e')\n        plt.plot(n_e,history.history['val_dice_coe'],'-o',label='Val dice_coe',color='#1f77b4')\n        x = np.argmax( history.history['val_dice_coe'] ); y = np.max( history.history['val_dice_coe'] )\n        xdist = plt.xlim()[1] - plt.xlim()[0]; ydist = plt.ylim()[1] - plt.ylim()[0]\n        plt.scatter(x,y,s=200,color='#1f77b4'); plt.text(x-0.03*xdist,y-0.13*ydist,'max dice_coe\\n%.2f'%y,size=14)\n        plt.ylabel('dice_coe',size=14); plt.xlabel('Epoch',size=14)\n        plt.legend(loc=2)\n        plt2 = plt.gca().twinx()\n        plt2.plot(n_e,history.history['loss'],'-o',label='Train Loss',color='#2ca02c')\n        plt2.plot(n_e,history.history['val_loss'],'-o',label='Val Loss',color='#d62728')\n        x = np.argmin( history.history['val_loss'] ); y = np.min( history.history['val_loss'] )\n        ydist = plt.ylim()[1] - plt.ylim()[0]\n        plt.scatter(x,y,s=200,color='#d62728'); plt.text(x-0.03*xdist,y+0.05*ydist,'min loss',size=14)\n        plt.ylabel('Loss',size=14)\n        plt.legend(loc=3)\n        plt.show()\n        \n    model.load_weights(f\"{config['backbone']}_UNet_{config['img_size']}x{config['img_size']}_best_fold_{fold+1}.hdf5\")\n    model.save(f\"{config['backbone']}_UNet_{config['img_size']}x{config['img_size']}_best_fold_{fold+1}\")\n    \n    if TRAIN_ONE_EPOCH:\n        break","metadata":{"execution":{"iopub.status.busy":"2022-07-15T09:51:13.036752Z","iopub.execute_input":"2022-07-15T09:51:13.037359Z","iopub.status.idle":"2022-07-15T09:54:25.621054Z","shell.execute_reply.started":"2022-07-15T09:51:13.037317Z","shell.execute_reply":"2022-07-15T09:54:25.619668Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in np.random.choice(val_dataset.__len__(), 5, replace=False):\n    \n    img, mask = val_dataset.__getitem__(i)\n    pred = model.predict(img[:1])\n    \n    fig, ax = plt.subplots(1, 3, figsize=(15, 5))\n\n    ax[0].imshow(img[0])\n    ax[0].set_title('Image')\n    ax[1].imshow(mask[0])\n    ax[1].set_title('GT Mask')\n    ax[2].imshow(pred[0]>0.5)\n    ax[2].set_title('Pred Mask, Dice coef: {0:.2f}'.format(dice_coe(mask[:1],pred[:1]).numpy()))\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-07-15T09:55:53.354284Z","iopub.execute_input":"2022-07-15T09:55:53.354656Z","iopub.status.idle":"2022-07-15T09:55:56.281754Z","shell.execute_reply.started":"2022-07-15T09:55:53.354625Z","shell.execute_reply":"2022-07-15T09:55:56.280662Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}