{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport numpy as np \nimport pandas as pd \nfrom datetime import datetime\nimport time\nimport random\nfrom tqdm import tqdm_notebook as tqdm # progress bar\nimport matplotlib.pyplot as plt\n\n# torch\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset,DataLoader\nfrom torch.utils.data.sampler import SequentialSampler, RandomSampler\n\n# torchvision\nimport torchvision\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor, FasterRCNN\nfrom torchvision.models.detection.backbone_utils import resnet_fpn_backbone\n\n# sklearn\nfrom sklearn.model_selection import StratifiedKFold\n\n# CV\nimport cv2\n\n# Albumenatations\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\n\n# Glob\nfrom glob import glob\n\nimport warnings\nwarnings.filterwarnings('ignore') #Ignore \"future\" warnings and Data-Frame-Slicing warnings.","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-11-06T09:47:15.849153Z","iopub.execute_input":"2021-11-06T09:47:15.849487Z","iopub.status.idle":"2021-11-06T09:47:18.467305Z","shell.execute_reply.started":"2021-11-06T09:47:15.849456Z","shell.execute_reply":"2021-11-06T09:47:18.466146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install livelossplot --quiet","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:47:43.51753Z","iopub.execute_input":"2021-11-06T09:47:43.517878Z","iopub.status.idle":"2021-11-06T09:47:52.002278Z","shell.execute_reply.started":"2021-11-06T09:47:43.51784Z","shell.execute_reply":"2021-11-06T09:47:52.001014Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Lets check our Hardware first\ngpu_info = !nvidia-smi\ngpu_info = '\\n'.join(gpu_info)\nif gpu_info.find('failed') >= 0:\n    print('Select the Runtime > \"Change runtime type\" menu to enable a GPU accelerator, ')\n    print('and then re-execute this cell.')\nelse:\n    print(gpu_info)","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:47:56.200358Z","iopub.execute_input":"2021-11-06T09:47:56.20072Z","iopub.status.idle":"2021-11-06T09:47:56.259417Z","shell.execute_reply.started":"2021-11-06T09:47:56.200688Z","shell.execute_reply":"2021-11-06T09:47:56.258282Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"n_folds = 5\nseed = 2020\nnum_classes = 2\nBATCH_SIZE = 4\nLR = 0.05\n\nEPOCHS = 20\nimg_size_training = 800\n\nAdam = False\nif Adam: \n    Adam_config = {\"lr\" : 0.001, \"betas\" : (0.9, 0.999), \"eps\" : 1e-08}\nelse:\n    SGD_config = {\"lr\" : 0.001, \"momentum\" : 0.9, \"weight_decay\" : 0.001}","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:47:56.801091Z","iopub.execute_input":"2021-11-06T09:47:56.801457Z","iopub.status.idle":"2021-11-06T09:47:56.808432Z","shell.execute_reply.started":"2021-11-06T09:47:56.801423Z","shell.execute_reply":"2021-11-06T09:47:56.807497Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = True\nseed_everything(seed)","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:47:59.310118Z","iopub.execute_input":"2021-11-06T09:47:59.310539Z","iopub.status.idle":"2021-11-06T09:47:59.321563Z","shell.execute_reply.started":"2021-11-06T09:47:59.310499Z","shell.execute_reply":"2021-11-06T09:47:59.320593Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"bboxes_df = pd.read_csv('../input/global-wheat-detection/train.csv')\nbboxes_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:47:59.840348Z","iopub.execute_input":"2021-11-06T09:47:59.840693Z","iopub.status.idle":"2021-11-06T09:48:00.119157Z","shell.execute_reply.started":"2021-11-06T09:47:59.840663Z","shell.execute_reply":"2021-11-06T09:48:00.118301Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get an array of arrays of all bboxes in the form [[x], [y], [w], [h]]\nbboxs = np.stack(bboxes_df['bbox'].apply(lambda x: np.fromstring(x[1:-1], sep = ',')))\n# save the values in the respective column\nfor i, column in enumerate(['x', 'y', 'w', 'h']):\n    bboxes_df[column] = bboxs[:,i]\nbboxes_df.drop(columns=['bbox'], inplace=True)\nbboxes_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:48:00.29334Z","iopub.execute_input":"2021-11-06T09:48:00.293975Z","iopub.status.idle":"2021-11-06T09:48:01.023661Z","shell.execute_reply.started":"2021-11-06T09:48:00.293929Z","shell.execute_reply":"2021-11-06T09:48:01.02277Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Creating stratified Folds\nskf = StratifiedKFold(n_splits = n_folds, shuffle = True, random_state = seed)\n\ndf_folds = bboxes_df[['image_id']].copy()\ndf_folds.loc[:, 'bbox_count'] = 1\n\n# get number of bboxes for each image_id\ndf_folds = df_folds.groupby('image_id').count()\n\n# add [source] column\ndf_folds.loc[:, 'source'] = bboxes_df[['image_id', 'source']].groupby('image_id').min()['source']\n\n# concat two arays of strings: source and (_bboxcount // 15) to separate into folds\ndf_folds.loc[:, 'stratify_group'] = np.char.add(\n    df_folds['source'].values.astype(str),\n    df_folds['bbox_count'].apply(lambda x: f'_{x // 15}').values.astype(str))\ndf_folds.loc[:, 'fold'] = 0\n\nfor fold_number, (train_index, val_index) in enumerate(skf.split(        \n        X = df_folds.index,\n        y = df_folds['stratify_group'])):\n    df_folds.loc[df_folds.iloc[val_index].index, 'fold'] = fold_number","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:48:01.095673Z","iopub.execute_input":"2021-11-06T09:48:01.096011Z","iopub.status.idle":"2021-11-06T09:48:01.521278Z","shell.execute_reply.started":"2021-11-06T09:48:01.095978Z","shell.execute_reply":"2021-11-06T09:48:01.520489Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DIR_TRAIN = '../input/global-wheat-detection/train'\n\nclass WheatDataset(Dataset):\n    def __init__(self,image_ids,dataframe,transforms = None):\n        self.image_ids = image_ids\n        self.df = dataframe\n        self.transforms = transforms\n        \n        \n    def __len__(self) -> int:\n        return self.image_ids.shape[0]\n    \n    def __getitem__(self,index):\n        image_id = self.image_ids[index]\n        records = self.df[self.df['image_id'] == image_id]\n        \n        image = cv2.imread(f'{DIR_TRAIN}/{image_id}.jpg', cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0\n        \n        # converting from Coco to Pascal_voc format        \n        boxes = records[['x', 'y', 'w', 'h']].values\n        boxes[:, 2] = boxes[:, 0] + boxes[:, 2]\n        boxes[:, 3] = boxes[:, 1] + boxes[:, 3]\n        \n        # calculate area of bbox\n        area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])\n        area = torch.as_tensor(area, dtype=torch.float32)\n        \n        # there is only one target class: wheat \n        labels = torch.ones((boxes.shape[0],), dtype=torch.int64)\n        \n        \"\"\"\n        What is 'iscrowd' meaning and doing?:\n        iscrowd: For single, seperable objects iscrowd is set to zero.\n        If we want to do segmentation, for a closeby group or collection of objects\n        in the image, we set iscrowd=1, in which case RLE is used.\n        \"\"\"\n        # suppose all instances are not crowd\n        iscrowd = torch.zeros((records.shape[0],), dtype=torch.int64)\n        \n        target = {}\n        target['boxes'] = boxes\n        target['labels'] = labels\n        target['image_id'] = torch.tensor([index])\n        target['area'] = area\n        target['iscrowd'] = iscrowd\n        \n        if self.transforms:\n            sample = {\n                'image': image,\n                'bboxes': target['boxes'],\n                'labels': labels\n            }\n            sample = self.transforms(**sample)\n            image = sample['image']\n            \n            # if this creates issues, use target['boxes'] = torch.as_tensor(sample['bboxes']) instead of below line\n            # target['boxes'] = torch.stack(tuple(map(torch.tensor, zip(*sample['bboxes'])))).permute(1, 0)\n            target['boxes'] = torch.as_tensor(sample['bboxes'])\n            target['boxes'] = target['boxes'].float()\n        \n        return image, target, image_id","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:48:01.735726Z","iopub.execute_input":"2021-11-06T09:48:01.7361Z","iopub.status.idle":"2021-11-06T09:48:01.754126Z","shell.execute_reply.started":"2021-11-06T09:48:01.736067Z","shell.execute_reply":"2021-11-06T09:48:01.753052Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## Albumentations\n\ndef get_train_transforms():\n    return A.Compose(\n        [   A.OneOf([\n                A.HueSaturationValue(hue_shift_limit = 0.2, \n                                     sat_shift_limit = 0.2,\n                                     val_shift_limit = 0.2,\n                                     p = 0.3), \n            \n                A.RandomBrightnessContrast(brightness_limit = 0.2,                                             \n                                           contrast_limit = 0.2,\n                                           p = 0.3),\n                # RGB shift normally expects not-normalized images, so make sure to normalize the RGB shift!\n                A.RGBShift(r_shift_limit = 20/255, \n                           g_shift_limit = 20/255, \n                           b_shift_limit = 10/255,\n                           p = 0.3)\n            ], \n            p = 0.2),\n         \n            A.OneOf([\n                A.RandomGamma(gamma_limit = (80, 120),\n                              p = 0.3),\n                A.Blur(p = 0.6),\n                A.GaussNoise(var_limit = (0.01, 0.05), mean = 0, p = 0.05),\n                A.ToGray(p = 0.05)\n                ],\n                p = 0.1),\n\n            A.OneOf([\n                A.HorizontalFlip(p = 1), \n                A.VerticalFlip(p = 1),  \n                A.Transpose(p = 1),                \n                A.RandomRotate90(p = 1)\n                ], \n                p = 0.7),  \n         \n            A.RandomFog(fog_coef_lower = 0.1,\n                        fog_coef_upper = 0.2,\n                        p = 0.02),   \n         \n            A.RandomSizedBBoxSafeCrop(img_size_training, \n                                      img_size_training, \n                                      p = 0.05),         \n            A.Resize(height = img_size_training, \n                     width = img_size_training, \n                     p = 1),\n         \n            A.Cutout(num_holes = random.randint(1, 6),\n                     max_h_size = 64, \n                     max_w_size = 64,\n                     fill_value = 0, \n                     p = 0.15),\n         \n            ToTensorV2(p = 1.0),\n        ],\n        p = 1.0, bbox_params = {'format': 'pascal_voc', 'label_fields': ['labels']})\n\ndef get_valid_transforms():\n    return A.Compose([\n        A.Resize(height = img_size_training,\n                 width = img_size_training,\n                 p = 1),\n        \n        ToTensorV2(p = 1.0)\n    ], bbox_params = {'format': 'pascal_voc', 'label_fields': ['labels']})","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:48:02.187709Z","iopub.execute_input":"2021-11-06T09:48:02.188087Z","iopub.status.idle":"2021-11-06T09:48:02.203677Z","shell.execute_reply.started":"2021-11-06T09:48:02.188054Z","shell.execute_reply":"2021-11-06T09:48:02.202794Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## In several occasions in the next sections we will need to create a dataset and a dataloader.\n# In this section we create helper functions for this to stay DRY (dont repeast yourself).\n\ndef get_validation_datset(df):\n    return WheatDataset(\n    image_ids = df.index.values,\n    dataframe = bboxes_df,\n    transforms = get_valid_transforms()\n    )\n    \ndef get_train_datset(df):    \n    return WheatDataset(\n    image_ids = df.index.values,\n    dataframe = bboxes_df,\n    transforms = get_train_transforms()\n    )\n\ndef get_validation_data_loader(valid_dataset, batch_size = 16):\n    return DataLoader(\n    valid_dataset,\n    batch_size = batch_size,\n    shuffle = False,\n    num_workers = 4,\n    collate_fn = collate_fn\n    )    \n\ndef get_train_data_loader(train_dataset, batch_size = 16):\n    return DataLoader(\n    train_dataset,\n    batch_size = batch_size,\n    shuffle = False,\n    num_workers = 4,\n    collate_fn = collate_fn\n    ) \n\ndef collate_fn(batch):\n    return tuple(zip(*batch))","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:48:02.782646Z","iopub.execute_input":"2021-11-06T09:48:02.783052Z","iopub.status.idle":"2021-11-06T09:48:02.791799Z","shell.execute_reply.started":"2021-11-06T09:48:02.783017Z","shell.execute_reply":"2021-11-06T09:48:02.790774Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## Lets check some augmentations!\n\n# get a fold\ndf_train = df_folds[df_folds['fold'] != 0]\n\n# create train dataset and data-loader\ntrain_dataset = get_train_datset(df_train)\ntrain_data_loader = get_train_data_loader(train_dataset, batch_size = 16)\n\ndevice=torch.device('cuda')\nimages, targets, image_ids = next(iter(train_data_loader))\n\nimages = list(image.to(device) for image in images)\ntargets = [{k: v.to(device) for k, v in t.items()} for t in targets]\n\n# plot some augmentations!\nfig, ax = plt.subplots(figsize=(20, 20),  \n                       nrows = 4,\n                       ncols = 4)\nfor i in range (16):    \n    boxes = targets[i]['boxes'].cpu().numpy().astype(np.int32)\n    sample = images[i].permute(1,2,0).cpu().numpy()\n    for box in boxes:\n        cv2.rectangle(sample,\n                      (box[0], box[1]),\n                      (box[2], box[3]),\n                      (220, 0, 0), 3)\n    \n    ax[i // 4][i % 4].imshow(sample)   ","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:48:03.150251Z","iopub.execute_input":"2021-11-06T09:48:03.15058Z","iopub.status.idle":"2021-11-06T09:48:19.297276Z","shell.execute_reply.started":"2021-11-06T09:48:03.15055Z","shell.execute_reply":"2021-11-06T09:48:19.296044Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_model():    \n    # load pre-trained model incl. head\n    model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True)\n\n    # get number of input features for the classifier custom head\n    in_features = model.roi_heads.box_predictor.cls_score.in_features\n\n    # replace the pre-trained head with a new one\n    model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)\n\n    return model","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:48:19.299149Z","iopub.execute_input":"2021-11-06T09:48:19.299477Z","iopub.status.idle":"2021-11-06T09:48:19.306485Z","shell.execute_reply.started":"2021-11-06T09:48:19.299444Z","shell.execute_reply":"2021-11-06T09:48:19.305714Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class AverageMeter(object):\n    \"\"\"Computes and stores the average and current value\"\"\"\n    # initialize and reset to zero\n    def __init__(self):\n        self.reset()\n\n    # reset everything to zero    \n    def reset(self):\n        self.val = 0\n        self.avg = 0\n        self.sum = 0\n        self.count = 0\n\n    # update loss after each batch\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-11-06T09:48:19.30777Z","iopub.execute_input":"2021-11-06T09:48:19.308284Z","iopub.status.idle":"2021-11-06T09:48:19.317304Z","shell.execute_reply.started":"2021-11-06T09:48:19.308246Z","shell.execute_reply":"2021-11-06T09:48:19.316199Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train_fn(data_loader,model,optimizer,device,scheduler,epoch):\n    # set pytorch to train mode, in which the model will return losses\n    model.train()\n        \n    summary_loss = AverageMeter()\n    \n    tk0 = tqdm(data_loader, total = len(data_loader))\n    \n    # actual training steps\n    for step, (images, targets, image_ids) in enumerate(tk0):   \n        \n        # if targets is empty, create empty target tensor        \n        if len(targets) < 1:\n            target['boxes'] = torch.zeros((0, 4))\n            \n        # prepare data\n        images = list(image.to(device) for image in images)\n        targets = [{k: v.to(device) for k, v in t.items()} for t in targets]        \n\n        # calculate loss        \n        loss_dict = model(images, targets)        \n        losses = sum(loss for loss in loss_dict.values())\n                \n        # resets gradients of all optimized Tensors before next training step\n        optimizer.zero_grad() \n        \n        # computes derivative of the loss w.r.t. the parameters \n        losses.backward() \n        \n        # updates the parameter based on the gradients from previous step\n        optimizer.step()\n        \n        # updates the scheduler if it's defined\n        if scheduler is not None:\n            scheduler.step()\n            \n        summary_loss.update(losses.item(),BATCH_SIZE)\n        tk0.set_postfix(loss = summary_loss.avg)\n        \n    return summary_loss","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:48:19.715221Z","iopub.execute_input":"2021-11-06T09:48:19.715556Z","iopub.status.idle":"2021-11-06T09:48:19.727199Z","shell.execute_reply.started":"2021-11-06T09:48:19.715526Z","shell.execute_reply":"2021-11-06T09:48:19.726101Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def calculate_iou(gt, pr, form='pascal_voc') -> float:\n    \"\"\"Calculates the Intersection over Union.\n\n    Args:\n        gt: (np.ndarray[Union[int, float]]) coordinates of the ground-truth box\n        pr: (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    if form == 'coco':\n        gt = gt.copy()\n        pr = pr.copy()\n\n        gt[2] = gt[0] + gt[2]\n        gt[3] = gt[1] + gt[3]\n        pr[2] = pr[0] + pr[2]\n        pr[3] = pr[1] + pr[3]\n\n    # Calculate overlap area\n    dx = min(gt[2], pr[2]) - max(gt[0], pr[0]) + 1\n    \n    if dx < 0:\n        return 0.0\n    dy = min(gt[3], pr[3]) - max(gt[1], pr[1]) + 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[2] - gt[0] + 1) * (gt[3] - gt[1] + 1) +\n            (pr[2] - pr[0] + 1) * (pr[3] - pr[1] + 1) -\n            overlap_area\n    )\n\n    return overlap_area / union_area\n\ndef find_best_match(gts, pred, pred_idx, threshold = 0.5, form = 'pascal_voc', ious=None) -> int:\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    for gt_idx in range(len(gts)):\n        \n        if gts[gt_idx][0] < 0:\n            # Already matched GT-box\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, form = form)\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_precision(gts, preds, threshold = 0.5, form = 'coco', ious=None) -> float:\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    n = len(preds)\n    tp = 0\n    fp = 0\n    \n    for pred_idx in range(n):\n\n        best_match_gt_idx = find_best_match(gts, preds[pred_idx], pred_idx,\n                                            threshold=threshold, form=form, 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        else:\n            # No match\n            # False positive: indicates a predicted box had no associated gt box.\n            fp += 1\n\n    # False negative: indicates a gt box had no associated predicted box.\n    fn = (gts.sum(axis=1) > 0).sum()\n\n    return tp / (tp + fp + fn)\n\n\n\ndef calculate_image_precision(gts, preds, thresholds = (0.5, ), form = 'coco') -> float:\n    \"\"\"Calculates image precision.\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        thresholds: (float) Different thresholds\n        form: (str) Format of the coordinates\n\n    Return:\n        (float) Precision\n    \"\"\"\n    n_threshold = len(thresholds)\n    image_precision = 0.0\n    \n    ious = np.ones((len(gts), len(preds))) * -1\n    # ious = None\n\n    for threshold in thresholds:\n        precision_at_threshold = calculate_precision(gts.copy(), \n                                                     preds, \n                                                     threshold = threshold,\n                                                     form = form,\n                                                     ious = ious)\n        image_precision += precision_at_threshold / n_threshold\n\n    return image_precision\n","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-11-06T09:48:19.803538Z","iopub.execute_input":"2021-11-06T09:48:19.803873Z","iopub.status.idle":"2021-11-06T09:48:19.828648Z","shell.execute_reply.started":"2021-11-06T09:48:19.803821Z","shell.execute_reply":"2021-11-06T09:48:19.827643Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def eval_fn(data_loader, model, device):\n    model.eval()\n    summary_loss_eval = AverageMeter()\n    iou_thresholds = [x for x in np.arange(0.5, 0.76, 0.05)]\n    validation_image_precisions = []\n    \n    with torch.no_grad():\n        \n        tk0 = tqdm(data_loader, total=len(data_loader))\n        for step, (images, targets, image_ids) in enumerate(tk0):\n            \n            images = list(image.to(device) for image in images)\n            targets = [{k: v.to(device) for k, v in t.items()} for t in targets]\n            \n            \"\"\"\n            In model.train() mode, model(images)  is returning losses.\n            We are using model.eval() mode --> it will return boxes and scores. \n            \"\"\"\n            outputs = model(images)\n\n            for i, image in enumerate(images):\n                boxes = outputs[i]['boxes'].data.cpu().numpy()\n                scores = outputs[i]['scores'].data.cpu().numpy()\n                gt_boxes = targets[i]['boxes'].cpu().numpy()\n                preds_sorted_idx = np.argsort(scores)[::-1]\n                preds_sorted = boxes[preds_sorted_idx]\n                image_precision = calculate_image_precision(preds_sorted,\n                                                            gt_boxes,\n                                                            thresholds = iou_thresholds,\n                                                            form = 'pascal_voc')\n                validation_image_precisions.append(image_precision)\n\n    valid_prec = np.mean(validation_image_precisions)\n        \n    return valid_prec","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:48:27.590575Z","iopub.execute_input":"2021-11-06T09:48:27.590931Z","iopub.status.idle":"2021-11-06T09:48:27.603155Z","shell.execute_reply.started":"2021-11-06T09:48:27.590898Z","shell.execute_reply":"2021-11-06T09:48:27.60204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from livelossplot import PlotLosses\n\ndef run(fold):\n    summary_loss = AverageMeter()\n    liveloss = PlotLosses()\n    \n    # split data in training and validation set based on the fold\n    df_train = df_folds[df_folds['fold'] != fold]\n    df_valid = df_folds[df_folds['fold'] == fold]\n    \n    # get data loaders\n    train_dataset = get_train_datset(df_train)\n    train_data_loader = get_train_data_loader(train_dataset, batch_size = BATCH_SIZE)\n    \n    validation_dataset = get_validation_datset(df_valid)\n    validation_data_loader = get_validation_data_loader(validation_dataset, batch_size = 16)\n          \n    # get GPU device\n    device = torch.device('cuda')\n    \n    # get model\n    model = get_model()\n    model = model.to(device)\n    \n    # get all trainable parameters for the optimizer\n    params = [p for p in model.parameters() if p.requires_grad]\n    \n    # get the configured optimizer\n    if Adam:\n        optimizer = torch.optim.Adam(params, **Adam_config)\n    else:\n        optimizer = torch.optim.SGD(params, **SGD_config)\n        \n    best_precision = 0.01\n    lpllogs = {}\n    for epoch in range(EPOCHS):\n        \n        # reset loss for each new epoch\n        summary_loss.reset()\n                                \n        train_loss = train_fn(train_data_loader, \n                              model, optimizer, \n                              device, \n                              scheduler = None, \n                              epoch = epoch)\n        lpllogs['train_loss'] = train_loss.avg\n        \n        valid_precision = eval_fn(validation_data_loader, \n                                  model,\n                                  device)\n        lpllogs['val_precision'] = valid_precision\n        liveloss.update(lpllogs)\n        liveloss.send()\n        \n        print('|EPOCH {}| TRAIN_LOSS {}| VAL_PRECISION {}|'.format(\n            epoch+1,\n            train_loss.avg,\n            valid_precision))\n        \n        if valid_precision > best_precision:\n            best_precision = valid_precision\n            print('Best model found for Epoch {}'.format(epoch+1))\n            torch.save(model.state_dict(), f'FRCNN_best_{fold}.pth')","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:52:41.110427Z","iopub.execute_input":"2021-11-06T09:52:41.110913Z","iopub.status.idle":"2021-11-06T09:52:41.149178Z","shell.execute_reply.started":"2021-11-06T09:52:41.110871Z","shell.execute_reply":"2021-11-06T09:52:41.14824Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"run(fold = 0)","metadata":{"execution":{"iopub.status.busy":"2021-11-06T09:52:42.91581Z","iopub.execute_input":"2021-11-06T09:52:42.916173Z","iopub.status.idle":"2021-11-06T12:11:05.985166Z","shell.execute_reply.started":"2021-11-06T09:52:42.916137Z","shell.execute_reply":"2021-11-06T12:11:05.983064Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def view_sample(df_valid,model,device):\n    '''\n    Code merged from Peter's & KnowNothing's great Kernels\n    https://www.kaggle.com/pestipeti/pytorch-starter-fasterrcnn-train\n    https://www.kaggle.com/tanulsingh077/end-to-end-object-detection-with-transformers-detr\n    '''\n    \n    validation_dataset = get_validation_datset(df_valid)\n    validation_data_loader = get_validation_data_loader(validation_dataset, batch_size = 16)\n    \n    # not needed\n    # images, targets, image_ids = next(iter(validation_data_loader))\n    # images = list(img.to(device) for img in images)\n    # targets = [{k: v.to(device) for k, v in t.items()} for t in targets]\n    \n    #_,h,w = images[0].shape # for de normalizing images\n    \n    images, targets, image_ids = next(iter(validation_data_loader))\n    images = list(img.to(device) for img in images)\n    targets = [{k: v.to(device) for k, v in t.items()} for t in targets]\n    \n    boxes_gt = targets[0]['boxes'].cpu().numpy().astype(np.int32)\n    sample = images[0].permute(1,2,0).cpu().numpy()\n    \n    model.to(device)\n    model.eval()\n    cpu_device = torch.device(\"cpu\")\n    #model.cuda()\n\n    outputs = model(images)\n    outputs = [{k: v.to(cpu_device) for k, v in t.items()} for t in outputs]\n    \n    fig, ax = plt.subplots(1, 1, figsize=(16, 8))\n\n    # paint ground_truth boxes (red)\n    for box in boxes_gt:\n        cv2.rectangle(sample,\n                      (box[0], box[1]),\n                      (box[2], box[3]),\n                      (220, 0, 0), 3)\n        \n    boxes = outputs[0]['boxes'].data.cpu().numpy()\n    scores = outputs[0]['scores'].data.cpu().numpy()\n          \n    # paint predicted boxes (blue)\n    pred_box_count = 0\n    for box,p in zip(boxes,scores):        \n        if p >0.5:\n            pred_box_count += 1\n            color = (0,0,220) \n            cv2.rectangle(sample,\n                  (box[0], box[1]),\n                  (box[2], box[3]),\n                  color, 2)\n    \n    \n    print(f\"Predicted {pred_box_count} BBoxes (blue); Number of GT BBoxes (red) : {len(boxes_gt)}\")\n    ax.set_axis_off()\n    ax.imshow(sample)       \n\n  ","metadata":{"execution":{"iopub.status.busy":"2021-11-06T12:11:09.383282Z","iopub.execute_input":"2021-11-06T12:11:09.383613Z","iopub.status.idle":"2021-11-06T12:11:09.400757Z","shell.execute_reply.started":"2021-11-06T12:11:09.383582Z","shell.execute_reply":"2021-11-06T12:11:09.399858Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = get_model()\n# load the best saved weights\nmodel.load_state_dict(torch.load(\"./FRCNN_best_0.pth\"))\n# show a prediction\nview_sample(df_folds[df_folds['fold'] == 0],\n            model = model,\n            device = torch.device('cuda'))","metadata":{"execution":{"iopub.status.busy":"2021-11-06T12:11:17.943611Z","iopub.execute_input":"2021-11-06T12:11:17.943951Z","iopub.status.idle":"2021-11-06T12:11:25.122904Z","shell.execute_reply.started":"2021-11-06T12:11:17.943914Z","shell.execute_reply":"2021-11-06T12:11:25.121933Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}