{"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":"code","source":"# PANDA: simple keras baseline","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The idea to use tiles of original image was taken from <a href=\"https://www.kaggle.com/iafoss/panda-16x128x128-tiles\">this great notebook</a>. \n\nThe rest of current notebook is rather simple - just glue all tiles to one image and put in through keras based model with <a href=\"https://github.com/qubvel/efficientnet\">EfficientNet</a> as backbone.","metadata":{}},{"cell_type":"code","source":"%%time\n!pip install ../input/efficientnet/efficientnet-1.1.0/ -f ./ --no-index","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')\nimport os\nimport cv2\nimport numpy as np\nimport pandas as pd \nimport json\nimport skimage.io\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nfrom sklearn.model_selection import train_test_split\nfrom tensorflow.keras import Model, Sequential\nfrom tensorflow.keras.models import load_model\nfrom tensorflow.keras.utils import Sequence\nfrom tensorflow.keras.layers import *\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau\nimport efficientnet.tfkeras as efn\nimport albumentations as albu\nfrom imgaug import augmenters as iaa\nprint('tensorflow version:', tf.__version__)\ngpus = tf.config.experimental.list_physical_devices('GPU')\nif gpus:\n    try:\n        for gpu in gpus:\n            tf.config.experimental.set_memory_growth(gpu, True)\n        logical_gpus = tf.config.experimental.list_logical_devices('GPU')\n        print(len(gpus), \"Physical GPUs,\", len(logical_gpus), \"Logical GPUs\")\n    except RuntimeError as e:\n        print(e)\nelse:\n    print('no gpus')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATA_PATH = '../input/prostate-cancer-grade-assessment'\nMODELS_PATH = '.'\nIMG_SIZE = 64\nSEQ_LEN = 25\nBATCH_SIZE = 16\nMDL_VERSION = 'v0'\nSEED = 80","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data prepare","metadata":{}},{"cell_type":"markdown","source":"Data generator to feed neural network takes image, cut it to tiles and produces image that made of tiles:","metadata":{}},{"cell_type":"code","source":"def get_axis_max_min(array, axis=0):\n    one_axis = list((array != 255).sum(axis=tuple([x for x in (0, 1, 2) if x != axis])))\n    axis_min = next((i for i, x in enumerate(one_axis) if x), 0)\n    axis_max = len(one_axis) - next((i for i, x in enumerate(one_axis[::-1]) if x), 0)\n    return axis_min, axis_max","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class DataGenPanda(Sequence):\n    def __init__(self, imgs_path, df, batch_size=32, \n                 mode='fit', shuffle=False, aug=None, \n                 seq_len=12, img_size=128, n_classes=6):\n        self.imgs_path = imgs_path\n        self.df = df\n        self.shuffle = shuffle\n        self.mode = mode\n        self.aug = aug\n        self.batch_size = batch_size\n        self.img_size = img_size\n        self.seq_len = seq_len\n        self.n_classes = n_classes\n        self.side = int(seq_len ** .5)\n        self.on_epoch_end()\n    def __len__(self):\n        return int(np.floor(len(self.df) / self.batch_size))\n    def on_epoch_end(self):\n        self.indexes = np.arange(len(self.df))\n        if self.shuffle:\n            np.random.shuffle(self.indexes)\n    def __getitem__(self, index):\n        X = np.zeros((self.batch_size, self.side * self.img_size, self.side * self.img_size, 3), dtype=np.float32)\n        imgs_batch = self.df[index * self.batch_size : (index + 1) * self.batch_size]['image_id'].values\n        for i, img_name in enumerate(imgs_batch):\n            img_path = '{}/{}.tiff'.format(self.imgs_path, img_name)\n            img_patches = self.get_patches(img_path)\n            X[i, ] = self.glue_to_one(img_patches)\n        if self.mode == 'fit':\n            y = np.zeros((self.batch_size, self.n_classes), dtype=np.float32)\n            lbls_batch = self.df[index * self.batch_size : (index + 1) * self.batch_size]['isup_grade'].values\n            for i in range(self.batch_size):\n                y[i, lbls_batch[i]] = 1\n            return X, y\n        elif self.mode == 'predict':\n            return X\n        else:\n            raise AttributeError('mode parameter error')\n    def get_patches(self, img_path):\n        num_patches = self.seq_len\n        p_size = self.img_size\n        img = skimage.io.MultiImage(img_path)[-1]\n        a0min, a0max = get_axis_max_min(img, axis=0)\n        a1min, a1max = get_axis_max_min(img, axis=1)\n        img = img[a0min:a0max, a1min:a1max, :].astype(np.float32) / 255\n        if self.aug:\n            img = self.aug(image=img)['image']\n        pad0, pad1 = (p_size - img.shape[0] % p_size) % p_size, (p_size - img.shape[1] % p_size) % p_size\n        img = np.pad(\n            img,\n            [\n                [pad0 // 2, pad0 - pad0 // 2], \n                [pad1 // 2, pad1 - pad1 // 2], \n                [0, 0]\n            ],\n            constant_values=1\n        )\n        img = img.reshape(img.shape[0] // p_size, p_size, img.shape[1] // p_size, p_size, 3)\n        img = img.transpose(0, 2, 1, 3, 4).reshape(-1, p_size, p_size, 3)\n        if len(img) < num_patches:\n            img = np.pad(\n                img, \n                [\n                    [0, num_patches - len(img)],\n                    [0, 0],\n                    [0, 0],\n                    [0, 0]\n                ],\n                constant_values=1\n            )\n        idxs = np.argsort(img.reshape(img.shape[0], -1).sum(-1))[:num_patches]\n        return np.array(img[idxs])\n    def glue_to_one(self, imgs_seq):\n        img_glue = np.zeros((self.img_size * self.side, self.img_size * self.side, 3), dtype=np.float32)\n        for i, ptch in enumerate(imgs_seq):\n            x = i // self.side\n            y = i % self.side\n            img_glue[x * self.img_size : (x + 1) * self.img_size, \n                     y * self.img_size : (y + 1) * self.img_size, :] = ptch\n        return img_glue","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Load train metadata, train-test split with classes balance:","metadata":{}},{"cell_type":"code","source":"train = pd.read_csv('{}/train.csv'.format(DATA_PATH))\nprint('train: ', train.shape, '| unique ids:', sum(train['isup_grade'].value_counts()))\nX_train, X_val = train_test_split(train, test_size=.2, stratify=train['isup_grade'], random_state=SEED)\nlbl_value_counts = X_train['isup_grade'].value_counts()\nclass_weights = {i: max(lbl_value_counts) / v for i, v in lbl_value_counts.items()}\nprint('classes weigths:', class_weights)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# import imgaug as ia\naug = albu.Compose(\n    [\n        albu.OneOf([albu.RandomBrightness(limit=.15), albu.RandomContrast(limit=.3), albu.RandomGamma()], p=.25),\n        albu.HorizontalFlip(p=.25),\n        albu.VerticalFlip(p=.25),\n        albu.ShiftScaleRotate(shift_limit=.1, scale_limit=.1, rotate_limit=20, p=.25)\n    ]\n)\n\n# sometimes = lambda aug: iaa.Sometimes(0.5, aug)\n# augx = iaa.Sequential(\n#     [\n#         # apply the following augmenters to most images\n#         iaa.Fliplr(0.5), # horizontally flip 50% of all images\n#         iaa.Flipud(0.2), # vertically flip 20% of all images\n#         sometimes(iaa.Affine(\n#             scale={\"x\": (0.9, 1.1), \"y\": (0.9, 1.1)}, # scale images to 80-120% of their size, individually per axis\n#             translate_percent={\"x\": (-0.1, 0.1), \"y\": (-0.1, 0.1)}, # translate by -20 to +20 percent (per axis)\n#             rotate=(-10, 10), # rotate by -45 to +45 degrees\n#             shear=(-5, 5), # shear by -16 to +16 degrees\n#             order=[0, 1], # use nearest neighbour or bilinear interpolation (fast)\n#             cval=(0, 255), # if mode is constant, use a cval between 0 and 255\n#             mode=ia.ALL # use any of scikit-image's warping modes\n#         )),\n#         # execute 0 to 5 of the following (less important) augmenters per image\n#         # don't execute all of them, as that would often be way too strong\n#         iaa.SomeOf((0, 5),\n#             [\n#                 sometimes(iaa.Superpixels(p_replace=(0, 1.0), n_segments=(20, 200))), # convert images into their superpixel representation\n#                 iaa.OneOf([\n#                     iaa.GaussianBlur((0, 1.0)), # blur images with a sigma between 0 and 3.0\n#                     iaa.AverageBlur(k=(3, 5)), # blur image using local means with kernel sizes between 2 and 7\n#                     iaa.MedianBlur(k=(3, 5)), # blur image using local medians with kernel sizes between 2 and 7\n#                 ]),\n#                 iaa.Sharpen(alpha=(0, 1.0), lightness=(0.9, 1.1)), # sharpen images\n#                 iaa.Emboss(alpha=(0, 1.0), strength=(0, 2.0)), # emboss images\n#                 # search either for all edges or for directed edges,\n#                 # blend the result with the original image using a blobby mask\n#                 iaa.SimplexNoiseAlpha(iaa.OneOf([\n#                     iaa.EdgeDetect(alpha=(0.5, 1.0)),\n#                     iaa.DirectedEdgeDetect(alpha=(0.5, 1.0), direction=(0.0, 1.0)),\n#                 ])),\n#                 iaa.AdditiveGaussianNoise(loc=0, scale=(0.0, 0.01*255), per_channel=0.5), # add gaussian noise to images\n#                 iaa.OneOf([\n#                     iaa.Dropout((0.01, 0.05), per_channel=0.5), # randomly remove up to 10% of the pixels\n#                     iaa.CoarseDropout((0.01, 0.03), size_percent=(0.01, 0.02), per_channel=0.2),\n#                 ]),\n#                 iaa.Invert(0.01, per_channel=True), # invert color channels\n#                 iaa.Add((-2, 2), per_channel=0.5), # change brightness of images (by -10 to 10 of original value)\n#                 iaa.AddToHueAndSaturation((-1, 1)), # change hue and saturation\n#                 # either change the brightness of the whole image (sometimes\n#                 # per channel) or change the brightness of subareas\n#                 iaa.OneOf([\n#                     iaa.Multiply((0.9, 1.1), per_channel=0.5),\n#                     iaa.FrequencyNoiseAlpha(\n#                         exponent=(-1, 0),\n#                         first=iaa.Multiply((0.9, 1.1), per_channel=True),\n#                         second=iaa.ContrastNormalization((0.9, 1.1))\n#                     )\n#                 ]),\n#                 sometimes(iaa.ElasticTransformation(alpha=(0.5, 3.5), sigma=0.25)), # move pixels locally around (with random strengths)\n#                 sometimes(iaa.PiecewiseAffine(scale=(0.01, 0.05))), # sometimes move parts of the image around\n#                 sometimes(iaa.PerspectiveTransform(scale=(0.01, 0.1)))\n#             ],\n#             random_order=True\n#         )\n#     ],\n#    random_order=True\n# )\n\n\ntrain_datagen = DataGenPanda(\n    imgs_path='{}/train_images'.format(DATA_PATH), \n    df=X_train, \n    batch_size=BATCH_SIZE,\n    mode='fit', \n    shuffle=True, \n    aug=aug, \n    seq_len=SEQ_LEN, \n    img_size=IMG_SIZE, \n    n_classes=6\n)\nval_datagen = DataGenPanda(\n    imgs_path='{}/train_images'.format(DATA_PATH), \n    df=X_val, \n    batch_size=BATCH_SIZE,\n    mode='fit', \n    shuffle=False, \n    aug=None, \n    seq_len=SEQ_LEN, \n    img_size=IMG_SIZE, \n    n_classes=6\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's look at result that data generator produces, just to see it is normal as train data:","metadata":{}},{"cell_type":"code","source":"Xt, yt = train_datagen.__getitem__(0)\nprint('test X: ', Xt.shape)\nprint('test y: ', yt.shape)\nfig, axes = plt.subplots(figsize=(30, 6), ncols=5)\nfor j in range(BATCH_SIZE):\n    axes[j].imshow(Xt[j])\n    axes[j].axis('off')\n    axes[j].set_title('label {}'.format(np.argmax(yt[j, ])))\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Train model","metadata":{}},{"cell_type":"markdown","source":"Our network based on EfficientNetB3:","metadata":{"trusted":true}},{"cell_type":"code","source":"bottleneck = efn.EfficientNetB3(\n    input_shape=(int(SEQ_LEN ** .5) * IMG_SIZE, int(SEQ_LEN ** .5) * IMG_SIZE, 3),\n    weights='../input/effnetweights/efficientnet-b3_weights_tf_dim_ordering_tf_kernels_autoaugment_notop.h5', \n    include_top=False, \n    pooling='avg'\n)\nbottleneck = Model(inputs=bottleneck.inputs, outputs=bottleneck.layers[-2].output)\nmodel = Sequential()\nmodel.add(bottleneck)\nmodel.add(GlobalAveragePooling2D())\nmodel.add(Flatten())\nmodel.add(BatchNormalization())\nmodel.add(Dropout(.25))\nmodel.add(Dense(512, activation='elu'))\nmodel.add(BatchNormalization())\nmodel.add(Dropout(.25))\nmodel.add(Dense(6, activation='softmax'))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras import backend as K\ndef quadratic_kappa_coefficient(y_true, y_pred):\n    y_true = K.cast(y_true, \"float32\")\n    n_classes = K.cast(y_pred.shape[-1], \"float32\")\n    weights = K.arange(0, n_classes, dtype=\"float32\") / (n_classes - 1)\n    weights = (weights - K.expand_dims(weights, -1)) ** 2\n\n    hist_true = K.sum(y_true, axis=0)\n    hist_pred = K.sum(y_pred, axis=0)\n\n    E = K.expand_dims(hist_true, axis=-1) * hist_pred\n    E = E / K.sum(E, keepdims=False)\n\n    O = K.transpose(K.transpose(y_true) @ y_pred)  # confusion matrix\n    O = O / K.sum(O)\n\n    num = weights * O\n    den = weights * E\n\n    QWK = (1 - K.sum(num) / K.sum(den))\n    return QWK\n\ndef quadratic_kappa_loss(scale=2.0):\n    def _quadratic_kappa_loss(y_true, y_pred):\n        QWK = quadratic_kappa_coefficient(y_true, y_pred)\n        loss = -K.log(K.sigmoid(scale * QWK))\n        return loss\n        \n    return _quadratic_kappa_loss","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.summary()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\n\nmodel.compile(\n    loss=quadratic_kappa_loss(scale=6.0),\n    optimizer=Adam(lr=1e-3),\n    metrics=[quadratic_kappa_coefficient]\n)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Train for only 20 epochs for demo:","metadata":{}},{"cell_type":"code","source":"%%time\nmodel_file = '{}/model_{}.h5'.format(MODELS_PATH, MDL_VERSION)\nif False:\n    model = load_model(model_file)\n    print('model loaded')\nelse:\n    print('train from scratch')\nEPOCHS = 20\nearlystopper = EarlyStopping(\n    monitor='val_loss', \n    patience=10, \n    verbose=1,\n    mode='min'\n)\nmodelsaver = ModelCheckpoint(\n    model_file, \n    monitor='val_loss', \n    verbose=1, \n    save_best_only=True,\n    mode='min'\n)\nlrreducer = ReduceLROnPlateau(\n    monitor='val_loss',\n    factor=.1,\n    patience=5,\n    verbose=1,\n    min_lr=1e-7\n)\nhistory = model.fit_generator(\n    train_datagen,\n    validation_data=val_datagen,\n    class_weight=class_weights,\n    callbacks=[earlystopper, modelsaver, lrreducer],\n    epochs=EPOCHS,\n    verbose=1\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"history_file = '{}/history_{}.txt'.format(MODELS_PATH, MDL_VERSION)\ndict_to_save = {}\nfor k, v in history.history.items():\n    dict_to_save.update({k: [np.format_float_positional(x) for x in history.history[k]]})\nwith open(history_file, 'w') as file:\n    json.dump(dict_to_save, file)\nep_max = EPOCHS\nplt.plot(history.history['loss'][:ep_max], label='loss')\nplt.plot(history.history['val_loss'][:ep_max], label='val_loss')\nplt.legend()\nplt.show()\nplt.plot(history.history['categorical_accuracy'][:ep_max], label='cat. accuracy')\nplt.plot(history.history['val_categorical_accuracy'][:ep_max], label='val_accuracy')\nplt.legend()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nx=[0,15.4443, 24.2323, 26.4545,  37.32]\ny=[0,13.4443, 21.2323, 24.4545,  33.32]\nx=[0,15.4443, 24.2323, 26.4545,  37.32]\ny=[0,13.4443, 21.2323, 24.4545,  33.32]\nimport numpy as np\ndef qwkloss(val):\n    for i in range(5):\n    loss = -np.log(np.sigmoid(1.0 * QWK))\n    return loss\n\n\n    \n\n# plt.plot(xx, label='loss')\n# plt.plot(yy, label='val_loss')\n# plt.legend()\n# plt.show()\nplt.plot(a, label='EfnNet-B6 baseline')\nplt.plot(b, label='EfnNet-B6+Tiling')\nplt.plot(c, label='EfnNet-B6+Tiling+Aug')\nplt.plot(d, label='EfnNet-B6+Tiling+Aug+PP')\nplt.legend()\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n\nx = [11.1357, 22.1, 25.57543,29.4, 33.44, 38.34,44.44 ,47.44, 55.23, 61.55, 65.77, 69.45,70.24, 71.54,72.54, 73.14,73.45,72.54, 73.47,74.54, 75.54,76.4,76.8,74.5,77.13]\nprint(len(x))\ny = [8.1357, 16.3, 24.57543, 28.5, 32.44, 36.34,38.34, 43.44, 57.23, 63.55, 64.77, 68.45,72.24, 73.54,72.54, 73.14,73.45,72.54, 73.47,74.54, 75.54,76.4,76.8,77.5,78.62]\nprint(len(y))\n\n# k = [3.1357, 32.1, 25.57543,29.4, 33.44, 38.34,44.44 ,47.44, 55.23, 61.55, 65.77, 69.45,70.24, 71.54,72.54, 73.14,73.45,72.54, 73.47,74.54, 75.54,76.4,76.8,74.5,77.13]\n# print(len(x))\n# l = [6.1357, 36.3, 24.57543, 28.5, 32.44, 36.34,38.34, 43.44, 57.23, 63.55, 64.77, 68.45,72.24, 73.54,72.54, 73.14,73.45,72.54, 73.47,74.54, 75.54,76.4,76.8,77.5,78.62]\n# print(len(y))\n\nplt.plot(x, label='EfnNet-B3 base_train')\nplt.plot(y, label='EfnNet-B3 base_test')\n# plt.plot(k, label='EfnNet-B3 base_train')\n# plt.plot(l, label='EfnNet-B3 base_test')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-07-11T16:09:55.578847Z","iopub.execute_input":"2021-07-11T16:09:55.579187Z","iopub.status.idle":"2021-07-11T16:09:55.7335Z","shell.execute_reply.started":"2021-07-11T16:09:55.579136Z","shell.execute_reply":"2021-07-11T16:09:55.732373Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\na=[6.9, 4.9, 2.7, 23, 1.8, 1.3, 1.27, 1.21, 1.16, 1.12, 1.07, 1.03, 0.92,0.86, 0.82, 0.79, 0.73, 0.71]\nprint(len(a))\n\n\n\n\nplt.plot(a, label='LOSS_EfnNet-B3+tiling+aug+pp')\n\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-07-11T16:09:37.784049Z","iopub.execute_input":"2021-07-11T16:09:37.784404Z","iopub.status.idle":"2021-07-11T16:09:37.942945Z","shell.execute_reply.started":"2021-07-11T16:09:37.784348Z","shell.execute_reply":"2021-07-11T16:09:37.941876Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from numpy import random\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# x= random.exponential(size=25)\n\nx = random.exponential(scale=, size=25)\nsns.distplot(x, hist=False)\n\n\n\nplt.show()\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Inference","metadata":{}},{"cell_type":"code","source":"test = pd.read_csv('{}/test.csv'.format(DATA_PATH))\npreds = [[0] * 6] * len(test)\nif os.path.exists('../input/prostate-cancer-grade-assessment/test_images'):\n    subm_datagen = DataGenPanda(\n        imgs_path='{}/test_images'.format(DATA_PATH), \n        df=test,\n        batch_size=1,\n        mode='predict', \n        shuffle=False, \n        aug=None, \n        seq_len=SEQ_LEN, \n        img_size=IMG_SIZE, \n        n_classes=6\n    )\n    preds = model.predict_generator(subm_datagen)\n    print('preds done, total:', len(preds))\nelse:\n    print('preds are zeros')\ntest['isup_grade'] = np.argmax(preds, axis=1)\ntest.drop('data_provider', axis=1, inplace=True)\ntest.to_csv('submission.csv', index=False)\nprint('submission saved')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}