{"cells":[{"metadata":{"_kg_hide-output":false,"trusted":true},"cell_type":"code","source":"!pip install timm effdet","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"EVAL_CKPTS = True\n\nCKPTS_v = [\n    '../input/vinbigdata-effdet-d2-f0f2-ckpts/F1_E79_ModelX_v4_T0.325_V0.410.ckpt', \n    '../input/vinbigdata-effdet-d2-f0f2-ckpts/F2_E82_ModelX_v4_T0.321_V0.409.ckpt',\n]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import os, sys\nimport glob\nimport pickle\nfrom collections import OrderedDict, namedtuple, deque\nfrom copy import deepcopy\nimport colorsys\n\n\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nimport h5py\nfrom tqdm import tqdm\n\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport cv2\nfrom PIL import Image\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\n\n\nimport torch\nfrom torch import nn, optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.utils.data.sampler import SequentialSampler, RandomSampler\n\nfrom effdet import get_efficientdet_config, EfficientDet, DetBenchTrain, DetBenchPredict","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Data directories list\nDS_DIR_v = [\n    \"../input/vinbigdata-chest-xray-abnormalities-detection\"]\n\nfor DS_DIR in DS_DIR_v:\n    if os.path.exists(DS_DIR):\n        print(f' DS_DIR Found: \"{DS_DIR}\"')\n        break\nelse:\n    raise Exception(' Dataset not found.')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"DS_PATH = '../input/train-test-ds-bbox-cache'","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":false},"cell_type":"code","source":"# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n# Validation Functions \n# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #\n\n\nclass FastSMA():\n    def __init__(\n        self,\n        iterator=None,\n        maxlen=1000,\n        label='mean = ',\n        print_format='0.02f',\n        save_filename='loss_trn.fsma'):\n        \n        \n        assert type(maxlen) == int and maxlen > 0, 'ERROR length must be a positive int.'\n        self.maxlen = maxlen\n        self.label = label\n        self.print_format = print_format\n        self.save_filename = save_filename\n        self.clear()\n\n        if iterator is not None:\n            if type(next(iter(iterator))) in [list, tuple, np.ndarray]:\n                for i in iterator:\n                    self.append(*i)\n            else:\n                for i in iterator:\n                    self.append(i)\n            \n        return None\n\n    def append(self, v, i_step=None):        \n        self.cumsum += v\n        \n        if i_step is None:\n            i_step = self.step_hist_v[-1] + 1 if len(self.step_hist_v)  else 0\n        \n        if len(self.history) == self.maxlen:\n            self.cumsum -= self.history[0]\n            self.last_mean = self.cumsum / self.maxlen\n        else:\n            self.last_mean = self.cumsum / (len(self.history) + 1) \n\n        self.history.append( v )\n\n        self.sma_hist_v.append(self.last_mean)\n        self.val_hist_v.append(v)\n        self.step_hist_v.append( i_step )\n            \n        return None\n\n    def mean(self):\n        return self.last_mean\n\n    def clear(self):\n        self.history = deque(maxlen=self.maxlen)\n        self.cumsum = 0.0\n        self.last_mean = 0.0\n        self.sma_hist_v = []\n        self.val_hist_v = []\n        self.step_hist_v = []\n        \n        \n        self.key2save_v = [\n            'history',\n            'cumsum',\n            'last_mean',\n            'sma_hist_v',\n            'val_hist_v',\n            'step_hist_v',\n        ]\n        return None\n        \n    def get_sma_history(self):\n        return np.array(self.step_hist_v), np.array(self.sma_hist_v)\n    \n    def get_val_history(self):\n        return np.array(self.step_hist_v), np.array(self.val_hist_v)\n\n    def __str__(self):\n        return ('{}{:'+self.print_format+'}').format(self.label, self.mean())\n\n    def __repr__(self):\n        return self.__str__()\n    \n    \n    def save(self, filename=None):\n        if filename is None:\n            filename = self.save_filename\n            \n        to_save_d = {}\n        for key2save in self.key2save_v:\n            to_save_d[key2save] = deepcopy( getattr(self, key2save) )\n        \n        with open(filename, 'wb') as f:\n            f.write( pickle.dumps(to_save_d) )\n                \n        print(f' Saved complete: \"{filename}\"')\n            \n        return None\n\n    def load(self, filename=None):\n        if filename is None:\n            filename = self.save_filename\n            \n        with open(filename, 'rb') as f:\n            data = f.read()\n            \n        to_save_d = pickle.loads(data)\n        \n        \n        for k, v in to_save_d.items():\n            if k in self.key2save_v:\n                setattr(self, k, v)\n\n\n        self.maxlen = self.history.maxlen\n        print(f' Restored: \"{filename}\"')\n        \n        return self\n    \n    \n    def plot(self, label=None, do_show=False):\n        x, y = self.get_sma_history()\n        plt.plot(x, y, label=label)\n        plt.grid()\n        \n        if do_show:\n            plt.show()\n        \n        return None\n\n\n    def plot_sma(self, label='', step=500):\n        if label == '':\n            label = os.path.split( self.save_filename )[-1].replace('.fsma', '')\n            \n        step_v, loss_v = self.get_val_history()\n        \n        m = loss_v.shape[0] % step\n        loss_v = loss_v[m:].reshape(-1, step).mean(axis=-1)\n        step_v = step_v[m:].reshape(-1, step)[:,-1]\n\n        plt.plot(step_v, loss_v, label=label)\n\n        return None\n    \n\ndef calc_iou(bb0, bb1):\n    if (len(bb0.shape) == 2):\n        bb0 = bb0.T\n        \n    if (len(bb1.shape) == 2):\n        bb1 = bb1.T\n        \n\n    bb0_x0, bb0_y0, bb0_x1, bb0_y1 = bb0\n    bb1_x0, bb1_y0, bb1_x1, bb1_y1 = bb1\n    \n    assert (bb0_x0 < bb0_x1).all()\n    assert (bb0_y0 < bb0_y1).all()\n    assert (bb1_x0 < bb1_x1).all()\n    assert (bb1_y0 < bb1_y1).all()\n\n    # determine the coordinates of the intersection rectangle\n    x_left   = np.maximum(bb0_x0, bb1_x0)\n    y_top    = np.maximum(bb0_y0, bb1_y0)\n    x_right  = np.minimum(bb0_x1, bb1_x1)\n    y_bottom = np.minimum(bb0_y1, bb1_y1)\n\n#     if (x_right < x_left).all(axis=0) or (y_bottom < y_top).all(axis=0):\n#         return np.zeros( out_dim )\n    \n    ret_mask = ~( (x_right < x_left) + (y_bottom < y_top) )\n\n    # The intersection of two axis-aligned bounding boxes is always an\n    # axis-aligned bounding box\n    intersection_area = (x_right - x_left) * (y_bottom - y_top)\n\n    # compute the area of both AABBs\n    bb0_area = (bb0_x1 - bb0_x0) * (bb0_y1 - bb0_y0)\n    bb1_area = (bb1_x1 - bb1_x0) * (bb1_y1 - bb1_y0)\n    \n    iou = intersection_area / (bb0_area + bb1_area - intersection_area)\n    \n    \n    return iou * ret_mask\n\n\ndef join_preds(bbox_v, p_det_v=None, mode='p_det_weight'):\n    \n    if p_det_v is None:\n        p_det_v = np.ones(bbox_v.shape[0])\n        \n    if mode == 'p_det_weight':\n        p_v = ( p_det_v / p_det_v.sum() )[:,None]\n        \n        bbox = (bbox_v * p_v).sum(axis=0)\n        p = p_det_v.mean()\n        \n    elif mode == 'p_det_max':\n        i_max = p_det_v.argmax()\n        \n        bbox = bbox_v[i_max]\n        p    = p_det_v[i_max]\n    \n    elif mode == 'random':\n        i_max = np.random.randint(0, p_det_v.shape[0])\n        \n        bbox = bbox_v[i_max]\n        p    = p_det_v[i_max]\n        \n    else:\n        raise Exception(f'Unknown mode \"{mode}\"')\n        \n        \n    return bbox, p\n    \n\ndef clean_predictions(preds_v, iou_th=0.1, mode='p_det_weight', consensus_level=1):\n    ret_preds_v = []\n    for pred_d in preds_v:\n        \n        cls_v = pred_d['cls']\n        \n        if 'bbox' in pred_d.keys():\n            bbox_key = 'bbox'\n        else:\n            bbox_key = 'bboxes'\n            \n        bbox_v = pred_d[bbox_key]\n        \n        if 'p_det' in pred_d.keys():\n            ret_p_det = True\n            p_det_v = pred_d['p_det']\n        else:\n            ret_p_det = False\n            p_det_v = np.ones(pred_d['cls'].shape)\n        \n        \n        if 'rad_id' in pred_d.keys():\n            ret_rad_id = True\n            rad_id_v = pred_d['rad_id']\n        else:\n            ret_rad_id = False\n            \n            \n        new_cls_v = []\n        new_bbox_v = []\n        new_p_det_v = []\n        new_rad_id_v = []\n        for i_c in np.unique(cls_v):\n            f_c = (cls_v == i_c)\n            \n            n_c = f_c.sum()\n            if n_c == 1:\n                if consensus_level > 1 and i_c != -1:\n                    continue\n                    \n                    \n                if ret_rad_id:\n                    if i_c == -1:\n                        n_rads = rad_id_v.size\n                        \n                        if n_rads < consensus_level:\n                            continue\n                            \n                        else:\n                            if n_rads > 1:\n                                new_rad_id_v.append( np.concatenate(rad_id_v, axis=-1) )\n                            else:\n                                new_rad_id_v.append( rad_id_v[f_c][0] )\n                    else:\n                        new_rad_id_v.append( rad_id_v[f_c][0] )\n                    \n\n                new_cls_v.append( i_c )\n                new_bbox_v.append( bbox_v[f_c][0] )\n                new_p_det_v.append( p_det_v[f_c][0] )\n                \n                \n                \n            else:\n                f_cls_v = cls_v[f_c]\n                f_bbox_v = bbox_v[f_c]\n                f_p_det_v = p_det_v[f_c]\n                if ret_rad_id:\n                    f_rad_id_v = rad_id_v[f_c]\n                    \n                to_join_idxs_v = []\n                for i in range(0, n_c):\n                    idxs_s = set( np.argwhere( calc_iou(f_bbox_v[i], f_bbox_v) > iou_th ).T[0] )\n                    \n#                     print(idxs_s)\n                    for i in range(len(to_join_idxs_v)):\n                        if len( idxs_s.intersection(to_join_idxs_v[i]) ) > 0:\n                            to_join_idxs_v[i] = to_join_idxs_v[i].union(idxs_s)\n                            break\n                            \n                    else:\n                        to_join_idxs_v.append(idxs_s)\n                    \n                for to_join_idxs in to_join_idxs_v:\n                    to_join_idxs = list(to_join_idxs)\n                    \n                    if len(to_join_idxs) < consensus_level:\n                        continue\n                        \n                    bbox, p_det = join_preds(\n                        f_bbox_v[to_join_idxs],\n                        f_p_det_v[to_join_idxs],\n                        mode=mode,\n                    )\n                    \n                    new_cls_v.append( i_c )\n                    new_bbox_v.append( bbox )\n                    new_p_det_v.append( p_det )\n                    \n                    if ret_rad_id:\n                        new_rad_id_v.append( np.concatenate(f_rad_id_v[to_join_idxs], axis=-1))\n        \n        ret_preds_d = {\n            'cls': np.array(new_cls_v),\n            bbox_key: np.array(new_bbox_v),\n        }\n        \n        if ret_p_det:\n            ret_preds_d['p_det'] = np.array(new_p_det_v)\n            \n        if ret_rad_id:\n            ret_preds_d['rad_id'] = np.array(new_rad_id_v)\n            \n        for k in pred_d.keys():\n            if k not in ['cls', bbox_key, 'p_det', 'rad_id']:\n                ret_preds_d[k] = pred_d[k]\n        \n        ret_preds_v.append(ret_preds_d)\n    \n    return ret_preds_v\n\n\ndef evalueate_dataset(\n    ds,\n    model,\n    det_th=0.25,\n    unscale_bboxes=True,\n    batch_size=16,\n    num_workers=8,\n    pin_memory=True,\n    do_clean_predictions=True,\n    clean_iou_th=0.10,\n    clean_mode='p_det_weight',\n):\n    \n    ds_iter = tqdm(DataLoader(\n        ds,\n        batch_size=batch_size,\n        shuffle=False,\n        num_workers=num_workers,\n        pin_memory=pin_memory,\n        collate_fn=batch_merge))\n    \n    \n    ret_preds_v = []\n    for data in ds_iter:\n        pred_v = model.predict(\n            data,\n            det_th=det_th,\n            unscale_bboxes=unscale_bboxes)\n        \n        if do_clean_predictions:\n            pred_v =  clean_predictions(\n                pred_v,\n                iou_th=clean_iou_th,\n                mode=clean_mode)\n            \n        for i_s, pred_d in enumerate(pred_v):\n            pred_d['sample_id']      = data['sample_id'][i_s]\n            pred_d['original_shape'] = data['original_shape'][i_s]\n            \n            \n        ret_preds_v.extend(pred_v)\n\n    return ret_preds_v\n    \n    \n    \ndef pred_to_str(pred_d):\n    cls_v = pred_d['cls']\n    bbox_v = pred_d['bbox']\n    p_det_v = pred_d['p_det']\n    \n    if len(cls_v) == 0:\n        ret_s = '14 1 0 0 1 1'\n    \n    else:\n        s_v = []\n        for cls, p_det, bbox in zip(cls_v.astype(np.int), p_det_v, np.round(bbox_v).astype(np.int)):\n            s = '{} {:0.05} {} {} {} {}'.format(\n                int(cls),\n                p_det,\n                *bbox\n            )\n            \n            s_v.append(s)\n            \n        ret_s = ' '.join(s_v)\n    \n    return ret_s\n\n\ndef predictions_to_df(\n    preds_v,\n    save_path=None,\n):\n    pred_summary_d = {\n        'image_id':[],\n        'PredictionString':[]\n    }\n    \n    for pred_d in preds_v:\n        pred_str = pred_to_str(pred_d)\n        pred_summary_d['image_id'].append( pred_d['sample_id'] )\n        pred_summary_d['PredictionString'].append( pred_str )\n        \n    pred_summary_df = pd.DataFrame(pred_summary_d)\n    \n    if save_path is not None:\n        pred_summary_df.to_csv(\n            save_path,\n            index=None)\n        \n        print(f' Saved submission: \"{save_path}\"')\n        \n    return pred_summary_df\n\n\n\n\ndef voc_ap(recall, precision, use_07_metric=False):\n    \"\"\"\n    Reference:\n        https://github.com/wang-tf/pascal_voc_tools/blob/master/pascal_voc_tools/Evaluater/tools.py\n    \n    ap = voc_ap(recall, precision, [use_07_metric])\n    Compute VOC AP given precision and recall.\n    If use_07_metric is true, uses  the\n    VOC 07 11 point method (default: False).\n    Please make shure that recall and precison are sorted by scores.\n    Args:\n        recall: the shape of (n,) ndarray;\n        precision: the shape of (n,) ndarray;\n        use_07_metric: if true, the 11 points method will be used.\n    Returns:\n        the float number result of average precision.\n    \"\"\"\n    if use_07_metric:\n        # 11 point metric\n        ap = 0.\n        for t in np.arange(0., 1.1, 0.1):\n            if np.sum(recall >= t) == 0:\n                p = 0\n            else:\n                p = np.max(precision[recall >= t])\n            ap = ap + p / 11.\n    else:\n        # correct AP calculation\n        # first append sentinel values at the end\n        mrec = np.concatenate(([0.], recall, [1.]))\n        mpre = np.concatenate(([0.], precision, [0.]))\n\n        # compute the precision envelope\n        for i in range(mpre.size - 1, 0, -1):\n            mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i])\n\n        # to calculate area under PR curve, look for points\n        # where X axis (recall) changes value\n        i = np.where(mrec[1:] != mrec[:-1])[0]\n\n        # and sum (\\Delta recall) * prec\n        ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1])\n    return ap\n\n\ndef compute_overlaps(boxes, one_box):\n    \"\"\"\n    Reference:\n        https://github.com/wang-tf/pascal_voc_tools/blob/master/pascal_voc_tools/Evaluater/tools.py\n        \n    iou = compute_overlaps(boxes, one_box)\n    compute intersection over union of ndarray.\n    The format of one_box is [xmin, ymin, xmax, ymax].\n    Args:\n        boxes: the (n, 4) shape ndarray, ground truth boundboxes;\n        bb: the (4,) shape ndarray, detected boundboxes;\n    Returns:\n        a (n, ) shape ndarray.\n    \"\"\"\n    # compute overlaps\n    # intersection\n    ixmin = np.maximum(boxes[:, 0], one_box[0])\n    iymin = np.maximum(boxes[:, 1], one_box[1])\n    ixmax = np.minimum(boxes[:, 2], one_box[2])\n    iymax = np.minimum(boxes[:, 3], one_box[3])\n    iw = np.maximum(ixmax - ixmin + 1., 0.)\n    ih = np.maximum(iymax - iymin + 1., 0.)\n    inters = iw * ih\n\n    # union\n    boxes_area = (boxes[:, 2] - boxes[:, 0] + 1.) * (boxes[:, 3] -\n                                                     boxes[:, 1] + 1.)\n    one_box_area = (one_box[2] - one_box[0] + 1.) * (one_box[3] - one_box[1] +\n                                                     1.)\n    iou = inters / (one_box_area + boxes_area - inters)\n\n    return iou\n\n\ndef voc_eval(class_recs: dict,\n             detect: dict,\n             iou_thresh: float = 0.5,\n             use_07_metric: bool = False):\n    \"\"\"\n    Reference:\n        https://github.com/wang-tf/pascal_voc_tools/blob/master/pascal_voc_tools/Evaluater/tools.py\n        \n    recall, precision, ap = voc_eval(class_recs, detection,\n                                [iou_thresh],\n                                [use_07_metric])\n    Top level function that does the PASCAL VOC evaluation.\n    Please make sure that the class_recs only have one class annotations.\n    precision = tp / (tp + fp)\n    recall = tp / (tp + fn)\n    Args:\n        class_recalls: recalls dict of a class\n            class_recs[image_name]={'bbox': []}.\n        detection: Path to annotations\n            detection={'image_ids':[], bbox': [], 'confidence':[]}.\n        [iou_thresh]: Overlap threshold (default = 0.5)\n        [use_07_metric]: Whether to use VOC07's 11 point AP computation\n            (default False)\n    Returns:\n        a dict of result including true_positive_number, false_positive_number,\n        recall, precision and average_precision.\n    Raises:\n        TypeError: the data format is not np.ndarray.\n    \"\"\"\n    # format data\n    # class_rec data load\n    npos = 0\n    for imagename in class_recs.keys():\n        if not isinstance(class_recs[imagename]['bbox'], np.ndarray):\n            raise TypeError\n        detected_num = class_recs[imagename]['bbox'].shape[0]\n        npos += detected_num\n        class_recs[imagename]['det'] = [False] * detected_num\n\n    # detections data load\n    image_ids = detect['image_ids']\n    confidence = detect['confidence']\n    BB = detect['bbox']\n    if not isinstance(confidence, np.ndarray):\n        raise TypeError\n    if not isinstance(BB, np.ndarray):\n        raise TypeError\n    \n    # sort by confidence\n    sorted_ind = np.argsort(-confidence)\n    BB = BB[sorted_ind]\n    image_ids = [image_ids[x] for x in sorted_ind]\n\n    # go down dets and mark TPs and FPs\n    nd = len(image_ids)\n    tp = np.zeros(nd)\n    fp = np.zeros(nd)\n    for d in range(nd):\n        R = class_recs[image_ids[d]]\n        bb = BB[d, :].astype(float)\n        iou_max = -np.inf\n        BBGT = R['bbox'].astype(float)\n\n        if BBGT.size > 0:\n            overlaps = compute_overlaps(BBGT, bb)\n            iou_max = np.max(overlaps)\n            iou_max_index = np.argmax(overlaps)\n\n        if iou_max > iou_thresh:\n            if not R['det'][iou_max_index]:\n                tp[d] = 1.\n                R['det'][iou_max_index] = 1\n            else:\n                fp[d] = 1.\n        else:\n            fp[d] = 1.\n\n    # compute precision recall\n    fp = np.cumsum(fp)\n    tp = np.cumsum(tp)\n    true_positive_number = tp[-1] if len(tp) > 0 else 0\n    false_positive_number = fp[-1] if len(fp) > 0 else 0\n\n    recall = tp / np.maximum(float(npos), np.finfo(np.float64).eps)\n    # avoid divide by zero in case the first detection matches a difficult ground truth\n    precision = tp / np.maximum(tp + fp, np.finfo(np.float64).eps)\n    average_precision = voc_ap(recall, precision, use_07_metric)\n\n    result = {}\n    result['true_positive_number'] = true_positive_number\n    result['false_positive_number'] = false_positive_number\n    result['positive_number'] = npos\n    result['recall'] = recall\n    result['precision'] = precision\n    result['average_precision'] = average_precision\n    return result\n\n\n\ndef make_class_detection_d(preds_v, n_classes=15):\n    detection_v = [ {\n        'image_ids': [],\n        'bbox': [],\n        'confidence': [],\n    } for i in range(n_classes)]\n    \n    \n    for pred_d in preds_v:\n        sample_id = pred_d['sample_id']\n        if len(pred_d['bbox']) > 0:\n            for bbox, p_det, cls in zip(pred_d['bbox'], pred_d['p_det'], pred_d['cls']):\n                detection_v[cls]['image_ids'].append(sample_id) \n                detection_v[cls]['bbox'].append(bbox) \n                detection_v[cls]['confidence'].append(p_det) \n        else:\n            bbox = np.array([0.0, 0.0, 1.0, 1.0])\n            p_det = 1.0\n            cls = n_classes-1\n            \n            detection_v[cls]['image_ids'].append(sample_id) \n            detection_v[cls]['bbox'].append(bbox) \n            detection_v[cls]['confidence'].append(p_det) \n            \n            \n    for cls in range(len(detection_v)):\n        for k in detection_v[cls].keys():            \n            detection_v[cls][k] = np.array(detection_v[cls][k])\n    \n    return detection_v\n\n\ndef make_class_gt_d(gt_df, n_classes=15):\n    \n    class_gt_v = []\n    for class_id in range(n_classes):\n        class_gt_d = {}\n        for k in gt_df.image_id.unique():\n            class_gt_d[k] = {'bbox': []}\n        \n        class_gt_v.append( class_gt_d )\n    \n    for image_id, class_id, x_min, y_min, x_max, y_max in gt_df[['image_id', 'class_id', 'x_min', 'y_min', 'x_max', 'y_max']].values:\n        bbox = (x_min, y_min, x_max, y_max)\n        \n        class_gt_v[class_id][image_id]['bbox'].append(bbox)\n        \n    for cls in range(len(class_gt_v)):\n        for k in class_gt_v[cls].keys():\n            class_gt_v[cls][k]['bbox'] = np.array(class_gt_v[cls][k]['bbox'])\n            \n    return class_gt_v\n        \n\n        \ndef calc_metrics(preds_v, gt_df, iou_thresh=0.4, show_summay=True, n_classes=15, use_07_metric=False):\n    class_recs_v = make_class_gt_d(gt_df, n_classes=n_classes)\n    detect_v     = make_class_detection_d(preds_v, n_classes=n_classes)\n    \n    mAP = 0.0\n    metrics_v = []\n    for i_class in range(n_classes):\n        class_val_d = voc_eval(\n            class_recs_v[i_class],\n             detect_v[i_class],\n             iou_thresh=iou_thresh,\n             use_07_metric=use_07_metric)\n        \n        metrics_v.append(class_val_d)\n        \n        mAP += class_val_d['average_precision']\n    \n    mAP /= n_classes\n    \n    summary_df = pd.DataFrame(\n        columns=['Cls', 'TP', 'FP', 'P', 'Prec', 'Rec', 'AP']\n    )\n    for i_c, m_d in enumerate(metrics_v):\n        summary_df = summary_df.append({\n            'Cls': i_c,\n            'TP': m_d['true_positive_number'],\n            'FP': m_d['false_positive_number'],\n            'P': m_d['positive_number'],\n            'Prec': m_d['precision'][-1] if len(m_d['precision']) > 0 else 0.0,\n            'Rec': m_d['recall'][-1] if len(m_d['recall']) > 0 else 0.0,\n            'AP': m_d['average_precision'],\n        },\n        ignore_index=True,)\n        \n    summary_df = summary_df.astype({\n            'Cls': np.int,\n            'TP': np.int,\n            'FP': np.int,\n            'P': np.int,\n            'Prec': np.float32,\n            'Rec': np.float32,\n            'AP': np.float32,\n        })\n\n    summary_df = summary_df.set_index( 'Cls' )\n\n    if show_summay:\n        print(' Summary:')\n        print( summary_df )\n        print(f'mAP = {mAP:0.04f}')\n    \n    return metrics_v, summary_df, mAP\n\n\n\n\ndef plot_PvsR_curve(metrics_v, class2color_v):\n    \n    plt.figure(0, figsize=(20,10))\n    for i_c, metrics_d in enumerate(metrics_v):\n        plt.plot(\n            metrics_d['recall'],\n            metrics_d['precision'],\n            label=f'cls={i_c}',\n            c=np.array(class2color_v[i_c])/255 )\n\n\n    plt.xlabel('recall')\n    plt.ylabel('precision')\n\n    plt.xlim( (0,1) )\n    plt.ylim( (0,1) )\n\n    plt.legend()\n    plt.show()\n    \n    return None\n    \n    \n    \ndef filter_det_th(preds_v, det_th=0.15):\n    new_pred_v = []\n    for pred_d in preds_v:\n        \n        if len(pred_d['p_det']) > 0:\n            if type(det_th) is float:\n                det_th_v = det_th\n            else:\n                det_th_v = np.zeros(pred_d['p_det'].shape[0])\n\n                for i_c, i_cls in enumerate(pred_d['cls']):\n                    det_th_v[i_c] = det_th[i_cls]\n                    \n            f = pred_d['p_det'] >= det_th_v\n        else:\n            f = None\n            \n        new_pred_d = {}\n        for k in pred_d.keys():\n            if (f is not None) and (k in ['cls', 'p_det', 'bbox']):\n                new_pred_d[k] = pred_d[k][f]\n                \n            else:\n                new_pred_d[k] = pred_d[k]\n        \n        new_pred_v.append(new_pred_d)\n        \n    return new_pred_v","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class2str_v = [\n    'Aortic enlargement',\n    'Atelectasis',\n    'Calcification',\n    'Cardiomegaly',\n    'Consolidation',\n    'ILD',\n    'Infiltration',\n    'Lung Opacity',\n    'Nodule/Mass',\n    'Other lesion',\n    'Pleural effusion',\n    'Pleural thickening',\n    'Pneumothorax',\n    'Pulmonary fibrosis',\n    'Nothing',\n]\n\n\nclass2color_v = [\n    tuple(round(i * 255) for i in colorsys.hsv_to_rgb(i_c/len(class2str_v), 1, 1))\n    for i_c in range(len(class2str_v))\n]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def save_obj(obj, filename):\n    with open(filename, 'wb') as f:\n        pickle.dump(obj, f)\n    print(f'Saved: {filename}')\n    return None\n\ndef load_obj(filename):\n    with open(filename, 'rb') as f:\n        obj = pickle.load(f)\n    print(f'Loaded: {filename}')\n    return obj","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def read_dicom_image(\n    path,\n    voi_lut=True,\n    fix_monochrome=True,\n    do_norm=True):\n\n    # Original from: https://www.kaggle.com/raddar/convert-dicom-to-np-array-the-correct-way\n    dicom = pydicom.read_file(path)\n\n    # VOI LUT (if available by DICOM device) is used to transform raw DICOM data to \n    # \"human-friendly\" view\n    if voi_lut:\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n    else:\n        data = dicom.pixel_array\n\n    # depending on this value, X-ray may look inverted - fix that:\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.max(data) - data\n\n    if do_norm:\n        max_value = (2 ** dicom.BitsStored) - 1\n        data = data / max_value\n\n        assert (data.max() <= 1.0) and (data.min() >= 0.0), f'Normalization ERROR in file: \"{path}\"'\n\n#     if do_norm:\n#         max_val = np.max(data)\n#         min_val = np.min(data)\n#         data = (data - min_val) / (max_val- min_val)\n\n    return data.astype(np.float32)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def calc_image_features(image):\n\n    img_exp  = image ** 3.0\n    img_uint = (255 * image).astype(np.uint8)\n    img_equ  = cv2.equalizeHist(img_uint)\n    img_edge = cv2.Canny(img_equ, 50, 130)\n\n    img_ret = np.concatenate(\n        [\n            image[:,:,None],\n            img_exp[:,:,None],\n            img_equ[:,:,None].astype(np.float32)  / 255,\n            img_edge[:,:,None].astype(np.float32) / 255,\n            ],\n        axis=-1)\n\n    return img_ret","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class FoldDataset():\n    def __init__(\n        self,\n        ds_path='./',\n        ds_name='test',\n        images_dir='./test_images',\n        \n        model_resolution=(512, 512),\n        df_path=None,\n        \n        mode='none',\n        \n        i_fold=0,\n        n_folds=5,\n        test_split=0.1,\n        \n        do_augmentation=True,\n        \n        downsample_factor=2,\n        remove_classes_v=None,\n        select_classes_v=None,\n        show_warnings=True,\n        \n        do_random_shuffle=True,\n        random_seed=3128,\n        sample_bbox_and_class=False,\n        clean_boxes=True,\n        clean_mode='random',\n        clean_iou_th=0.1,\n        \n        use_img_cache=False,\n    ):\n        \n        \n        self.ds_path = ds_path\n        self.ds_name = ds_name\n        \n        self.model_resolution = model_resolution\n        self.bboxes_df_path   = df_path\n        self.i_fold           = i_fold\n        self.n_folds          = n_folds\n        self.do_augmentation  = do_augmentation\n        \n        self.images_dir        = images_dir\n        self.downsample_factor = downsample_factor\n        self.remove_classes_v  = remove_classes_v\n        self.select_classes_v  = select_classes_v\n        self.show_warnings     = show_warnings\n        self.test_split        = test_split\n        \n        self.do_random_shuffle = do_random_shuffle\n        self.random_seed       = random_seed\n        \n        self.sample_bbox_and_class = sample_bbox_and_class\n        \n        self.clean_boxes = clean_boxes\n        self.clean_mode = clean_mode\n        self.clean_iou_th = clean_iou_th\n        \n        \n        self.use_img_cache = use_img_cache\n        \n        \n        if (self.select_classes_v is not None) and (14 in self.select_classes_v):\n            self.select_classes_v.remove(14)\n            self.select_classes_v.append(-1)\n                    \n        \n        self.mode = mode.lower()\n        posible_modes_v = ['cv_trn', 'cv_val', 'cv_tst', 'none']\n        assert self.mode in posible_modes_v, f'Parameter \"mode\" must be in: {posible_modes_v}'\n        \n        if mode == 'cv_trn':\n            self.do_CV  = True\n            self.iter_trn = True\n            self.iter_val = False\n            self.iter_tst = False\n            \n        elif mode == 'cv_val':\n            self.do_CV  = True\n            self.iter_trn = False\n            self.iter_val = True\n            self.iter_tst = False\n            \n        elif mode == 'cv_tst':\n            self.do_CV  = True\n            self.iter_trn = False\n            self.iter_val = False\n            self.iter_tst = True\n        \n        elif mode == 'none':\n            self.do_CV  = False\n            self.iter_trn = False\n            self.iter_val = False\n            self.iter_tst = False\n        \n        \n        \n        self.h5_path = os.path.join(self.ds_path, f'{self.ds_name}.h5df')\n        self.misc_path = os.path.join(self.ds_path, f'{self.ds_name}.pickle')\n        self.cache_path = os.path.join(self.ds_path, f'{self.ds_name}.cache')\n        \n        self.f_h5 = None\n        \n        if self.use_img_cache:\n            if not os.path.exists(self.h5_path) or not os.path.exists(self.misc_path):\n                self._gen_images_ds()\n\n            self.open_h5_file()\n            \n        self.read_misc_d()\n        \n        self.bbox_df = None\n        \n        self._set_all_samples_ids()\n        \n        self._build_albumentations()\n        \n        if self.bboxes_df_path is not None:\n            if not os.path.exists(self.cache_path):\n                self.read_bboxes_df()\n                self._save_ds_state()\n                \n            else:\n                self._load_ds_state()\n                \n            \n        self.update_fold_filter()\n        \n        return None\n    \n    def _save_ds_state(self):    \n        to_save_d = {\n            'bbox_df': self.bbox_df,\n            'class_to_sample_v': self.class_to_sample_v,\n            'bbox_d': self.bbox_d,\n        }\n        \n        save_obj(to_save_d, self.cache_path)\n        \n        return None\n    \n    \n    def _load_ds_state(self):\n        state_d = load_obj( self.cache_path )\n        \n        for k, v in state_d.items():\n            self.__setattr__(k, v)\n        \n        return None\n    \n    def _set_all_samples_ids(self):\n        self.all_sample_ids = np.array( sorted( self.misc_d.keys() ) )\n        \n        if self.bbox_df is not None:\n            assert ( self.all_sample_ids == np.array(sorted(self.bbox_df['image_id'].unique()) ) ).all()\n        \n        \n        if self.do_random_shuffle:\n            np.random.seed(self.random_seed)\n            \n        return None\n    \n    \n    def _build_bbox_d(self):\n        self.bbox_d  = {}\n        self.class_to_sample_v = [ [] for i in range( self.bbox_df.class_id.max() + 1)]\n\n        sample_it = tqdm(self.all_sample_ids, desc='Building bboxes')\n        for s_id in sample_it:\n            sample_bboxes_df = self.bbox_df[self.bbox_df.image_id == s_id]\n\n            cls = sample_bboxes_df['class_id'].values\n            bboxes = sample_bboxes_df[['x_min', 'y_min', 'x_max', 'y_max']].values\n            rad_id = sample_bboxes_df[ ['rad_id'] ].values\n            \n            for i_c in np.unique(cls):\n                self.class_to_sample_v[i_c].append( s_id )\n                \n            # Class 14 filter\n            f_14 = (cls==14)\n            if f_14.any():\n                i_14 = np.argmax(f_14)\n                \n                orig_img_h, orig_img_w = self.misc_d[s_id]\n                bboxes[i_14] = np.array([0, 0, orig_img_w, orig_img_h])\n                cls[i_14]    = -1  # if I add 1 will be the class  0 (backfround for effdet)\n                \n                f_14 = ~f_14\n                f_14[i_14] = True\n                bboxes = bboxes[f_14]\n                cls    = cls[f_14]\n                \n            \n            self.bbox_d[s_id] = {\n                'bboxes': bboxes,\n                'cls':    cls,\n                'rad_id': rad_id,\n            }\n\n        for i_c in range(len(self.class_to_sample_v)):\n            self.class_to_sample_v[i_c] = np.array(self.class_to_sample_v[i_c])\n\n        return None\n\n\n\n    def read_bboxes_df(self):\n        self.bbox_df = pd.read_csv(\n            self.bboxes_df_path,\n            dtype={\n                'x_min':np.float32,\n                'y_min':np.float32,\n                'x_max':np.float32,\n                'y_max':np.float32,\n            })\n        \n#         self.bbox_df.fillna(0s.0)\n        \n        self._build_bbox_d()\n        return None\n        \n        \n    def update_fold_filter(self):\n        assert self.i_fold >= 0 and self.i_fold < self.n_folds, 'ERROR, incorrect i_fold.'\n        \n        # Apply sample class filtering\n        if self.select_classes_v is None:\n            self.selected_sample_ids = self.all_sample_ids\n            \n        else:\n            self.selected_sample_ids = set()\n            for i_c in self.select_classes_v:\n                self.selected_sample_ids = self.selected_sample_ids.union( self.class_to_sample_v[i_c] )\n            \n            self.selected_sample_ids = np.array( sorted(self.selected_sample_ids) )\n            \n            \n        if self.remove_classes_v is not None:\n            self.selected_sample_ids = set(self.selected_sample_ids)\n            \n            for i_c in self.remove_classes_v:\n                self.selected_sample_ids = self.selected_sample_ids.difference( self.class_to_sample_v[i_c] )\n            \n            self.selected_sample_ids = np.array( sorted(self.selected_sample_ids) )\n        # # # # # # # # # # # # # # # # #\n        \n        n_samples = len( self.selected_sample_ids )\n        n_samples_cv = int( (1.0-self.test_split) * n_samples)\n        \n        if self.do_CV:\n            if self.iter_tst:\n                f_samples = np.zeros(n_samples, dtype=np.bool)\n                f_samples[n_samples_cv:] = True\n                \n            else:\n                n_samples_per_fold = n_samples_cv // self.n_folds\n                f_samples = np.zeros(n_samples, dtype=np.bool)\n\n                if self.i_fold < self.n_folds - 1:\n                    f_samples[ self.i_fold * n_samples_per_fold: (self.i_fold+1) * n_samples_per_fold] = True\n                else:\n                    f_samples[ self.i_fold * n_samples_per_fold: n_samples_cv] = True\n\n                if self.iter_trn:\n                    f_samples[:n_samples_cv]  = ~(f_samples[:n_samples_cv])\n                    \n        else:\n            f_samples = np.ones(n_samples, dtype=np.bool)\n            \n        self.fold_sample_filter = f_samples \n        self.fold_samples = self.selected_sample_ids[self.fold_sample_filter]\n\n        return None\n    \n    \n    def get_image(self, sample_id):\n        \n        if self.use_img_cache:\n            image = self.f_h5[sample_id][:]\n        \n        else:\n            file_path = os.path.join(self.images_dir, f'{sample_id}.dicom')\n            image, image_shape = self.read_and_downsample_dicom_image(\n                file_path\n            )\n        \n        return image\n    \n    def __getitem__(self, index):\n        \n        if index < 0:\n            index = self.__len__() + index\n        \n        sample_id = self.fold_samples[index]\n        \n        image = self.get_image(sample_id)\n        \n        original_shape = self.misc_d[sample_id]\n        \n        img_h, img_w = image.shape\n        orig_img_h, orig_img_w = original_shape\n        \n        scale_h = img_h / orig_img_h\n        scale_w = img_w / orig_img_w\n        \n        image = calc_image_features(image)\n        \n        sample = {\n            'sample_id': sample_id,\n            'image': image,\n            'original_shape': self.misc_d[sample_id],\n        }\n        \n        \n        if self.bbox_df is not None:\n            # The images are already resized.\n            # Format at this point is (x0,y0,x1,y1)\n            scale_v = np.array([scale_w, scale_h, scale_w, scale_h])\n            sample['bboxes'] = self.bbox_d[sample_id]['bboxes'] * scale_v\n            sample['cls'] = self.bbox_d[sample_id]['cls']\n            \n            if self.select_classes_v is not None:\n                f = np.zeros(sample['cls'].shape[0], dtype=np.bool)\n                    \n                for i in self.select_classes_v:\n                    f[sample['cls'] == i] = True\n\n                sample['bboxes'] = sample['bboxes'][f]\n                sample['cls']    = sample['cls'][f]            \n            \n            if self.sample_bbox_and_class:\n                idx = np.random.randint(0, sample['cls'].shape[0])\n                \n                (x0, y0, x1, y1) = sample['bboxes'][idx].astype(np.int)\n                \n                d_x = np.random.randint(0, (x1-x0)//4 )\n                d_y = np.random.randint(0, (y1-y0)//4 )\n                x0 = max(0, x0-d_x)\n                y0 = max(0, y0-d_y)\n                \n                x1 = min(sample['image'].shape[1], x1+d_x)\n                y1 = min(sample['image'].shape[0], y1+d_y)\n                \n                sample['image'] = sample['image'][y0:y1, x0:x1]\n                sample['cls'] = sample['cls'][idx:idx+1]\n                sample['bboxes'] = np.array( [ (0, 0, sample['image'].shape[1], sample['image'].shape[0]) ] )\n                \n            if self.clean_boxes:\n                to_clean_d = {'bbox': sample['bboxes'], 'cls':sample['cls']}\n                cleaned_d = clean_predictions(\n                    [to_clean_d],\n                    iou_th=self.clean_iou_th,\n                    mode=self.clean_mode)[0]\n                \n                sample['bboxes'] = cleaned_d['bbox']\n                sample['cls'] = cleaned_d['cls']\n                \n                \n                \n        else:\n            sample['bboxes'] = np.array( [ (0, 0, image.shape[1], image.shape[0]) ] )\n            sample['cls']    = np.array( [-1] )\n        \n        \n\n        if self.do_augmentation:\n            for i_try in range(10):\n                try:\n                    transform_sample = self.TR_trn(**sample)\n                    if len(transform_sample['bboxes']) > 0:\n                        # Updating sample\n                        sample = transform_sample\n                        break\n\n                except Exception as e:\n                    pass\n\n            else:\n                if self.show_warnings:\n                    print(f' - WARNING: Imposible to Augmentate image, idx={index}.', file=sys.stderr)\n                    \n                # doing a basic transform\n                sample = self.TR_val(**sample)\n                \n        else:\n            sample = self.TR_val(**sample)\n                \n        \n        # Format (x0,y0,x1,y1) to (y0,x0,y1,x1) for EffDet\n        sample['bboxes']     = torch.tensor(sample['bboxes'],     dtype=torch.float32)[:, [1,0,3,2]]  \n        sample['cls']        = torch.tensor(sample['cls'],        dtype=torch.int64)\n#         sample['extra']      = torch.tensor([sample['s_norm'], sample['a_norm'], sample['d_norm']], dtype=torch.float32).T\n        \n        \n        return sample\n    \n    \n    def open_h5_file(self, mode='r'):\n        if self.f_h5 is not None:\n            self.close_file()\n            \n        if mode == 'w':\n            if os.path.exists(self.h5_path):\n                print(f' The file \"{self.h5_path}\" already exists, if you continue the file will be deleted. Contunue (y/n) ?')\n                r = input()\n                if r.lower() != 'y':\n                    print('Operation aborted.')\n                    sys.exit()\n            \n        self.f_h5 = h5py.File(self.h5_path, mode)\n        return None\n    \n    \n    def close_file(self):\n        if self.f_h5 is not None:\n            self.f_h5.close()\n            \n        return None\n    \n    \n    def read_misc_d(self):\n        self.misc_d = load_obj( self.misc_path )\n        return None\n        \n        \n    def save_misc_d(self, warn=True):\n        if warn and self.misc_d:\n            if os.path.exists(self.misc_d):\n                print(f' The file \"{self.misc_d}\" already exists, if you continue the file will be deleted. Contunue (y/n) ?')\n                r = input()\n                if r.lower() != 'y':\n                    print('Operation aborted.')\n                    sys.exit()\n            \n        \n        save_obj(self.misc_d, self.misc_path)\n        return None\n    \n    \n    def downsample_img(\n        self,\n        image,\n        downsample_factor=2):\n        \n        new_dims_v = (\n            image.shape[1] // downsample_factor,\n            image.shape[0] // downsample_factor )\n        \n        image_rs = cv2.resize(\n            image,\n            new_dims_v\n        )\n        \n        return image_rs\n    \n    \n    def read_and_downsample_dicom_image(self, file_path):\n        image = read_dicom_image(file_path)\n        image_shape = image.shape\n\n        image_rs = self.downsample_img(\n            image,\n            downsample_factor=self.downsample_factor)\n        \n        return image_rs, image_shape\n        \n        \n    def _gen_images_ds(self):\n        all_files_v = glob.glob(os.path.join(self.images_dir, '*.dicom') )\n        \n        self.open_h5_file(mode='w')\n        self.misc_d = {}\n        \n        file_it = tqdm( all_files_v )\n        for file_path in file_it:\n            file_id = os.path.splitext(os.path.basename(file_path))[0]\n            file_it.set_description(file_id)\n            \n            self.read_dicom_image(file_path)\n            \n            image_rs, image_shape = self.read_and_downsample_dicom_image(file_path)\n                        \n            self.f_h5.create_dataset(file_id, data=image_rs)\n            self.misc_d[file_id] = image_shape\n        \n        self.close_file()\n        self.save_misc_d(warn=False)\n        \n        return None\n    \n    \n    def __len__(self):\n        return len(self.fold_samples)\n    \n    \n    def _build_albumentations(self):\n        self.TR_trn = A.Compose(\n            [\n#                 A.RandomSizedCrop(\n#                     min_max_height=[int(0.7*self.image_resolution[0]), int(1.0*self.image_resolution[0])],\n#                     height=int(0.9*self.image_resolution[0]),\n#                     width=int(0.9*self.image_resolution[1]), \n#                     p=0.5),\n                \n                \n                A.Crop(\n                    x_min=128,\n                    y_min=128,\n                    x_max=128,\n                    y_max=128,\n                    p=0.5),\n\n#                     A.OneOf([\n#                         A.HueSaturationValue(\n#                             hue_shift_limit=0.2,\n#                             sat_shift_limit= 0.2,\n#                             val_shift_limit=0.2,\n#                             p=0.9),\n\n#                         A.RandomBrightnessContrast(\n#                             brightness_limit=0.2, \n#                             contrast_limit=0.2,\n#                             p=0.9),\n\n#                     ],\n#                         p=0.9),\n\n#                     A.ToGray(p=0.01),\n\n                A.HorizontalFlip(p=0.5),\n\n    #             A.VerticalFlip(p=0.5),\n\n    #             A.RandomRotate90(p=0.5),\n\n                A.Rotate(\n                    limit=15,\n                    p=0.6,\n                ),\n\n#                     A.Transpose(p=0.5),\n\n                A.Blur(blur_limit=3, p=0.6),\n\n#                     A.OneOf([\n#                         A.Blur(blur_limit=3, p=1.0),\n#                         A.MedianBlur(blur_limit=3, p=1.0)\n#                     ],\n#                         p=0.1),\n\n                A.Cutout(\n                    num_holes=20,\n                    max_h_size=64,\n                    max_w_size=64,\n                    fill_value=0,\n                    p=0.5),\n\n                A.Resize(\n                    height=self.model_resolution[0],\n                    width=self.model_resolution[1],\n                    p=1.0),\n\n                ToTensorV2(p=1.0),\n            ],\n\n            p=1.0, \n            bbox_params=A.BboxParams(\n                format='pascal_voc',\n                min_area=0, \n                min_visibility=0,\n                label_fields=['cls']\n            )\n            )\n        \n        if self.sample_bbox_and_class:\n            self.TR_trn = A.Compose(\n            [\n\n                A.HorizontalFlip(p=0.5),\n\n                A.Rotate(\n                    limit=15,\n                    p=0.6,\n                ),\n\n#                     A.Transpose(p=0.5),\n\n                A.Blur(blur_limit=3, p=0.6),\n\n                A.Cutout(\n                    num_holes=10,\n                    max_h_size=5,\n                    max_w_size=5,\n                    fill_value=0,\n                    p=0.5),\n\n                A.Resize(\n                    height=self.model_resolution[0],\n                    width=self.model_resolution[1],\n                    p=1.0),\n\n                ToTensorV2(p=1.0),\n            ],\n\n            p=1.0, \n            bbox_params=A.BboxParams(\n                format='pascal_voc',\n                min_area=0, \n                min_visibility=0,\n                label_fields=['cls']\n            )\n            )\n            \n        \n        \n        self.TR_val = A.Compose(\n            [\n                A.Resize(\n                    height=self.model_resolution[0],\n                    width=self.model_resolution[1],\n                    p=1.0),\n\n                ToTensorV2(p=1.0),\n            ], \n\n            p=1.0, \n            bbox_params=A.BboxParams(\n                format='pascal_voc',\n                min_area=0, \n                min_visibility=0,\n                label_fields=['cls']\n            )\n        )\n\n        \n        return None\n\n    \n    def get_GT_Dataframe(self, merge_mode='mean'):\n        \"\"\" Reeturns the fold GT Dataframe\"\"\"\n        gt_df = self.bbox_df[self.bbox_df.image_id.isin(self.fold_samples)].copy()\n        gt_df.fillna(0, inplace=True)\n        gt_df.loc[gt_df[\"class_id\"] == 14, ['x_max', 'y_max']] = 1.0\n        \n        \n        \n        gb = gt_df.groupby(['image_id', 'class_id'])\n\n        if merge_mode == 'mean':\n            gt_df = gb.agg(lambda x: ' '.join(np.unique(x.values)) if type(x.values[0]) is str else x.values.mean() ).reset_index()\n\n        elif merge_mode == 'first':\n            gt_df = gb.first().reset_index()\n\n        elif merge_mode is None:\n            pass\n\n        else:\n            raise Exception(f'Mode \"{merge_mode}\" unknown.')\n\n        return gt_df\n\n\nds = FoldDataset(\n    ds_path='../input/train-test-ds-bbox-cache/',\n    ds_name='train',\n    images_dir=os.path.join(DS_DIR, 'train'),\n    model_resolution=(512, 512),\n    df_path=os.path.join(DS_DIR, 'train.csv'),\n    \n    mode='cv_trn',\n    i_fold=1,\n    n_folds=5,\n    test_split=0.1,\n    \n    do_augmentation=True,\n    downsample_factor=2,\n    remove_classes_v=[14],\n    select_classes_v=None,\n    show_warnings=False,\n    random_seed=3128,\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class ModelX(nn.Module):\n    def __init__(\n        self,\n        model_resolution=(768, 512), \n        n_input_channels=4,\n        n_classes=14,\n        n_extras=0,\n        extra_loss_weight=1.0,\n\n        init_lr=1e-4,\n        optimizer_name='adam',\n        clip_grad_norm=5.0,\n        weight_decay=0.0,\n        \n        use_pretrained_model=True,\n        backbone_name='tf_efficientdet_d4',\n        \n        checkpoint_base_path='./model_checkpoint',\n        model_name=' ModelX_v1',\n        device=None,\n        ):\n        \n        super().__init__()\n        \n        self.model_resolution = np.array(model_resolution) \n        self.n_input_channels = n_input_channels\n        self.n_classes        = n_classes\n        self.n_extras         = n_extras\n        self.extra_loss_weight = extra_loss_weight\n        \n        self.lr               = init_lr\n        self.optimizer_name   = optimizer_name\n        self.clip_grad_norm   = clip_grad_norm\n        self.weight_decay     = weight_decay\n        \n        self.use_pretrained_model = use_pretrained_model\n        self.backbone_name        = backbone_name.lower()\n        \n        self.checkpoint_base_path = checkpoint_base_path\n        self.model_name           = model_name\n        \n        \n#         self.resolution_scale = self.image_resolution / self.model_resolution\n#         self.boxes_scale = np.array(\n#             [\n#                 self.resolution_scale[1],\n#                 self.resolution_scale[0],\n#                 self.resolution_scale[1],\n#                 self.resolution_scale[0],\n#             ],\n#             np.float32\n#         )\n        \n        print(f'New Model: \"{self.model_name}\"')\n        \n        # Seting device\n        self.set_device(device)\n        \n        # Creating output dir\n        if not os.path.exists(self.checkpoint_base_path):\n            print(f'Creating save dir: \"{self.checkpoint_base_path}\"')\n            os.makedirs(self.checkpoint_base_path)        \n        \n        \n        # Building Backbone\n        self._build_backbone()\n        \n        \n        # Building Optimizers\n        self.build_optimizer()\n        \n        \n        # Moving model to device\n        self.to(self.device)\n        \n        # Model Summary\n        self.calc_total_weights()\n        \n        return None\n    \n    \n    @torch.jit.ignore\n    def build_optimizer(self, params_v=None):\n        if params_v is None:\n            params_v = self.parameters()\n        \n        if self.optimizer_name.lower() == 'adam':\n            self.optimizer = optim.Adam(\n                params_v,\n                lr=self.lr,\n                weight_decay=self.weight_decay,\n            )\n            \n        else:\n            raise Exception(f'Un implemented optimizer: {self.optimizer_name}')\n        \n        return self.optimizer\n    \n    \n\n    @torch.jit.ignore\n    def _build_backbone(self):\n        \n        self.backbone_config = get_efficientdet_config(self.backbone_name)\n        self.backbone_config.num_classes = self.n_classes\n        self.backbone_config.image_size  = tuple([int(i) for i in self.model_resolution])\n        self.backbone_config.extra_variables = self.n_extras\n        self.backbone_config.extra_loss_weight = self.extra_loss_weight\n        \n        self.backbone = EfficientDet(\n            self.backbone_config,\n            pretrained_backbone=self.use_pretrained_model)\n        \n        first_conv = self.backbone.backbone.conv_stem\n\n        # Updating first ConvHead\n        self.backbone.backbone.conv_stem = nn.Conv2d(\n            self.n_input_channels,\n            first_conv.out_channels,\n            kernel_size=first_conv.kernel_size,\n            stride=first_conv.stride,\n            padding=first_conv.padding,\n            bias=False,\n        )\n        \n        # Deleting unused conv\n        del(first_conv)\n        \n        self.net_labeler_train   = DetBenchTrain(self.backbone)\n        self.net_labeler_predict = DetBenchPredict(self.backbone)\n            \n        return None\n    \n    \n    \n    def forward(self, x):\n        return self.backbone(x)\n        \n        \n    @torch.jit.ignore\n    def save_checkpoint(\n        self,\n        step=0,\n        loss=None,\n        file_name='weights.ckpt',\n        verbose=True,\n        ):\n        \n        \n        base_path = self.checkpoint_base_path\n        \n        PATH = os.path.join(base_path, file_name)\n        \n        torch.save({\n            'step': step,\n            'model_state_dict':     self.state_dict(),\n            'optimizer_state_dict': self.optimizer.state_dict() if self.optimizer is not None else None,\n            'loss': loss,\n            }, PATH)\n        \n        if verbose:\n            print(f' Saved checkpoint: {PATH}.')\n        return None\n    \n    @torch.jit.ignore\n    def restore_checkpoint(self, PATH, verbose=True):\n        checkpoint = torch.load(\n            PATH,\n            map_location=self.device,)\n        \n        load_optimizer = True\n        saved_state_dict   = checkpoint['model_state_dict']\n        current_state_dict = self.state_dict()\n        new_state_dict = OrderedDict()\n        for key in current_state_dict.keys():\n            if (key in saved_state_dict.keys()) and (saved_state_dict[key].shape == current_state_dict[key].shape):\n                new_state_dict[key] = saved_state_dict[key]\n\n            else:\n                load_optimizer = False\n                if key not in saved_state_dict.keys():\n                    print(f' - WARNING: key=\"{key}\" not found in saved checkpoint.\\n   Weights will not be loaded.', file=sys.stderr)\n                else:\n                    s0 = tuple(saved_state_dict[key].shape)\n                    s1 = tuple(current_state_dict[key].shape)\n                    print(f' - WARNING: shapes mismatch in \"{key}\": {s0} vs {s1}.\\n   Weights will not be loaded.', file=sys.stderr)\n                new_state_dict[key] = current_state_dict[key]\n        \n        self.load_state_dict( new_state_dict )\n        \n        if self.optimizer is not None:\n            if load_optimizer:\n                try:\n                    self.optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n                except Exception as e:\n                    print(' - WARNING: ERROR while loading the optimizer. The optimizer will be reseted.', file=sys.stderr)\n                    self.build_optimizer()\n            else:\n                print(' - WARNING: Optimizer will not be loaded.', file=sys.stderr)\n        \n        if verbose:\n            print(f' Restored checkpoint: {PATH}.')\n        \n        return checkpoint \n    \n    @torch.jit.ignore\n    def calc_total_weights(self, verbose=True):\n        n_w = 0\n        for p in self.parameters():\n            n_w += np.prod(p.shape, dtype=np.int)\n        \n        if verbose:\n            print(' - Total weights: {:0.02f}M'.format(n_w/1e6))\n        \n        return n_w\n    \n    \n    @torch.jit.export\n    def flip(self, tensor, dim=1):\n        \"\"\" Just flip a tensro dim.\"\"\"\n        fliped_idx    = torch.arange(tensor.size(dim)-1, -1, -1).long().to(self.device)\n        fliped_tensor = tensor.index_select(dim, fliped_idx)\n        return fliped_tensor\n    \n    @torch.jit.ignore\n    def set_device(self, device=None):\n        if device is None:\n            self.device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n        elif type(device) == str:\n            self.device = torch.device( device )\n        else:\n            raise Exception('Not implemented')\n            \n        print(f' - Selecting device: {self.device}')\n        return self.device\n    \n    \n    @torch.jit.ignore\n    def get_bboxes(self, outputs, det_th, data=None, unscale_bboxes=False):\n        preds_v = []\n        \n        if data is not None:\n            assert len(data['sample_id']) == outputs.shape[0]\n            \n        for i_sample, img_pred in enumerate(outputs.detach().cpu().numpy()):\n            \n            bboxes, p_det, idx_class = np.split(img_pred, [4,5], axis=-1)\n            f_det = p_det[:,0] > det_th\n            \n            if unscale_bboxes:\n                model_img_h, model_img_w = self.model_resolution\n                orig_img_h, orig_img_w = data['original_shape'][i_sample]\n                \n                scale_h = model_img_h / orig_img_h\n                scale_w = model_img_w / orig_img_w\n                \n                # x0, y0, x1, y1\n                scale_v = np.array([scale_w, scale_h, scale_w, scale_h])\n                \n                bboxes = bboxes / scale_v\n            \n\n            preds_v.append(\n                {\n                    'bbox': bboxes[f_det],\n                    'p_det': p_det[f_det, 0],\n                    'cls':  idx_class[f_det, 0].astype(int) - 1, # We must substract 1 to the class number\n                })\n\n        return preds_v\n\n    \n\n    @torch.jit.ignore\n    def predict(self, data, det_th=0.4, output_losses=False, training=False, filter_boxes=True, unscale_bboxes=False):\n        \n        if training:\n            self.train()\n            torch.set_grad_enabled(True)\n            \n        else:\n            self.eval()\n            torch.set_grad_enabled(False)\n            \n            \n        images = data['image'].to(self.device)\n        \n        if output_losses:\n            target_d = {\n                'bbox': [x.to(self.device) for x in data['bboxes']],\n                'cls':  [x.to(self.device) + 1 for x in data['cls']],   # We must sum 1 to the class number\n#                 'img_scale': None,\n#                 'img_size': None,\n            }\n            \n            if self.n_extras > 0:\n                target_d['extra'] = [x.to(self.device) for x in data['extra']]\n                \n            # dict with: 'loss', 'class_loss', 'box_loss'\n            outputs = self.net_labeler_train.forward(\n                images,\n                target_d\n            )\n            \n            if not training and filter_boxes:\n                outputs['detections'] = self.get_bboxes(outputs['detections'], det_th, data, unscale_bboxes)\n            \n        else:\n            outputs = self.net_labeler_predict.forward(\n                images,\n            )\n            \n            if filter_boxes:\n                outputs = self.get_bboxes(outputs, det_th, data, unscale_bboxes)\n        \n        return outputs\n\n    \n    @torch.jit.ignore\n    def train_step(self, data):\n        \n        outputs = self.predict(data=data, output_losses=True, training=True)\n        loss = outputs['loss']\n        \n        # Backward pass\n        self.optimizer.zero_grad()\n        loss.backward()\n        \n        if self.clip_grad_norm > 0.0:\n            torch.nn.utils.clip_grad_norm_(\n                self.parameters(),\n                self.clip_grad_norm)\n            \n        self.optimizer.step()\n        \n#         trn_batch_loss = loss.item()\n\n        return outputs\n\n\n    @torch.jit.ignore\n    def put_boxes(self, preds_v, data, do_plot=False):\n        BS = len( preds_v )\n\n        assert BS == data['image'].shape[0], 'Wrong Batch Size'\n\n\n        box_image_v = []\n        for i_b in range(BS):\n\n            pred_d = preds_v[i_b]\n\n            image = data['image'][i_b].cpu().numpy().transpose([1,2,0])\n            box_image = (image * 255)\n\n\n            if len(box_image.shape) == 3 and box_image.shape[-1] > 3:\n                box_image = box_image[:,:,:3]\n\n            elif len(box_image.shape) == 3:\n                box_image = box_image[:,:,0]\n\n            if len(box_image.shape) == 2:\n                box_image = box_image[:,:, None] * np.ones(3)\n\n            box_image = box_image.copy().astype(np.uint8)\n\n            \n            for bbox, idx_class in zip( data['bboxes'][i_b].numpy(), data['cls'][i_b].numpy()):\n                (y0, x0, y1, x1) = bbox.astype(np.int)\n                \n                box_image = cv2.rectangle(\n                    box_image,\n                    (x0,y0),\n                    (x1,y1),\n                    class2color_v[idx_class],\n                    thickness=2,\n                )\n\n#                 box_image = cv2.putText(\n#                     box_image,\n#                     class2str_v[idx_class] + '(GT)',\n#                     org=(x0, y0-3),\n#                     fontFace=cv2.FONT_HERSHEY_SIMPLEX,\n#                     fontScale=0.3,\n#                     color=class2color_v[idx_class],\n#                     thickness=1,\n#                     lineType=cv2.LINE_AA,\n#                     bottomLeftOrigin=False\n#                     )      \n\n            \n            for bbox, idx_class, p_det in zip( pred_d['bbox'], pred_d['cls'], pred_d['p_det']):\n\n                (x0, y0, x1, y1) = bbox.astype(np.int)\n\n                box_image = cv2.rectangle(\n                    box_image,\n                    (x0,y0),\n                    (x1,y1),\n                    class2color_v[idx_class],\n                    thickness=1,\n                    )\n\n                box_image = cv2.putText(\n                    box_image,\n                    class2str_v[idx_class] + f'({p_det:0.1f})',\n                    org=(x0, y0-3),\n                    fontFace=cv2.FONT_HERSHEY_SIMPLEX,\n                    fontScale=0.3,\n                    color=class2color_v[idx_class],\n                    thickness=1,\n                    lineType=cv2.LINE_AA,\n                    bottomLeftOrigin=False\n                    )\n            \n\n                \n                \n\n            box_image_v.append(box_image)\n\n            if do_plot:\n                plt.figure(0, figsize=(20,20) )\n                plt.imshow(box_image)\n                plt.show()\n\n        return np.array( box_image_v )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Building Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"MODEL_D = 2\nN_FOLDS = 5\nI_FOLD  = 1\nMODEL_RESOLUTION = (768, 512)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_cfg_d = {\n    'model_resolution': MODEL_RESOLUTION, \n    'n_input_channels': 4,\n    \n    'n_classes': 14,\n    'n_extras': 0,\n    'extra_loss_weight':6.0,\n    \n    'use_pretrained_model':True,\n    'backbone_name':f'tf_efficientdet_d{MODEL_D}',\n    \n    'init_lr': 1e-4,\n    'optimizer_name': 'adam',\n    'clip_grad_norm':3.0,\n    'weight_decay': 1e-5,\n    'model_name':'ModelX_v4',\n    'checkpoint_base_path':f'./EffDet_d{MODEL_D}_F{I_FOLD}_v5',\n    \n    'device': None,\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\nmodel  = ModelX(**model_cfg_d)\n\nN_HIST = 1000\n\nloss_names_v = ['loss', 'box_loss', 'class_loss'] #, 'extra_loss']\n\ntrn_fsma_d = {}\nfor k in loss_names_v:\n    trn_fsma_d[k] = FastSMA(\n        maxlen=N_HIST,\n        label='mean = ',\n        print_format='0.02f',\n        save_filename=os.path.join(model.checkpoint_base_path, f'{k}_trn.fsma')\n    )\n\n    \nval_fsma_d = {}\nfor k in loss_names_v:\n    val_fsma_d[k] = FastSMA(\n        maxlen=N_HIST,\n        label='mean = ',\n        print_format='0.02f',\n        save_filename=os.path.join(model.checkpoint_base_path, f'{k}_val.fsma')\n    )","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Train only Detection Heads"},{"metadata":{"trusted":true},"cell_type":"code","source":"trainable_params_v = sum([\n    list( model.backbone.backbone.conv_stem.parameters() ),\n    list( model.backbone.class_net.parameters() ),\n    list( model.backbone.box_net.parameters() ),\n#     list( model.backbone.extra_net.parameters() ),\n    list( model.backbone.fpn.parameters() ),\n], [])\n\nn_w = 0\nfor p in trainable_params_v:\n    n_w += np.prod(p.shape)\n    \nprint(f' Total trainable parameters: {n_w/1e6:0.02f}M')\n\n_ = model.build_optimizer(trainable_params_v)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Loading Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"_ = model.restore_checkpoint(\n    '../input/vinbigdata-effdet-d2-f0f2-ckpts/F1_E79_ModelX_v4_T0.325_V0.410.ckpt'\n)\n\ntry:\n    for k, fsma in trn_fsma_d.items():\n        fsma.load()\n\n    for k, fsma in val_fsma_d.items():\n        fsma.load()\nexcept Exception:\n    print(' WARNING: FastSMA not loaded.')\n    pass","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training Functions"},{"metadata":{"trusted":true},"cell_type":"code","source":"def batch_merge(samples_v):\n    ret_batch_d = {k : [] for k in samples_v[0].keys()}\n    \n    for sample_d in samples_v:\n        for k, v in sample_d.items():\n            ret_batch_d[k].append( v )\n    \n    ret_batch_d['image'] = torch.stack(ret_batch_d['image'])\n    return ret_batch_d\n\n\ndef train_one_epoch(\n    model,\n    i_fold,\n    i_epoch,\n    ds_trn,\n    trn_fsma_d,\n    batch_size=1,\n    shuffle=True,\n    num_workers=16,\n    pin_memory=False):\n    \n    trn_iter = tqdm(DataLoader(\n        ds_trn,\n        batch_size=batch_size,\n        shuffle=shuffle,\n        num_workers=num_workers,\n        pin_memory=pin_memory,\n        collate_fn=batch_merge))\n    \n    # Train Step\n    epoch_losses_d = {k:[] for k in trn_fsma_d.keys()}\n    for i_step, trn_data in enumerate(trn_iter):\n        trn_loss_d = model.train_step(trn_data)\n        \n        for k, fsma in trn_fsma_d.items():\n            l = trn_loss_d[k].item()\n            fsma.append(l)\n            epoch_losses_d[k].append(l)\n            \n            \n        trn_iter.set_description('[TRN_F={:d}E={:d}_L={:0.03f}_B={:0.03f}_C={:0.03f}_X={:0.03f}]'.format(\n            i_fold,\n            i_epoch,\n            trn_fsma_d['loss'].mean(),\n            trn_fsma_d['box_loss'].mean(),\n            trn_fsma_d['class_loss'].mean(),\n            trn_fsma_d['extra_loss'].mean() if 'extra_loss' in trn_fsma_d.keys() else 0.0,\n            ) )\n        \n    \n    for k in epoch_losses_d.keys():\n        epoch_losses_d[k] = np.mean(epoch_losses_d[k])\n        \n    return epoch_losses_d\n\n\ndef validate_one_epoch(\n    model,\n    i_fold,\n    i_epoch,\n    ds_val,\n    val_fsma_d,\n    batch_size=1,\n    shuffle=False,\n    num_workers=16,\n    pin_memory=False,\n    \n    det_th=0.15,\n    clear_predictions=True,\n    clear_iou_th=0.10,\n    clear_mode='p_det_weight',\n):\n    \n    gt_df = ds_val.get_GT_Dataframe()\n    \n    val_iter = tqdm(DataLoader(\n        ds_val,\n        batch_size=batch_size,\n        shuffle=shuffle,\n        num_workers=num_workers,\n        pin_memory=pin_memory,\n        collate_fn=batch_merge))\n\n    # Validation step\n    epoch_losses_d = {k:[] for k in val_fsma_d.keys()}\n    \n    all_preds_v = []\n    for i_step, val_data in enumerate(val_iter):\n        val_loss_d = model.predict(\n            val_data,\n            output_losses=True,\n            training=False,\n            det_th=det_th,\n            filter_boxes=True,\n            unscale_bboxes=True)\n        \n        \n        # all_preds_v calculation\n        pred_v =  clean_predictions(\n            val_loss_d['detections'],\n            iou_th=clear_iou_th,\n            mode=clear_mode)\n        \n        \n        if clear_predictions:\n            pred_v =  clean_predictions(\n                pred_v,\n                iou_th=clear_iou_th,\n                mode=clear_mode)\n            \n        for i_s, pred_d in enumerate(pred_v):\n            pred_d['sample_id']      = val_data['sample_id'][i_s]\n            pred_d['original_shape'] = val_data['original_shape'][i_s]\n            \n        all_preds_v += pred_v\n        # all_preds_v calculation\n            \n            \n        for k, fsma in val_fsma_d.items():\n            l = val_loss_d[k].item()\n            fsma.append(l)\n            epoch_losses_d[k].append(l)\n            \n                \n        val_iter.set_description('[VAL_F={:d}E={:d}_L={:0.03f}_B={:0.03f}_C={:0.03f}_X={:0.03f}]'.format(\n            i_fold,\n            i_epoch,\n            val_fsma_d['loss'].mean(),\n            val_fsma_d['box_loss'].mean(),\n            val_fsma_d['class_loss'].mean(),\n            val_fsma_d['extra_loss'].mean() if 'extra_loss' in val_fsma_d.keys() else 0.0,\n            ) )\n    \n    for k in epoch_losses_d.keys():\n        epoch_losses_d[k] = np.mean(epoch_losses_d[k])\n    \n    try:\n        metrics_v, summary_df, mAP = calc_metrics(\n            all_preds_v,\n            gt_df,\n            iou_thresh=0.4,\n            show_summay=True)\n\n\n        plot_PvsR_curve(metrics_v, class2color_v)\n        \n    except:\n        print('Problems with evaluation')\n    \n    \n    return epoch_losses_d\n\n\n\ndef save_model(\n    model,\n    trn_fsma_d,\n    val_fsma_d,\n    i_epoch,\n    i_fold):\n        \n    f_sma_trn = trn_fsma_d['loss']\n    f_sma_val = val_fsma_d['loss']\n    \n    model_filename = f'F{i_fold:}_E{i_epoch:}_{model.model_name}_T{f_sma_trn.mean():0.03f}_V{f_sma_val.mean():0.03f}.ckpt'\n    model.save_checkpoint(\n        step={'i_epoch':i_epoch, 'i_fold': i_fold},\n        loss={'trn':f_sma_trn.mean(), 'val':f_sma_val.mean()},\n        file_name=model_filename,\n        verbose=True,\n    )\n    \n    for k, fsma in trn_fsma_d.items():\n        fsma.save()\n    \n    for k, fsma in val_fsma_d.items():\n        fsma.save()\n    \n    return None","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Loading datasets"},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_fold_ds(i_fold, N_FOLDS=5, SELECT_CLASSES=None, DS_PATH='./'):\n    ds_trn = FoldDataset(\n        ds_path=DS_PATH,\n        ds_name='train',\n        images_dir=os.path.join(DS_DIR, 'train'),\n        model_resolution=MODEL_RESOLUTION,\n        df_path=os.path.join(DS_DIR, 'train.csv'),\n        \n        mode='cv_trn',\n        i_fold=i_fold,\n        n_folds=N_FOLDS,\n        test_split=0.1,\n        \n        do_augmentation=True,\n        downsample_factor=2,\n        remove_classes_v=[],\n        select_classes_v=SELECT_CLASSES,\n        show_warnings=False,\n        do_random_shuffle=True,\n        random_seed=3128,\n        \n        clean_boxes=True,\n        clean_mode='random',\n        clean_iou_th=0.1,\n    )\n\n    ds_val = FoldDataset(\n        ds_path=DS_PATH,\n        ds_name='train',\n        images_dir=os.path.join(DS_DIR, 'train'),\n        model_resolution=MODEL_RESOLUTION,\n        df_path=os.path.join(DS_DIR, 'train.csv'),\n        \n        mode='cv_val',\n        i_fold=i_fold,\n        n_folds=N_FOLDS,\n        test_split=0.1,\n        \n        do_augmentation=False,\n        downsample_factor=2,\n        remove_classes_v=[],\n        select_classes_v=SELECT_CLASSES,\n        show_warnings=False,\n        do_random_shuffle=True,\n        random_seed=3128,\n        \n        clean_boxes=True,\n        clean_mode='random',\n        clean_iou_th=0.1,\n    )\n    \n\n    ds_tst_oof = FoldDataset(\n        ds_path=DS_PATH,\n        ds_name='train',\n        images_dir=os.path.join(DS_DIR, 'train'),\n        model_resolution=MODEL_RESOLUTION,\n        df_path=os.path.join(DS_DIR, 'train.csv'),\n\n        mode='cv_tst',\n        i_fold=I_FOLD,\n        n_folds=N_FOLDS,\n        test_split=0.1,\n\n        do_augmentation=False,\n        downsample_factor=2,\n        remove_classes_v=[],\n        select_classes_v=SELECT_CLASSES,\n        show_warnings=False,\n        do_random_shuffle=False,\n        random_seed=3128,\n    )\n        \n    ds_tst = FoldDataset(\n        ds_path=DS_PATH,\n        ds_name='test',\n        images_dir=os.path.join(DS_DIR, 'test'),\n        model_resolution=MODEL_RESOLUTION,\n        df_path=None,\n\n        mode='none',\n        i_fold=I_FOLD,\n        n_folds=N_FOLDS,\n        test_split=0.1,\n\n        do_augmentation=False,\n        downsample_factor=2,\n        remove_classes_v=[],\n        select_classes_v=None,\n        show_warnings=False,\n        do_random_shuffle=False,\n        random_seed=3128,\n    )\n\n\n    return ds_trn, ds_val, ds_tst, ds_tst_oof","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training"},{"metadata":{"trusted":true},"cell_type":"code","source":"if not EVAL_CKPTS:\n    n_epochs = 100\n    N_WORKERS = 4\n    BATCH_SIZE = 5\n    PIN_MEMORY = True\n\n    SELECT_CLASSES = None #[2, 14]\n\n    ds_trn, ds_val, ds_tst, ds_tst_oof = load_fold_ds(\n        i_fold=I_FOLD,\n        N_FOLDS=5,\n        SELECT_CLASSES=None,\n        DS_PATH=DS_PATH\n    )\n\n    for i_fold in range(I_FOLD, I_FOLD+1):\n        ds_trn, ds_val = load_fold_ds(i_fold, N_FOLD, SELECT_CLASSES)\n    #     sys.exit(0)\n\n        for i_epoch in range(3, n_epochs):\n            print(f'Starting new epoch: Epoch = {i_epoch}  Fold = {i_fold}')\n            trn_loss_epoch_d = train_one_epoch(\n                model,\n                i_fold,\n                i_epoch,\n                ds_trn,\n                trn_fsma_d,\n                batch_size=BATCH_SIZE,\n                shuffle=True,\n                num_workers=N_WORKERS,\n                pin_memory=PIN_MEMORY)\n\n            val_loss_epoch_d = validate_one_epoch(\n                model,\n                i_fold,\n                i_epoch,\n                ds_val,\n                val_fsma_d,\n                batch_size=BATCH_SIZE,\n                shuffle=False,\n                num_workers=N_WORKERS,\n                pin_memory=PIN_MEMORY)\n\n            print(f' Epoch {i_epoch} Summary:')\n            print(' - trn_loss_epoch_d:')\n            for k, v in trn_loss_epoch_d.items():\n                print(f'  |-> {k} = {v:0.04f}')\n\n            print()\n            print(' - val_loss_epoch_d:')\n            for k, v in val_loss_epoch_d.items():\n                print(f'  |-> {k} = {v:0.04f}')\n\n            print()\n            save_model(\n                model,\n                trn_fsma_d,\n                val_fsma_d,\n                i_epoch,\n                i_fold)\n\n            print()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Inference"},{"metadata":{"trusted":true},"cell_type":"code","source":"if EVAL_CKPTS:\n    N_WORKERS = 4\n    \n    for ckpt_path in CKPTS_v:\n        I_FOLD = int(os.path.split(ckpt_path)[1].split('_')[0][1])\n        \n        _ = model.restore_checkpoint(ckpt_path)\n        \n        ds_trn, ds_val, ds_tst, ds_tst_oof = load_fold_ds(\n            i_fold=I_FOLD,\n            N_FOLDS=5,\n            SELECT_CLASSES=None,\n            DS_PATH=DS_PATH\n        )\n            \n        \n        all_preds_v = evalueate_dataset(\n            ds_tst,\n            model,\n            det_th=0.00,\n            unscale_bboxes=True,\n            batch_size=16,\n            num_workers=N_WORKERS,\n            pin_memory=True,\n            do_clean_predictions=False,\n            clean_iou_th=0.1,\n            clean_mode='p_det_weight',\n        )\n\n        preds_df = predictions_to_df(\n            all_preds_v,\n            f'ds_tst_F{I_FOLD}_noTH_noClean.csv')\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}