{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **EfficientDet Train**","metadata":{}},{"cell_type":"markdown","source":"* [Dependencies and imports](#section-one)\n* [Basic configurations](#section-two)\n* [Overide check_box function](#sub-section-three)\n    - [check_box function](#sub-section-three-one)\n    - [_post_process function](#sub-section-three-two)\n* [Check labels distribution](#section-four)\n* [Split data to folds](#section-five)\n* [Data augmentation using Albumentations](#section-six)\n* [Custom dataset](#section-seven)\n* [Metric](#section-eight)\n* [Fitter](#section-nine)\n* [Train](#section-ten)","metadata":{}},{"cell_type":"markdown","source":"<a id=\"section-one\"></a>\n## **Dependencies and imports**","metadata":{}},{"cell_type":"code","source":"!pip install --no-deps '../input/timm-package/timm-0.1.26-py3-none-any.whl' > /dev/null\n!pip install --no-deps '../input/pycocotools/pycocotools-2.0-cp37-cp37m-linux_x86_64.whl' > /dev/null","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-08-09T10:29:39.602237Z","iopub.execute_input":"2021-08-09T10:29:39.602653Z","iopub.status.idle":"2021-08-09T10:29:43.593633Z","shell.execute_reply.started":"2021-08-09T10:29:39.602546Z","shell.execute_reply":"2021-08-09T10:29:43.59255Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import sys\nsys.path.append(\"../input/timm-efficientdet-pytorch\")\nsys.path.append(\"../input/omegaconf\")\n\nimport torch\nimport os\nimport ast\nfrom glob import glob\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\n# --- time ---\nfrom datetime import datetime\nimport time\n# --- images ---\nimport cv2\nimport albumentations as A\n# --- data ---\nfrom sklearn.model_selection import StratifiedShuffleSplit\nfrom torch.utils.data import Dataset,DataLoader\nfrom torch.utils.data.sampler import SequentialSampler, RandomSampler\n# --- effdet ---\nfrom effdet import get_efficientdet_config, EfficientDet, DetBenchTrain, DetBenchEval\nfrom effdet.efficientdet import HeadNet\n# --- wandb ---\nimport wandb\nfrom kaggle_secrets import UserSecretsClient","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-08-09T10:29:43.59533Z","iopub.execute_input":"2021-08-09T10:29:43.595686Z","iopub.status.idle":"2021-08-09T10:29:47.453486Z","shell.execute_reply.started":"2021-08-09T10:29:43.595647Z","shell.execute_reply":"2021-08-09T10:29:47.452321Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"offline = False\nif not offline:\n    user_secrets = UserSecretsClient()\n    wandb_key = user_secrets.get_secret(\"wandb-key\")\n    wandb.login(key=wandb_key)\n\n    run = wandb.init(project=\"siim-covid19-detection\", name=\"object_detection_version2/efficientdet_d7\", resume=True, mode='online') #resume=True,","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:29:49.433669Z","iopub.execute_input":"2021-08-09T10:29:49.43406Z","iopub.status.idle":"2021-08-09T10:29:56.915635Z","shell.execute_reply.started":"2021-08-09T10:29:49.434024Z","shell.execute_reply":"2021-08-09T10:29:56.914666Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"section-two\"></a>\n## **Basic configurations**","metadata":{}},{"cell_type":"code","source":"# --- configs ---\nTYPICAL = 'typical'\nINDETERMINATE = 'indeterminate'\nATYPICAL = 'atypical'\n\nclass Configs:\n    img_size = 512\n    n_folds = 5\n    test_size = 0.2\n    thing_classes = {TYPICAL:1, INDETERMINATE:2, ATYPICAL:3}\n    device = torch.device(\"cuda\") if torch.cuda.is_available() else torch.device(\"cpu\")","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:29:56.920061Z","iopub.execute_input":"2021-08-09T10:29:56.922228Z","iopub.status.idle":"2021-08-09T10:29:56.980777Z","shell.execute_reply.started":"2021-08-09T10:29:56.922184Z","shell.execute_reply":"2021-08-09T10:29:56.979531Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"sub-section-one-one\"></a>","metadata":{}},{"cell_type":"code","source":"# Read train df\ntrain_df = pd.read_csv('../input/df-files/train_df.csv')","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:29:56.984958Z","iopub.execute_input":"2021-08-09T10:29:56.985737Z","iopub.status.idle":"2021-08-09T10:29:57.044244Z","shell.execute_reply.started":"2021-08-09T10:29:56.985684Z","shell.execute_reply":"2021-08-09T10:29:57.043248Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"section-three\"></a>\n## **Override functions**","metadata":{}},{"cell_type":"markdown","source":"<a id=\"sub-section-three-one\"></a>\n### **check_box function**\nMake sure bounding boxes whithin image bounds","metadata":{}},{"cell_type":"code","source":"rows_to_change = {}\n#check if exist boxes with negative values or higher than image boundaries\nfor index, row in train_df.iterrows():\n    boxes = ast.literal_eval(row['pascal_voc_boxes'])\n    new_boxes = []\n    change = False\n    for box in boxes:\n        x1,y1,x2,y2 = box\n        img_shape = ast.literal_eval(row['original_shape'])\n        if x1<0 or y1<0 or x2>img_shape[1] or y2>img_shape[0]:\n            print(\"For image: {}\\nShape: {}\\nBounding box: {}\".format(row['img_id'], img_shape, box))\n            change = True\n        \n            if x1<0:\n                x1=0\n            if y1<0:\n                y1=0\n            if x2>img_shape[1]:\n                x2=img_shape[1]\n            if y2>img_shape[0]:\n                y2=img_shape[0]\n\n        new_boxes.append([x1,y1,x2,y2])\n        \n    if change:\n        rows_to_change[row['img_id']] = new_boxes\n        \nfor img_id, new_boxes in rows_to_change.items():\n    train_df.loc[train_df['img_id'] == img_id, 'pascal_voc_boxes'] = str(new_boxes)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:29:57.046039Z","iopub.execute_input":"2021-08-09T10:29:57.046425Z","iopub.status.idle":"2021-08-09T10:29:58.002049Z","shell.execute_reply.started":"2021-08-09T10:29:57.046388Z","shell.execute_reply":"2021-08-09T10:29:58.000897Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# If box bounderies exceeds image boundaries set to min/max value accordingly\ndef new_check_bbox(bbox):\n    \"\"\"Check if bbox boundaries are in range 0, 1 and minimums are lesser then maximums\"\"\"\n    # added block \n    bbox=list(bbox)\n    for i in range(4):\n        if (bbox[i]<0) :\n            bbox[i]=0\n        elif (bbox[i]>1) :\n            bbox[i]=1\n    bbox=tuple(bbox)\n    # end added block \n    \n    for name, value in zip([\"x_min\", \"y_min\", \"x_max\", \"y_max\"], bbox[:4]):\n        if not 0 <= value <= 1:\n            raise ValueError(\n                \"Expected {name} for bbox {bbox} \"\n                \"to be in the range [0.0, 1.0], got {value}.\".format(bbox=bbox, name=name, value=value)\n            )\n    x_min, y_min, x_max, y_max = bbox[:4]\n    if x_max <= x_min:\n        raise ValueError(\"x_max is less than or equal to x_min for bbox {bbox}.\".format(bbox=bbox))\n    if y_max <= y_min:\n        raise ValueError(\"y_max is less than or equal to y_min for bbox {bbox}.\".format(bbox=bbox))\n\nA.augmentations.bbox_utils.check_bbox = new_check_bbox","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:29:58.006937Z","iopub.execute_input":"2021-08-09T10:29:58.009124Z","iopub.status.idle":"2021-08-09T10:29:58.021098Z","shell.execute_reply.started":"2021-08-09T10:29:58.009081Z","shell.execute_reply":"2021-08-09T10:29:58.020359Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we are sure there is no exceeding from image bounds for our bboxes, avoid floating point precision issues make sure bbox boundaries are in range 0,1 by adding a block to albumentations check_bbox function","metadata":{}},{"cell_type":"markdown","source":"<a id=\"sub-section-three-two\"></a>\n### **_post_process function**\nMake sure indices are integers","metadata":{}},{"cell_type":"code","source":"import effdet.bench as bench\nfrom effdet.anchors import MAX_DETECTION_POINTS\n\ndef new_post_process(config, cls_outputs, box_outputs):\n    \"\"\"Selects top-k predictions.\n\n    Post-proc code adapted from Tensorflow version at: https://github.com/google/automl/tree/master/efficientdet\n    and optimized for PyTorch.\n\n    Args:\n        config: a parameter dictionary that includes `min_level`, `max_level`,  `batch_size`, and `num_classes`.\n\n        cls_outputs: an OrderDict with keys representing levels and values\n            representing logits in [batch_size, height, width, num_anchors].\n\n        box_outputs: an OrderDict with keys representing levels and values\n            representing box regression targets in [batch_size, height, width, num_anchors * 4].\n    \"\"\"\n    batch_size = cls_outputs[0].shape[0]\n    cls_outputs_all = torch.cat([\n        cls_outputs[level].permute(0, 2, 3, 1).reshape([batch_size, -1, config.num_classes])\n        for level in range(config.num_levels)], 1)\n\n    box_outputs_all = torch.cat([\n        box_outputs[level].permute(0, 2, 3, 1).reshape([batch_size, -1, 4])\n        for level in range(config.num_levels)], 1)\n\n    _, cls_topk_indices_all = torch.topk(cls_outputs_all.reshape(batch_size, -1), dim=1, k=MAX_DETECTION_POINTS)\n    ############################# changed / to // as indices should be int64 #############################\n    indices_all = cls_topk_indices_all // config.num_classes \n    ######################################################################################################\n    classes_all = cls_topk_indices_all % config.num_classes\n\n    box_outputs_all_after_topk = torch.gather(\n        box_outputs_all, 1, indices_all.unsqueeze(2).expand(-1, -1, 4))\n\n    cls_outputs_all_after_topk = torch.gather(\n        cls_outputs_all, 1, indices_all.unsqueeze(2).expand(-1, -1, config.num_classes))\n    cls_outputs_all_after_topk = torch.gather(\n        cls_outputs_all_after_topk, 2, classes_all.unsqueeze(2))\n\n    return cls_outputs_all_after_topk, box_outputs_all_after_topk, indices_all, classes_all\n\nbench._post_process  = new_post_process","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:29:58.025008Z","iopub.execute_input":"2021-08-09T10:29:58.027506Z","iopub.status.idle":"2021-08-09T10:29:58.043425Z","shell.execute_reply.started":"2021-08-09T10:29:58.027467Z","shell.execute_reply":"2021-08-09T10:29:58.042556Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"sub-section-four\"></a>\n## **Check labels distribution**","metadata":{}},{"cell_type":"code","source":"num_negatives = len(train_df[train_df['study_level'] == 'negative']) \nnum_typicals = len(train_df[train_df['study_level'] == 'typical']) \nnum_atypical = len(train_df[train_df['study_level'] == 'atypical']) \nnum_indeterminates = len(train_df[train_df['study_level'] == 'indeterminate'])\n\ncounts = {'negative': num_negatives, 'typical': num_typicals, 'atypical': num_atypical, 'indeterminate': num_indeterminates}\n\nprint(counts)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:29:58.048125Z","iopub.execute_input":"2021-08-09T10:29:58.050624Z","iopub.status.idle":"2021-08-09T10:29:58.073817Z","shell.execute_reply.started":"2021-08-09T10:29:58.050561Z","shell.execute_reply":"2021-08-09T10:29:58.07304Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"total = counts['negative'] + counts['typical'] + counts['atypical'] + counts['indeterminate']\n\nprint('Total : ', total)\nprint('%negatives = {:.2f}'.format((counts['negative']/total) * 100))\nprint('%typicals = {:.2f}'.format((counts['typical']/total) * 100))\nprint('%atypicals = {:.2f}'.format((counts['atypical']/total) * 100))\nprint('%indeterminates = {:.2f}'.format((counts['indeterminate']/total) * 100))","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:29:58.078973Z","iopub.execute_input":"2021-08-09T10:29:58.081679Z","iopub.status.idle":"2021-08-09T10:29:58.097499Z","shell.execute_reply.started":"2021-08-09T10:29:58.081633Z","shell.execute_reply":"2021-08-09T10:29:58.096386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can see the data is highly imbalanced. </br>","metadata":{}},{"cell_type":"markdown","source":"### **Approaches for imbalanced data**\n1. Oversample - \"create\" new data for the less common class </br>\n2. StratifiedShuffleSplit - balanced distribution of the data to folds </br>\n3. Focal Loss","metadata":{}},{"cell_type":"markdown","source":"<a id=\"section-five\"></a>\n## **Split data to folds**","metadata":{}},{"cell_type":"code","source":"class DataFolds:\n    def __init__(self, train_df, continue_train=False, debug=False):\n        assert Configs.n_folds > 0, \"num folds must be a positive number\"\n        if continue_train:\n            self.train_df = pd.read_csv('../input/models/object_detection_models/tf_efficientdet_d7/splitted_train_df.csv')\n            \n            if debug:\n                self.train_df = self.train_df.sample(frac=0.05)\n        else:\n            self.train_df = train_df\n            self.drop_negatives()\n            self.oversample([INDETERMINATE, ATYPICAL])\n            self.split_to_folds(Configs.test_size)\n            \n            if debug:\n                self.train_df = self.train_df.sample(frac=0.05)\n    \n    def oversample(self, classes):\n        for cls in classes:\n            # add an amount of 80% of the difference between most frequent class ('typical') and oversampled class\n            amount = int((counts['typical'] - counts[cls])*0.8)\n            rows_to_add = self.train_df[(self.train_df['study_level']==cls)&(self.train_df['num_boxes']>0)].sample(n=amount, replace=True)\n            self.train_df = self.train_df.append(rows_to_add, ignore_index = True)\n            \n    def drop_negatives(self):\n        # drop negatives from df, train only for non-negative data\n        self.train_df = self.train_df[(self.train_df['study_level'] == TYPICAL)|(self.train_df['study_level'] == ATYPICAL)|(self.train_df['study_level'] == INDETERMINATE)]\n                \n    def split_to_folds(self, test_size):\n        skf = StratifiedShuffleSplit(n_splits=Configs.n_folds, test_size=test_size)\n        for n, (train_index, val_index) in enumerate(skf.split(X=self.train_df.index, y=self.train_df['int_label'])):\n            self.train_df.loc[self.train_df.iloc[val_index].index, 'fold'] = int(n)\n        # drop samples not in any fold\n        self.train_df = self.train_df[self.train_df['fold'].notna()]\n    \n    def get_train_df(self, fold_number): \n        # return df where fold != fold (this fold is for validation)\n        if fold_number >= 0 and fold_number < Configs.n_folds:\n            return self.train_df[self.train_df['fold'] != fold_number]\n\n    def get_val_df(self, fold_number):\n        # return df where fold == fold \n        if fold_number >= 0 and fold_number < Configs.n_folds:\n            return self.train_df[self.train_df['fold'] == fold_number]","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:29:58.102764Z","iopub.execute_input":"2021-08-09T10:29:58.10522Z","iopub.status.idle":"2021-08-09T10:29:58.127544Z","shell.execute_reply.started":"2021-08-09T10:29:58.105177Z","shell.execute_reply":"2021-08-09T10:29:58.126709Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_folds = DataFolds(train_df, continue_train=True)\n#data_folds.train_df.to_csv(\"splitted_train_df.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:04.571129Z","iopub.execute_input":"2021-08-09T10:30:04.571502Z","iopub.status.idle":"2021-08-09T10:30:04.624129Z","shell.execute_reply.started":"2021-08-09T10:30:04.571468Z","shell.execute_reply":"2021-08-09T10:30:04.623249Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Visualize distribution of labels over folds**","metadata":{}},{"cell_type":"code","source":"# Plot distibution\ndef plot_folds(data_folds):\n    nrows = Configs.n_folds//2\n    if Configs.n_folds%2 != 0:\n        nrows += 1\n    \n    fig, ax = plt.subplots(nrows=nrows, ncols=2, figsize=(30,15))\n    row = 0\n    for fold in range(Configs.n_folds):\n        if fold%2 == 0:\n            col = 0\n            if fold != 0:\n                row += 1\n        else:\n            col = 1\n\n        labels_count = {}\n        labels_count[TYPICAL] = len(data_folds.train_df[((data_folds.train_df['fold'] == fold)&(data_folds.train_df['study_level'] == TYPICAL))])\n        labels_count[ATYPICAL] = len(data_folds.train_df[((data_folds.train_df['fold'] == fold)&(data_folds.train_df['study_level'] == ATYPICAL))])\n        labels_count[INDETERMINATE] = len(data_folds.train_df[((data_folds.train_df['fold'] == fold)&(data_folds.train_df['study_level'] == INDETERMINATE))])\n\n        ax[row, col].bar(list(labels_count.keys()), list(labels_count.values()))\n\n        for j, value in enumerate(labels_count.values()):\n            ax[row, col].text(j, value+2, str(value), color='#267DBE', fontweight='bold')\n\n        ax[row, col].grid(axis='y', alpha=0.75)\n        ax[row, col].set_title(\"For fold #{}\".format(fold), fontsize=15)\n        ax[row, col].set_ylabel(\"count\")","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-08-09T10:30:05.375445Z","iopub.execute_input":"2021-08-09T10:30:05.375804Z","iopub.status.idle":"2021-08-09T10:30:05.391408Z","shell.execute_reply.started":"2021-08-09T10:30:05.375772Z","shell.execute_reply":"2021-08-09T10:30:05.389468Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_folds(data_folds)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:05.710185Z","iopub.execute_input":"2021-08-09T10:30:05.710854Z","iopub.status.idle":"2021-08-09T10:30:06.679004Z","shell.execute_reply.started":"2021-08-09T10:30:05.710808Z","shell.execute_reply":"2021-08-09T10:30:06.678077Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"section-six\"></a>\n## **Data augmentation using Albumentations**","metadata":{}},{"cell_type":"code","source":"def get_transforms(train: bool=True):\n    if train:\n        return A.Compose([\n            A.HorizontalFlip(p=0.5),\n            A.Rotate(limit=10),\n            A.OneOf([\n                A.HueSaturationValue(), \n                A.RandomBrightnessContrast(),\n                A.CLAHE(p=0.6),\n            ], p=0.4),\n            A.OneOf([\n                A.Blur(blur_limit=3, p=0.5),\n                A.MedianBlur(blur_limit=3, p=0.5),\n                A.GaussNoise(p=0.5),\n                A.Sharpen(p=0.5)\n                ],p=0.4),\n            A.Resize(height=Configs.img_size, width=Configs.img_size, p=1),], \n            bbox_params=A.BboxParams(format='pascal_voc',\n                                     min_area=0, \n                                     min_visibility=0,\n                                     label_fields=['labels'])\n        )\n\n    else:\n        # for validation only resize image\n        return A.Compose([\n            A.Resize(height=Configs.img_size, width=Configs.img_size, p=1),], \n            bbox_params=A.BboxParams(format='pascal_voc',\n                                     min_area=0, \n                                     min_visibility=0,\n                                     label_fields=['labels'])\n        )","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:06.748772Z","iopub.execute_input":"2021-08-09T10:30:06.749083Z","iopub.status.idle":"2021-08-09T10:30:06.760057Z","shell.execute_reply.started":"2021-08-09T10:30:06.749053Z","shell.execute_reply":"2021-08-09T10:30:06.758809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"section-seven\"></a>\n## **Custom dataset**","metadata":{}},{"cell_type":"code","source":"class Covid19Dataset(Dataset):\n    def __init__(self, df, train=True, transform=None):\n        super().__init__()\n        self.df = df\n        self.train = train\n        self.transform = transform\n\n    def __getitem__(self, idx):\n        row = self.df.iloc[idx]\n        img_path = row['png_path']\n        \n        img = cv2.imread(img_path, cv2.IMREAD_COLOR)\n        bboxes = ast.literal_eval(row['pascal_voc_boxes'])\n        if row['num_boxes'] > 0:\n            labels = [row['int_label']]*row['num_boxes']\n        else:\n            labels = [row['int_label']]\n            \n        if self.transform:\n            transformed = self.transform(**{'image': img, 'bboxes': bboxes, 'labels': labels})\n            img = transformed['image']\n            transformed_bboxes = transformed['bboxes']        \n            \n            if row['num_boxes'] > 0:\n                bboxes = transformed_bboxes\n\n        # normalize img\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astype(np.float32)\n        img /= 255.0\n        \n        # convert everything into a torch.Tensor\n        img = torch.as_tensor(img, dtype=torch.float32)\n        bboxes = torch.as_tensor(bboxes, dtype=torch.float32)\n        labels = torch.as_tensor(labels, dtype=torch.int64)\n        idx = torch.tensor([idx])\n        \n        bboxes[:,[0,1,2,3]] = bboxes[:,[1,0,3,2]]  #yxyx: be warning\n        \n        # targets for object detection\n        target = {}\n        target['bbox'] = bboxes\n        target['cls'] = labels\n        target['img_id'] = idx\n                    \n        # permute image to [C,H,W] from [H,W,C] and normalize\n        img = img.permute(2, 0, 1)\n        \n        return img, target, row['img_id']\n\n    def __len__(self):\n        return len(self.df)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:07.470755Z","iopub.execute_input":"2021-08-09T10:30:07.471073Z","iopub.status.idle":"2021-08-09T10:30:07.484948Z","shell.execute_reply.started":"2021-08-09T10:30:07.471044Z","shell.execute_reply":"2021-08-09T10:30:07.4837Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# helper function to get train/validation data by fold\ndef get_dataset_fold(data_folds, fold,train=True):\n    if train:\n        return Covid19Dataset(data_folds.get_train_df(fold), train=True, transform=get_transforms(train))\n    return Covid19Dataset(data_folds.get_val_df(fold), train=False, transform=get_transforms(train))","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:07.905405Z","iopub.execute_input":"2021-08-09T10:30:07.905753Z","iopub.status.idle":"2021-08-09T10:30:07.910958Z","shell.execute_reply.started":"2021-08-09T10:30:07.905717Z","shell.execute_reply":"2021-08-09T10:30:07.910026Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Visualize dataset**","metadata":{}},{"cell_type":"code","source":"train_dataset = get_dataset_fold(data_folds, 0)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:08.51477Z","iopub.execute_input":"2021-08-09T10:30:08.515077Z","iopub.status.idle":"2021-08-09T10:30:08.522862Z","shell.execute_reply.started":"2021-08-09T10:30:08.51505Z","shell.execute_reply":"2021-08-09T10:30:08.522109Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image, target, image_id = train_dataset[3]\nimage = image.numpy()\nimage = np.transpose(image, (1,2,0))\n\nprint(target)\nboxes = target['bbox']\nnew_img = np.copy(image)\nfig, ax = plt.subplots(1, 1, figsize=(16, 8))\n\nfor box in boxes:\n    y1,x1,y2,x2 = box\n    cv2.rectangle(new_img,\n                  (int(x1), int(y1)), (int(x2),  int(y2)), (0, 255, 0), Configs.img_size//200)\n    \nax.set_axis_off()\nax.imshow(new_img)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:09.23293Z","iopub.execute_input":"2021-08-09T10:30:09.233291Z","iopub.status.idle":"2021-08-09T10:30:09.692212Z","shell.execute_reply.started":"2021-08-09T10:30:09.23326Z","shell.execute_reply":"2021-08-09T10:30:09.691375Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"section-eight\"></a>\n## **Metric**","metadata":{}},{"cell_type":"markdown","source":"The competitions metric is PASCAL VOC 2010 mean average precision (mAP) at IoU > 0.5","metadata":{}},{"cell_type":"code","source":"# https://www.kaggle.com/chenyc15/mean-average-precision-metric \ndef calculate_iou(gt, pred):\n    \"\"\"Calculates the Intersection over Union.\n\n    Args:\n        gt: (np.ndarray[Union[int, float]]) coordinates of the ground-truth box\n        pred: (np.ndarray[Union[int, float]]) coordinates of the prdected box\n        form: (str) gt/pred coordinates format\n            - pascal_voc: [xmin, ymin, xmax, ymax]\n            - coco: [xmin, ymin, w, h]\n    Returns:\n        (float) Intersection over union (0.0 <= iou <= 1.0)\n    \"\"\"\n    \n    gt_x1, gt_y1, gt_x2, gt_y2 = gt\n    pred_x1, pred_y1, pred_x2, pred_y2 = pred\n\n    # Calculate overlap area\n    dx = min(gt_x2, pred_x2) - max(gt_x1, pred_x1) + 1\n    \n    if dx < 0:\n        return 0.0\n    \n    dy = min(gt_y2, pred_y2) - max(gt_y1, pred_y1) + 1\n\n    if dy < 0:\n        return 0.0\n\n    overlap_area = dx * dy\n\n    # Calculate union area\n    union_area = (\n            (gt_x2 - gt_x1 + 1) * (gt_y2 - gt_x1 + 1) +\n            (pred_x2 - pred_x1 + 1) * (pred_y2 - pred_y1 + 1) -\n            overlap_area\n    )\n\n    return overlap_area / union_area\n\ndef find_best_match(gts, pred, pred_idx, threshold = 0.5, ious=None):\n    \"\"\"Returns the index of the 'best match' between the\n    ground-truth boxes and the prediction. The 'best match'\n    is the highest IoU. (0.0 IoUs are ignored).\n\n    Args:\n        gts: (List[List[Union[int, float]]]) Coordinates of the available ground-truth boxes\n        pred: (List[Union[int, float]]) Coordinates of the predicted box\n        pred_idx: (int) Index of the current predicted box\n        threshold: (float) Threshold\n        form: (str) Format of the coordinates\n        ious: (np.ndarray) len(gts) x len(preds) matrix for storing calculated ious.\n\n    Return:\n        (int) Index of the best match GT box (-1 if no match above threshold)\n    \"\"\"\n    best_match_iou = -np.inf\n    best_match_idx = -1\n\n    for gt_idx in range(len(gts)):\n        if gts[gt_idx][0] < 0:\n            # Already matched GT-box (set to -1)\n            continue\n        \n        iou = -1 if ious is None else ious[gt_idx][pred_idx]\n\n        if iou < 0:\n            iou = calculate_iou(gts[gt_idx], pred)\n            \n            if ious is not None:\n                ious[gt_idx][pred_idx] = iou\n\n        if iou < threshold:\n            continue\n\n        if iou > best_match_iou:\n            best_match_iou = iou\n            best_match_idx = gt_idx\n\n    return best_match_idx\n\ndef calculate_image_precision_recall(gts, preds, threshold = 0.5):\n    \"\"\"Calculates precision for GT - prediction pairs at one threshold.\n\n    Args:\n        gts: (List[List[Union[int, float]]]) Coordinates of the available ground-truth boxes\n        preds: (List[List[Union[int, float]]]) Coordinates of the predicted boxes,\n               sorted by confidence value (descending)\n        threshold: (float) Threshold\n        form: (str) Format of the coordinates\n        ious: (np.ndarray) len(gts) x len(preds) matrix for storing calculated ious.\n\n    Return:\n        (float) Precision\n    \"\"\"\n    ious = np.ones((len(gts), len(preds))) * -1\n    \n    n = len(preds)\n    tp = 0\n    fp = 0\n    \n    # for pred_idx, pred in enumerate(preds_sorted):\n    for pred_idx in range(n):\n        best_match_gt_idx = find_best_match(gts, preds[pred_idx], pred_idx,\n                                            threshold=threshold, ious=ious)\n\n        if best_match_gt_idx >= 0:\n            # True positive: The predicted box matches a gt box with an IoU above the threshold.\n            tp += 1\n            # Remove the matched GT box\n            gts[best_match_gt_idx] = -1\n\n        else:\n            # No match\n            # False positive: indicates a predicted box had no associated gt box.\n            fp += 1\n    \n    precision = tp / (tp + fp)\n    recall = tp / len(gts)\n    \n    return precision, recall\n\n# https://github.com/rwightman/efficientdet-pytorch/blob/master/effdet/evaluation/metrics.py\ndef compute_average_precision(precision: np.ndarray, recall: np.ndarray):\n    \"\"\"Compute Average Precision according to the definition in VOCdevkit.\n    Precision is modified to ensure that it does not decrease as recall\n    decrease.\n    Args:\n        precision: A float [N, 1] numpy array of precisions\n        recall: A float [N, 1] numpy array of recalls\n    Raises:\n        ValueError: if the input is not of the correct format\n    Returns:\n        average_precison: The area under the precision recall curve. NaN if\n            precision and recall are None.\n    \"\"\"\n    if precision is None:\n        if recall is not None:\n            raise ValueError(\"If precision is None, recall must also be None\")\n        return np.NAN\n\n    if len(precision) != len(recall):\n        raise ValueError(\"precision and recall must be of the same size.\")\n    if not precision.size:\n        return 0.0\n    if np.amin(precision) < 0 or np.amax(precision) > 1:\n        raise ValueError(\"Precision must be in the range of [0, 1].\")\n    if np.amin(recall) < 0 or np.amax(recall) > 1:\n        raise ValueError(\"recall must be in the range of [0, 1].\")\n    if not all(recall[i] <= recall[i + 1] for i in range(len(recall) - 1)):\n        raise ValueError(\"recall must be a non-decreasing array\")\n    \n    # recall sorted in increasing order with values 0-1\n    recall = np.concatenate([[0], recall, [1]])\n    # precision as well, but we need max precision so we don't concatenate with 1 at the end\n    precision = np.concatenate([[0], precision, [0]])  \n\n    # \"smooth\" curves to rectangles by getting the max value\n    for i in range(len(precision) - 2, -1, -1):\n        precision[i] = np.maximum(precision[i], precision[i + 1])\n    \n    # calculate the sum of rectangle areas\n    indices = np.where(recall[1:] != recall[:-1])[0] + 1\n    average_precision = np.sum((recall[indices] - recall[indices - 1]) * precision[indices])\n    return average_precision","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:10.045013Z","iopub.execute_input":"2021-08-09T10:30:10.04533Z","iopub.status.idle":"2021-08-09T10:30:10.059909Z","shell.execute_reply.started":"2021-08-09T10:30:10.045301Z","shell.execute_reply":"2021-08-09T10:30:10.059055Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"section-nine\"></a>\n## **Fitter**","metadata":{}},{"cell_type":"code","source":"class AverageMeter(object):\n    \"\"\"Computes and stores the average and current value\"\"\"\n    def __init__(self):\n        self.reset()\n\n    def reset(self):\n        self.val = 0\n        self.avg = 0\n        self.sum = 0\n        self.count = 0\n\n    def update(self, val, n=1):\n        self.val = val\n        self.sum += val * n\n        self.count += n\n        self.avg = self.sum / self.count","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:11.472298Z","iopub.execute_input":"2021-08-09T10:30:11.472637Z","iopub.status.idle":"2021-08-09T10:30:11.48403Z","shell.execute_reply.started":"2021-08-09T10:30:11.4726Z","shell.execute_reply":"2021-08-09T10:30:11.47933Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Fitter:\n    def __init__(self, dir):\n        self.epoch = 0\n        # create effdet model\n        self.model = get_net()\n        self.device = Configs.device\n        \n        # dir to save outputs\n        self.dir = dir\n        if not os.path.exists(self.dir):\n            os.makedirs(self.dir)\n        \n        self.log_path = os.path.join(self.dir, 'log.txt')\n        self.best_summary_loss = 10**5\n\n        self.optimizer = torch.optim.AdamW(self.model.parameters(), lr=TrainGlobalConfig.lr)\n        self.scheduler = TrainGlobalConfig.SchedulerClass(self.optimizer, **TrainGlobalConfig.scheduler_params)\n        self.log(f'Fitter prepared. Device is {self.device}')\n        \n    def fit(self, fold, train_loader, validation_loader, continue_train=False):\n        if continue_train:\n            path = f'../input/models/object_detection_models/tf_efficientdet_d7/tf_efficientdet_d7_fold{fold}/last-checkpoint.bin'\n            self.load(path)\n        else:\n            self.log(f\"Fold {fold}\")\n            \n        while self.epoch < TrainGlobalConfig.n_epochs:\n            if TrainGlobalConfig.verbose:\n                lr = self.optimizer.param_groups[0]['lr']\n                timestamp = datetime.utcnow().isoformat()\n\n            # train one epoch\n            t = time.time()\n            summary_loss, summary_box_loss, summary_class_loss = self.train_one_epoch(train_loader)\n            # log train losses to console/log file\n            self.log(f'[RESULT]: Train. Epoch: {self.epoch},\\t' + \\\n                     f'total loss: {summary_loss.avg:.5f},\\t' + \\\n                     f'loss_cls: {summary_class_loss.avg:.5f},\\t' + \\\n                     f'loss_box_reg: {summary_box_loss.avg:.5f},\\t' + \\\n                     f'time: {(time.time() - t):.5f}')\n            # log train losses to wandb\n            if not offline:\n                run.log({f\"train/total_loss_fold{fold}\": summary_loss.avg})\n                run.log({f\"train/loss_box_reg_fold{fold}\": summary_box_loss.avg})\n                run.log({f\"train/loss_cls_fold{fold}\": summary_class_loss.avg})\n            \n            # save last checkpoint\n            self.save(f'{self.dir}/last-checkpoint.bin')\n            \n            # validate one epoch\n            t = time.time()\n            summary_loss, summary_box_loss, summary_class_loss, mAP = self.validation_one_epoch(validation_loader)\n            \n            # log val losses to console/log file\n            if (self.epoch+1)%10 == 0 and self.epoch != 0:\n                self.log(f'[RESULT]: Val. Epoch: {self.epoch},\\ttotal loss: {summary_loss.avg:.5f},\\t' + \\\n                     f'loss_cls: {summary_class_loss.avg:.5f},\\t' + \\\n                     f'loss_box_reg: {summary_box_loss.avg:.5f},\\t' + \\\n                     f'time: {(time.time() - t):.5f}\\n' + \\\n                     '-'*100 + \\\n                     f'\\nmAP@IoU=0.5: {mAP},\\n' + \\\n                     '-'*100)\n            else:\n                 self.log(f'[RESULT]: Val. Epoch: {self.epoch},\\ttotal loss: {summary_loss.avg:.5f},\\t' + \\\n                     f'loss_cls: {summary_class_loss.avg:.5f},\\t' + \\\n                     f'loss_box_reg: {summary_box_loss.avg:.5f},\\t' + \\\n                     f'time: {(time.time() - t):.5f}')   \n\n            # log val losses to wandb\n            if not offline:\n                run.log({f\"val/total_loss_fold{fold}\": summary_loss.avg})\n                run.log({f\"val/loss_box_reg_fold{fold}\": summary_box_loss.avg})\n                run.log({f\"val/loss_cls_fold{fold}\": summary_class_loss.avg})\n                if (self.epoch+1)%10 == 0 and self.epoch != 0:\n                    run.log({f\"mAP_fold{fold}/IoU=0.5\": mAP})\n                \n            # update best val losses and save best checkpoint if needed\n            if summary_loss.avg < self.best_summary_loss:\n                self.best_summary_loss = summary_loss.avg\n                self.model.eval()\n                self.save(os.path.join(self.dir, 'best-checkpoint.bin'))\n                if not offline:\n                    wandb.save(os.path.join(self.dir, 'best-checkpoint.bin'))\n                for path in sorted(glob(os.path.join(self.dir, 'best-checkpoint.bin')))[:-3]:\n                    os.remove(path)\n            \n            # perform scheduler step\n            if TrainGlobalConfig.validation_scheduler:\n                self.scheduler.step() #metrics=summary_loss.avg)\n\n            self.epoch += 1\n    \n    def train_one_epoch(self, train_loader):\n        self.model.train()\n        summary_loss = AverageMeter()\n        summary_box_loss = AverageMeter()\n        summary_class_loss = AverageMeter()\n        \n        t = time.time()\n        for step, (images, targets, image_ids) in enumerate(train_loader):\n            if TrainGlobalConfig.verbose:\n                print(f'Train Step {step}/{len(train_loader)},\\t' + \\\n                    f'total_loss: {summary_loss.avg:.5f},\\t' + \\\n                    f'loss_cls: {summary_class_loss.avg:.5f},\\t' + \\\n                    f'loss_box_reg: {summary_box_loss.avg:.5f},\\t' + \\\n                    f'time: {(time.time() - t):.5f}', end='\\r'\n                )\n\n            images = torch.stack(images)\n            images = images.to(self.device).float()\n            batch_size = images.shape[0]\n            boxes = [target['bbox'].to(self.device).float() for target in targets]\n            labels = [target['cls'].to(self.device).float() for target in targets]\n            \n            self.optimizer.zero_grad()\n            \n            # output = {'loss': loss, 'class_loss': class_loss, 'box_loss': box_loss}\n            loss, class_loss, box_loss = self.model(images, boxes, labels)\n            loss.backward()\n\n            summary_loss.update(loss.detach().item(), batch_size)\n            summary_box_loss.update(box_loss.detach().item(), batch_size)\n            summary_class_loss.update(class_loss.detach().item(), batch_size)\n            \n            self.optimizer.step()\n            del images, targets, image_ids\n            torch.cuda.empty_cache()\n            \n        return summary_loss, summary_box_loss, summary_class_loss\n    \n    def validation_one_epoch(self, val_loader):\n        self.model.eval()\n        summary_loss = AverageMeter()\n        summary_box_loss = AverageMeter()\n        summary_class_loss = AverageMeter()\n        \n        t = time.time()\n        precisions = []\n        recalls = []\n            \n        for step, (images, targets, image_ids) in enumerate(val_loader):\n            if TrainGlobalConfig.verbose:\n                print(\n                    f'Val Step {step}/{len(val_loader)}, ' + \\\n                    f'summary_loss: {summary_loss.avg:.5f}, ' + \\\n                    f'time: {(time.time() - t):.5f}', end='\\r'\n                )\n            with torch.no_grad():\n                # predict and get losses for validation batch\n                images = torch.stack(images)\n                batch_size = images.shape[0]\n                images = images.to(self.device).float()\n                boxes = [target['bbox'].to(self.device).float() for target in targets]\n                labels = [target['cls'].to(self.device).float() for target in targets]\n\n                loss, class_loss, box_loss = self.model(images, boxes, labels)\n                \n                summary_loss.update(loss.detach().item(), batch_size)\n                summary_box_loss.update(box_loss.detach().item(), batch_size)\n                summary_class_loss.update(class_loss.detach().item(), batch_size)\n                \n                # evaluate mAP every 10 epochs (9, 19, 29, 39)\n                if (self.epoch+1)%10 == 0 and self.epoch != 0:\n                    # get prediction\n                    eval_model = get_net(train=False)\n                    state_dict = self.model.state_dict()\n                    state_dict['anchors.boxes'] = state_dict.pop('anchor_labeler.anchors.boxes')\n                    eval_model.load_state_dict(state_dict)\n\n                    outputs = eval_model(images, torch.tensor([1]*images.shape[0]).float().cuda())          \n                    for i, (image, gt_boxes) in enumerate(zip(images, boxes)):               \n                        pred_boxes = self.to_xyxy_format(outputs[i].detach().cpu().numpy()[:,:4])  # convert from xywh to xyxy\n                        pred_scores = outputs[i].detach().cpu().numpy()[:,4]\n                        pred_labels = outputs[i].detach().cpu().numpy()[:, 5]\n                        \n                        # sort predictions by score\n                        preds_sorted_idx = np.argsort(pred_scores)[::-1]\n                        preds_sorted_boxes = pred_boxes[preds_sorted_idx]\n                        \n                        precision, recall = calculate_image_precision_recall(gt_boxes.detach().cpu().numpy(), pred_boxes)\n                        precisions.append(precision)\n                        recalls.append(recall)\n                  \n            del images, targets, image_ids\n            torch.cuda.empty_cache()\n        \n        if (self.epoch+1)%10 == 0 and self.epoch != 0:\n            # sort by recall (increasing order)\n            recalls = np.array(recalls)\n            precisions = np.array(precisions)\n            sorted_idx = np.argsort(recalls)\n            recalls = recalls[sorted_idx]\n            precisions = precisions[sorted_idx]\n            mAP = compute_average_precision(precisions, recalls)\n        else:\n            mAP = None\n            \n        return summary_loss, summary_box_loss, summary_class_loss, mAP\n    \n    def to_xyxy_format(self, boxes):   \n        new_boxes = []\n        for box in boxes:\n            x,y,w,h = box\n            x1=x\n            x2=x+w\n            y1=y\n            y2=y+h\n            new_boxes.append(np.array([x1,y1,x2,y2]))\n\n        return np.array(new_boxes)  \n    \n    # save checkpoint to given path\n    def save(self, path):\n        self.model.eval()\n        torch.save({\n            'model_state_dict': self.model.state_dict(),\n            'optimizer_state_dict': self.optimizer.state_dict(),\n            'scheduler_state_dict': self.scheduler.state_dict(),\n            'best_summary_loss': self.best_summary_loss,\n            'epoch': self.epoch,\n        }, path)\n\n    # load checkpoint from given path\n    def load(self, path):\n        checkpoint = torch.load(path)\n        self.model.load_state_dict(checkpoint['model_state_dict'])\n        self.optimizer.load_state_dict(checkpoint['optimizer_state_dict'])\n        self.scheduler.load_state_dict(checkpoint['scheduler_state_dict'])\n        self.best_summary_loss = checkpoint['best_summary_loss']\n        self.epoch = checkpoint['epoch'] + 1\n    \n    # log to console and log file\n    def log(self, message):\n        if TrainGlobalConfig.verbose:\n            print(message)\n        with open(self.log_path, 'a+') as logger:\n            logger.write(f'{message}\\n')","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:15.241047Z","iopub.execute_input":"2021-08-09T10:30:15.241376Z","iopub.status.idle":"2021-08-09T10:30:15.285686Z","shell.execute_reply.started":"2021-08-09T10:30:15.241346Z","shell.execute_reply":"2021-08-09T10:30:15.284763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Create effdet model**","metadata":{}},{"cell_type":"code","source":"def get_net(architecture='tf_efficientdet_d7', train=True):\n    config = get_efficientdet_config(architecture)\n    config.num_classes = len(Configs.thing_classes)\n    config.image_size = Configs.img_size\n    config.gamma = 2\n    net = EfficientDet(config, pretrained_backbone=True)\n    net.class_net = HeadNet(config, num_outputs=config.num_classes)\n    \n    if train:\n        print(config)\n        return DetBenchTrain(net, config).to(Configs.device)\n\n    else:\n        model = DetBenchEval(net, config)\n        model.eval();\n        return model.to(Configs.device)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:16.97362Z","iopub.execute_input":"2021-08-09T10:30:16.97396Z","iopub.status.idle":"2021-08-09T10:30:16.987522Z","shell.execute_reply.started":"2021-08-09T10:30:16.97393Z","shell.execute_reply":"2021-08-09T10:30:16.986515Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<a id=\"section-ten\"></a>\n## **Train**","metadata":{}},{"cell_type":"markdown","source":"**Train configurations**","metadata":{}},{"cell_type":"code","source":"class TrainGlobalConfig:\n    n_epochs = 20\n    num_workers = 8\n    batch_size = 4\n    lr = 0.001\n    verbose = True\n    validation_scheduler = True  \n\n    SchedulerClass = torch.optim.lr_scheduler.MultiStepLR\n    scheduler_params = dict(\n        milestones=[5,10,15], \n        gamma=0.1\n    )","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:18.505895Z","iopub.execute_input":"2021-08-09T10:30:18.506209Z","iopub.status.idle":"2021-08-09T10:30:18.512816Z","shell.execute_reply.started":"2021-08-09T10:30:18.506183Z","shell.execute_reply":"2021-08-09T10:30:18.511737Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Run train** ","metadata":{}},{"cell_type":"code","source":"def collate_fn(batch):\n    return tuple(zip(*batch))\n\ndef run_training(fold, train_dataset, val_dataset, continue_train=False):\n    # create tain/validation data loaders\n    train_loader = torch.utils.data.DataLoader(\n        train_dataset,\n        batch_size=TrainGlobalConfig.batch_size,\n        sampler=RandomSampler(train_dataset),\n        pin_memory=False,\n        drop_last=True,\n        num_workers=TrainGlobalConfig.num_workers,\n        collate_fn=collate_fn,\n    )\n    val_loader = torch.utils.data.DataLoader(\n        val_dataset, \n        batch_size=TrainGlobalConfig.batch_size,\n        num_workers=TrainGlobalConfig.num_workers,\n        shuffle=False,\n        sampler=SequentialSampler(val_dataset),\n        pin_memory=False,\n        collate_fn=collate_fn,\n    )\n    \n    # create fitter for model\n    fitter = Fitter(f'./effdet_d7_fold{fold}')\n    # run train by calling fit function\n    fitter.fit(fold, train_loader, val_loader, continue_train)","metadata":{"execution":{"iopub.status.busy":"2021-08-09T10:30:19.397855Z","iopub.execute_input":"2021-08-09T10:30:19.398178Z","iopub.status.idle":"2021-08-09T10:30:19.409235Z","shell.execute_reply.started":"2021-08-09T10:30:19.39815Z","shell.execute_reply":"2021-08-09T10:30:19.408297Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Run train for 5 models over the different folds**","metadata":{}},{"cell_type":"code","source":"for fold in range(Configs.n_folds):\n    train_dataset = get_dataset_fold(data_folds, fold)\n    val_dataset = get_dataset_fold(data_folds, fold, train=False)\n\n    run_training(fold, train_dataset, val_dataset)","metadata":{"execution":{"iopub.status.busy":"2021-08-06T21:45:52.979776Z","iopub.status.idle":"2021-08-06T21:45:52.980586Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**zip results and save files**","metadata":{}},{"cell_type":"code","source":"!zip -r ./effdet_d7_fold0.zip ./effdet_d7_fold0\n!zip -r ./effdet_d7_fold1.zip ./effdet_d7_fold1\n!zip -r ./effdet_d7_fold2.zip ./effdet_d7_fold2\n!zip -r ./effdet_d7_fold3.zip ./effdet_d7_fold3\n!zip -r ./effdet_d7_fold4.zip ./effdet_d7_fold4","metadata":{"execution":{"iopub.status.busy":"2021-08-06T21:45:52.981786Z","iopub.status.idle":"2021-08-06T21:45:52.982598Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from IPython.display import FileLink\nFileLink('./effdet_d7_fold0.zip')","metadata":{"execution":{"iopub.status.busy":"2021-08-06T21:45:52.983825Z","iopub.status.idle":"2021-08-06T21:45:52.984639Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"FileLink('./effdet_d7_fold1.zip')","metadata":{"execution":{"iopub.status.busy":"2021-08-06T21:45:52.985844Z","iopub.status.idle":"2021-08-06T21:45:52.986714Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"FileLink('./effdet_d7_fold2.zip')","metadata":{"execution":{"iopub.status.busy":"2021-08-06T21:45:52.98797Z","iopub.status.idle":"2021-08-06T21:45:52.988778Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"FileLink('./effdet_d7_fold3.zip')","metadata":{"execution":{"iopub.status.busy":"2021-08-06T21:45:52.99001Z","iopub.status.idle":"2021-08-06T21:45:52.990816Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"FileLink('./effdet_d7_fold4.zip')","metadata":{"execution":{"iopub.status.busy":"2021-08-06T21:45:52.992022Z","iopub.status.idle":"2021-08-06T21:45:52.992856Z"},"trusted":true},"execution_count":null,"outputs":[]}]}