{"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":"## Image Object detection - Introduction\nThe data consists of images extracted from videos (3 videos) which contains star fish. These videos are captured by surfers using camera's under water. we will be \n- splitting the data into train (video_0 and video_1) and val (video_2)\n- implement pytorch dataset.\n- implement transforms for object detection.\n- implement pytorch lightning data loader.\n- implement metrics - f2score\n- implement Lightning module for training and validation.\n- train the model for n_epochs. \n\nThis is a very minimilistic implementation. I got a validation score of 0.431 f-score.\n\nI have used this Pytorch [Faster-RCNN](https://pytorch.org/tutorials/intermediate/torchvision_tutorial.html) tutorial and refactored the code using lightning module.","metadata":{}},{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n\nimport torch \nimport torchvision\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport pytorch_lightning as pl\n\nfrom torchvision.transforms import functional as F\nfrom torchvision.transforms import transforms as T\nfrom torchvision.models.detection.anchor_utils import AnchorGenerator\nfrom torchvision.models.detection.faster_rcnn import RPNHead, MultiScaleRoIAlign, TwoMLPHead, FastRCNNPredictor\n\nfrom torch import nn, Tensor\nfrom typing  import Optional, Dict, Tuple, List, Union\nfrom dataclasses import dataclass, asdict \nfrom pathlib import Path, PosixPath\nfrom PIL import Image\nfrom pytorch_lightning.callbacks import ModelCheckpoint\nfrom pytorch_lightning.callbacks.progress import TQDMProgressBar\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-01-19T17:15:02.829202Z","iopub.status.idle":"2022-01-19T17:15:02.829526Z","shell.execute_reply.started":"2022-01-19T17:15:02.829376Z","shell.execute_reply":"2022-01-19T17:15:02.829397Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f\"lightning: {pl.__version__}\")\nprint(f\"torch: {torch.__version__}\")\nprint(f\"torchvision: {torchvision.__version__}\")","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.831007Z","iopub.status.idle":"2022-01-19T17:15:02.831523Z","shell.execute_reply.started":"2022-01-19T17:15:02.831279Z","shell.execute_reply":"2022-01-19T17:15:02.831304Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### List all the files","metadata":{}},{"cell_type":"code","source":"root = Path(\"/kaggle/input/tensorflow-great-barrier-reef/\")\nprint(root)\nlist(root.iterdir())","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.832871Z","iopub.status.idle":"2022-01-19T17:15:02.833334Z","shell.execute_reply.started":"2022-01-19T17:15:02.833111Z","shell.execute_reply":"2022-01-19T17:15:02.833133Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- read the train dataset","metadata":{}},{"cell_type":"code","source":"df = pd.read_csv(root / \"train.csv\")\nprint(df.shape)\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.835455Z","iopub.status.idle":"2022-01-19T17:15:02.836436Z","shell.execute_reply.started":"2022-01-19T17:15:02.836189Z","shell.execute_reply":"2022-01-19T17:15:02.836214Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create train and test data\n- We can ignore images which doesn't have star-fish (for training) as these cannot be used for training object detection models.\n- we will consider \"video_0\", \"video_1\" for training and \"video_2\" for validation","metadata":{}},{"cell_type":"code","source":"df_only_annots = df[df[\"annotations\"] != \"[]\"].reset_index(drop=True)\ntrain_df = df_only_annots[df_only_annots[\"video_id\"] !=2].reset_index(drop=True)\nval_df = df_only_annots[df_only_annots[\"video_id\"] ==2].reset_index(drop=True)\nprint(df_only_annots.shape, train_df.shape, val_df.shape)","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.83766Z","iopub.status.idle":"2022-01-19T17:15:02.838629Z","shell.execute_reply.started":"2022-01-19T17:15:02.83829Z","shell.execute_reply":"2022-01-19T17:15:02.838314Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define the dataset. ","metadata":{}},{"cell_type":"code","source":"## Define a dataset \nclass GBRDataset(torch.utils.data.Dataset):\n    def __init__(self, root, df, transforms):\n        self.root = root\n        self.df = df \n        self.transforms = transforms\n    \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        meta = self.df.iloc[idx]\n        video_id = f\"video_{meta['video_id']}\"\n        ## read image \n        loc = self.root / \"train_images\" / video_id / (str(meta[\"video_frame\"])+\".jpg\")\n        img = Image.open(loc).convert(\"RGB\")\n        \n        ## get bbox \n        bbox=np.asarray([[i[\"x\"], i[\"y\"], i[\"x\"]+i[\"width\"], i[\"y\"]+i[\"height\"]] for i in eval(meta[\"annotations\"]) if i != \"[]\"])\n        bbox = torch.as_tensor(bbox, dtype=torch.float32)\n        \n        labels = torch.ones((len(bbox),), dtype=torch.int64)\n        \n        image_id = torch.tensor([idx])\n        area = (bbox[:, 3] - bbox[:, 1]) * (bbox[:, 2] - bbox[:, 0])\n        # suppose all instances are not crowd\n        iscrowd = torch.zeros((len(bbox),), dtype=torch.int64)\n\n        target = {}\n        target[\"boxes\"] = bbox\n        target[\"labels\"] = labels\n        target[\"image_id\"] = image_id\n        target[\"area\"] = area\n        target[\"iscrowd\"] = iscrowd\n\n        if self.transforms is not None:\n            img, target = self.transforms(img, target)\n        return img, target","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.839853Z","iopub.status.idle":"2022-01-19T17:15:02.840669Z","shell.execute_reply.started":"2022-01-19T17:15:02.840416Z","shell.execute_reply":"2022-01-19T17:15:02.840439Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## check if it is working \ntrain_ds = GBRDataset(root, train_df, None)\nimg, target = train_ds[0]\nimg.size","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.841735Z","iopub.status.idle":"2022-01-19T17:15:02.842556Z","shell.execute_reply.started":"2022-01-19T17:15:02.84233Z","shell.execute_reply":"2022-01-19T17:15:02.842353Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## transforms \n- RandomHorizontalFlip \n- Compose \n- ToTensor","metadata":{}},{"cell_type":"code","source":"class Compose:\n    def __init__(self, transforms):\n        self.transforms = transforms\n\n    def __call__(self, image, target):\n        for t in self.transforms:\n            image, target = t(image, target)\n        return image, target\n\n\nclass RandomHorizontalFlip(T.RandomHorizontalFlip):\n    def forward(\n        self, image: Tensor, target: Optional[Dict[str, Tensor]] = None\n    ) -> Tuple[Tensor, Optional[Dict[str, Tensor]]]:\n        if torch.rand(1) < self.p:\n            image = F.hflip(image)\n            if target is not None:\n                width, _ = image.shape[1:][::-1] if isinstance(image, Tensor) else image.size\n                target[\"boxes\"][:, [0, 2]] = width - target[\"boxes\"][:, [2, 0]]\n        return image, target\n\n\nclass ToTensor(nn.Module):\n    def forward(\n        self, image: Tensor, target: Optional[Dict[str, Tensor]] = None\n    ) -> Tuple[Tensor, Optional[Dict[str, Tensor]]]:\n        image = torch.as_tensor(np.array(image))\n        image = F.convert_image_dtype(image)\n        return image.permute((2, 0, 1)), target\n    \ndef collate_fn(batch):\n    return tuple(zip(*batch))","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.843783Z","iopub.status.idle":"2022-01-19T17:15:02.844731Z","shell.execute_reply.started":"2022-01-19T17:15:02.84447Z","shell.execute_reply":"2022-01-19T17:15:02.844494Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define the lighting dataloader","metadata":{}},{"cell_type":"code","source":"@dataclass\nclass GDRDataLoader(pl.LightningDataModule):\n    root: PosixPath\n    train_df: pd.DataFrame\n    val_df: pd.DataFrame\n    train_batch_size: int=4\n    val_batch_size: int=1\n    stage: int = None\n\n    def __post_init__(self):\n        super().__init__()\n        self.setup(self.stage)\n\n    def setup(self, stage=None):        \n        self.train_ds = GBRDataset(self.root, self.train_df, self.get_transform(train=True))\n        self.val_ds = GBRDataset(self.root, self.val_df, self.get_transform(train=False))\n\n    def train_dataloader(self):\n        train_loader = torch.utils.data.DataLoader(self.train_ds, batch_size=self.train_batch_size, shuffle=True, collate_fn=collate_fn, num_workers=2)\n        return train_loader\n    \n    def val_dataloader(self):\n        val_loader = torch.utils.data.DataLoader(self.val_ds, batch_size=self.val_batch_size, shuffle=False, collate_fn=collate_fn, num_workers=2)\n        return val_loader\n    \n    @staticmethod\n    def get_transform(train):\n        transforms = []\n        transforms.append(ToTensor())\n        if train:\n            transforms.append(RandomHorizontalFlip(0.5))\n        return Compose(transforms)","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.845968Z","iopub.status.idle":"2022-01-19T17:15:02.846967Z","shell.execute_reply.started":"2022-01-19T17:15:02.846626Z","shell.execute_reply":"2022-01-19T17:15:02.846651Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## check dataset outputs with transforms\ntrain_ds = GBRDataset(root, train_df, GDRDataLoader.get_transform(True))\nimg, target = train_ds[0]\nimg.shape","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.848147Z","iopub.status.idle":"2022-01-19T17:15:02.849309Z","shell.execute_reply.started":"2022-01-19T17:15:02.84903Z","shell.execute_reply":"2022-01-19T17:15:02.849058Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## check the dataloader outputs\ndl = GDRDataLoader(root, train_df, val_df)\nimages, targets = next(iter(dl.train_dataloader()))\nprint([i.shape for i in images])","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.850621Z","iopub.status.idle":"2022-01-19T17:15:02.851487Z","shell.execute_reply.started":"2022-01-19T17:15:02.85125Z","shell.execute_reply":"2022-01-19T17:15:02.851274Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Metric implementation\n- bboxes = [x, y, x, y, c]\n- pred_bbox = [x, y, x, y, c, score]\n\nIn this case, since we have only one class, lets ignore c and make it \n- bboxes = [x, y, x, y]\n- pred_bbox = [score, x, y, x, y]\n\nwhere score is the probability score. It is a hyper-parameter to tune. Since the competition guidelines said it is okay to have false positives inorder to not miss any of the actual star_fish, we will keep this `score` as small as possible.\n\n### Sudo Algorithm for metric\n```markdown\n- For a particular IOU threshold [0.3:0.8:0.05]\n-   for each image\n-     Get bboxes and pred_bboxes of the image. \n-     calculate total fp, fn, tp of the image.\n-   aggregate the score. \n```\n\nI copied the following functions from [here](https://www.kaggle.com/bamps53/competition-metric-implementation?scriptVersionId=81087805)","metadata":{}},{"cell_type":"code","source":"def calc_iou(bboxes1, bboxes2, bbox_mode='xywh'):\n    assert len(bboxes1.shape) == 2 and bboxes1.shape[1] == 4\n    assert len(bboxes2.shape) == 2 and bboxes2.shape[1] == 4\n    \n    bboxes1 = bboxes1.copy()\n    bboxes2 = bboxes2.copy()\n    \n    if bbox_mode == 'xywh':\n        bboxes1[:, 2:] += bboxes1[:, :2]\n        bboxes2[:, 2:] += bboxes2[:, :2]\n\n    x11, y11, x12, y12 = np.split(bboxes1, 4, axis=1)\n    x21, y21, x22, y22 = np.split(bboxes2, 4, axis=1)\n    xA = np.maximum(x11, np.transpose(x21))\n    yA = np.maximum(y11, np.transpose(y21))\n    xB = np.minimum(x12, np.transpose(x22))\n    yB = np.minimum(y12, np.transpose(y22))\n    interArea = np.maximum((xB - xA + 1), 0) * np.maximum((yB - yA + 1), 0)\n    boxAArea = (x12 - x11 + 1) * (y12 - y11 + 1)\n    boxBArea = (x22 - x21 + 1) * (y22 - y21 + 1)\n    iou = interArea / (boxAArea + np.transpose(boxBArea) - interArea)\n    return iou\n\ndef f_beta(tp, fp, fn, beta=2):\n    return (1+beta**2)*tp / ((1+beta**2)*tp + beta**2*(fn+fp))\n\ndef calc_is_correct_at_iou_th(gt_bboxes, pred_bboxes, iou_th, verbose=False):\n    ## gt bboxes and pred_bboxes are numpy arrays with [N, 4] and [N, 5] in shape. iou_th is the threshold.\n    gt_bboxes = gt_bboxes.copy()\n    pred_bboxes = pred_bboxes.copy()\n    \n    tp = 0\n    fp = 0\n    for pred_bbox in pred_bboxes:\n        ious = calc_iou(gt_bboxes, pred_bbox[None, 1:], bbox_mode=\"xyxy\")\n        max_iou = ious.max()\n        if max_iou > iou_th:\n            tp += 1\n            gt_bboxes = np.delete(gt_bboxes, ious.argmax(), axis=0)\n        else:\n            fp += 1\n        if len(gt_bboxes) == 0:\n            fp += len(pred_bboxes)\n            break\n\n    fn = len(gt_bboxes)\n    return tp, fp, fn\n\ndef calc_is_correct(gt_bboxes, pred_bboxes):\n    \"\"\"\n    gt_bboxes: (N, 4) np.array in xyxy format\n    pred_bboxes: (N, 5) np.array in conf+xyxy format\n    \"\"\"\n    if len(gt_bboxes) == 0 and len(pred_bboxes) == 0:\n        tps, fps, fns = 0, 0, 0\n        return tps, fps, fns\n    \n    elif len(gt_bboxes) == 0:\n        tps, fps, fns = 0, len(pred_bboxes), 0\n        return tps, fps, fns\n    \n    elif len(pred_bboxes) == 0:\n        tps, fps, fns = 0, 0, len(gt_bboxes)\n        return tps, fps, fns\n    \n    pred_bboxes = pred_bboxes[pred_bboxes[:,0].argsort()[::-1]] # sort by conf\n    \n    # https://peltarion.com/knowledge-center/documentation/evaluation-view/classification-loss-metrics/micro-f1-score\n    # Micro-averaging F1-score is performed by first calculating the sum of all tp, fp, and fn over all the labels. \n    #Then we compute the micro-precision and micro-recall from the sums.\n    #And finally we compute the harmonic mean to get the micro F1-score.\n    # So as per above statement, we need to calculate tps, fps, fns for all the thresholds first and then calculate fscore. \n    \n    tps, fps, fns = 0, 0, 0\n    for iou_th in np.arange(0.3, 0.85, 0.05):\n        tp, fp, fn = calc_is_correct_at_iou_th(gt_bboxes, pred_bboxes, iou_th)\n        tps += tp\n        fps += fp\n        fns += fn\n    return tps, fps, fns\n\ndef calc_f2_score(gt_bboxes_list, pred_bboxes_list, verbose=False):\n    \"\"\"\n    gt_bboxes_list: list of (N, 4) np.array in xyxy format\n    pred_bboxes_list: list of (N, 5) np.array in conf+xyxy format\n    \"\"\"\n    tps, fps, fns = 0, 0, 0\n    for gt_bboxes, pred_bboxes in zip(gt_bboxes_list, pred_bboxes_list):\n        tp, fp, fn = calc_is_correct(gt_bboxes, pred_bboxes)\n        tps += tp\n        fps += fp\n        fns += fn\n        if verbose:\n            num_gt = len(gt_bboxes)\n            num_pred = len(pred_bboxes)\n            print(f'num_gt:{num_gt:<3} num_pred:{num_pred:<3} tp:{tp:<3} fp:{fp:<3} fn:{fn:<3}')\n    return f_beta(tps, fps, fns, beta=2)","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.852852Z","iopub.status.idle":"2022-01-19T17:15:02.853445Z","shell.execute_reply.started":"2022-01-19T17:15:02.85321Z","shell.execute_reply":"2022-01-19T17:15:02.853233Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define LightningModule - Network, optimizer and forward function","metadata":{}},{"cell_type":"code","source":"@dataclass\nclass ModelParams:\n    # https://github.com/pytorch/vision/blob/93ec8bfd31ac6aed58b79d7764070fcc2a1dfd51/torchvision/models/detection/faster_rcnn.py\n    num_classes: int=2 \n    pretrained_backbone: bool=True\n    min_size: int=800\n    max_size: int =1333\n    # RPN parameters\n    rpn_anchor_generator: Optional[AnchorGenerator]=None \n    rpn_head: Optional[RPNHead]=None \n    rpn_pre_nms_top_n_train: int=2000\n    rpn_pre_nms_top_n_test: int=1000\n    rpn_post_nms_top_n_train: int=2000\n    rpn_post_nms_top_n_test: int=1000\n    rpn_nms_thresh: float=0.7\n    rpn_fg_iou_thresh: float=0.7\n    rpn_bg_iou_thresh: float=0.3\n    rpn_batch_size_per_image: int=256\n    rpn_positive_fraction: float=0.5\n    rpn_score_thresh: float=0.0\n    # Box parameters\n    box_roi_pool: Optional[MultiScaleRoIAlign]=None\n    box_head: Optional[TwoMLPHead]=None\n    box_predictor: Optional[FastRCNNPredictor]=None\n    box_score_thresh: float=0.05\n    box_nms_thresh: float=0.5\n    box_detections_per_img: int=100\n    box_fg_iou_thresh: float=0.5\n    box_bg_iou_thresh: float=0.5\n    box_batch_size_per_image: int=512\n    box_positive_fraction: float=0.25\n    bbox_reg_weights: Optional[List[float]]=None","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.854957Z","iopub.status.idle":"2022-01-19T17:15:02.855903Z","shell.execute_reply.started":"2022-01-19T17:15:02.855675Z","shell.execute_reply":"2022-01-19T17:15:02.855699Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## define train params. \n\n@dataclass\nclass TrainParams:\n    val_batch_size: int=1\n    train_batch_size: int=4\n        \n    ## optimizers \n    lr: float= 0.01\n    momentum: float=0.9\n    weight_decay: float=0.0005\n    \n    ## stuff\n    epochs: int = 20 \n    gpus: Union[int, List[int]]=1","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.857126Z","iopub.status.idle":"2022-01-19T17:15:02.858115Z","shell.execute_reply.started":"2022-01-19T17:15:02.857872Z","shell.execute_reply":"2022-01-19T17:15:02.857897Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class GDRTrainer(pl.LightningModule):\n    def __init__(self, cfg):\n        super().__init__()\n        self.cfg = cfg \n        self.model = torchvision.models.detection.fasterrcnn_resnet50_fpn(**asdict(ModelParams()))\n    \n    def training_step(self, batch, batch_idx):\n        images, targets = batch \n        images = list(image.to(self.device) for image in images)\n        targets = [{k: v.to(self.device) for k, v in t.items()} for t in targets]\n        loss_dict = self.model(images, targets)\n        losses = sum(loss for loss in loss_dict.values())\n            \n        self.log(f\"train_loss\", losses, prog_bar=True)\n        return {\"loss\": losses, \"outputs\": {k:v.detach() for k, v in loss_dict.items()}} \n\n    def validation_step(self, batch, batch_idx):\n        images, targets = batch \n        images = list(image.to(self.device) for image in images)\n        targets = [{k: v.to(self.device) for k, v in t.items()} for t in targets]\n        ## we are using image size of 1 \n        ## Example: {'boxes': tensor([], device='cuda:0', size=(0, 4)), 'labels': tensor([], device='cuda:0', dtype=torch.int64), 'scores': tensor([], device='cuda:0')}\n        preds = self.model(images, targets)[0]\n        return {\"preds\": preds, \"targets\": targets[0]} \n\n    def training_epoch_end(self, outputs):\n        self.epoch_end(outputs, \"train\")\n\n    def validation_epoch_end(self, outputs):\n        self.epoch_end(outputs, \"val\") \n\n    def epoch_end(self, outputs, phase):\n        if phase == \"val\":\n            preds = [i[\"preds\"] for i in outputs]\n            pred_bboxes = [torch.hstack([i[\"scores\"].reshape(-1, 1), i[\"boxes\"]]).cpu().numpy() for i in preds]\n            target_bboxes = [i[\"targets\"][\"boxes\"].cpu().numpy() for i in outputs]\n            pred_count = sum([i.shape[0] for i in pred_bboxes])\n            f2_score = calc_f2_score(target_bboxes, pred_bboxes, False)\n            self.log(\"val_f2_score\", f2_score, prog_bar=True)\n            self.log(\"pred_count\", torch.as_tensor(pred_count).float(), prog_bar=True)\n            \n        elif phase == \"train\":\n            final_train_loss = torch.mean(torch.stack([i[\"loss\"] for i in outputs]))\n            self.log(\"train_epoch_loss\", final_train_loss, prog_bar=True)\n            \n            for loss_type in [\"loss_classifier\", \"loss_box_reg\", \"loss_objectness\", \"loss_rpn_box_reg\"]:\n                final_loss = torch.mean(torch.stack([i[\"outputs\"][loss_type] for i in outputs]))\n                self.log(f\"train_{loss_type}\", final_train_loss, prog_bar=True)\n        \n\n    def configure_optimizers(self):\n        optimizer = torch.optim.SGD(self.model.parameters(), lr=self.cfg[\"lr\"], momentum=self.cfg[\"momentum\"], weight_decay=self.cfg[\"weight_decay\"])\n        # and a learning rate scheduler\n        lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1)\n        return {\"optimizer\": optimizer, \"lr_scheduler\": { \"scheduler\": lr_scheduler, \"interval\": \"epoch\",},} ","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.8595Z","iopub.status.idle":"2022-01-19T17:15:02.860312Z","shell.execute_reply.started":"2022-01-19T17:15:02.860082Z","shell.execute_reply":"2022-01-19T17:15:02.860107Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tc = TrainParams() ## train_configs \nmodel = GDRTrainer(cfg=asdict(tc))\ndl = GDRDataLoader(root, train_df, val_df, train_batch_size=tc.train_batch_size, val_batch_size=tc.val_batch_size)\ncheckpoint_callback = ModelCheckpoint(\n        monitor=\"val_f2_score\",\n        save_top_k=5,\n        filename=\"{epoch}-{step}-{val_f2_score:.3f}\",\n        save_last=True,\n        mode=\"max\", \n    )","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.86155Z","iopub.status.idle":"2022-01-19T17:15:02.862445Z","shell.execute_reply.started":"2022-01-19T17:15:02.862209Z","shell.execute_reply":"2022-01-19T17:15:02.862232Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Train the model ","metadata":{}},{"cell_type":"code","source":"trainer = pl.Trainer(\n        gpus=tc.gpus,\n        max_epochs=tc.epochs,\n        callbacks=[checkpoint_callback, TQDMProgressBar(refresh_rate=2)],\n        logger=pl.loggers.TensorBoardLogger(f\"/kaggle/working/lightning_logs/exp1/\", name=\"faster_rcnn\"))","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.863703Z","iopub.status.idle":"2022-01-19T17:15:02.864704Z","shell.execute_reply.started":"2022-01-19T17:15:02.864345Z","shell.execute_reply":"2022-01-19T17:15:02.864435Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"trainer.fit(model, dl)","metadata":{"execution":{"iopub.status.busy":"2022-01-19T17:15:02.865834Z","iopub.status.idle":"2022-01-19T17:15:02.866443Z","shell.execute_reply.started":"2022-01-19T17:15:02.866207Z","shell.execute_reply":"2022-01-19T17:15:02.866232Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## End Notes. \nWe have trained the model for 20 epochs and In the next tutorial, we will use the best model for inference and make a submission","metadata":{}}]}