{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport seaborn as sns \nimport matplotlib.pyplot as plt \nimport random\nimport cv2\n\nfrom sklearn.model_selection import train_test_split\nfrom keras.callbacks import ModelCheckpoint\nfrom keras.applications import ResNet50, InceptionResNetV2\nfrom keras.layers import Dense, Input, Lambda\nfrom tensorflow import keras\nfrom keras.models import Model \nimport tensorflow as tf\nfrom math import ceil\nfrom keras import Model\nfrom keras.optimizers import Adam\n\nimport random\nimport os\nimport gc\nfrom keras.callbacks import ReduceLROnPlateau\n!pip install efficientnet\nimport albumentations\nfrom albumentations import RandomCrop, Compose, HorizontalFlip, VerticalFlip, OneOf\nfrom albumentations.core.transforms_interface import DualTransform\nfrom albumentations.augmentations import functional as F\nimport efficientnet.keras as efn","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    random.seed(seed)\n    np.random.seed(seed)\n    tf.random.set_seed(seed)\n\nseed_everything(42)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"seed = 69\nimg_dir = \"../input/plant-pathology-2020-fgvc7//images/\"\npath = \"../input/plant-pathology-2020-fgvc7/\"\ninput_shape = (512,512,3)\n#Version - 1.0 4D_augmentation  LB: 0.941\n#Version - 2.0 4D_augmentation + TTA LB: 0.963\n#Version -3.0 4D_augmentation + TTA + GridMask 0.967\n#Version -4.0 4D_augmentation + TTA + reduce_lr + GridMask + Effnet 0.966\n#Version -5.0 4D_augmentation + TTA + reduce_lr + GridMask + Effnet 0.966","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class GridMask(DualTransform):\n    \n    \"\"\"GridMask augmentation for image classification and object detection.\n    \n    Author: Qishen Ha\n    Email: haqishen@gmail.com\n    2020/01/29\n\n    Args:\n        num_grid (int): number of grid in a row or column.\n        fill_value (int, float, lisf of int, list of float): value for dropped pixels.\n        rotate ((int, int) or int): range from which a random angle is picked. If rotate is a single int\n            an angle is picked from (-rotate, rotate). Default: (-90, 90)\n        mode (int):\n            0 - cropout a quarter of the square of each grid (left top)\n            1 - reserve a quarter of the square of each grid (left top)\n            2 - cropout 2 quarter of the square of each grid (left top & right bottom)\n\n    Targets:\n        image, mask\n\n    Image types:\n        uint8, float32\n\n    Reference:\n    |  https://arxiv.org/abs/2001.04086\n    |  https://github.com/akuxcw/GridMask\n    \"\"\"\n\n    def __init__(self, num_grid=3, fill_value=0, rotate=0, mode=0, always_apply=False, p=0.5):\n        super(GridMask, self).__init__(always_apply, p)\n        if isinstance(num_grid, int):\n            num_grid = (num_grid, num_grid)\n        if isinstance(rotate, int):\n            rotate = (-rotate, rotate)\n        self.num_grid = num_grid\n        self.fill_value = fill_value\n        self.rotate = rotate\n        self.mode = mode\n        self.masks = None\n        self.rand_h_max = []\n        self.rand_w_max = []\n\n    def init_masks(self, height, width):\n        if self.masks is None:\n            self.masks = []\n            n_masks = self.num_grid[1] - self.num_grid[0] + 1\n            for n, n_g in enumerate(range(self.num_grid[0], self.num_grid[1] + 1, 1)):\n                grid_h = height / n_g\n                grid_w = width / n_g\n                this_mask = np.ones((int((n_g + 1) * grid_h), int((n_g + 1) * grid_w))).astype(np.uint8)\n                for i in range(n_g + 1):\n                    for j in range(n_g + 1):\n                        this_mask[\n                             int(i * grid_h) : int(i * grid_h + grid_h / 2),\n                             int(j * grid_w) : int(j * grid_w + grid_w / 2)\n                        ] = self.fill_value\n                        if self.mode == 2:\n                            this_mask[\n                                 int(i * grid_h + grid_h / 2) : int(i * grid_h + grid_h),\n                                 int(j * grid_w + grid_w / 2) : int(j * grid_w + grid_w)\n                            ] = self.fill_value\n                \n                if self.mode == 1:\n                    this_mask = 1 - this_mask\n\n                self.masks.append(this_mask)\n                self.rand_h_max.append(grid_h)\n                self.rand_w_max.append(grid_w)\n\n    def apply(self, image, mask, rand_h, rand_w, angle, **params):\n        h, w = image.shape[:2]\n        mask = F.rotate(mask, angle) if self.rotate[1] > 0 else mask\n        mask = mask[:,:,np.newaxis] if image.ndim == 3 else mask\n        image *= mask[rand_h:rand_h+h, rand_w:rand_w+w].astype(image.dtype)\n        return image\n\n    def get_params_dependent_on_targets(self, params):\n        img = params['image']\n        height, width = img.shape[:2]\n        self.init_masks(height, width)\n\n        mid = np.random.randint(len(self.masks))\n        mask = self.masks[mid]\n        rand_h = np.random.randint(self.rand_h_max[mid])\n        rand_w = np.random.randint(self.rand_w_max[mid])\n        angle = np.random.randint(self.rotate[0], self.rotate[1]) if self.rotate[1] > 0 else 0\n\n        return {'mask': mask, 'rand_h': rand_h, 'rand_w': rand_w, 'angle': angle}\n\n    @property\n    def targets_as_params(self):\n        return ['image']\n\n    def get_transform_init_args_names(self):\n        return ('num_grid', 'fill_value', 'rotate', 'mode')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def plot_validate(model, loss_acc):\n    '''\n    Plot model accuracy or loss for both train and test validation per epoch\n    model : fitted model\n    loss_acc : input 'loss' or 'acc' to plot respective graph\n    '''\n    history = model.history.history\n\n    if loss_acc == 'loss':\n        axis_title = 'loss'\n        title = 'Loss'\n        epoch = len(history['loss'])\n    elif loss_acc == 'acc':\n        axis_title = 'categorical_accuracy'\n        title = 'Accuracy'\n        epoch = len(history['loss'])\n\n    plt.figure(figsize=(15,4))\n    plt.plot(history[axis_title])\n    plt.plot(history['val_' + axis_title])\n    plt.title('Model ' + title)\n    plt.ylabel(title)\n    plt.xlabel('Epoch')\n\n    plt.grid(b=True, which='major')\n    plt.minorticks_on()\n    plt.grid(b=True, which='minor', alpha=0.2)\n\n    plt.legend(['Train', 'Test'])\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def augment(aug, image):\n    '''\n    image augmentation\n    aug : augmentation from albumentations\n\n    '''\n    aug_img = aug(image=image)['image']\n    return aug_img\n\ndef VH_augment(image):\n    \n    '''\n    Vertical and horizontal flip image\n    '''\n    image = HorizontalFlip(p=1)(image=image)['image']\n    image = VerticalFlip(p=1)(image=image)['image']\n    return image\n\ndef strong_aug(p=1.0):\n    \n    '''\n    4D - augmentations\n    '''\n    return  OneOf([\n            HorizontalFlip(p=0.33),\n            VerticalFlip(p=0.33),\n           Compose([HorizontalFlip(p=1),\n                    VerticalFlip(p=1)], p=0.33)\n        ], p=1)\n\ndef four_D_augment(name):\n    name = str(name)\n    image = cv2.imread(img_dir + name)\n    aug_img_1 = augment(HorizontalFlip(p=1), image)\n    aug_img_2 = augment(VerticalFlip(p=1), image)\n    aug_img_3 = VH_augment(image)\n    fig, ax = plt.subplots(nrows = 1, ncols = 4, figsize = (25,25))\n    ax[0].imshow(image[...,[2,1,0]])\n    ax[0].set_title('Original image', fontsize=14)\n    ax[1].imshow(aug_img_1[...,[2,1,0]])\n    ax[1].set_title('Horizontal flip image', fontsize=14)\n    ax[2].imshow(aug_img_2[...,[2,1,0]])\n    ax[2].set_title('Vertical flip image', fontsize=14)\n    ax[3].imshow(aug_img_3[...,[2,1,0]])\n    ax[3].set_title('Vertical and horizontal flip image', fontsize=14)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#4D-augment\nfour_D_augment('Train_1400.jpg')\nfour_D_augment('Train_1420.jpg')\nfour_D_augment('Train_1430.jpg')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# GridMask\ntransforms_train = albumentations.Compose([\n    GridMask(num_grid=4, rotate=15, p=0.6),])\n\nfig, ax = plt.subplots(nrows=1 , ncols=2, figsize=(15,15))\nimage = cv2.imread(img_dir+'Train_100.jpg')\nax[0].imshow(image[...,[2,1,0]])\naug = augment(transforms_train, image)\nax[1].imshow(aug[...,[2,1,0]])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv(path + 'train.csv')\ntest = pd.read_csv(path + 'test.csv')\nsample_submission = pd.read_csv(path + 'sample_submission.csv')\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gm_exp = tf.Variable(3.0, dtype = tf.float32)\ndef generalized_mean_pool_2d(X):\n    pool = (tf.reduce_mean(tf.abs(X**(gm_exp)), \n                        axis = [1, 2], \n                        keepdims = False) + 1.e-7)**(1./gm_exp)\n    return pool\n\n\ndef create_model(input_shape):\n    input = Input(shape = input_shape)\n\n    #Create and complite model and show summary\n    \n    x_model = ResNet50(weights = 'imagenet', include_top = False, input_tensor = input, pooling = None,\n                        classes = None)\n    for layer in x_model.layers:\n        layer.trainable = True\n    \n    # Gem \n    lambda_layer = Lambda(generalized_mean_pool_2d)\n    lambda_layer.trainable_weights.extend([gm_exp])\n    x = lambda_layer(x_model.output)\n    \n    #output \n    healthy = Dense(4, activation = 'softmax', name = 'plan_diseases')(x)\n   \n    \n    #model \n    model = Model(inputs = x_model.input, outputs = healthy )\n    \n    return model\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def _read(path):\n    img = cv2.imread(path)    \n    return img\n\nclass TrainDataGenerator(tf.keras.utils.Sequence):\n    \n    def __init__(self , X_set, Y_set, img_dir, ids, batch_size = 2, img_size = (512,512,3), augmentation = False, GridMask = False):\n        self.X = X_set\n        self.Y = Y_set\n        self.batch_size = batch_size\n        self.ids = ids\n        self.img_size  = img_size  \n        self.img_dir = img_dir\n        self.augmentation = augmentation\n        self.GridMask = GridMask\n        self.on_epoch_end()\n        \n        #Split Data\n        self.x_indexed = X_set[self.ids]\n        \n    def __len__(self):\n        return int(ceil(len(self.ids)/self.batch_size))\n\n    def __getitem__(self, index):\n        indices = self.ids[index*self.batch_size:(index+1)*self.batch_size]\n        X, Y = self.__generator__(indices)\n        return X, Y\n    \n    def on_epoch_end(self):\n        self.indices = np.arange(len(self.ids))\n        \n        \n    def __generator__(self, indices):\n        X = np.empty((self.batch_size, *self.img_size))\n        Y = np.empty((self.batch_size, 4))\n        for i, index in enumerate(indices):\n            ID = self.X[index]\n            image = _read(self.img_dir+ID+\".jpg\")\n            #image = image[200:1100, 200:1700]\n            image = cv2.resize(image, (512,512))\n            if self.augmentation == True:\n                aug = strong_aug(p=1.0)\n                image = augment(aug, image)\n            elif self.GridMask == True:\n                image = augment(transforms_train, image)\n            X[i,] = image/255.\n            Y[i,] = self.Y.loc[index].values\n        return X, Y    \n    \nclass TestDataGenerator(tf.keras.utils.Sequence):\n    \n    def __init__(self, Test_set, img_dir, ids, batch_size = 3, img_size = (512,512,3), augmentation = None):\n        self.X = Test_set\n        self.img_dir = img_dir\n        self.ids = ids\n        self.batch_size = batch_size\n        self.img_size = img_size\n        self.augmentation = augmentation\n                 \n    def __len__(self):\n        return int(ceil(len(self.ids)/self.batch_size))\n    \n    def __getitem__(self, index):\n        indices = self.ids[index*self.batch_size:(index+1)*self.batch_size]\n        X = self.__generator__(indices)\n        return X\n    \n    def __generator__(self, indices):\n        X = np.empty((self.batch_size, *self.img_size))\n        for i, index in enumerate(indices):\n            ID = self.X[index]\n            image = _read(self.img_dir+ID+\".jpg\")\n           #image = image[200:1100, 200:1700]\n            image = cv2.resize(image, (512,512))\n                 # TTA\n            if self.augmentation is not None:\n                if self.augmentation == \"HorizontalFlip\":\n                    image = augment(HorizontalFlip(p=1), image)\n                elif self.augmentation == \"VerticalFlip\":\n                    image = augment(VerticalFlip(p=1), image)\n                elif self.augmentation == 'VH':\n                    image = VH_augment(image)\n                 \n            X[i,] = image/255.\n        return X\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#Prepary X and Y\ntgt_cols = ['healthy', 'multiple_diseases', 'rust' , 'scab']\ntrain_df = train[tgt_cols]\nY = pd.get_dummies(train_df)\nX = train['image_id']\nX_train, X_test, Y_train, Y_test = train_test_split(X,Y, test_size = 0.15, random_state = seed)\nids_train = np.array(X_train.index)\nids_test = X_test.index\ndel train\ndel X\ndel Y","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = create_model(input_shape)\nmodel.compile(optimizer = Adam(lr = 0.00016),\n              loss = 'categorical_crossentropy',\n              metrics = ['categorical_accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"best_w = ModelCheckpoint('plant_best.h5',\n                                monitor='val_loss',\n                                verbose=0,\n                                save_best_only=True,\n                                save_weights_only=True,\n                                mode='auto',\n                                period=1)\n\nlast_w = ModelCheckpoint('plant_last.h5',\n                               monitor='val_loss',\n                                verbose=0,\n                                save_best_only=False,\n                              mode='auto',\n                                period=1)\n\nreduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.2,\n                              patience=2, min_lr=0.000001, verbose=1,cooldown=1)\ncallbacks = [best_w, last_w, reduce_lr]\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_size = 5\nmodel.fit_generator(TrainDataGenerator(X_train,Y_train , img_dir, ids_train, \n                                              batch_size, augmentation = True, GridMask = True),\n               epochs=15, \n               verbose=1,\n               callbacks=callbacks,\n               validation_data=TrainDataGenerator(X_test, Y_test , img_dir, ids_test,\n                                                  batch_size, augmentation = False, GridMask = False),\n               #max_queue_size=100,\n               workers=1,\n               use_multiprocessing=False,\n               shuffle=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gc.collect()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_validate(model, 'loss')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_validate(model, 'acc')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.load_weights('plant_best.h5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\nids = test.index\nTest_set = test['image_id']\ndata_generator_test = TestDataGenerator(Test_set, img_dir, ids, batch_size, input_shape, augmentation = None ) \ndata_generator_test_Horizontal = TestDataGenerator(Test_set, img_dir, ids, batch_size, input_shape, augmentation = \"HorizontalFlip\" ) \ndata_generator_test_Vertical =  TestDataGenerator(Test_set, img_dir, ids, batch_size, input_shape, augmentation = \"VerticalFlip\" ) \ndata_generator_test_VH = TestDataGenerator(Test_set, img_dir, ids, batch_size, input_shape, augmentation = \"VH\" ) \npreds_1 = model.predict_generator(data_generator_test, verbose = 1)\npreds_2 = model.predict_generator(data_generator_test_Horizontal, verbose = 1)\npreds_3 = model.predict_generator(data_generator_test_Vertical, verbose = 1)\npreds_4 = model.predict_generator(data_generator_test_VH, verbose = 1)\n\nfor index, pred in enumerate(zip(preds_1,preds_2, preds_3, preds_4)):\n    sample_submission[\"healthy\"][index] = (pred[0][0] + pred[1][0] + pred[2][0]  + pred[3][0])/4\n    sample_submission[\"multiple_diseases\"][index] = (pred[0][1] + pred[1][1] + pred[2][1]  + pred[3][1])/4\n    sample_submission[\"rust\"][index] = (pred[0][2] + pred[1][2] + pred[2][2]  + pred[3][2])/4\n    sample_submission[\"scab\"][index] = (pred[0][3] + pred[1][3] + pred[2][3]  + pred[3][3])/4","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_submission.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_submission.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}