{"cells":[{"metadata":{"papermill":{"duration":0.009951,"end_time":"2020-08-13T06:22:28.48251","exception":false,"start_time":"2020-08-13T06:22:28.472559","status":"completed"},"tags":[]},"cell_type":"markdown","source":"This notebook shows how to train an efficientDet by tensorflow on TPU. \n\n1. The speed of training on TPU is extremely fast compares to GPU.\n    * Using EfficientDet-D4 to train 1 epoch on full datasets only needs 4 minutes. (Batch Size: 32)\n    * Using EfficientDet-D7 to train 1 epoch on full datasets only needs 10 minutes (Batch Size: 16)\n2. Pure Code\n3. Read Datasets from tfrecords\n4. Several Dataset Augmentation: Crop / Rotate /  Flip / Mixup / Mosaic  / Hue / Gaussian Noise\n5. mAP metric\n6. NMS / SoftNMS / Weighted Boxes Fusion\n7. Test TTA (4x Flip + Rot90)\n8. Search for best iou_theshold and score_threshold from prediction\n9. To train, please select TPU as accelerator and turn on internet.\n10. To sumbit, please select GPU as accelerator and turn off internet.\n11. Todo: Pseudo Labeling","execution_count":null},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","execution":{"iopub.execute_input":"2020-08-13T06:22:28.506169Z","iopub.status.busy":"2020-08-13T06:22:28.505418Z","iopub.status.idle":"2020-08-13T06:22:35.117323Z","shell.execute_reply":"2020-08-13T06:22:35.116592Z"},"papermill":{"duration":6.626383,"end_time":"2020-08-13T06:22:35.117513","exception":false,"start_time":"2020-08-13T06:22:28.49113","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import *\nimport numpy as np\nimport collections\nimport math\nimport string\nimport os\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport cv2\nfrom kaggle_datasets import KaggleDatasets\nfrom tqdm import tqdm\nimport datetime","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:35.141549Z","iopub.status.busy":"2020-08-13T06:22:35.140607Z","iopub.status.idle":"2020-08-13T06:22:35.143168Z","shell.execute_reply":"2020-08-13T06:22:35.143676Z"},"papermill":{"duration":0.016905,"end_time":"2020-08-13T06:22:35.143803","exception":false,"start_time":"2020-08-13T06:22:35.126898","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"def print_time(title):\n    print(\"{0}: {1}\".format(title, datetime.datetime.now()))","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:35.168807Z","iopub.status.busy":"2020-08-13T06:22:35.168111Z","iopub.status.idle":"2020-08-13T06:22:35.18183Z","shell.execute_reply":"2020-08-13T06:22:35.181302Z"},"papermill":{"duration":0.029365,"end_time":"2020-08-13T06:22:35.181932","exception":false,"start_time":"2020-08-13T06:22:35.152567","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# Detect hardware, return appropriate distribution strategy\ntry:\n    # TPU detection. No parameters necessary if TPU_NAME environment variable is\n    # set: this is always the case on Kaggle.\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print('Running on TPU ', tpu.master())\nexcept ValueError:\n    tpu = None\n    print('Running on GPU or CPU')\n\nif tpu:\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nelse:\n    # Default distribution strategy in Tensorflow. Works on CPU and single GPU.\n    strategy = tf.distribute.get_strategy()\n\nprint(\"REPLICAS: \", strategy.num_replicas_in_sync)","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:35.208134Z","iopub.status.busy":"2020-08-13T06:22:35.207309Z","iopub.status.idle":"2020-08-13T06:22:35.211673Z","shell.execute_reply":"2020-08-13T06:22:35.212441Z"},"papermill":{"duration":0.020532,"end_time":"2020-08-13T06:22:35.212601","exception":false,"start_time":"2020-08-13T06:22:35.192069","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"BATCH_SIZE = (4 * strategy.num_replicas_in_sync) if tpu else 8\nNUM_CLASSES = 1\nPHI = 4\nIMAGE_SIZE = [512, 640, 768, 896, 1024, 1280, 1408, 1536][PHI]\nIMAGE_SHAPE_ORG = (1024, 1024, 3) # The shape of images in Tfrecords (H, W, 3)\nBOX_SHAPE_ORG = (120, 5) # The shape of boxes in Tfrecords (116: Num of Boxes, 5: x1, y1, x2, y2, classes_idx)\n\nprint(\"Batch Size: {0}\".format(BATCH_SIZE))","execution_count":null,"outputs":[]},{"metadata":{"papermill":{"duration":0.009195,"end_time":"2020-08-13T06:22:35.23132","exception":false,"start_time":"2020-08-13T06:22:35.222125","status":"completed"},"tags":[]},"cell_type":"markdown","source":"The code of how generates tfrecords is shown here: https://www.kaggle.com/davidtong/wheat-gen-tf","execution_count":null},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:35.260859Z","iopub.status.busy":"2020-08-13T06:22:35.260183Z","iopub.status.idle":"2020-08-13T06:22:36.958212Z","shell.execute_reply":"2020-08-13T06:22:36.958743Z"},"papermill":{"duration":1.718407,"end_time":"2020-08-13T06:22:36.958889","exception":false,"start_time":"2020-08-13T06:22:35.240482","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# Search all Tfrecords\n\nAUTO = tf.data.experimental.AUTOTUNE\n\nTRAIN_GCS_DS_PATH, VALID_GCS_DS_PATH, TRAIN_STEPS_PER_EPOCH, VALID_STEPS_PER_EPOCH = [], [], 0, 0\n\ntry:\n    GCS_DS_PATH = [KaggleDatasets().get_gcs_path(path) for path in [\"wheat-tfrecords\" + str(i) for i in range(5)]]\n    print(GCS_DS_PATH)\n    VALID_GCS_START, VALID_GCS_END = 4, 5 # 80% training datasets, 20% validation datasets\n    TRAIN_GCS_DS_PATH = GCS_DS_PATH[0 : VALID_GCS_START] + GCS_DS_PATH[VALID_GCS_END : ]\n    VALID_GCS_DS_PATH = GCS_DS_PATH[VALID_GCS_START : VALID_GCS_END]\n    TRAIN_STEPS_PER_EPOCH = math.ceil(3373 * len(TRAIN_GCS_DS_PATH) / (len(TRAIN_GCS_DS_PATH) + len(VALID_GCS_DS_PATH)) / BATCH_SIZE)\n    VALID_STEPS_PER_EPOCH = math.floor(3373 * len(VALID_GCS_DS_PATH) / (len(TRAIN_GCS_DS_PATH) + len(VALID_GCS_DS_PATH)) / BATCH_SIZE)\n\n    np.random.shuffle(TRAIN_GCS_DS_PATH)\n    np.random.shuffle(VALID_GCS_DS_PATH)\n    # print(len(TRAIN_GCS_DS_PATH), len(VALID_GCS_DS_PATH), TRAIN_STEPS_PER_EPOCH, VALID_STEPS_PER_EPOCH)\nexcept:\n    print(\"Failed to load tfrecords\")","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:37.008034Z","iopub.status.busy":"2020-08-13T06:22:36.99086Z","iopub.status.idle":"2020-08-13T06:22:40.723559Z","shell.execute_reply":"2020-08-13T06:22:40.724232Z"},"papermill":{"duration":3.756167,"end_time":"2020-08-13T06:22:40.724429","exception":false,"start_time":"2020-08-13T06:22:36.968262","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# Load all tfrecords\n\nclass tfrecords_parser:\n    def __init__(self):\n        self.feature_description_dict = {\n            'ndarray' : self._bytes_feature_description, \n            'bytes' : self._bytes_feature_description, \n            'float' : self._float_feature_description,\n            'double' : self._float_feature_description, \n            'bool' : self._int64_feature_description,\n            'enum' : self._int64_feature_description, \n            'int' : self._int64_feature_description,\n            'uint' : self._int64_feature_description\n        }\n    \n    def _bytes_feature_description(self):\n        return tf.io.FixedLenFeature([], tf.string)\n\n    def _float_feature_description(self):\n        return tf.io.FixedLenFeature([], tf.float)\n\n    def _int64_feature_description(self):\n        return tf.io.FixedLenFeature([], tf.int64)\n    \n    def _decode_example(self, e, example):\n        res = []\n        for vname in example:\n            vtype = type(example[vname]).__name__\n            if vtype == \"ndarray\":\n                res.append(tf.reshape(tf.io.decode_raw(e[vname], {\n                    'float32' : tf.float32,\n                    'float64' : tf.float64,\n                    'int32' : tf.int32,\n                    'uint16' : tf.uint16,\n                    'uint8' : tf.uint8,\n                    'int16' : tf.int16,\n                    'int8' : tf.int8,\n                    'int64' : tf.int64\n                }[str(example[vname].dtype)]), example[vname].shape))\n            else:\n                res.append(tf.cast(e[vname], {\n                    'float' : tf.float32,\n                    'int' : tf.int32\n                }[vtype]))\n        return res\n    \n    def run(self, filename, example):\n        reader = tf.data.TFRecordDataset(filename, num_parallel_reads=AUTO)\n        feature_description = {}\n        for vname in example:\n            vtype = type(example[vname]).__name__\n            feature_description[vname] = self.feature_description_dict[vtype]()\n        reader = reader.map(lambda e: tf.io.parse_single_example(e, feature_description))\n        reader = reader.map(lambda e: self._decode_example(e, example))\n        return reader\n\ndef tfrecords_loader(files_path, ignore_order=True, cache = True):\n    @tf.function\n    def _normalize(x, y):\n        return tf.cast(x, tf.float32) / 255.0, y\n    \n    def _define_template():\n        # Define the template of x and y\n        x_template = np.zeros(IMAGE_SHAPE_ORG, np.uint8)\n        y_template = np.zeros(BOX_SHAPE_ORG, np.float32)\n        return { \"x\": x_template, \"y\": y_template }\n      \n    ignore_order = tf.data.Options()\n    ignore_order.experimental_deterministic = False\n    datasets = tfrecords_parser().run(tf.io.gfile.glob(files_path), _define_template())\n    datasets = datasets.with_options(ignore_order) if ignore_order else datasets\n    datasets = datasets.apply(tf.data.experimental.ignore_errors())\n    datasets = datasets.cache() if cache else datasets\n    datasets = datasets.map(_normalize)\n    return datasets\n\n\ntrain_datasets, valid_datasets = None, None\ntry:\n    train_datasets = tfrecords_loader([os.path.join(dir_name, '*.tfrecord')  for dir_name in TRAIN_GCS_DS_PATH])\n    valid_datasets = tfrecords_loader([os.path.join(dir_name, '*.tfrecord')  for dir_name in VALID_GCS_DS_PATH], ignore_order=False)\nexcept:\n    print(\"Failed to load tfrecords\")","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:40.767006Z","iopub.status.busy":"2020-08-13T06:22:40.765919Z","iopub.status.idle":"2020-08-13T06:22:41.117141Z","shell.execute_reply":"2020-08-13T06:22:41.117634Z"},"papermill":{"duration":0.383562,"end_time":"2020-08-13T06:22:41.117781","exception":false,"start_time":"2020-08-13T06:22:40.734219","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# Anchor Box\ndef get_anchors(image_size):\n    all_anchors = []\n    grid_size = np.array([8, 16, 32, 64, 128], np.float32)\n    anchor_sizes = grid_size * 4.\n    anchor_ratios = np.array([0.5, 1, 2], np.float32)\n    anchor_scales = np.array([2 ** 0, 2 ** (1.0 / 3.0), 2 ** (2.0 / 3.0)], np.float32)\n    for i in range(5):\n        # anchors (1, 9, 4)\n        anchors_list = np.zeros((len(anchor_ratios) * len(anchor_scales), 4))\n        anchors_list[:, 2] = np.tile(anchor_sizes[i] * anchor_scales, 3) / np.sqrt(np.repeat(anchor_ratios, len(anchor_scales)))\n        anchors_list[:, 3] = np.tile(anchor_sizes[i] * anchor_scales, 3) * np.sqrt(np.repeat(anchor_ratios, len(anchor_scales)))\n        anchors_list[:, 0::2] -= anchors_list[:, 2:3] * 0.5\n        anchors_list[:, 1::2] -= anchors_list[:, 3:4] * 0.5\n        anchors_list = np.expand_dims(anchors_list, axis=0)\n        \n        # shift (K, 1, 4)\n        shift_pts = np.arange(grid_size[i] // 2, image_size, grid_size[i], dtype=np.float32)  # [4, 12, 30, ...]\n        shift_list = np.zeros((len(shift_pts) ** 2, 4)) # (K, 1, 4)\n        shift_list[:,0] = shift_list[:,2] = np.tile(shift_pts, len(shift_pts)) # x1 x2   np.tile (0, 1, 2) => (0, 1, 2, 0, 1, 2, 0, 1, 2)\n        shift_list[:,1] = shift_list[:,3] = np.repeat(shift_pts, len(shift_pts)) # y1 y2  np.repeat (0, 1, 2) => (0, 0, 0, 1, 1, 1, 2, 2, 2)\n        shift_list = np.expand_dims(shift_list, axis=1)\n        \n        # merge (K, 9, 4) = > (K * 9, 4)\n        all_anchors.append(np.reshape(anchors_list + shift_list, (-1, 4)))\n    all_anchors = np.concatenate(all_anchors, axis=0)\n    return all_anchors\n\n\nPRIORS_TF = tf.cast(tf.convert_to_tensor(get_anchors(IMAGE_SIZE)), tf.float32)\nPRIORS_CENTER_TF = 0.5 * (PRIORS_TF[:, :2] + PRIORS_TF[:, 2:4])\nPRIORS_WH_TF = PRIORS_TF[:, 2:4] - PRIORS_TF[:, :2] + 1\nPRIORS_AREA_TF = (PRIORS_TF[:, 2] - PRIORS_TF[:, 0]) * (PRIORS_TF[:, 3] - PRIORS_TF[:, 1]) # (196416)\n\nPRIORS = PRIORS_TF.numpy()\nPRIORS_CENTER = PRIORS_CENTER_TF.numpy()\nPRIORS_WH = PRIORS_WH_TF.numpy()\nPRIORS_AREA = PRIORS_AREA_TF.numpy()\n\nprint(PRIORS_TF.shape)","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:41.298655Z","iopub.status.busy":"2020-08-13T06:22:41.155895Z","iopub.status.idle":"2020-08-13T06:22:41.316501Z","shell.execute_reply":"2020-08-13T06:22:41.315914Z"},"papermill":{"duration":0.189487,"end_time":"2020-08-13T06:22:41.316622","exception":false,"start_time":"2020-08-13T06:22:41.127135","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"@tf.function\ndef datasets_aug(x, y, batch_size = BATCH_SIZE, hue_en = True, gaussian_noise_en = False, flip_en = True, rotate_en = True, crop_en = True, mosaic_en = True, mixup_en = False):\n    # x (Batch Size, H, W, 3)\n    # y (Batch Size, N, 5)  x1, y1, x2, y2, class\n    \n    # Color Aug\n    if hue_en:\n        x = tf.image.random_hue(x, 0.1)\n\n    # Flip + Rotate + Crop + Gaussian Noise\n    x_list, y_list = [], []\n    for i in range(batch_size):\n        x1, y1, y2 = x[i], y[i][:, 0: 4], y[i][:, 4:] # x, x1y1x2y2, classes_idx\n        \n        if flip_en: # Flip\n            # Flip X\n            flip_x = tf.random.uniform([]) >= 0.5\n            x1 = tf.cond(flip_x, lambda: tf.image.flip_left_right(x1), lambda: x1)\n            y1 = tf.cond(flip_x, lambda: tf.stack([x1.shape[1] - y1[:, 2], y1[:, 1], x1.shape[1] - y1[:, 0], y1[:, 3]], axis=-1), lambda: y1)\n            # Flip Y\n            flip_y = tf.random.uniform([]) >= 0.5\n            x1 = tf.cond(flip_y, lambda: tf.image.flip_up_down(x1), lambda: x1)\n            y1 = tf.cond(flip_y, lambda: tf.stack([y1[:, 0], x1.shape[0] - y1[:, 3], y1[:, 2], x1.shape[0] - y1[:, 1]], axis=-1), lambda: y1)\n        \n        if rotate_en: # Rotate 90\n            rotate_90 = tf.random.uniform([]) >= 0.5\n            y1 = tf.cond(rotate_90, lambda: tf.stack([y1[:, 1], x1.shape[1] - y1[:, 2], y1[:, 3], x1.shape[1] - y1[:, 0]], axis=-1), lambda: y1)\n            x1 = tf.cond(rotate_90, lambda: tf.image.rot90(x1, k=1), lambda: x1) # counter-clock wise 90 degree\n            \n        if crop_en: # Crop\n            #crop = tf.random.uniform([]) >= 0.5\n            hw_org = x1.shape[:2]\n            hw_expand = tf.cast(tf.cast(hw_org, tf.float32) * tf.random.uniform([], 1., 1.2), tf.int32)\n            hw_scale = tf.cast(hw_expand, tf.float32) / tf.cast(hw_org, tf.float32)\n            hw_crop = tf.cast(tf.cast(hw_expand - hw_org, tf.float32) * tf.random.uniform([]), tf.int32)\n\n            x1 = tf.image.resize(x1, hw_expand, method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)\n            x1 = x1[ hw_crop[0] : hw_crop[0] + hw_org[0], hw_crop[1] : hw_crop[1] + hw_org[1],:]\n            x1 = tf.reshape(x1, [hw_org[0], hw_org[1], x1.shape[2]])  # Dynamic Shape to Static Shape\n\n            y1 = y1 * [hw_scale[1], hw_scale[0], hw_scale[1], hw_scale[0]]\n            y1 = y1 - [hw_crop[1], hw_crop[0], hw_crop[1], hw_crop[0]]\n            y1 = tf.clip_by_value(y1, clip_value_min=[0., 0., 0., 0.], clip_value_max=[hw_org[1], hw_org[0], hw_org[1], hw_org[0]])\n        \n        if gaussian_noise_en: # Gaussian Noise\n            x1 = x1 + ((tf.random.normal(x1.shape, mean=0.0, stddev=1, dtype=tf.float32) - 0.5) * 2) * (10. / 255.)\n            x1 = tf.clip_by_value(x1, 0., 1.)\n        \n        x_list.append(x1)\n        y_list.append(tf.concat([y1, y2], axis= -1))\n    x, y = tf.stack(x_list, axis=0), tf.stack(y_list, axis=0)\n    \n    # Mosaic\n    \n    if mosaic_en:\n        x_list, y_list = [], []\n        for i in range(batch_size):\n            x_center = tf.cast(tf.random.uniform([], 0.2, 0.8) * x.shape[2], tf.int32)\n            y_center = tf.cast(tf.random.uniform([], 0.2, 0.8) * x.shape[1], tf.int32)\n            \n            batch_idx_mosaic0 = i\n            batch_idx_mosaic1 = tf.random.uniform([], 0, batch_size, tf.int32)\n            batch_idx_mosaic2 = tf.random.uniform([], 0, batch_size, tf.int32)\n            batch_idx_mosaic3 = tf.random.uniform([], 0, batch_size, tf.int32)\n        \n            x1_shape = x.shape[1:]\n            x1 = tf.concat([tf.concat([x[batch_idx_mosaic0][:y_center, :x_center, :], \n                                      x[batch_idx_mosaic1][:y_center, x_center:, :]], axis=1), \n                            tf.concat([x[batch_idx_mosaic2][y_center:, :x_center, :], \n                                       x[batch_idx_mosaic3][y_center:, x_center:, :]], axis=1)], axis=0)\n            x1 = tf.reshape(x1, x1_shape)\n            fmin, fmax = -1e7, 1e7\n            y1 = tf.concat([tf.clip_by_value(y[batch_idx_mosaic0,:,:4],\n                                            clip_value_min=[fmin, fmin, fmin, fmin], \n                                            clip_value_max=[fmax, fmax, x_center, y_center]),\n                           tf.clip_by_value(y[batch_idx_mosaic1,:,:4],\n                                            clip_value_min=[x_center, fmin, fmin, fmin],\n                                            clip_value_max=[fmax, fmax, fmax, y_center]),\n                           tf.clip_by_value(y[batch_idx_mosaic2,:,:4],\n                                            clip_value_min=[fmin, y_center, fmin, fmin], \n                                            clip_value_max=[fmax, fmax, x_center, fmax]),\n                           tf.clip_by_value(y[batch_idx_mosaic3,:,:4],\n                                            clip_value_min=[x_center, y_center, fmin, fmin],\n                                            clip_value_max=[fmax, fmax, fmax, fmax]\n                                           )], axis=0)\n            y2 = tf.concat([y[batch_idx_mosaic0,:, 4:], y[batch_idx_mosaic1,:, 4:], y[batch_idx_mosaic2,:, 4:], y[batch_idx_mosaic3,:, 4:]], axis=0)\n            x_list.append(x1)\n            y_list.append(tf.concat([y1, y2], axis= -1))\n        x, y = tf.stack(x_list, axis=0), tf.stack(y_list, axis=0)\n        \n    # Mixup\n    if mixup_en:\n        apply_mix = tf.random.uniform([batch_size]) >= 0.5\n        batch_idx = tf.range(batch_size)\n        batch_idx_mix = tf.random.shuffle(batch_idx)\n        mix_x = tf.gather(x, tf.where(apply_mix, batch_idx_mix, batch_idx))\n        mix_y = tf.gather(y, tf.where(apply_mix, batch_idx_mix, batch_idx))\n        x = (x + mix_x) / 2\n        y = tf.concat([y, mix_y], axis=0)\n        \n    return x, y","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:41.365414Z","iopub.status.busy":"2020-08-13T06:22:41.349687Z","iopub.status.idle":"2020-08-13T06:22:41.377656Z","shell.execute_reply":"2020-08-13T06:22:41.377131Z"},"papermill":{"duration":0.051293,"end_time":"2020-08-13T06:22:41.377756","exception":false,"start_time":"2020-08-13T06:22:41.326463","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"@tf.function\ndef datasets_encode(x, y, batch_size = BATCH_SIZE, overlap_threshold = 0.5, ignore_threshold = 0.4):\n    # x (Batch Size, H, W, 3)\n    # y (Batch Size, N, 5)  x1, y1, x2, y2, class\n\n    # resize image\n    if IMAGE_SIZE != x.shape[2] or IMAGE_SIZE != x.shape[1]:\n        x_scale, y_scale = IMAGE_SIZE / x.shape[2], IMAGE_SIZE / x.shape[1]\n        y = y * [x_scale, y_scale, x_scale, y_scale, 1]\n        x = tf.image.resize(x, [IMAGE_SIZE, IMAGE_SIZE])\n    \n\n    x_list, regression_list, classification_list = [], [], []\n    for i in range(batch_size):   # One of Batch\n        assignment_x = tf.zeros((PRIORS_TF.shape[0]), tf.float32)\n        assignment_y = tf.zeros((PRIORS_TF.shape[0]), tf.float32)\n        assignment_w = tf.zeros((PRIORS_TF.shape[0]), tf.float32)\n        assignment_h = tf.zeros((PRIORS_TF.shape[0]), tf.float32)\n        assignment_classes_idx = tf.zeros((PRIORS_TF.shape[0]), tf.float32) + NUM_CLASSES\n        assignment_is_obj = tf.zeros((PRIORS_TF.shape[0]), tf.float32)\n        \n\n        # filter correct boxes (area > 0)\n        priors = tf.expand_dims(PRIORS_TF, axis=0) # (1, 196416, 4)\n        box = y[i] # (N, 5)\n        indices_for_object = tf.where(tf.math.reduce_all([box[:,4] >= 0, \n                                                          box[:, 2] >= box[:, 0], \n                                                          box[:, 3] >= box[:, 1]], axis=0))\n        box = tf.cond(tf.shape(indices_for_object)[0] == 0, \n                        lambda: tf.reshape([0., 0., -1., -1., 0.], (-1, 1, box.shape[-1])),\n                        lambda: tf.reshape(tf.gather(box, indices_for_object), (-1, 1, box.shape[-1]))) # (?, 1, 5)\n        area_true = tf.maximum(box[:, :, 2] - box[:, :, 0] + 1, 0) * tf.maximum(box[:, :, 3] - box[:, :, 1] + 1, 0) # (?, 1)\n        \n        # calc iou\n        inter_upleft = tf.maximum(priors[:,:, :2], box[:,:,:2])\n        inter_botright = tf.minimum(priors[:,:, 2:4], box[:,:,2:4])\n        inter_wh = inter_botright - inter_upleft + 1\n        inter_wh = tf.maximum(inter_wh, 0)  # (?, 196416, 2)\n        inter = inter_wh[:, :, 0] * inter_wh[:, :, 1] #(?, 196416)\n        area_gt = tf.expand_dims(PRIORS_AREA_TF, axis=0) # (1, 196416)\n        union = area_true + area_gt - inter # (?, 196416)\n        iou = inter / union # (?, 196416)\n        iou_max = tf.math.reduce_max(iou, axis=0) # (196416)\n        iou_max_idxs = tf.math.argmax(iou, axis=0) # (196416)\n        \n        # ignore box\n        ignore_mask = tf.math.logical_and(iou_max > ignore_threshold, iou_max < overlap_threshold) # (196416)\n        assignment_is_obj = tf.where(ignore_mask, -1., assignment_is_obj)\n        \n        # object box\n        assign_mask = tf.math.logical_or(iou_max > overlap_threshold, iou_max == tf.math.maximum(tf.math.reduce_max(iou_max), 1e-7)) # (196416)\n        box_best = tf.gather(tf.squeeze(box, axis=1), iou_max_idxs, axis=0) # (?, 5) + (196416) => (196416, 5)\n        assignment_is_obj = tf.where(assign_mask, 1., assignment_is_obj)\n        assignment_classes_idx = tf.where(assign_mask, box_best[:, 4], assignment_classes_idx)\n        \n        box_center = 0.5 * (box_best[:,:2] + box_best[:,2:4]) #  (196416, 2)\n        box_wh = box_best[:,2:4] - box_best[:,:2] + 1 #  (196416, 2)\n        assigned_xy = (box_center - PRIORS_CENTER_TF) / PRIORS_WH_TF #  (196416, 2)\n        assigned_wh = tf.math.log(box_wh / PRIORS_WH_TF) #  (196416, 2)\n        assignment_x = tf.where(assign_mask, assigned_xy[:, 0], assignment_x)\n        assignment_y = tf.where(assign_mask, assigned_xy[:, 1], assignment_y)\n        assignment_w = tf.where(assign_mask, assigned_wh[:, 0], assignment_w)\n        assignment_h = tf.where(assign_mask, assigned_wh[:, 1], assignment_h)\n            \n        x_list.append(x[i])\n        regression_list.append(tf.stack([\n            assignment_x, assignment_y, \n            assignment_w, assignment_h, \n            assignment_is_obj], axis=-1))\n\n        classification_list.append(tf.concat([\n            tf.one_hot(tf.cast(assignment_classes_idx, tf.int32), NUM_CLASSES + 1)[:, 0:-1], \n            tf.reshape(assignment_is_obj, (-1, 1))], axis=-1))\n        \n    x_list = tf.stack(x_list, axis=0)\n    regression_list = tf.stack(regression_list, axis=0)\n    classification_list = tf.stack(classification_list, axis=0)\n    \n    print(x_list.shape, regression_list.shape, classification_list.shape)\n    \n    return x_list, (regression_list, classification_list)","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:41.419469Z","iopub.status.busy":"2020-08-13T06:22:41.403928Z","iopub.status.idle":"2020-08-13T06:22:41.446108Z","shell.execute_reply":"2020-08-13T06:22:41.446581Z"},"papermill":{"duration":0.059859,"end_time":"2020-08-13T06:22:41.446707","exception":false,"start_time":"2020-08-13T06:22:41.386848","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"def boxes_decode(regression, classification):\n    regression = regression[:, :, 0:4]  # (Batch Size, 196416, 4)\n    classification = classification[:, :, 0:NUM_CLASSES] # (Batch Size, 196416, NUM_CLASSES)\n    tx = (regression[:,:,0] * PRIORS_WH[:, 0] + PRIORS_CENTER[:, 0]) # (Batch Size, 196416)\n    ty = (regression[:,:,1] * PRIORS_WH[:, 1] + PRIORS_CENTER[:, 1]) # (Batch Size, 196416)\n    tw = np.exp(regression[:,:,2]) * PRIORS_WH[:, 0] # (Batch Size, 196416)\n    th = np.exp(regression[:,:,3]) * PRIORS_WH[:, 1] # (Batch Size, 196416)\n    regression = np.stack([tx - tw / 2, ty - th / 2, tx + tw / 2, ty + th / 2], axis=-1) # (Batch Size, 196416, 4) \n    return regression, classification\n\ndef boxes_ensemble(regression, classification, iou_threshold=0.5, score_threshold=0.5, method='wbf', sigma=0.5):\n    batch_obj_box_list = []\n    for i in range(regression.shape[0]):\n        boxes, scores, labels = regression[i], np.expand_dims(np.amax(classification[i], axis=-1), axis=-1), np.expand_dims(np.argmax(classification[i], axis=-1), axis=-1)\n        # Boxes ensemble\n        valid_idxs = tf.where(scores >=score_threshold)[:,0]\n        boxes, scores, labels = np.take(boxes, valid_idxs, axis=0), np.take(scores, valid_idxs, axis=0), np.take(labels, valid_idxs, axis=0)\n        areas = (np.maximum(boxes[:, 2] - boxes[:, 0] + 1, 0.0) * np.maximum(boxes[:, 3] - boxes[:, 1] + 1, 0.0))[:, None]\n        dets = np.concatenate((boxes, scores, labels, areas), axis=1) # (?, 7)\n        if method == 'wbf':\n            wboxes, w_boxes_score = np.zeros((0, 7)), [] # x1, y2, x2, y2, score_sum, label, area\n            for i in np.argsort(-dets[:,4]):\n                if wboxes.shape[0] != 0:\n                    inter = np.maximum(np.minimum(dets[i, 2], wboxes[:, 2]) - np.maximum(dets[i, 0], wboxes[:, 0]) + 1, 0.0) * \\\n                            np.maximum(np.minimum(dets[i, 3], wboxes[:, 3]) - np.maximum(dets[i, 1], wboxes[:, 1]) + 1, 0.0)\n                    iou = inter / (dets[i, -1] + wboxes[:, -1] - inter)\n                    iou = np.where(wboxes[:, 5] == dets[i, 5], iou, 0)\n                    best_iou_idx = np.argmax(iou)\n                    if iou[best_iou_idx] >= iou_threshold:\n                        best_wboxes = wboxes[best_iou_idx]\n                        wcoord, wscores = best_wboxes[:4], best_wboxes[4]\n                        dcoord, dscore, dlabel = dets[i, :4], dets[i, 4], dets[i, 5]\n                        ncoord, nscore = (wcoord * wscores + dcoord * dscore) / (wscores + dscore), (wscores + dscore)\n                        wboxes[best_iou_idx] = [ncoord[0], ncoord[1], ncoord[2], ncoord[3], nscore, dlabel, (ncoord[2] - ncoord[0] + 1) * (ncoord[3] - ncoord[1] + 1)]\n                        w_boxes_score[best_iou_idx].append(dscore)\n                        continue\n                wboxes = np.concatenate((wboxes, dets[i:i+1]), axis=0)\n                w_boxes_score.append([dets[i, 4]])\n            for i in range(len(w_boxes_score)): # Update Score\n                wboxes[i, 4] = np.mean(w_boxes_score[i]) * min(1, len(w_boxes_score[i])) / 1\n            batch_obj_box_list.append(wboxes[:, : -1])\n        elif (method == 'linear' or method == 'gaussian' or method == 'nms'):\n            retained_box = []\n            while dets.size > 0:\n                max_idx = np.argmax(dets[:, 4], axis=0)\n                dets[[0, max_idx], :] = dets[[max_idx, 0], :]        \n                retained_box.append(dets[0, :-1])\n                inter = np.maximum(np.minimum(dets[0, 2], dets[1:, 2]) - np.maximum(dets[0, 0], dets[1:, 0]) + 1, 0.0) * \\\n                        np.maximum(np.minimum(dets[0, 3], dets[1:, 3]) - np.maximum(dets[0, 1], dets[1:, 1]) + 1, 0.0)\n                iou = inter / (dets[0, -1] + dets[1:, -1] - inter)\n                iou = np.where(dets[0, 5] == dets[1:, 5], iou, 0)\n                weight = None\n                if method == 'linear':\n                    weight = np.ones_like(iou)\n                    weight[iou > iou_threshold] -= iou[iou > iou_threshold]\n                elif method == 'gaussian':\n                    weight = np.exp(-(iou * iou) / sigma)\n                else:  # traditional nms\n                    weight = np.ones_like(iou)\n                    weight[iou > iou_threshold] = 0\n                dets[1:, 4] *= weight\n                retained_idx = np.where(dets[1:, 4] >= score_threshold)[0]\n                dets = dets[retained_idx + 1, :]\n            batch_obj_box_list.append(np.vstack(retained_box))\n    return batch_obj_box_list\n\ndef datasets_decode(regression, classification, iou_threshold = 0.5, score_threshold=0.5, method='wbf'):\n    # x (Batch Size, H, W, 3)\n    regression, classification = boxes_decode(regression, classification)\n    batch_obj_box_list = boxes_ensemble(regression, classification, iou_threshold=iou_threshold, score_threshold=score_threshold, method=method)\n    return batch_obj_box_list","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:41.483934Z","iopub.status.busy":"2020-08-13T06:22:41.478585Z","iopub.status.idle":"2020-08-13T06:22:41.488957Z","shell.execute_reply":"2020-08-13T06:22:41.488462Z"},"papermill":{"duration":0.033186,"end_time":"2020-08-13T06:22:41.489067","exception":false,"start_time":"2020-08-13T06:22:41.455881","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# mAP\ndef metric_mAP(obj_info_true, obj_info_pred, iou_thresholds = [0.5, 0.55, 0.6, 0.65, 0.7, 0.75]):\n    if len(iou_thresholds) == 0:\n        return 0.\n    batch_size = len(obj_info_true)\n    precision_list = []\n\n    for i in range(batch_size):\n        y_true, y_pred = obj_info_true[i], obj_info_pred[i] # (K1, 6) (K2, 6)\n        # sort by scores\n        y_true = y_true[np.argsort(- y_true[:,4])] #tf.gather\n        y_pred = y_pred[np.argsort(- y_pred[:,4])]\n        area_true = (y_true[:, 2] - y_true[:, 0] + 1) * (y_true[:, 3] - y_true[:, 1] + 1)  # (K1)\n        area_pred = (y_pred[:, 2] - y_pred[:, 0] + 1) * (y_pred[:, 3] - y_pred[:, 1] + 1) # (K2)\n        for iou_threshold in iou_thresholds:\n            tp, fp, true_cnt, pred_cnt = 0, 0, y_true.shape[0], y_pred.shape[0]\n            dismatched_list = np.arange(true_cnt) # 0, 1, 2, 3\n            for j in range(pred_cnt):\n                # calc iou\n                inter_upleft = np.maximum(y_true[:,:2], y_pred[j,:2]) # (K1, 2) + (2) => (K1, 2)\n                inter_botright = np.minimum(y_true[:,2:4], y_pred[j,2:4]) # (K1, 2) + (2) => (K1, 2)\n                inter_wh = inter_botright - inter_upleft + 1\n                inter_wh = np.maximum(inter_wh, 0)  # (K1, 2)\n                inter = inter_wh[:, 0] * inter_wh[:, 1] #(K1)\n                iou = inter / (area_true + area_pred[j] - inter) # (K1)\n                iou = np.where(np.logical_and(dismatched_list >= 0, y_pred[j,5] == y_true[:,5]), iou, 0.)\n                iou_matched = (np.amax(iou) >= iou_threshold)\n                # update\n                if (iou_matched == True):\n                    tp += 1\n                    dismatched_list[np.argmax(iou)] = -1\n                else:\n                    fp += 1\n            fn = np.count_nonzero(dismatched_list >= 0)\n            precision_list.append(tp / (tp + fp + fn))\n    # Filter Nan\n    precision_list = np.array(precision_list)\n    precision_list = precision_list[np.where(np.isnan(precision_list) == False)]\n    return np.mean(precision_list)","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:41.521482Z","iopub.status.busy":"2020-08-13T06:22:41.520712Z","iopub.status.idle":"2020-08-13T06:22:41.523804Z","shell.execute_reply":"2020-08-13T06:22:41.524316Z"},"papermill":{"duration":0.025567,"end_time":"2020-08-13T06:22:41.524455","exception":false,"start_time":"2020-08-13T06:22:41.498888","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"def visualize_decode(batch_x, batch_obj_info, display_cnt = 4):\n    batch_x = np.array(batch_x)\n    for i in range(batch_x.shape[0])[0:display_cnt]:\n        img, obj_info = batch_x[i], batch_obj_info[i]\n        for j in range(obj_info.shape[0]):\n            tx1, ty1, tx2, ty2, score, classes_idx = obj_info[j]\n            tx1, ty1, tx2, ty2, score, classes_idx =  int(tx1), int(ty1), int(tx2), int(ty2), round(score, 1), int(classes_idx)\n            cv2.rectangle(img, (tx1, ty1), (tx2, ty2), (127, 0, 127), 5)\n            cv2.putText(img, str(score), (tx1, ty1), cv2.FONT_HERSHEY_SIMPLEX , 1, (0, 127, 127), 2, cv2.LINE_AA)\n        plt.figure(i)\n        plt.figure(figsize=(6, 6))\n        plt.axis('off')\n        plt.title(\"{0} objects\".format(obj_info.shape[0]))\n        plt.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:41.553572Z","iopub.status.busy":"2020-08-13T06:22:41.552881Z","iopub.status.idle":"2020-08-13T06:22:49.333572Z","shell.execute_reply":"2020-08-13T06:22:49.332853Z"},"papermill":{"duration":7.79865,"end_time":"2020-08-13T06:22:49.333722","exception":false,"start_time":"2020-08-13T06:22:41.535072","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"train_dataset_encode, valid_dataset_encode = [], []\nif (train_datasets is not None) and (valid_datasets is not None):\n    train_dataset_encode = train_datasets.repeat().shuffle(256).batch(BATCH_SIZE)\n    train_dataset_encode = train_dataset_encode.map(datasets_aug, num_parallel_calls=AUTO)\n    train_dataset_encode = train_dataset_encode.map(datasets_encode, num_parallel_calls=AUTO).prefetch(AUTO)\n\n    valid_dataset_encode = valid_datasets.repeat().batch(BATCH_SIZE)\n    valid_dataset_encode = valid_dataset_encode.map(datasets_encode, num_parallel_calls=AUTO).prefetch(AUTO)\nelse:\n    print(\"Failed to load tfrecors\")","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:22:49.360423Z","iopub.status.busy":"2020-08-13T06:22:49.359793Z","iopub.status.idle":"2020-08-13T06:23:22.383189Z","shell.execute_reply":"2020-08-13T06:23:22.383787Z"},"papermill":{"duration":33.039515,"end_time":"2020-08-13T06:23:22.383942","exception":false,"start_time":"2020-08-13T06:22:49.344427","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"for batch_x, (batch_regression, batch_classification) in train_dataset_encode:\n    batch_obj_info = datasets_decode(batch_regression, batch_classification, iou_threshold = 0.99, score_threshold=1.0)\n    print(\"mAP: {0}\".format(metric_mAP(batch_obj_info, batch_obj_info)))\n    visualize_decode(batch_x, batch_obj_info)\n    break","execution_count":null,"outputs":[]},{"metadata":{"papermill":{"duration":0.023841,"end_time":"2020-08-13T06:23:22.43192","exception":false,"start_time":"2020-08-13T06:23:22.408079","status":"completed"},"tags":[]},"cell_type":"markdown","source":"The code of building EfficientDet is copied from https://github.com/bubbliiiing/efficientdet-keras","execution_count":null},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:23:22.519605Z","iopub.status.busy":"2020-08-13T06:23:22.492106Z","iopub.status.idle":"2020-08-13T06:23:22.550797Z","shell.execute_reply":"2020-08-13T06:23:22.550248Z"},"papermill":{"duration":0.095807,"end_time":"2020-08-13T06:23:22.550957","exception":false,"start_time":"2020-08-13T06:23:22.45515","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"def EfficientNetBN(n, input_tensor=None, input_shape=None, **kwargs):\n    CONV_KERNEL_INITIALIZER = {\n        'class_name': 'VarianceScaling',\n        'config': {\n            'scale': 2.0,\n            'mode': 'fan_out',\n            # EfficientNet actually uses an untruncated normal distribution for\n            # initializing conv layers, but keras.initializers.VarianceScaling use\n            # a truncated distribution.\n            # We decided against a custom initializer for better serializability.\n            'distribution': 'normal'\n        }\n    }\n\n    def get_swish():\n        def swish(x):\n            return x * tf.math.sigmoid(x)\n        return swish\n\n\n    def get_dropout():\n        class FixedDropout(layers.Dropout):\n            def _get_noise_shape(self, inputs):\n                if self.noise_shape is None:\n                    return self.noise_shape\n                symbolic_shape = tf.shape(inputs)\n                noise_shape = [symbolic_shape[axis] if (shape is None) else shape for axis, shape in enumerate(self.noise_shape)]\n                return tuple(noise_shape)\n        return FixedDropout\n\n\n    def round_filters(filters, width_coefficient, depth_divisor):\n        filters *= width_coefficient\n        new_filters = int(filters + depth_divisor / 2) // depth_divisor * depth_divisor\n        new_filters = max(depth_divisor, new_filters)\n        if new_filters < 0.9 * filters:\n            new_filters += depth_divisor\n        return int(new_filters)\n\n\n    def round_repeats(repeats, depth_coefficient):\n        return int(math.ceil(depth_coefficient * repeats))\n\n\n    def mb_conv_block(inputs, block_args, activation, drop_rate=None, prefix='', freeze_bn=False):\n        has_se = (block_args.se_ratio is not None) and (0 < block_args.se_ratio <= 1)\n        bn_axis = 3 \n\n        Dropout = get_dropout()\n\n        filters = block_args.input_filters * block_args.expand_ratio\n        if block_args.expand_ratio != 1:\n            x = layers.Conv2D(filters, 1, padding='same', use_bias=False, kernel_initializer=CONV_KERNEL_INITIALIZER, name=prefix + 'expand_conv')(inputs)\n            x = layers.BatchNormalization(axis=bn_axis, name=prefix + 'expand_bn')(x)\n            x = layers.Activation(activation, name=prefix + 'expand_activation')(x)\n        else:\n            x = inputs\n\n        # Depthwise Convolution\n        x = layers.DepthwiseConv2D(block_args.kernel_size, strides=block_args.strides, padding='same', use_bias=False, depthwise_initializer=CONV_KERNEL_INITIALIZER, name=prefix + 'dwconv')(x)\n        x = layers.BatchNormalization(axis=bn_axis, name=prefix + 'bn')(x)\n        x = layers.Activation(activation, name=prefix + 'activation')(x)\n\n        # Squeeze and Excitation phase\n        if has_se:\n            num_reduced_filters = max(1, int(block_args.input_filters * block_args.se_ratio))\n            se_tensor = layers.GlobalAveragePooling2D(name=prefix + 'se_squeeze')(x)\n\n            target_shape = (1, 1, filters) if backend.image_data_format() == 'channels_last' else (filters, 1, 1)\n            se_tensor = layers.Reshape(target_shape, name=prefix + 'se_reshape')(se_tensor)\n            se_tensor = layers.Conv2D(num_reduced_filters, 1, activation=activation, padding='same', use_bias=True, kernel_initializer=CONV_KERNEL_INITIALIZER, name=prefix + 'se_reduce')(se_tensor)\n            se_tensor = layers.Conv2D(filters, 1, activation='sigmoid', padding='same', use_bias=True, kernel_initializer=CONV_KERNEL_INITIALIZER, name=prefix + 'se_expand')(se_tensor)\n            if backend.backend() == 'theano':\n                pattern = ([True, True, True, False] if (backend.image_data_format() == 'channels_last') else [True, False, True, True])\n                se_tensor = layers.Lambda(lambda x: backend.pattern_broadcast(x, pattern), name=prefix + 'se_broadcast')(se_tensor)\n            x = layers.multiply([x, se_tensor], name=prefix + 'se_excite')\n\n        # Output phase\n        x = layers.Conv2D(block_args.output_filters, 1, padding='same', use_bias=False, kernel_initializer=CONV_KERNEL_INITIALIZER, name=prefix + 'project_conv')(x)\n        x = layers.BatchNormalization(axis=bn_axis, name=prefix + 'project_bn')(x)\n        if block_args.id_skip and all(s == 1 for s in block_args.strides) and block_args.input_filters == block_args.output_filters:\n            if drop_rate and (drop_rate > 0):\n                x = Dropout(drop_rate, noise_shape=(None, 1, 1, 1), name=prefix + 'drop')(x)\n            x = layers.add([x, inputs], name=prefix + 'add')\n        return x\n\n\n    def EfficientNet(width_coefficient, depth_coefficient, drop_connect_rate=0.2, depth_divisor=8, input_tensor=None, input_shape=None, freeze_bn=False, **kwargs):\n        BlockArgs = collections.namedtuple('BlockArgs', [\n            'kernel_size', 'num_repeat', 'input_filters', 'output_filters',\n            'expand_ratio', 'id_skip', 'strides', 'se_ratio'\n        ])\n        blocks_args = [\n            BlockArgs(kernel_size=3, num_repeat=1, input_filters=32, output_filters=16, expand_ratio=1, id_skip=True, strides=[1, 1], se_ratio=0.25),\n            BlockArgs(kernel_size=3, num_repeat=2, input_filters=16, output_filters=24, expand_ratio=6, id_skip=True, strides=[2, 2], se_ratio=0.25),\n            BlockArgs(kernel_size=5, num_repeat=2, input_filters=24, output_filters=40, expand_ratio=6, id_skip=True, strides=[2, 2], se_ratio=0.25),\n            BlockArgs(kernel_size=3, num_repeat=3, input_filters=40, output_filters=80, expand_ratio=6, id_skip=True, strides=[2, 2], se_ratio=0.25),\n            BlockArgs(kernel_size=5, num_repeat=3, input_filters=80, output_filters=112, expand_ratio=6, id_skip=True, strides=[1, 1], se_ratio=0.25),\n            BlockArgs(kernel_size=5, num_repeat=4, input_filters=112, output_filters=192, expand_ratio=6, id_skip=True, strides=[2, 2], se_ratio=0.25),\n            BlockArgs(kernel_size=3, num_repeat=1, input_filters=192, output_filters=320, expand_ratio=6, id_skip=True, strides=[1, 1], se_ratio=0.25)\n        ]\n        \n        features = []\n\n        img_input = layers.Input(shape=input_shape) if (input_tensor is None) else (input_tensor)\n\n        bn_axis = 3 \n        activation = get_swish(**kwargs)\n\n        # Build stem\n        x = img_input\n        x = layers.Conv2D(round_filters(32, width_coefficient, depth_divisor), 3, strides=(2, 2), padding='same', use_bias=False, kernel_initializer=CONV_KERNEL_INITIALIZER, name='stem_conv')(x)\n        x = layers.BatchNormalization(axis=bn_axis, name='stem_bn')(x)\n        x = layers.Activation(activation, name='stem_activation')(x)\n        # Build blocks\n        num_blocks_total = sum(block_args.num_repeat for block_args in blocks_args)\n        block_num = 0\n        for idx, block_args in enumerate(blocks_args):\n            assert block_args.num_repeat > 0\n            # Update block input and output filters based on depth multiplier.\n            block_args = block_args._replace(\n                input_filters=round_filters(block_args.input_filters, width_coefficient, depth_divisor),\n                output_filters=round_filters(block_args.output_filters, width_coefficient, depth_divisor),\n                num_repeat=round_repeats(block_args.num_repeat, depth_coefficient))\n\n            # The first block needs to take care of stride and filter size increase.\n            drop_rate = drop_connect_rate * float(block_num) / num_blocks_total\n            x = mb_conv_block(x, block_args, activation=activation, drop_rate=drop_rate, prefix='block{}a_'.format(idx + 1), freeze_bn=freeze_bn)\n            block_num += 1\n            if block_args.num_repeat > 1:\n                # pylint: disable=protected-access\n                block_args = block_args._replace(\n                    input_filters=block_args.output_filters, strides=[1, 1])\n                # pylint: enable=protected-access\n                for bidx in range(block_args.num_repeat - 1):\n                    drop_rate = drop_connect_rate * float(block_num) / num_blocks_total\n                    block_prefix = 'block{}{}_'.format(idx + 1, string.ascii_lowercase[bidx + 1])\n                    x = mb_conv_block(x, block_args, activation=activation, drop_rate=drop_rate, prefix=block_prefix, freeze_bn=freeze_bn)\n                    block_num += 1\n            if idx < len(blocks_args) - 1 and blocks_args[idx + 1].strides[0] == 2:\n                features.append(x)\n            elif idx == len(blocks_args) - 1:\n                features.append(x)\n        return features\n\n    \n    parms = [\n        { \"width_coefficient\" : 1.0, \"depth_coefficient\" : 1.0, \"default_resolution\" : 224},\n        { \"width_coefficient\" : 1.0, \"depth_coefficient\" : 1.1, \"default_resolution\" : 240},\n        { \"width_coefficient\" : 1.1, \"depth_coefficient\" : 1.2, \"default_resolution\" : 260},\n        { \"width_coefficient\" : 1.2, \"depth_coefficient\" : 1.4, \"default_resolution\" : 300},\n        { \"width_coefficient\" : 1.4, \"depth_coefficient\" : 1.8, \"default_resolution\" : 380},\n        { \"width_coefficient\" : 1.6, \"depth_coefficient\" : 2.2, \"default_resolution\" : 456},\n        { \"width_coefficient\" : 1.8, \"depth_coefficient\" : 2.6, \"default_resolution\" : 528},\n        { \"width_coefficient\" : 2.0, \"depth_coefficient\" : 3.1, \"default_resolution\" : 600},\n    ][n]\n    return EfficientNet(parms['width_coefficient'], parms['depth_coefficient'], input_tensor=input_tensor, input_shape=input_shape, **kwargs)\n\n#print(EfficientNetBN(7, input_shape=(600, 600, 3)))","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:23:22.617155Z","iopub.status.busy":"2020-08-13T06:23:22.595991Z","iopub.status.idle":"2020-08-13T06:23:22.773301Z","shell.execute_reply":"2020-08-13T06:23:22.77387Z"},"papermill":{"duration":0.202941,"end_time":"2020-08-13T06:23:22.774012","exception":false,"start_time":"2020-08-13T06:23:22.571071","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"MOMENTUM = 0.99\nEPSILON = 1e-3\n\nclass wBiFPNAdd(layers.Layer):\n    def __init__(self, epsilon=1e-4, **kwargs):\n        super(wBiFPNAdd, self).__init__(**kwargs)\n        self.epsilon = epsilon\n\n    def build(self, input_shape):\n        num_in = len(input_shape)\n        self.w = self.add_weight(name=self.name, shape=(num_in,), initializer=initializers.constant(1 / num_in), trainable=True, dtype=tf.float32)\n\n    def call(self, inputs, **kwargs):\n        w = activations.relu(self.w)\n        x = tf.reduce_sum([w[i] * inputs[i] for i in range(len(inputs))], axis=0)\n        x = x / (tf.reduce_sum(w) + self.epsilon)\n        return x\n\n    def compute_output_shape(self, input_shape):\n        return input_shape[0]\n\n    def get_config(self):\n        config = super(wBiFPNAdd, self).get_config()\n        config.update({ 'epsilon': self.epsilon })\n        return config\n    \n    \ndef SeparableConvBlock(num_channels, kernel_size, strides, name, freeze_bn=False):\n    f1 = layers.SeparableConv2D(num_channels, kernel_size=kernel_size, strides=strides, padding='same',\n                                use_bias=True, name=f'{name}/conv')\n    f2 = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON, name=f'{name}/bn')\n    return lambda *args, **kwargs: f2(f1(*args, **kwargs))\n\n\ndef build_wBiFPN(features, num_channels, id, freeze_bn=False):\n    if id == 0:\n        _, _, C3, C4, C5 = features\n        # 第一次BIFPN需要 下采样 与 降通道 获得 p3_in p4_in p5_in p6_in p7_in\n        #-----------------------------下采样 与 降通道----------------------------#\n        P3_in = C3\n        P3_in = layers.Conv2D(num_channels, kernel_size=1, padding='same',\n                              name=f'fpn_cells/cell_{id}/fnode3/resample_0_0_8/conv2d')(P3_in)\n        P3_in = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON,\n                                          name=f'fpn_cells/cell_{id}/fnode3/resample_0_0_8/bn')(P3_in)\n\n        P4_in = C4\n        P4_in_1 = layers.Conv2D(num_channels, kernel_size=1, padding='same',\n                                name=f'fpn_cells/cell_{id}/fnode2/resample_0_1_7/conv2d')(P4_in)\n        P4_in_1 = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON,\n                                            name=f'fpn_cells/cell_{id}/fnode2/resample_0_1_7/bn')(P4_in_1)\n        P4_in_2 = layers.Conv2D(num_channels, kernel_size=1, padding='same',\n                                name=f'fpn_cells/cell_{id}/fnode4/resample_0_1_9/conv2d')(P4_in)\n        P4_in_2 = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON,\n                                            name=f'fpn_cells/cell_{id}/fnode4/resample_0_1_9/bn')(P4_in_2)\n\n        P5_in = C5\n        P5_in_1 = layers.Conv2D(num_channels, kernel_size=1, padding='same',\n                                name=f'fpn_cells/cell_{id}/fnode1/resample_0_2_6/conv2d')(P5_in)\n        P5_in_1 = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON,\n                                            name=f'fpn_cells/cell_{id}/fnode1/resample_0_2_6/bn')(P5_in_1)\n        P5_in_2 = layers.Conv2D(num_channels, kernel_size=1, padding='same',\n                                name=f'fpn_cells/cell_{id}/fnode5/resample_0_2_10/conv2d')(P5_in)\n        P5_in_2 = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON,\n                                            name=f'fpn_cells/cell_{id}/fnode5/resample_0_2_10/bn')(P5_in_2)\n\n        P6_in = layers.Conv2D(num_channels, kernel_size=1, padding='same', name='resample_p6/conv2d')(C5)\n        P6_in = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON, name='resample_p6/bn')(P6_in)\n        P6_in = layers.MaxPooling2D(pool_size=3, strides=2, padding='same', name='resample_p6/maxpool')(P6_in)\n\n        P7_in = layers.MaxPooling2D(pool_size=3, strides=2, padding='same', name='resample_p7/maxpool')(P6_in)\n        #-------------------------------------------------------------------------#\n\n        #--------------------------构建BIFPN的上下采样循环-------------------------#\n        P7_U = layers.UpSampling2D()(P7_in)\n        P6_td = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode0/add')([P6_in, P7_U])\n        P6_td = layers.Activation(lambda x: tf.nn.swish(x))(P6_td)\n        P6_td = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                   name=f'fpn_cells/cell_{id}/fnode0/op_after_combine5')(P6_td)\n        \n        P6_U = layers.UpSampling2D()(P6_td)\n        P5_td = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode1/add')([P5_in_1, P6_U])\n        P5_td = layers.Activation(lambda x: tf.nn.swish(x))(P5_td)\n        P5_td = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                   name=f'fpn_cells/cell_{id}/fnode1/op_after_combine6')(P5_td)\n\n        P5_U = layers.UpSampling2D()(P5_td)\n        P4_td = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode2/add')([P4_in_1, P5_U])\n        P4_td = layers.Activation(lambda x: tf.nn.swish(x))(P4_td)\n        P4_td = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                   name=f'fpn_cells/cell_{id}/fnode2/op_after_combine7')(P4_td)\n\n        P4_U = layers.UpSampling2D()(P4_td)\n        P3_out = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode3/add')([P3_in, P4_U])\n        P3_out = layers.Activation(lambda x: tf.nn.swish(x))(P3_out)\n        P3_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode3/op_after_combine8')(P3_out)\n\n        P3_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P3_out)\n        P4_out = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode4/add')([P4_in_2, P4_td, P3_D])\n        P4_out = layers.Activation(lambda x: tf.nn.swish(x))(P4_out)\n        P4_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode4/op_after_combine9')(P4_out)\n\n        P4_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P4_out)\n        P5_out = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode5/add')([P5_in_2, P5_td, P4_D])\n        P5_out = layers.Activation(lambda x: tf.nn.swish(x))(P5_out)\n        P5_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode5/op_after_combine10')(P5_out)\n\n        P5_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P5_out)\n        P6_out = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode6/add')([P6_in, P6_td, P5_D])\n        P6_out = layers.Activation(lambda x: tf.nn.swish(x))(P6_out)\n        P6_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode6/op_after_combine11')(P6_out)\n\n        P6_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P6_out)\n        P7_out = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode7/add')([P7_in, P6_D])\n        P7_out = layers.Activation(lambda x: tf.nn.swish(x))(P7_out)\n        P7_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode7/op_after_combine12')(P7_out)\n\n    else:\n        P3_in, P4_in, P5_in, P6_in, P7_in = features\n        P7_U = layers.UpSampling2D()(P7_in)\n        P6_td = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode0/add')([P6_in, P7_U])\n        P6_td = layers.Activation(lambda x: tf.nn.swish(x))(P6_td)\n        P6_td = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                   name=f'fpn_cells/cell_{id}/fnode0/op_after_combine5')(P6_td)\n\n        P6_U = layers.UpSampling2D()(P6_td)\n        P5_td = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode1/add')([P5_in, P6_U])\n        P5_td = layers.Activation(lambda x: tf.nn.swish(x))(P5_td)\n        P5_td = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                   name=f'fpn_cells/cell_{id}/fnode1/op_after_combine6')(P5_td)\n\n        P5_U = layers.UpSampling2D()(P5_td)\n        P4_td = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode2/add')([P4_in, P5_U])\n        P4_td = layers.Activation(lambda x: tf.nn.swish(x))(P4_td)\n        P4_td = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                   name=f'fpn_cells/cell_{id}/fnode2/op_after_combine7')(P4_td)\n\n        P4_U = layers.UpSampling2D()(P4_td)\n        P3_out = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode3/add')([P3_in, P4_U])\n        P3_out = layers.Activation(lambda x: tf.nn.swish(x))(P3_out)\n        P3_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode3/op_after_combine8')(P3_out)\n\n        P3_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P3_out)\n        P4_out = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode4/add')([P4_in, P4_td, P3_D])\n        P4_out = layers.Activation(lambda x: tf.nn.swish(x))(P4_out)\n        P4_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode4/op_after_combine9')(P4_out)\n\n        P4_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P4_out)\n        P5_out = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode5/add')([P5_in, P5_td, P4_D])\n        P5_out = layers.Activation(lambda x: tf.nn.swish(x))(P5_out)\n        P5_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode5/op_after_combine10')(P5_out)\n\n        P5_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P5_out)\n        P6_out = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode6/add')([P6_in, P6_td, P5_D])\n        P6_out = layers.Activation(lambda x: tf.nn.swish(x))(P6_out)\n        P6_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode6/op_after_combine11')(P6_out)\n\n        P6_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P6_out)\n        P7_out = wBiFPNAdd(name=f'fpn_cells/cell_{id}/fnode7/add')([P7_in, P6_D])\n        P7_out = layers.Activation(lambda x: tf.nn.swish(x))(P7_out)\n        P7_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode7/op_after_combine12')(P7_out)\n    return [P3_out, P4_out, P5_out, P6_out, P7_out]\n\ndef build_BiFPN(features, num_channels, id, freeze_bn=False):\n    if id == 0:\n        # 第一次BIFPN需要 下采样 与 降通道 获得 p3_in p4_in p5_in p6_in p7_in\n        #-----------------------------下采样 与 降通道----------------------------#\n        _, _, C3, C4, C5 = features\n        P3_in = C3\n        P3_in = layers.Conv2D(num_channels, kernel_size=1, padding='same',\n                              name=f'fpn_cells/cell_{id}/fnode3/resample_0_0_8/conv2d')(P3_in)\n        P3_in = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON,\n                                          name=f'fpn_cells/cell_{id}/fnode3/resample_0_0_8/bn')(P3_in)\n\n        P4_in = C4\n        P4_in_1 = layers.Conv2D(num_channels, kernel_size=1, padding='same',\n                                name=f'fpn_cells/cell_{id}/fnode2/resample_0_1_7/conv2d')(P4_in)\n        P4_in_1 = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON,\n                                            name=f'fpn_cells/cell_{id}/fnode2/resample_0_1_7/bn')(P4_in_1)\n        P4_in_2 = layers.Conv2D(num_channels, kernel_size=1, padding='same',\n                                name=f'fpn_cells/cell_{id}/fnode4/resample_0_1_9/conv2d')(P4_in)\n        P4_in_2 = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON,\n                                            name=f'fpn_cells/cell_{id}/fnode4/resample_0_1_9/bn')(P4_in_2)\n\n        P5_in = C5\n        P5_in_1 = layers.Conv2D(num_channels, kernel_size=1, padding='same',\n                                name=f'fpn_cells/cell_{id}/fnode1/resample_0_2_6/conv2d')(P5_in)\n        P5_in_1 = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON,\n                                            name=f'fpn_cells/cell_{id}/fnode1/resample_0_2_6/bn')(P5_in_1)\n        P5_in_2 = layers.Conv2D(num_channels, kernel_size=1, padding='same',\n                                name=f'fpn_cells/cell_{id}/fnode5/resample_0_2_10/conv2d')(P5_in)\n        P5_in_2 = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON,\n                                            name=f'fpn_cells/cell_{id}/fnode5/resample_0_2_10/bn')(P5_in_2)\n\n        P6_in = layers.Conv2D(num_channels, kernel_size=1, padding='same', name='resample_p6/conv2d')(C5)\n        P6_in = layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON, name='resample_p6/bn')(P6_in)\n        P6_in = layers.MaxPooling2D(pool_size=3, strides=2, padding='same', name='resample_p6/maxpool')(P6_in)\n\n        P7_in = layers.MaxPooling2D(pool_size=3, strides=2, padding='same', name='resample_p7/maxpool')(P6_in)\n        #-------------------------------------------------------------------------#\n\n        #--------------------------构建BIFPN的上下采样循环-------------------------#\n        P7_U = layers.UpSampling2D()(P7_in)\n        P6_td = layers.Add(name=f'fpn_cells/cell_{id}/fnode0/add')([P6_in, P7_U])\n        P6_td = layers.Activation(lambda x: tf.nn.swish(x))(P6_td)\n        P6_td = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                   name=f'fpn_cells/cell_{id}/fnode0/op_after_combine5')(P6_td)\n\n        P6_U = layers.UpSampling2D()(P6_td)\n        P5_td = layers.Add(name=f'fpn_cells/cell_{id}/fnode1/add')([P5_in_1, P6_U])\n        P5_td = layers.Activation(lambda x: tf.nn.swish(x))(P5_td)\n        P5_td = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                   name=f'fpn_cells/cell_{id}/fnode1/op_after_combine6')(P5_td)\n\n        P5_U = layers.UpSampling2D()(P5_td)\n        P4_td = layers.Add(name=f'fpn_cells/cell_{id}/fnode2/add')([P4_in_1, P5_U])\n        P4_td = layers.Activation(lambda x: tf.nn.swish(x))(P4_td)\n        P4_td = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                   name=f'fpn_cells/cell_{id}/fnode2/op_after_combine7')(P4_td)\n\n        P4_U = layers.UpSampling2D()(P4_td)\n        P3_out = layers.Add(name=f'fpn_cells/cell_{id}/fnode3/add')([P3_in, P4_U])\n        P3_out = layers.Activation(lambda x: tf.nn.swish(x))(P3_out)\n        P3_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode3/op_after_combine8')(P3_out)\n\n        P3_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P3_out)\n        P4_out = layers.Add(name=f'fpn_cells/cell_{id}/fnode4/add')([P4_in_2, P4_td, P3_D])\n        P4_out = layers.Activation(lambda x: tf.nn.swish(x))(P4_out)\n        P4_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode4/op_after_combine9')(P4_out)\n\n        P4_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P4_out)\n        P5_out = layers.Add(name=f'fpn_cells/cell_{id}/fnode5/add')([P5_in_2, P5_td, P4_D])\n        P5_out = layers.Activation(lambda x: tf.nn.swish(x))(P5_out)\n        P5_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode5/op_after_combine10')(P5_out)\n\n        P5_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P5_out)\n        P6_out = layers.Add(name=f'fpn_cells/cell_{id}/fnode6/add')([P6_in, P6_td, P5_D])\n        P6_out = layers.Activation(lambda x: tf.nn.swish(x))(P6_out)\n        P6_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode6/op_after_combine11')(P6_out)\n\n        P6_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P6_out)\n        P7_out = layers.Add(name=f'fpn_cells/cell_{id}/fnode7/add')([P7_in, P6_D])\n        P7_out = layers.Activation(lambda x: tf.nn.swish(x))(P7_out)\n        P7_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode7/op_after_combine12')(P7_out)\n\n    else:\n        P3_in, P4_in, P5_in, P6_in, P7_in = features\n        P7_U = layers.UpSampling2D()(P7_in)\n        P6_td = layers.Add(name=f'fpn_cells/cell_{id}/fnode0/add')([P6_in, P7_U])\n        P6_td = layers.Activation(lambda x: tf.nn.swish(x))(P6_td)\n        P6_td = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                   name=f'fpn_cells/cell_{id}/fnode0/op_after_combine5')(P6_td)\n\n        P6_U = layers.UpSampling2D()(P6_td)\n        P5_td = layers.Add(name=f'fpn_cells/cell_{id}/fnode1/add')([P5_in, P6_U])\n        P5_td = layers.Activation(lambda x: tf.nn.swish(x))(P5_td)\n        P5_td = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                   name=f'fpn_cells/cell_{id}/fnode1/op_after_combine6')(P5_td)\n\n        P5_U = layers.UpSampling2D()(P5_td)\n        P4_td = layers.Add(name=f'fpn_cells/cell_{id}/fnode2/add')([P4_in, P5_U])\n        P4_td = layers.Activation(lambda x: tf.nn.swish(x))(P4_td)\n        P4_td = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                   name=f'fpn_cells/cell_{id}/fnode2/op_after_combine7')(P4_td)\n\n        P4_U = layers.UpSampling2D()(P4_td)\n        P3_out = layers.Add(name=f'fpn_cells/cell_{id}/fnode3/add')([P3_in, P4_U])\n        P3_out = layers.Activation(lambda x: tf.nn.swish(x))(P3_out)\n        P3_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode3/op_after_combine8')(P3_out)\n\n        P3_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P3_out)\n        P4_out = layers.Add(name=f'fpn_cells/cell_{id}/fnode4/add')([P4_in, P4_td, P3_D])\n        P4_out = layers.Activation(lambda x: tf.nn.swish(x))(P4_out)\n        P4_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode4/op_after_combine9')(P4_out)\n\n        P4_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P4_out)\n        P5_out = layers.Add(name=f'fpn_cells/cell_{id}/fnode5/add')([P5_in, P5_td, P4_D])\n        P5_out = layers.Activation(lambda x: tf.nn.swish(x))(P5_out)\n        P5_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode5/op_after_combine10')(P5_out)\n\n        P5_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P5_out)\n        P6_out = layers.Add(name=f'fpn_cells/cell_{id}/fnode6/add')([P6_in, P6_td, P5_D])\n        P6_out = layers.Activation(lambda x: tf.nn.swish(x))(P6_out)\n        P6_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode6/op_after_combine11')(P6_out)\n\n        P6_D = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(P6_out)\n        P7_out = layers.Add(name=f'fpn_cells/cell_{id}/fnode7/add')([P7_in, P6_D])\n        P7_out = layers.Activation(lambda x: tf.nn.swish(x))(P7_out)\n        P7_out = SeparableConvBlock(num_channels=num_channels, kernel_size=3, strides=1,\n                                    name=f'fpn_cells/cell_{id}/fnode7/op_after_combine12')(P7_out)\n    return [P3_out, P4_out, P5_out, P6_out, P7_out]\n\nclass PriorProbability(initializers.Initializer):\n    \"\"\" Apply a prior probability to the weights.\n    \"\"\"\n    def __init__(self, probability=0.01):\n        self.probability = probability\n\n    def get_config(self):\n        return { 'probability': self.probability }\n\n    def __call__(self, shape, dtype=None):\n        # set bias to -log((1 - p)/p) for foreground\n        result = np.ones(shape) * -math.log((1 - self.probability) / self.probability)\n\n        return result\n\n    \nclass BoxNet(layers.Layer):\n    def __init__(self, width, depth, num_anchors=9, freeze_bn=False, name='box_net', **kwargs):\n        super().__init__()\n        \n        self.width = width\n        self.depth = depth\n        self.num_anchors = num_anchors\n        options = {\n            'kernel_size': 3,\n            'strides': 1,\n            'padding': 'same',\n            'bias_initializer': 'zeros',\n            'depthwise_initializer': initializers.VarianceScaling(),\n            'pointwise_initializer': initializers.VarianceScaling(),\n        }\n\n        self.convs = [layers.SeparableConv2D(filters=width, name=f'{name}/box-{i}', **options) for i in range(depth)]\n        self.head = layers.SeparableConv2D(filters=num_anchors * 4, name=f'{name}/box-predict', **options)\n\n        self.bns = [\n            [layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON, name=f'{name}/box-{i}-bn-{j}') for j in\n             range(3, 8)]\n            for i in range(depth)]\n\n        self.relu = layers.Lambda(lambda x: tf.nn.swish(x))\n        self.reshape = layers.Reshape((-1, 4))\n\n    def call(self, inputs):\n        feature, level = inputs\n        for i in range(self.depth):\n            feature = self.convs[i](feature)\n            feature = self.bns[i][level](feature)\n            feature = self.relu(feature)\n        outputs = self.head(feature)\n        outputs = self.reshape(outputs)\n        return outputs\n\n\nclass ClassNet(layers.Layer):\n    def __init__(self, width, depth, num_classes=20, num_anchors=9, freeze_bn=False, name='class_net', **kwargs):\n        super().__init__()\n        \n        self.width = width\n        self.depth = depth\n        self.num_classes = num_classes\n        self.num_anchors = num_anchors\n        options = {\n            'kernel_size': 3,\n            'strides': 1,\n            'padding': 'same',\n            'depthwise_initializer': initializers.VarianceScaling(),\n            'pointwise_initializer': initializers.VarianceScaling(),\n        }\n\n        self.convs = [layers.SeparableConv2D(filters=width, bias_initializer='zeros', name=f'{name}/class-{i}', **options) for i in range(depth)]\n        self.head = layers.SeparableConv2D(filters=num_classes * num_anchors, bias_initializer=PriorProbability(probability=0.01), name=f'{name}/class-predict', **options)\n        self.bns = [[layers.BatchNormalization(momentum=MOMENTUM, epsilon=EPSILON, name=f'{name}/class-{i}-bn-{j}') for j in range(3, 8)] for i in range(depth)]\n        self.relu = layers.Lambda(lambda x: tf.nn.swish(x))\n        self.reshape = layers.Reshape((-1, num_classes))\n        self.activation = layers.Activation('sigmoid')\n\n    def call(self, inputs):\n        feature, level = inputs\n        for i in range(self.depth):\n            feature = self.convs[i](feature)\n            feature = self.bns[i][level](feature)\n            feature = self.relu(feature)\n        outputs = self.head(feature)\n        outputs = self.reshape(outputs)\n        outputs = self.activation(outputs)\n        return outputs\n\ndef Efficientdet(phi, num_classes=20, num_anchors=9, freeze_bn=False):\n    assert phi in range(8)\n    fpn_num_filters = [64, 88, 112, 160, 224, 288, 384,384]\n    fpn_cell_repeats = [3, 4, 5, 6, 7, 7, 8, 8]\n    box_class_repeats = [3, 3, 3, 4, 4, 4, 5, 5]\n    image_sizes = [512, 640, 768, 896, 1024, 1280, 1408, 1536]\n    \n    image_input = layers.Input((image_sizes[phi], image_sizes[phi], 3), name='input')\n    features = EfficientNetBN(phi, input_tensor=image_input, freeze_bn=freeze_bn)\n    \n    fpn_features = features\n    if phi < 6:\n        for i in range(fpn_cell_repeats[phi]):\n            fpn_features = build_wBiFPN(fpn_features, fpn_num_filters[phi], i, freeze_bn=freeze_bn)\n    else:        \n        for i in range(fpn_cell_repeats[phi]):\n            fpn_features = build_BiFPN(fpn_features, fpn_num_filters[phi], i, freeze_bn=freeze_bn)\n\n    box_net = BoxNet(fpn_num_filters[phi], box_class_repeats[phi], num_anchors=num_anchors, freeze_bn=freeze_bn, name='box_net')\n    class_net = ClassNet(fpn_num_filters[phi], box_class_repeats[phi], num_classes=num_classes, num_anchors=num_anchors, freeze_bn=freeze_bn, name='class_net')\n    \n    classification = [class_net.call([feature, i]) for i, feature in enumerate(fpn_features)]\n    classification = layers.Concatenate(axis=1, name='classification')(classification)\n    regression = [box_net.call([feature, i]) for i, feature in enumerate(fpn_features)]\n    regression = layers.Concatenate(axis=1, name='regression')(regression)\n\n    model = models.Model(inputs=[image_input], outputs=[regression, classification], name='efficientdet')\n\n    return model\n\n#Efficientdet(0, num_classes=1).summary()","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:23:22.841266Z","iopub.status.busy":"2020-08-13T06:23:22.840278Z","iopub.status.idle":"2020-08-13T06:23:22.843672Z","shell.execute_reply":"2020-08-13T06:23:22.843095Z"},"papermill":{"duration":0.048506,"end_time":"2020-08-13T06:23:22.843779","exception":false,"start_time":"2020-08-13T06:23:22.795273","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# Classification Loss\ndef focal(alpha=0.25, gamma=2.0):\n    def _focal(y_true, y_pred):\n        # print(y_true.shape, y_pred.shape)\n        # y_true [batch_size, num_anchor, num_classes+1]\n        # y_pred [batch_size, num_anchor, num_classes]\n        labels         = y_true[:, :, :-1]\n        anchor_state   = y_true[:, :, -1]  # -1: ignrore, 0: background, 1: object\n        classification = y_pred\n\n\n        # Focal Loss for postive sample (Object)\n        indices_for_object        = tf.where(tf.equal(anchor_state, 1))\n        labels_for_object         = tf.gather_nd(labels, indices_for_object)\n        classification_for_object = tf.gather_nd(classification, indices_for_object)\n\n        alpha_factor_for_object = tf.ones_like(labels_for_object) * alpha\n        alpha_factor_for_object = tf.where(tf.equal(labels_for_object, 1), alpha_factor_for_object, 1 - alpha_factor_for_object)\n        focal_weight_for_object = tf.where(tf.equal(labels_for_object, 1), 1 - classification_for_object, classification_for_object)\n        focal_weight_for_object = alpha_factor_for_object * focal_weight_for_object ** gamma\n\n        cls_loss_for_object = focal_weight_for_object * backend.binary_crossentropy(labels_for_object, classification_for_object)\n        cls_loss_for_object = tf.reduce_sum(cls_loss_for_object)\n            \n        # Focal Loss for negative sample (Background)\n        indices_for_back        = tf.where(tf.equal(anchor_state, 0))\n        labels_for_back         = tf.gather_nd(labels, indices_for_back)\n        classification_for_back = tf.gather_nd(classification, indices_for_back)\n\n        alpha_factor_for_back = tf.ones_like(labels_for_back) * (1 - alpha)\n        focal_weight_for_back = classification_for_back\n        focal_weight_for_back = alpha_factor_for_back * focal_weight_for_back ** gamma\n\n        cls_loss_for_back = focal_weight_for_back * backend.binary_crossentropy(labels_for_back, classification_for_back)\n        cls_loss_for_back = tf.reduce_sum(cls_loss_for_back)\n\n        # num of postive sample (Object) \n        normalizer = tf.where(tf.equal(anchor_state, 1))\n        normalizer = tf.cast(tf.shape(normalizer)[0], tf.float32)\n        normalizer = tf.maximum(normalizer, 1.0)\n\n\n        # totoal loss\n        loss = (cls_loss_for_object + cls_loss_for_back) / normalizer  # norm by num of postive samples\n        return loss\n    return _focal\n\n# Regression Loss\ndef smooth_l1(sigma=3.0):\n    sigma_squared = sigma ** 2\n    def _smooth_l1(y_true, y_pred):\n        # print(y_true.shape, y_pred.shape)\n        # y_true [batch_size, num_anchor, 5]\n        # y_pred [batch_size, num_anchor, 4]\n        \n        regression        = y_pred\n        regression_target = y_true[:, :, :-1]\n        anchor_state      = y_true[:, :, -1]\n\n        # Select postive samples\n        indices           = tf.where(tf.equal(anchor_state, 1))\n        regression        = tf.gather_nd(regression, indices)\n        regression_target = tf.gather_nd(regression_target, indices)\n\n        # compute smooth L1 loss\n        # f(x) = 0.5 * (sigma * x)^2   if |x| < 1 / sigma / sigma\n        # |x| - 0.5 / sigma / sigma    otherwise\n        regression_diff = regression - regression_target\n        regression_diff = tf.abs(regression_diff)\n        regression_loss = tf.where(regression_diff <= (1.0 / sigma_squared), 0.5 * sigma_squared * tf.math.pow(regression_diff, 2), regression_diff - 0.5 / sigma_squared)\n\n        # compute the normalizer: the number of positive anchors\n        normalizer = tf.maximum(tf.shape(indices)[0], 1)\n        normalizer = tf.cast(normalizer, tf.float32)\n        return tf.reduce_sum(regression_loss) / normalizer / 4\n\n    return _smooth_l1","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:23:22.897759Z","iopub.status.busy":"2020-08-13T06:23:22.896974Z","iopub.status.idle":"2020-08-13T06:23:23.376609Z","shell.execute_reply":"2020-08-13T06:23:23.377671Z"},"papermill":{"duration":0.512717,"end_time":"2020-08-13T06:23:23.377832","exception":false,"start_time":"2020-08-13T06:23:22.865115","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# Load pretrained model\n\ndef load_model_weights(file_path=None):\n    initial_epoch, initial_learning_rate, initial_model = 0, 1e-3, None\n    file_paths = []\n    if file_path == None:\n        for dirname, _, filenames in os.walk('/kaggle/input'):\n            for filename in filenames:\n                if(filename.endswith('.h5')):\n                    file_paths.append(os.path.join(dirname, filename))\n    else:\n        file_paths.append(file_path)\n    for file_path in file_paths:\n        epoch, _, _, _, learning_rate = os.path.splitext(os.path.basename(file_path))[0].split('_')[1:]\n        epoch = int(epoch)\n        learning_rate = float(learning_rate)\n        if epoch >= initial_epoch:\n            initial_epoch, initial_learning_rate, initial_model = epoch, learning_rate, file_path\n    return initial_epoch, initial_learning_rate, initial_model\n\ninitial_epoch, initial_learning_rate, initial_model = load_model_weights()\nprint(initial_epoch, initial_learning_rate, initial_model)","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:23:23.428331Z","iopub.status.busy":"2020-08-13T06:23:23.427597Z","iopub.status.idle":"2020-08-13T06:23:33.578692Z","shell.execute_reply":"2020-08-13T06:23:33.579628Z"},"papermill":{"duration":10.179732,"end_time":"2020-08-13T06:23:33.579873","exception":false,"start_time":"2020-08-13T06:23:23.400141","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# Compile model\n\nwith strategy.scope():\n    model = Efficientdet(PHI, num_classes=NUM_CLASSES)\n    if initial_model is not None:\n        model.load_weights(initial_model, by_name=True, skip_mismatch=True)\n        print(\"Pretrained model loaded\")\n    else:\n        print(\"Pretrained model not found\")","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:23:33.634382Z","iopub.status.busy":"2020-08-13T06:23:33.633417Z","iopub.status.idle":"2020-08-13T06:23:33.636936Z","shell.execute_reply":"2020-08-13T06:23:33.636136Z"},"papermill":{"duration":0.035057,"end_time":"2020-08-13T06:23:33.637063","exception":false,"start_time":"2020-08-13T06:23:33.602006","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# Learning Rate\n\ndef cosine_annealing_with_warmup(epochIdx):\n    aMax, aMin = 5e-4, 6.25e-5\n    warmupEpochs, stagnateEpochs, cosAnnealingEpochs = 2, 0, 30\n    epochIdx = epochIdx % (warmupEpochs + stagnateEpochs + cosAnnealingEpochs)\n    if(epochIdx < warmupEpochs):\n        return aMin + (aMax - aMin) / (warmupEpochs - 1) * epochIdx\n    else:\n        epochIdx -= warmupEpochs\n    if(epochIdx < stagnateEpochs):\n        return aMax\n    else:\n        epochIdx -= stagnateEpochs\n    return aMin + 0.5 * (aMax - aMin) * (1 + math.cos((epochIdx + 1) / (cosAnnealingEpochs + 1) * math.pi))\n\n#lr_schedule = tf.keras.callbacks.ReduceLROnPlateau(monitor='loss', patience=2, factor=0.5, mode='auto')\n#lr_schedule = tf.keras.callbacks.LearningRateScheduler(tf.keras.experimental.CosineDecayRestarts(5e-5, 10), verbose=1)\nlr_schedule = tf.keras.callbacks.LearningRateScheduler(cosine_annealing_with_warmup, verbose=1)\n#lr_schedule = callbacks.ReduceLROnPlateau(monitor='loss', factor=0.5, patience=2, verbose=1)","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:23:33.686894Z","iopub.status.busy":"2020-08-13T06:23:33.686027Z","iopub.status.idle":"2020-08-13T06:23:33.689914Z","shell.execute_reply":"2020-08-13T06:23:33.688956Z"},"papermill":{"duration":0.031318,"end_time":"2020-08-13T06:23:33.690023","exception":false,"start_time":"2020-08-13T06:23:33.658705","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"class customized_callback(tf.keras.callbacks.Callback):\n    def on_epoch_end(self, epoch, logs={}):\n        loss, loss_classification, loss_regression  = model.evaluate(valid_dataset_encode, steps=VALID_STEPS_PER_EPOCH)\n        model.save_weights(\"model_{:.0f}_{:.4f}_{:.4f}_{:.4f}_{:.5f}.h5\".format(float(epoch + 1), loss, loss_classification, loss_regression, self.model.optimizer.learning_rate.numpy()))","execution_count":null,"outputs":[]},{"metadata":{"papermill":{"duration":0.022449,"end_time":"2020-08-13T06:23:33.734617","exception":false,"start_time":"2020-08-13T06:23:33.712168","status":"completed"},"tags":[]},"cell_type":"markdown","source":"It is slow to start training (Be patient for waiting 10 minutes).","execution_count":null},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:23:33.790001Z","iopub.status.busy":"2020-08-13T06:23:33.787972Z","iopub.status.idle":"2020-08-13T06:23:33.793418Z","shell.execute_reply":"2020-08-13T06:23:33.792574Z"},"papermill":{"duration":0.036708,"end_time":"2020-08-13T06:23:33.793579","exception":false,"start_time":"2020-08-13T06:23:33.756871","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"if (tpu):\n    Epoch = 32\n    callbacks_list = [lr_schedule, customized_callback()]\n    model.compile(loss={ 'regression' : smooth_l1(), 'classification': focal() }, optimizer=optimizers.Adam(initial_learning_rate)) #metrics=[mIOU]\n    model.fit(train_dataset_encode, epochs=Epoch, callbacks=callbacks_list, initial_epoch = initial_epoch, steps_per_epoch=TRAIN_STEPS_PER_EPOCH)\nelse:\n    print(\"Please, use TPU to train and turn on the internet\")","execution_count":null,"outputs":[]},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:23:33.859042Z","iopub.status.busy":"2020-08-13T06:23:33.852937Z","iopub.status.idle":"2020-08-13T06:23:33.862864Z","shell.execute_reply":"2020-08-13T06:23:33.862149Z"},"papermill":{"duration":0.046779,"end_time":"2020-08-13T06:23:33.862974","exception":false,"start_time":"2020-08-13T06:23:33.816195","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"def datasets_predict(batch_x, thresholds_comb=[[0.5, 0.5]], tta = False): # thresholds_comb => iou_threshold + score_threshold\n    if tta:\n        tta_parms = [{ \"flip_x_en\": False, \"flip_y_en\": False, \"rot90\": True }, \n                     { \"flip_x_en\": True, \"flip_y_en\": False, \"rot90\": False }, \n                     { \"flip_x_en\": False, \"flip_y_en\": True, \"rot90\": False }, \n                     { \"flip_x_en\": True, \"flip_y_en\": True, \"rot90\": False }]\n        batch_regression_list, batch_classification_list = [], []\n        for parms in tta_parms:\n            # Adjust image\n            batch_img = batch_x\n            if parms[\"flip_x_en\"]:\n                batch_img = np.flip(batch_img, axis=2)\n            if parms[\"flip_y_en\"]:\n                batch_img = np.flip(batch_img, axis=1)\n            if parms[\"rot90\"]:\n                batch_img = np.rot90(batch_img, 3, (1,2))\n            batch_regression, batch_classification = boxes_decode(*model.predict(batch_img))\n            # Adjust Regression\n            x1, y1, x2, y2 = batch_regression[:,:,0], batch_regression[:,:,1], batch_regression[:,:,2], batch_regression[:,:,3]\n            if parms[\"flip_x_en\"]:\n                x1, y1, x2, y2 = IMAGE_SIZE - x2, y1, IMAGE_SIZE - x1, y2\n            if parms[\"flip_y_en\"]:\n                x1, y1, x2, y2 = x1, IMAGE_SIZE - y2, x2, IMAGE_SIZE - y1\n            if parms[\"rot90\"]:\n                x1, y1, x2, y2 = y1, IMAGE_SIZE - x2, y2, IMAGE_SIZE - x1\n            batch_regression_list.append(np.stack([x1, y1, x2, y2], axis=-1))\n            batch_classification_list.append(batch_classification)\n        batch_regression_list = np.concatenate(batch_regression_list, axis=1)\n        batch_classification_list = np.concatenate(batch_classification_list, axis=1)\n        return [boxes_ensemble(batch_regression_list, batch_classification_list, iou_threshold=iou_thre, score_threshold=score_thre) for iou_thre, score_thre in thresholds_comb]\n    else:\n        batch_regression, batch_classification = boxes_decode(*model.predict(batch_x))\n        return [boxes_ensemble(batch_regression, batch_classification, iou_threshold=iou_thre, score_threshold=score_thre) for iou_thre, score_thre in thresholds_comb]","execution_count":null,"outputs":[]},{"metadata":{"papermill":{"duration":0.020976,"end_time":"2020-08-13T06:23:33.904628","exception":false,"start_time":"2020-08-13T06:23:33.883652","status":"completed"},"tags":[]},"cell_type":"markdown","source":"Test on validation datasets","execution_count":null},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:23:33.961592Z","iopub.status.busy":"2020-08-13T06:23:33.960091Z","iopub.status.idle":"2020-08-13T06:28:33.196793Z","shell.execute_reply":"2020-08-13T06:28:33.19731Z"},"papermill":{"duration":299.271395,"end_time":"2020-08-13T06:28:33.197506","exception":false,"start_time":"2020-08-13T06:23:33.926111","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"# Search for best mAP\niou_thresholds, score_thresholds = [0.2, 0.35, 0.5], [0.3, 0.5, 0.7]\nthresholds_comb = [(iou_threshold, score_threshold) for iou_threshold in iou_thresholds for score_threshold in score_thresholds]\n\nvalid_steps, mAP = min(VALID_STEPS_PER_EPOCH, 20), np.zeros((len(thresholds_comb)), np.float32)\nfor valid_step, (batch_x, (batch_regression, batch_classification)) in tqdm(enumerate(valid_dataset_encode)):\n    if (valid_step + 1) > valid_steps:\n        break\n    obj_info_true = datasets_decode(batch_regression, batch_classification, iou_threshold=0.99, score_threshold=1.)\n    obj_info_pred_list = datasets_predict(batch_x, thresholds_comb=thresholds_comb, tta = False)\n    for threshold_idx, obj_info_pred in enumerate(obj_info_pred_list):\n        mAP[threshold_idx] += metric_mAP(obj_info_true, obj_info_pred) / valid_steps\n        \nbest_mAP_idx = np.argmax(mAP)\nbest_mAP, (best_iou_threshold, best_score_threshold) = mAP[best_mAP_idx], thresholds_comb[best_mAP_idx]\nprint(\"Best mAP: {0:.4f} iou: {1} score : {2}\".format(best_mAP, best_iou_threshold, best_score_threshold))\n\n\nfor batch_x, _ in valid_dataset_encode:\n    obj_info_pred = datasets_predict(batch_x, thresholds_comb=thresholds_comb[best_mAP_idx:None], tta = False)[0]\n    visualize_decode(batch_x, obj_info_pred)\n    break","execution_count":null,"outputs":[]},{"metadata":{"papermill":{"duration":0.036329,"end_time":"2020-08-13T06:28:33.272368","exception":false,"start_time":"2020-08-13T06:28:33.236039","status":"completed"},"tags":[]},"cell_type":"markdown","source":"Test on test datasets (TTA)","execution_count":null},{"metadata":{"execution":{"iopub.execute_input":"2020-08-13T06:28:33.363705Z","iopub.status.busy":"2020-08-13T06:28:33.362932Z","iopub.status.idle":"2020-08-13T06:28:44.8826Z","shell.execute_reply":"2020-08-13T06:28:44.881972Z"},"papermill":{"duration":11.575261,"end_time":"2020-08-13T06:28:44.882727","exception":false,"start_time":"2020-08-13T06:28:33.307466","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"test_dir = '../input/global-wheat-detection/test/'\ntest_image_paths =  [os.path.join(test_dir, path) for path in os.listdir(test_dir)]\n\nclass batch_items:\n    def __init__(self, paths, batch_size):\n        self.group_paths = [paths[i : i + batch_size] for i in range(0, len(paths), batch_size)]\n        self.group_ids = [\"\" for i in range(batch_size)] # (Batch Size)\n        self.group_items = np.zeros((batch_size, IMAGE_SIZE, IMAGE_SIZE, 3), np.float32) # (Batch Size, H, W, 3)\n        self.group_idx = 0\n\n    def __iter__(self):\n        return self\n    \n    def __next__(self):\n        if(self.group_idx >= len(self.group_paths)):\n            raise StopIteration\n        paths = self.group_paths[self.group_idx]\n        for idx in range(len(paths)):\n            self.group_ids[idx] = os.path.basename(paths[idx]).split(\".\")[0]\n            self.group_items[idx] = cv2.resize(cv2.cvtColor(cv2.imread(paths[idx]), cv2.COLOR_BGR2RGB), (IMAGE_SIZE, IMAGE_SIZE)) / 255.0\n        self.group_idx += 1\n        return self.group_ids, self.group_items, len(paths)\n\nsumbit_res = []\nfor batch_idxs, batch_imgs, batch_size in batch_items(test_image_paths, BATCH_SIZE):\n    batch_obj_info = datasets_predict(batch_imgs, thresholds_comb=[[0.35, 0.5]], tta = True)[0]\n    #visualize_decode(tf.convert_to_tensor(batch_imgs), batch_obj_info)\n    for idx in range(batch_size):\n        for elem in batch_obj_info[idx]:\n            tx1, ty1, tx2, ty2, score, classes_idx = elem\n            tw, th = tx2 - tx1, ty2 - ty1\n            tx, ty = tx1 + tw / 2, ty1 + th / 2\n            sumbit_res.append({\n                'image_id': batch_idxs[idx],\n                'PredictionString': \"{0:.4f} {1} {2} {3} {4}\".format(score, int(tx), int(ty), int(tw), int(th))\n            })\n    \ntest_df = pd.DataFrame(sumbit_res, columns=['image_id', 'PredictionString'])\ntest_df.to_csv('submission.csv', index=False)\ntest_df.head()","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}