{"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":"IMPORT CÁC THƯ VIỆN CẦN THIẾT","metadata":{}},{"cell_type":"code","source":"# Import các thư viện cần thiết\nimport torch\nimport torchvision\nimport cv2\nimport numpy as np\nimport os\nfrom glob import glob\nfrom tqdm import tqdm\nfrom matplotlib import pyplot as plt\nfrom random import choice, choices, shuffle\nimport re\nfrom ipywidgets import IntProgress\nimport pandas as pd\nfrom torch.utils.data import DataLoader, Dataset\nfrom torch.utils.data.sampler import SequentialSampler, RandomSampler\nfrom torchvision.models.detection.rpn import AnchorGenerator\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensor, ToTensorV2\n\nimport numba\nimport ast\nfrom numba import jit\nfrom typing import List, Union, Tuple\n\nimport random\nfrom PIL import Image\nimport torch.nn.functional as F\nfrom albumentations import *\nfrom tqdm import tqdm\n\nfrom torchvision.models.detection.backbone_utils import resnet_fpn_backbone\nfrom torchvision.models.detection.faster_rcnn import FasterRCNN\nfrom torchvision.models.detection.backbone_utils import BackboneWithFPN\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom torchvision.models.detection.retinanet import RetinaNetHead\n\n!pip install /kaggle/input/wheat-packages/ensemble_boxes-1.0.4-py3-none-any.whl -f ./ --no-index\nfrom ensemble_boxes import weighted_boxes_fusion\n\nBOX_COLOR = (0, 0, 255)\nTEXT_COLOR = (255, 255, 255)\nTRAIN_IMG_DIR = \"../input/global-wheat-detection/train\"","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","papermill":{"duration":4.904133,"end_time":"2022-01-03T13:02:43.016845","exception":false,"start_time":"2022-01-03T13:02:38.112712","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:05.194006Z","iopub.execute_input":"2022-01-04T14:35:05.194308Z","iopub.status.idle":"2022-01-04T14:35:14.803171Z","shell.execute_reply.started":"2022-01-04T14:35:05.194272Z","shell.execute_reply":"2022-01-04T14:35:14.801918Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"############## CÀI ĐẶT CÁC HÀM CẦN THIẾT ########################","metadata":{"papermill":{"duration":0.058363,"end_time":"2022-01-03T13:02:54.993046","exception":false,"start_time":"2022-01-03T13:02:54.934683","status":"completed"},"tags":[]}},{"cell_type":"code","source":"# HÀM THỰC HIỆN CÁC THAO TÁC XỬ LÝ ẢNH CƠ BẢN\n# ĐẦU VÀO LÀ LIST CÁC THAO TÁC CẦN THỰC HIỆN\ndef get_aug(aug):\n    return Compose(aug, bbox_params=BboxParams(format='pascal_voc', min_area=0, min_visibility=0, label_fields=['category_id']))\n\n# HÀM TÍNH TOÁN DIỆN TÍCH PHẦN TRĂM DIỆN TÍCH BOUDING BOX BỊ MẤT ĐI \ndef bb_overlap(boxA, boxB):\n    xA = max(boxA[0], boxB[0])\n    yA = max(boxA[1], boxB[1])\n    xB = min(boxA[2], boxB[2])\n    yB = min(boxA[3], boxB[3])\n    interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1)\n    boxAArea = (boxA[2] - boxA[0] + 1) * (boxA[3] - boxA[1] + 1)\n    iou = interArea / float(boxAArea)\n    return iou\n\n# CLASS WHEAT DATASET: KẾ THỪA CLASS DATASET CỦA PYTORCH\n# HÀM NÀY THỰC HIỆN CÁC CHỨC NĂNG NHƯ: LOAD ẢNH VÀ BOUDING BOX, XỬ LÝ ẢNH CƠ BẢN, RANDOM CROP, MOSAIC\n# INPUT:\n    # df: đọc từ tập train.csv\n    # img_size: truyền vào image_size mà ta muốn sử dụng\n    # network: truyền vào tên model\n    # bbox_removal_threshold: bounding box khi bị cắt còn lại bao nhiêu phần trăm thì giữ\n# OUTPUT: \n    # image: hình ảnh đã được load lên\n    # targets: là 1 dictionary gồm 2 key: boxes và labels\nclass WheatDataset(Dataset):\n    # Hàm khởi tạo\n    def __init__(self, df, img_size, mode='train', network='FasterRCNN', bbox_removal_threshold=0.25):\n        super(WheatDataset,self).__init__()\n        self.df = df\n        self.image_ids = list(np.unique(self.df.image_id.values))\n        self.img_size = img_size\n        self.root_dir = '../input/global-wheat-detection/train'\n        self.test_dir = '../input/global-wheat-detection/test'\n        assert mode in  ['train', 'valid']\n        self.mode = mode\n        assert network in ['FasterRCNN']\n        self.network = network\n        self.bbox_removal_threshold = bbox_removal_threshold\n        \n        if self.mode == 'train':\n            random.shuffle(self.image_ids)\n            \n        self.train_transforms = get_aug([\n            HorizontalFlip(p=0.5),\n            VerticalFlip(p=0.5),\n            ToGray(p=0.01),\n            GaussNoise(p=0.2),\n            OneOf([\n                MotionBlur(p=0.2),\n                MedianBlur(blur_limit=3, p=0.1),\n                Blur(blur_limit=3, p=0.1),\n            ], p=0.2),\n            RandomBrightnessContrast(p=0.25)\n        ])\n        \n        self.resize_transforms = get_aug([\n            Resize(height=self.img_size, width=self.img_size, interpolation=1, p=1)\n        ])\n    \n    # Hàm đếm số lượng image phân biệt trong dataset\n    def __len__(self):\n        return len(self.image_ids)\n\n    # Những bouding box nào có kích thước bé hơn 10 thì loại bỏ\n    def refine_boxes(self, boxes):\n        result_boxes = []\n        for box in boxes:\n            if box[2] - box[0] < 10 or box[3] - box[1] < 10:\n                continue\n            result_boxes.append(box)\n        result_boxes = np.array(result_boxes)\n        return result_boxes\n    \n    # Hàm resize lại ảnh theo kích thước truyền vào\n    def resize_image(self, image, boxes):\n        cats = np.ones(boxes.shape[0], dtype=int)\n        annotations = {'image': image, 'bboxes': boxes, 'category_id': cats}\n        augmented = self.resize_transforms(**annotations)\n        image = augmented['image']\n        boxes = np.array(augmented['bboxes'])\n        return image, boxes\n    \n    # Hàm random crop ảnh\n    def crop_image(self, image, boxes, xmin, ymin, xmax, ymax):\n        image = image[ymin:ymax,xmin:xmax,:]\n        cutout_box = [xmin, ymin, xmax, ymax]\n        result_boxes = []\n        for box in boxes:\n            iou = bb_overlap(box, cutout_box)\n            if iou > self.bbox_removal_threshold:\n                result_boxes.append(box)\n        if len(result_boxes) > 0:\n            result_boxes = np.array(result_boxes, dtype=float)\n            result_boxes[:,[0,2]] -= xmin\n            result_boxes[:,[1,3]] -= ymin\n            result_boxes[:,[0,2]] = result_boxes[:,[0,2]].clip(0, xmax-xmin)\n            result_boxes[:,[1,3]] = result_boxes[:,[1,3]].clip(0, ymax-ymin)\n        else:\n            result_boxes = np.array([], dtype=float).reshape(0,4)\n        return image, result_boxes\n    \n    # Hàm random crop ảnh và nó sẽ resize ảnh vừa random crop lại theo image size ta muốn\n    def random_crop_resize(self, image, boxes, img_size=1024, p=0.5):\n        if random.random() > p:\n            new_img_size = random.randint(int(0.75*img_size), img_size)\n            x = random.randint(0, img_size-new_img_size)\n            y = random.randint(0, img_size-new_img_size)\n            image, boxes = self.crop_image(image, boxes, x, y, x+new_img_size, y+new_img_size)\n            return self.resize_image(image, boxes)\n        else:\n            if self.img_size != 1024:\n                return self.resize_image(image, boxes)\n            else:\n                return image, boxes\n    \n    # Hàm load file ảnh và bounding box tương ứng\n    def load_image_and_boxes(self, image_id):\n        tmp_df = self.df.loc[self.df['image_id']==image_id]\n        source = np.unique(tmp_df.source.values)[0]\n        fold = np.unique(tmp_df.fold.values)[0]\n\n        if source == 'pseudo':\n            img_path = '{}/{}.jpg'.format(self.test_dir, image_id)\n        else:\n            img_path = '{}/{}.jpg'.format(self.root_dir, image_id)\n        \n        img = Image.open(img_path)\n        img = img.convert('RGB')\n        img = np.array(img, dtype=np.uint8)\n\n        boxes = []\n        for _, row in tmp_df.iterrows():\n            if row['isbox'] == False:\n                continue\n            boxes.append([float(row['xmin']),float(row['ymin']),float(row['xmax']),float(row['ymax'])])\n\n        boxes = self.refine_boxes(boxes)\n        if len(boxes) > 0:\n            boxes = np.array(boxes, dtype=float)\n        else:\n            boxes = np.array([], dtype=float).reshape(0,4)\n        return img, boxes, source\n    \n    # Hàm thực hiện chức năng MOSAIC, nghĩa là chọn 4 bức ảnh rồi cắt ghép chúng lại\n    def load_cutmix_image_and_boxes(self, image_id, imsize=1024):   #custom mosaic data augmentation\n        image_ids = self.image_ids.copy()\n        image_ids.remove(image_id)\n        cutmix_image_ids = [image_id] + random.sample(image_ids, 3)\n        result_image = np.full((imsize, imsize, 3), 1, dtype=np.uint8)\n        result_boxes = []\n        \n        xc, yc = [int(random.uniform(imsize * 0.25, imsize * 0.75)) for _ in range(2)]\n        for i, img_id in enumerate(cutmix_image_ids):\n            image, boxes, source = self.load_image_and_boxes(img_id)\n            if i == 0:\n                image, boxes = self.crop_image(image, boxes, imsize-xc, imsize-yc, imsize, imsize)\n                result_image[0:yc, 0:xc,:] = image\n                result_boxes.extend(boxes)\n            elif i == 1:\n                image, boxes = self.crop_image(image, boxes, 0, imsize-yc, imsize-xc, imsize)\n                result_image[0:yc, xc:imsize, :] = image\n                if boxes.shape[0] > 0:\n                    boxes[:,[0,2]] += xc\n                result_boxes.extend(boxes)\n            elif i == 2:\n                image, boxes = self.crop_image(image, boxes, 0, 0, imsize-xc, imsize-yc)\n                result_image[yc:imsize, xc:imsize, :] = image\n                if boxes.shape[0] > 0:\n                    boxes[:,[0,2]] += xc\n                    boxes[:,[1,3]] += yc\n                result_boxes.extend(boxes)\n            else:\n                image, boxes = self.crop_image(image, boxes, imsize-xc, 0, imsize, imsize-yc)\n                result_image[yc:imsize, 0:xc, :] = image\n                if boxes.shape[0] > 0:\n                    boxes[:,[1,3]] += yc\n                result_boxes.extend(boxes)\n            del image\n            del boxes\n        del cutmix_image_ids\n        del image_ids\n        if len(result_boxes) == 0:\n            result_boxes = np.array([], dtype=float).reshape(0,4)\n        else:\n            result_boxes = np.vstack(result_boxes)\n            result_boxes[:,[0,2]] = result_boxes[:,[0,2]].clip(0, imsize)\n            result_boxes[:,[1,3]] = result_boxes[:,[1,3]].clip(0, imsize)\n        return result_image, result_boxes\n    \n    # Hàm getitem được overide lại, nó sẽ thực hiện các chức năng nêu trên\n    def __getitem__(self, index):\n        image_id = self.image_ids[index]\n        if self.mode == 'train':\n            while(True):\n                if random.random() > 0.5:\n                    image, boxes, source = self.load_image_and_boxes(image_id)\n                    \n                else:\n                    image, boxes = self.load_cutmix_image_and_boxes(image_id)\n\n                image, boxes = self.random_crop_resize(image, boxes, p=0.5)\n                if len(boxes) > 0:\n                    cats = np.ones(boxes.shape[0], dtype=int)\n                    annotations = {'image': image, 'bboxes': boxes, 'category_id': cats}\n                    augmented = self.train_transforms(**annotations)\n                    image = augmented['image']\n                    boxes = np.array(augmented['bboxes'])\n                    break\n        else:\n            image, boxes, _ = self.load_image_and_boxes(image_id)\n            if self.img_size != 1024:\n                image, boxes = self.resize_image(image, boxes)\n\n       \n        if boxes.shape[0] == 0:\n            target = {\n                \"boxes\": torch.zeros((0, 4), dtype=torch.float32),\n                \"labels\": torch.zeros(0, dtype=torch.int64)\n\n            }\n        else:\n            target = {}\n            area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])\n            target['boxes'] = torch.as_tensor(boxes, dtype=torch.float32)\n            target['labels'] = torch.ones((boxes.shape[0],), dtype=torch.int64)\n\n        image = image.astype(np.float32)\n        image /= 255.0\n        image = torch.from_numpy(image).permute(2,0,1)\n        return image, target","metadata":{"papermill":{"duration":0.130573,"end_time":"2022-01-03T13:02:55.323997","exception":false,"start_time":"2022-01-03T13:02:55.193424","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:14.808206Z","iopub.execute_input":"2022-01-04T14:35:14.808491Z","iopub.status.idle":"2022-01-04T14:35:14.870916Z","shell.execute_reply.started":"2022-01-04T14:35:14.808453Z","shell.execute_reply":"2022-01-04T14:35:14.869861Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class AverageMeter(object):\n    # Hàm tính tổng độ lỗi của model\n    \"\"\"Computes and stores the average and current value\"\"\"\n    def __init__(self):\n        self.reset()\n    def reset(self):\n        self.val = 0\n        self.avg = 0\n        self.sum = 0\n        self.count = 0\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\n\n# Hàm định nghĩa cấu trúc dữ liệu đầu ra của DataLoader        \ndef collate_fn(batch):\n    return tuple(zip(*batch))","metadata":{"papermill":{"duration":0.067957,"end_time":"2022-01-03T13:02:55.450005","exception":false,"start_time":"2022-01-03T13:02:55.382048","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:14.872471Z","iopub.execute_input":"2022-01-04T14:35:14.873287Z","iopub.status.idle":"2022-01-04T14:35:14.888527Z","shell.execute_reply.started":"2022-01-04T14:35:14.873238Z","shell.execute_reply":"2022-01-04T14:35:14.887443Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# CELL này là hàm tính độ đo mAP của tác giả\n\n@jit(nopython=True)\ndef 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    \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\n\n@jit(nopython=True)\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\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\n@jit(nopython=True)\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, pred in enumerate(preds_sorted):\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\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    if tp == 0 and (tp + fp + fn) == 0:\n        return 1.0\n\n    return tp / (tp + fp + fn)\n\n\n@jit(nopython=True)\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(), preds, threshold=threshold,\n                                                     form=form, ious=ious)\n        image_precision += precision_at_threshold / n_threshold\n\n    return image_precision\n\n# Numba typed list!\niou_thresholds = numba.typed.List()\nfor x in [0.5, 0.55, 0.6, 0.65, 0.7, 0.75]:\n    iou_thresholds.append(x)\n\ndef calculate_final_score(all_predictions, score_threshold):\n    final_scores = []\n    for i in range(len(all_predictions)):\n        gt_boxes = all_predictions[i]['gt_boxes'].copy()\n        pred_boxes = all_predictions[i]['pred_boxes'].copy()\n        scores = all_predictions[i]['scores'].copy()\n\n        indexes = np.where(scores>score_threshold)\n        pred_boxes = pred_boxes[indexes]\n        scores = scores[indexes]\n\n        image_precision = calculate_image_precision(gt_boxes, pred_boxes,thresholds=iou_thresholds,form='pascal_voc')\n        final_scores.append(image_precision)\n\n    return np.mean(final_scores)","metadata":{"papermill":{"duration":0.827689,"end_time":"2022-01-03T13:02:56.335197","exception":false,"start_time":"2022-01-03T13:02:55.507508","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:14.89314Z","iopub.execute_input":"2022-01-04T14:35:14.893691Z","iopub.status.idle":"2022-01-04T14:35:14.926142Z","shell.execute_reply.started":"2022-01-04T14:35:14.893658Z","shell.execute_reply":"2022-01-04T14:35:14.92481Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hàm load các model\ndef fasterrcnn_resnet_fpn(backbone_name='resnet152', progress=True, num_classes=91, pretrained=True, pretrained_backbone=True, **kwargs):\n    if backbone_name == 'resnet50':\n        model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=pretrained)\n    elif backbone_name in ['resnet101', 'resnet152']:\n        backbone = resnet_fpn_backbone(backbone_name, pretrained_backbone)\n    else:\n        backbone = my_resnet_fpn_backbone(backbone_name, pretrained_backbone)\n    model = FasterRCNN(backbone, num_classes, **kwargs)\n    return model","metadata":{"execution":{"iopub.status.busy":"2022-01-04T14:35:14.928364Z","iopub.execute_input":"2022-01-04T14:35:14.929195Z","iopub.status.idle":"2022-01-04T14:35:14.941063Z","shell.execute_reply.started":"2022-01-04T14:35:14.929059Z","shell.execute_reply":"2022-01-04T14:35:14.939991Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#chuyển đổi cặp [imgs, targets] sang dạng tensor theo device cpu/gpu\ndef data_to_device(images, targets, device=torch.device(\"cuda\")):\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    return images, targets\n\n\ndef expand_bbox(x):\n    # Hàm chuyển đổi bounding box từ string sang int\n    r = np.array(re.findall(\"([0-9]+[.]?[0-9]*)\", x))\n    if len(r) == 0:\n        r = [-1, -1, -1, -1]\n    return r\n\n\n# đọc data từ file csv\n# output là 1 list chứa thông tin về các ảnh\n# mỗi phần tử bao gồm 1 image_id và 1 list các bounding box\ndef read_data_in_csv(csv_path=\"./wheat-dataset/train.csv\"):\n    df = pd.read_csv(csv_path)\n    df['x'], df['y'],  df['w'], df['h'] = -1, -1, -1, -1\n    df[['x', 'y', 'w', 'h']] = np.stack(df['bbox'].apply(lambda x: expand_bbox(x)))\n    df.drop(columns=['bbox'], inplace=True)\n    df['x'] = df['x'].astype(np.float)\n    df['y'] = df['y'].astype(np.float)\n    df['w'] = df['w'].astype(np.float)\n    df['h'] = df['h'].astype(np.float)\n    objs = []\n    img_ids = set(df[\"image_id\"])\n    \n    for img_id in tqdm(img_ids):\n        records = df[df[\"image_id\"] == img_id]\n        boxes = records[['x', 'y', 'w', 'h']].values\n        area = boxes[:,2]*boxes[:,3]\n        boxes[:,2] = boxes[:,0] + boxes[:,2]\n        boxes[:,3] = boxes[:,1] + boxes[:,3]\n\n        obj = {\n            \"img_id\": img_id,\n            \"boxes\": boxes,\n            \"area\":area\n        }\n        objs.append(obj)\n    return objs","metadata":{"papermill":{"duration":0.078311,"end_time":"2022-01-03T13:02:56.589223","exception":false,"start_time":"2022-01-03T13:02:56.510912","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:14.943526Z","iopub.execute_input":"2022-01-04T14:35:14.944417Z","iopub.status.idle":"2022-01-04T14:35:14.961985Z","shell.execute_reply.started":"2022-01-04T14:35:14.94437Z","shell.execute_reply":"2022-01-04T14:35:14.960795Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pickle\n# Hàm save output của các ảnh đã được predict ở dạng dictionary\ndef save_dict(obj, name):\n    with open(name, 'wb') as f:\n        pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL)\n\n# Hàm load output của các ảnh đã được predict ở dạng dictionary\ndef load_dict(name):\n    with open(name, 'rb') as f:\n        return pickle.load(f)","metadata":{"papermill":{"duration":0.067724,"end_time":"2022-01-03T13:02:56.715573","exception":false,"start_time":"2022-01-03T13:02:56.647849","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:14.964066Z","iopub.execute_input":"2022-01-04T14:35:14.964853Z","iopub.status.idle":"2022-01-04T14:35:14.976488Z","shell.execute_reply.started":"2022-01-04T14:35:14.964805Z","shell.execute_reply":"2022-01-04T14:35:14.975438Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hàm ensemble các bounding box của model sau khi predict kết hợp TTA\n# input: \n    # test_df: gồm danh sách tên các ảnh trong tập test\n    # box_pred1, score_pred1, label_pred1: là các bounding box, confident và label tương ứng\n# output: các bounding và scores đã được ensemble\ndef run_wbf(test_df, box_pred1, score_pred1, label_pred1, NMS_THRESH, BOX_THRESH, PP_THRESH):\n    \n    output_dict = {}\n    for image_id in np.unique(test_df.image_id.values):\n        boxes = box_pred1[image_id] \n        scores = score_pred1[image_id]\n        labels = label_pred1[image_id]\n\n        boxes, scores, labels = weighted_boxes_fusion(boxes, scores, labels, weights=None, iou_thr=NMS_THRESH, skip_box_thr=BOX_THRESH)\n        boxes = np.array(boxes)\n        scores = np.array(scores)\n\n        idxs = np.where(scores > PP_THRESH)[0]\n        boxes = boxes[idxs]\n        scores = scores[idxs]\n\n        if len(boxes) > 0:\n \n            boxes[:, [0,2]] = (boxes[:, [0,2]]*1024).clip(min=0, max=1024-1)\n            boxes[:, [1,3]] = (boxes[:, [1,3]]*1024).clip(min=0, max=1024-1)\n        output_dict[image_id] = (boxes, scores)\n    \n    return output_dict","metadata":{"papermill":{"duration":0.073533,"end_time":"2022-01-03T13:02:56.847603","exception":false,"start_time":"2022-01-03T13:02:56.77407","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:14.978542Z","iopub.execute_input":"2022-01-04T14:35:14.97922Z","iopub.status.idle":"2022-01-04T14:35:14.991553Z","shell.execute_reply.started":"2022-01-04T14:35:14.979173Z","shell.execute_reply":"2022-01-04T14:35:14.990385Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hàm ensemble các bounding box của 5 model sau khi predict kết hợp TTA\n# input: \n    # test_df: gồm danh sách tên các ảnh trong tập test\n    # box_pred, score_pred, label_pred: là các bounding box, confident và label tương ứng của các model\n# output: các bounding và scores đã được ensemble\ndef run_wbf_4preds(test_df, \n                   box_pred0, score_pred0, label_pred0, \n                   box_pred1, score_pred1, label_pred1, \n                   box_pred2, score_pred2, label_pred2, \n                   box_pred3, score_pred3, label_pred3,\n                   box_pred4, score_pred4, label_pred4, \n                NMS_THRESH, BOX_THRESH, PP_THRESH):\n    \n    output_dict = {}\n    for image_id in np.unique(test_df.image_id.values):\n        boxes = box_pred1[image_id] + box_pred2[image_id] + box_pred3[image_id] + box_pred0[image_id] + box_pred4[image_id]\n        scores = score_pred1[image_id] + score_pred2[image_id] + score_pred3[image_id] + score_pred0[image_id] + score_pred4[image_id]\n        labels = label_pred1[image_id] + label_pred2[image_id] + label_pred3[image_id] + label_pred0[image_id] + label_pred4[image_id] \n\n        boxes, scores, labels = weighted_boxes_fusion(boxes, scores, labels, weights=None, iou_thr=NMS_THRESH, skip_box_thr=BOX_THRESH)\n        boxes = np.array(boxes)\n        scores = np.array(scores)\n\n        idxs = np.where(scores > PP_THRESH)[0]\n        boxes = boxes[idxs]\n        scores = scores[idxs]\n\n        if len(boxes) > 0:\n \n            boxes[:, [0,2]] = (boxes[:, [0,2]]*1024).clip(min=0, max=1024-1)\n            boxes[:, [1,3]] = (boxes[:, [1,3]]*1024).clip(min=0, max=1024-1)\n        output_dict[image_id] = (boxes, scores)\n    \n    return output_dict","metadata":{"papermill":{"duration":0.077588,"end_time":"2022-01-03T13:02:56.984584","exception":false,"start_time":"2022-01-03T13:02:56.906996","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:14.993694Z","iopub.execute_input":"2022-01-04T14:35:14.99447Z","iopub.status.idle":"2022-01-04T14:35:15.009757Z","shell.execute_reply.started":"2022-01-04T14:35:14.994314Z","shell.execute_reply":"2022-01-04T14:35:15.008487Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hàm định dạng dataframe để submit trên kaggle \ndef format_prediction_string(boxes, scores):\n    pred_strings = []\n    for j in zip(scores, boxes):\n        pred_strings.append(\"{0:.5f} {1} {2} {3} {4}\".format(j[0], j[1][0], j[1][1], j[1][2], j[1][3]))\n    return \" \".join(pred_strings)","metadata":{"papermill":{"duration":0.06904,"end_time":"2022-01-03T13:02:57.113702","exception":false,"start_time":"2022-01-03T13:02:57.044662","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:15.015647Z","iopub.execute_input":"2022-01-04T14:35:15.016456Z","iopub.status.idle":"2022-01-04T14:35:15.024801Z","shell.execute_reply.started":"2022-01-04T14:35:15.016383Z","shell.execute_reply":"2022-01-04T14:35:15.023793Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Các hàm dùng để thực hiện TTA với: lật ngang, lật dọc và xoay 90 độ\nclass BaseWheatTTA:\n    def augment(self, images):\n        raise NotImplementedError\n\n    def prepare_boxes(self, boxes):\n        result_boxes = boxes.copy()\n        result_boxes[:,0] = np.min(boxes[:, [0,2]], axis=1)\n        result_boxes[:,2] = np.max(boxes[:, [0,2]], axis=1)\n        result_boxes[:,1] = np.min(boxes[:, [1,3]], axis=1)\n        result_boxes[:,3] = np.max(boxes[:, [1,3]], axis=1)\n        return result_boxes\n    \n    def deaugment_boxes(self, boxes):\n        raise NotImplementedError\n\nclass TTAHorizontalFlip(BaseWheatTTA):\n    def __init__(self, image_size):\n        self.image_size = image_size\n\n    def fasterrcnn_augment(self, images):\n        return list(image.flip(1) for image in images)\n\n    def deaugment_boxes(self, boxes):\n        boxes[:, [1,3]] = self.image_size - boxes[:, [3,1]]\n        return self.prepare_boxes(boxes)\n\nclass TTAVerticalFlip(BaseWheatTTA):\n    def __init__(self, image_size):\n        self.image_size = image_size\n\n    def fasterrcnn_augment(self, images):\n        return list(image.flip(2) for image in images)\n    \n    def deaugment_boxes(self, boxes):\n        boxes[:, [0,2]] = self.image_size - boxes[:, [2,0]]\n        return boxes\n\nclass TTARotate90(BaseWheatTTA):\n    def __init__(self, image_size):\n        self.image_size = image_size\n    \n    def fasterrcnn_augment(self, images):\n        return list(torch.rot90(image, 1, (1, 2)) for image in images)\n    \n    def deaugment_boxes(self, boxes):\n        res_boxes = boxes.copy()\n        res_boxes[:, [0,2]] = self.image_size - boxes[:, [1,3]]\n        res_boxes[:, [1,3]] = boxes[:, [2,0]]\n        return self.prepare_boxes(res_boxes)\n\nclass TTACompose:\n    def __init__(self, transforms):\n        self.transforms = transforms\n\n    def fasterrcnn_augment(self, images):\n        for transform in self.transforms:\n            images = transform.fasterrcnn_augment(images)\n        return images\n\n    def prepare_boxes(self, boxes):\n        result_boxes = boxes.copy()\n        result_boxes[:,0] = np.min(boxes[:, [0,2]], axis=1)\n        result_boxes[:,2] = np.max(boxes[:, [0,2]], axis=1)\n        result_boxes[:,1] = np.min(boxes[:, [1,3]], axis=1)\n        result_boxes[:,3] = np.max(boxes[:, [1,3]], axis=1)\n        return result_boxes\n\n    def deaugment_boxes(self, boxes):\n        for transform in self.transforms[::-1]:\n            boxes = transform.deaugment_boxes(boxes)\n        return self.prepare_boxes(boxes)","metadata":{"papermill":{"duration":0.087046,"end_time":"2022-01-03T13:02:57.380116","exception":false,"start_time":"2022-01-03T13:02:57.29307","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:15.026895Z","iopub.execute_input":"2022-01-04T14:35:15.027283Z","iopub.status.idle":"2022-01-04T14:35:15.050686Z","shell.execute_reply.started":"2022-01-04T14:35:15.027193Z","shell.execute_reply":"2022-01-04T14:35:15.049369Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hàm tạo class Test dataset\nclass WheatTestDataset(Dataset):\n\n    def __init__(self, dataframe, image_dir, transforms=None):\n        super().__init__()\n\n        self.image_ids = dataframe['image_id'].unique()\n        self.df = dataframe\n        self.image_dir = image_dir\n        self.transforms = transforms\n\n    def __getitem__(self, index: int):\n\n        image_id = self.image_ids[index]\n        records = self.df[self.df['image_id'] == image_id]\n\n        image = cv2.imread(f'{self.image_dir}/{image_id}.jpg', cv2.IMREAD_COLOR)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0\n\n        if self.transforms:\n            sample = {\n                'image': image,\n            }\n            sample = self.transforms(**sample)\n            image = sample['image']\n\n        return image, image_id\n\n    def __len__(self) -> int:\n        return self.image_ids.shape[0]","metadata":{"papermill":{"duration":0.071582,"end_time":"2022-01-03T13:02:57.510602","exception":false,"start_time":"2022-01-03T13:02:57.43902","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:15.052741Z","iopub.execute_input":"2022-01-04T14:35:15.053332Z","iopub.status.idle":"2022-01-04T14:35:15.065979Z","shell.execute_reply.started":"2022-01-04T14:35:15.053283Z","shell.execute_reply":"2022-01-04T14:35:15.064907Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Hàm chuyển image sang kiểu Tensor\ndef get_test_transform():\n    return A.Compose([\n        ToTensorV2(p=1.0)\n    ])","metadata":{"papermill":{"duration":0.067167,"end_time":"2022-01-03T13:02:57.642623","exception":false,"start_time":"2022-01-03T13:02:57.575456","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:15.069689Z","iopub.execute_input":"2022-01-04T14:35:15.069955Z","iopub.status.idle":"2022-01-04T14:35:15.07668Z","shell.execute_reply.started":"2022-01-04T14:35:15.069909Z","shell.execute_reply":"2022-01-04T14:35:15.07557Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"################## TRAIN CÁC MODEL #######################","metadata":{}},{"cell_type":"code","source":"# NẾU KHÔNG CẦN TRAIN MODEL THÌ COMMENT CELL BÊN DƯỚI LẠI VÀ RUN ALL CODE","metadata":{"execution":{"iopub.status.busy":"2022-01-04T14:35:15.078302Z","iopub.execute_input":"2022-01-04T14:35:15.078832Z","iopub.status.idle":"2022-01-04T14:35:15.086079Z","shell.execute_reply.started":"2022-01-04T14:35:15.078783Z","shell.execute_reply":"2022-01-04T14:35:15.084916Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Cấu hình các tham sô cần thiết cho việc train model\nfold = 0   # gồm các fold [0,1,2,3,4]\nimg_size = 1024\nbatch_size = 8\nnum_workers = 2\nname_model = 'FRCNN-Resnet152'\n# gồm các model sau: FRCNN-Resnet152, FRCNN-Resnet101, FRCNN-Resnet50, Retinanet-Resnet50\n\nepochs = 100 # Số epoch khi train, nên train trong khoảng 100 epoch\n# Nhóm nhận thấy trong khoảng này thì sẽ thu được model có mAP trên tập valid tốt nhất\n# Với FRCNN-Resnet152: khoảng 13 phút cho 1 epoch\n# Với FRCNN-Resnet101 và FRCNN-Resnet50: khoảng 10 phút cho 1 epoch\n# Với Retinanet-Resnet50: khoảng 8 phút cho 1 epoch\n\n\n# Đọc dữ liệu từ tập train\ndf = pd.read_csv('../input/wheat-src/csv/gwd2020.csv')\n\n# Tách train và valid với fold tương ứng\nvalid_df = df.loc[df['fold'] == fold]\ntrain_df = df.loc[~df.index.isin(valid_df.index)]\nvalid_df = valid_df.loc[valid_df['isbox']==True].reset_index(drop=True)\n\n# Tạo train_dataset và valid_dataset, gọi lại Class wheat dataset bên trên\ntrain_dataset = WheatDataset(df=train_df, img_size=img_size, mode='train', network='FasterRCNN')\nvalid_dataset = WheatDataset(df=valid_df, img_size=img_size, mode='valid', network='FasterRCNN')\n\n# Tạo dataloader để đưa input vào model\ntrain_loader = DataLoader(\n            train_dataset,\n            batch_size=batch_size,\n            sampler=RandomSampler(train_dataset),\n            pin_memory=False,\n            drop_last=False,\n            num_workers=num_workers,\n            collate_fn=collate_fn\n        )\n\nvalid_loader = DataLoader(\n            valid_dataset,\n            batch_size=batch_size,\n            shuffle=False,\n            sampler=SequentialSampler(valid_dataset),\n            pin_memory=False,\n            num_workers=num_workers,\n            collate_fn=collate_fn\n        )\n\n# Load các model tương ứng với name_model\n# FRCNN-Resnet152, FRCNN-Resnet101, FRCNN-Resnet50, Retinanet-Resnet50\nif name_model == 'FRCNN-Resnet50':\n    model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True, progress=False)\n    in_features = model.roi_heads.box_predictor.cls_score.in_features\n    model.roi_heads.box_predictor = FastRCNNPredictor(in_features, 2)\n    if torch.cuda.is_available():\n        model = model.cuda()\nelif name_model == 'Retinanet-Resnet50':\n    model = torchvision.models.detection.retinanet_resnet50_fpn(pretrained=True)\n    num_classes = 2 \n\n    # get number of input features and anchor boxed for the classifier\n    in_features = model.head.classification_head.conv[0].in_channels\n    num_anchors = model.head.classification_head.num_anchors\n\n    # replace the pre-trained head with a new one\n    model.head = RetinaNetHead(in_features, num_anchors, num_classes)\n    if torch.cuda.is_available():\n        model = model.cuda()\nelif name_model == 'FRCNN-Resnet152':\n    model = fasterrcnn_resnet_fpn(backbone_name='resnet152', pretrained=True, pretrained_backbone=True)\n    in_features = model.roi_heads.box_predictor.cls_score.in_features\n    model.roi_heads.box_predictor = FastRCNNPredictor(in_features, 2)\n    if torch.cuda.is_available():\n        model = model.cuda()\nelif name_model == 'FRCNN-Resnet101':\n    model = fasterrcnn_resnet_fpn(backbone_name='resnet101', pretrained=True, pretrained_backbone=True)\n    in_features = model.roi_heads.box_predictor.cls_score.in_features\n    model.roi_heads.box_predictor = FastRCNNPredictor(in_features, 2)\n    if torch.cuda.is_available():\n        model = model.cuda()\n        \n    \n\n# Set các para và chọn thuật toán và learning rate để optimize\nparams = [p for p in model.parameters() if p.requires_grad]\noptimizer = torch.optim.SGD(params, lr=0.0005, momentum=0.9, weight_decay=0.0005)\n\n# Thực hiện train model\nap_max = 0\n\nloss_hist = AverageMeter()\nfor epoch in range(epochs):\n    loss_hist.reset()\n    model.train()\n\n\n    loop = tqdm(train_loader)\n    for images, targets in loop:\n        ### mixup\n        if random.random() > 0.5:\n            images = torch.stack(images).cuda()\n            shuffle_indices = torch.randperm(images.size(0))\n            indices = torch.arange(images.size(0))\n            lam = np.clip(np.random.beta(1.0, 1.0), 0.35, 0.65)\n            images = lam * images + (1 - lam) * images[shuffle_indices, :]\n            mix_targets = []\n            for i, si in zip(indices, shuffle_indices):\n                if i.item() == si.item():\n                    target = targets[i.item()]\n                else:\n                    target = {\n                        'boxes': torch.cat([targets[i.item()]['boxes'], targets[si.item()]['boxes']]),\n                        'labels': torch.cat([targets[i.item()]['labels'], targets[si.item()]['labels']])\n                 }\n\n                mix_targets.append(target)\n            targets = mix_targets\n            \n        if torch.cuda.is_available():    # đưa image và targets vào GPU\n            images = list(image.cuda() for image in images)\n            targets = [{k: v.cuda() for k, v in t.items()} for t in targets]\n\n        # Tính forward và backward   \n        optimizer.zero_grad()\n        with torch.set_grad_enabled(True):\n            loss_dict = model(images, targets)\n            losses = sum(loss for loss in loss_dict.values())\n            if losses == 0 or not torch.isfinite(losses):\n                continue\n            losses.backward()\n            optimizer.step()\n            loss_hist.update(losses.detach().item(), len(images))\n    \n    train_loss = loss_hist.avg\n    print(f'Epoch {epoch} train_loss {train_loss}')\n\n    # Đánh giá độ mAP\n    model.eval()\n    all_predictions = []\n    for images, targets in tqdm(valid_loader):\n        images = list(image.cuda() for image in images)\n        with torch.set_grad_enabled(False):\n            outputs = model(images)\n        for t, o in zip(targets, outputs):\n            boxes = o['boxes'].data.cpu().numpy()\n            boxes = boxes.clip(min=0, max=1024-1)\n            scores = o['scores'].data.cpu().numpy()\n            all_predictions.append({\n                'pred_boxes': boxes.astype(int),\n                'scores': scores,\n                'gt_boxes': t['boxes'].cpu().numpy().astype(int)\n            })\n    ap = calculate_final_score(all_predictions, score_threshold=0.5)\n    print('Epoch {} Train loss: {:.5f} | Val AP: {:.5f}'.format(epoch, train_loss, ap))\n\n    if ap > ap_max:\n        ap_max = ap\n        torch.save(model, f'./model_ep-{epoch}_ap-{ap}')","metadata":{"execution":{"iopub.status.busy":"2022-01-04T14:35:15.087949Z","iopub.execute_input":"2022-01-04T14:35:15.088669Z","iopub.status.idle":"2022-01-04T14:35:15.101032Z","shell.execute_reply.started":"2022-01-04T14:35:15.08862Z","shell.execute_reply":"2022-01-04T14:35:15.099684Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"################### PREDICT VÀ ENSEMBLE ######################","metadata":{}},{"cell_type":"code","source":"# Đọc dữ tập từ tập test.csv\ntest_df = pd.read_csv('../input/global-wheat-detection/sample_submission.csv')\ntest_df.shape","metadata":{"papermill":{"duration":0.093482,"end_time":"2022-01-03T13:02:57.795191","exception":false,"start_time":"2022-01-03T13:02:57.701709","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:15.102793Z","iopub.execute_input":"2022-01-04T14:35:15.103864Z","iopub.status.idle":"2022-01-04T14:35:15.130557Z","shell.execute_reply.started":"2022-01-04T14:35:15.103814Z","shell.execute_reply":"2022-01-04T14:35:15.129558Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Tạo test dataset\ntest_dataset = WheatTestDataset(test_df, '../input/global-wheat-detection/test', get_test_transform())\n\n# Tạo test data loader\ntest_data_loader = DataLoader(\n    test_dataset,\n    batch_size=1,\n    shuffle=False,\n    num_workers=2,\n    drop_last=False,\n    collate_fn=collate_fn\n)","metadata":{"papermill":{"duration":0.077214,"end_time":"2022-01-03T13:02:57.931393","exception":false,"start_time":"2022-01-03T13:02:57.854179","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:15.132084Z","iopub.execute_input":"2022-01-04T14:35:15.132928Z","iopub.status.idle":"2022-01-04T14:35:15.140747Z","shell.execute_reply.started":"2022-01-04T14:35:15.132879Z","shell.execute_reply":"2022-01-04T14:35:15.139668Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Kiểm tra xem máy có cài cuda hay không\ndevice = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')","metadata":{"papermill":{"duration":0.130387,"end_time":"2022-01-03T13:02:58.120522","exception":false,"start_time":"2022-01-03T13:02:57.990135","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:15.14228Z","iopub.execute_input":"2022-01-04T14:35:15.143082Z","iopub.status.idle":"2022-01-04T14:35:15.149717Z","shell.execute_reply.started":"2022-01-04T14:35:15.143036Z","shell.execute_reply":"2022-01-04T14:35:15.148572Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from itertools import product","metadata":{"papermill":{"duration":0.066613,"end_time":"2022-01-03T13:02:58.245715","exception":false,"start_time":"2022-01-03T13:02:58.179102","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:15.151244Z","iopub.execute_input":"2022-01-04T14:35:15.152012Z","iopub.status.idle":"2022-01-04T14:35:15.158829Z","shell.execute_reply.started":"2022-01-04T14:35:15.151953Z","shell.execute_reply":"2022-01-04T14:35:15.1578Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Tạo TTA kết hợp các thao tác lật ảnh ngang - dọc - xoay ảnh 90 độ\n# Từ 1 ảnh ta sẽ thu được 8 ảnh qua nhiều góc nhìn\ntta_transforms = []\nfor tta_combination in product([TTAHorizontalFlip(1024), None], [TTAVerticalFlip(1024), None], [TTARotate90(1024), None]):\n    tta_transforms.append(TTACompose([tta_transform for tta_transform in tta_combination if tta_transform]))","metadata":{"papermill":{"duration":0.070833,"end_time":"2022-01-03T13:02:58.374985","exception":false,"start_time":"2022-01-03T13:02:58.304152","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:15.160366Z","iopub.execute_input":"2022-01-04T14:35:15.161324Z","iopub.status.idle":"2022-01-04T14:35:15.169318Z","shell.execute_reply.started":"2022-01-04T14:35:15.161275Z","shell.execute_reply":"2022-01-04T14:35:15.168342Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Tiền hành load các model đã train và predict\n# Sau khi predict save kết quả ra các file\npath_model0 = '../input/wheat5model/wheat5model/modelfold0.h5'\npath_model1 = '../input/wheat5model/wheat5model/modelfold1.h5'\npath_model2 = '../input/wheat5model/wheat5model/modelfold2.h5'\npath_model3 = '../input/wheat5model/wheat5model/modelfold3.h5'\npath_model4 = '../input/wheat5model/wheat5model/modelfold4.h5'\n\npath_models = [path_model0,path_model1,path_model2,path_model3,path_model4]\nthreshold = 0.5\nnum_model = 0\nfor path in path_models:\n    num_model += 1\n    model = torch.load(path)\n    model.eval()\n    x = model.to(device)\n    \n    pre_boxes = {}\n    pre_labels = {}\n    pre_scores = {}\n\n    for images, image_ids in test_data_loader:\n        pre_boxes[list(image_ids)[0]] = []\n        pre_labels[list(image_ids)[0]] = []\n        pre_scores[list(image_ids)[0]] = []\n\n        images = list(image.to(device) for image in images)\n        for tta_transform in tta_transforms:\n            with torch.set_grad_enabled(False):\n                out = model(tta_transform.fasterrcnn_augment(images.copy()))\n\n            for val in out:\n                boxes = val['boxes'].data.cpu().numpy()\n                scores = val['scores'].data.cpu().numpy()\n            boxes = tta_transform.deaugment_boxes(boxes)\n                    \n            idxs = np.where(scores>threshold)[0]\n            boxes = boxes[idxs]\n            scores = scores[idxs]\n            labels = np.zeros_like(scores)\n\n            if boxes.shape[0] > 0:\n                boxes /= float(1024)\n                boxes = boxes.clip(min=0, max=1)\n                \n            pre_boxes[list(image_ids)[0]].append(boxes.tolist())\n            pre_scores[list(image_ids)[0]].append(scores.tolist())\n            pre_labels[list(image_ids)[0]].append(labels.tolist())\n\n            del boxes\n            del scores\n            del labels\n\n        \n    save_dict(pre_boxes, f'model{num_model}_pre_boxes.pkl')\n    save_dict(pre_scores, f'model{num_model}_pre_scores.pkl')\n    save_dict(pre_labels, f'model{num_model}_pre_labels.pkl')\n    \n    del pre_boxes\n    del pre_scores\n    del pre_labels\n","metadata":{"papermill":{"duration":64.103766,"end_time":"2022-01-03T13:04:02.786373","exception":false,"start_time":"2022-01-03T13:02:58.682607","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:15.171949Z","iopub.execute_input":"2022-01-04T14:35:15.172245Z","iopub.status.idle":"2022-01-04T14:35:59.802656Z","shell.execute_reply.started":"2022-01-04T14:35:15.172214Z","shell.execute_reply":"2022-01-04T14:35:59.799352Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load các kết quả được save ra file lên\nmodel1_load_boxes = load_dict('model1_pre_boxes.pkl')\nmodel1_load_scores = load_dict('model1_pre_scores.pkl')\nmodel1_load_labels = load_dict('model1_pre_labels.pkl')\n\nmodel2_load_boxes = load_dict('model2_pre_boxes.pkl')\nmodel2_load_scores = load_dict('model2_pre_scores.pkl')\nmodel2_load_labels = load_dict('model2_pre_labels.pkl')\n\nmodel3_load_boxes = load_dict('model3_pre_boxes.pkl')\nmodel3_load_scores = load_dict('model3_pre_scores.pkl')\nmodel3_load_labels = load_dict('model3_pre_labels.pkl')\n\nmodel4_load_boxes = load_dict('model4_pre_boxes.pkl')\nmodel4_load_scores = load_dict('model4_pre_scores.pkl')\nmodel4_load_labels = load_dict('model4_pre_labels.pkl')\n\nmodel5_load_boxes = load_dict('model5_pre_boxes.pkl')\nmodel5_load_scores = load_dict('model5_pre_scores.pkl')\nmodel5_load_labels = load_dict('model5_pre_labels.pkl')","metadata":{"papermill":{"duration":0.092747,"end_time":"2022-01-03T13:04:04.074071","exception":false,"start_time":"2022-01-03T13:04:03.981324","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.803826Z","iopub.status.idle":"2022-01-04T14:35:59.804201Z","shell.execute_reply.started":"2022-01-04T14:35:59.803998Z","shell.execute_reply":"2022-01-04T14:35:59.804027Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Ensemble các kết quả lại\noutput_dict = run_wbf_4preds(test_df, \n                             model1_load_boxes, model1_load_scores, model1_load_labels,\n                             model2_load_boxes, model2_load_scores, model2_load_labels,\n                             model3_load_boxes, model3_load_scores, model3_load_labels,\n                             model4_load_boxes, model4_load_scores, model4_load_labels,\n                             model5_load_boxes, model5_load_scores, model5_load_labels,\n                             NMS_THRESH=0.50, BOX_THRESH=0.32, PP_THRESH=0.28)","metadata":{"papermill":{"duration":4.171778,"end_time":"2022-01-03T13:04:08.309196","exception":false,"start_time":"2022-01-03T13:04:04.137418","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.807618Z","iopub.status.idle":"2022-01-04T14:35:59.808509Z","shell.execute_reply.started":"2022-01-04T14:35:59.80816Z","shell.execute_reply":"2022-01-04T14:35:59.808193Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"################## MAKE PSUEDO 1 #############################","metadata":{"papermill":{"duration":0.060254,"end_time":"2022-01-03T13:04:08.431664","exception":false,"start_time":"2022-01-03T13:04:08.37141","status":"completed"},"tags":[]}},{"cell_type":"code","source":"# Hàm tạo data frame từ việc predict trên test\n# Data frame này có cấu trúc giống tập train.csv\ndef make_pseudo_dataframe(test_df, output_dict, PSEUDO_FOLD):\n    results = []\n    for image_id in list(np.unique(test_df.image_id.values)):\n        boxes, scores = output_dict[image_id]\n        if boxes.shape[0] == 0:\n            result = {\n                'image_id': image_id,\n                'fold': PSEUDO_FOLD,\n                'xmin': None,\n                'ymin': None,\n                'xmax': None,\n                'ymax': None,\n                'isbox': False,\n                'source': 'pseudo'\n            }\n            results.append(result)\n        else:\n            for box in boxes:\n                result = {\n                    'image_id': image_id,\n                    'fold': PSEUDO_FOLD,\n                    'xmin': box[0],\n                    'ymin': box[1],\n                    'xmax': box[2],\n                    'ymax': box[3],\n                    'isbox': True,\n                    'source': 'pseudo'\n                }\n                results.append(result)\n    pseudo_df = pd.DataFrame(results, columns=['image_id','fold', 'xmin', 'ymin', 'xmax', 'ymax', 'isbox', 'source'])\n    return pseudo_df","metadata":{"papermill":{"duration":0.072629,"end_time":"2022-01-03T13:04:08.694263","exception":false,"start_time":"2022-01-03T13:04:08.621634","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.810115Z","iopub.status.idle":"2022-01-04T14:35:59.812168Z","shell.execute_reply.started":"2022-01-04T14:35:59.811831Z","shell.execute_reply":"2022-01-04T14:35:59.811865Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pseudo_df = make_pseudo_dataframe(test_df, output_dict, 1)","metadata":{"papermill":{"duration":0.074557,"end_time":"2022-01-03T13:04:08.829704","exception":false,"start_time":"2022-01-03T13:04:08.755147","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.813645Z","iopub.status.idle":"2022-01-04T14:35:59.814713Z","shell.execute_reply.started":"2022-01-04T14:35:59.814365Z","shell.execute_reply":"2022-01-04T14:35:59.8144Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv('../input/wheat-src/csv/gwd2020.csv')","metadata":{"papermill":{"duration":0.311104,"end_time":"2022-01-03T13:04:09.200918","exception":false,"start_time":"2022-01-03T13:04:08.889814","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.816362Z","iopub.status.idle":"2022-01-04T14:35:59.818263Z","shell.execute_reply.started":"2022-01-04T14:35:59.817931Z","shell.execute_reply":"2022-01-04T14:35:59.817967Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fold = 1\nvalid_df = df.loc[df['fold'] == fold]\ntrain_df = df.loc[~df.index.isin(valid_df.index)]\nvalid_df = valid_df.loc[valid_df['isbox']==True].reset_index(drop=True)\n\ntrain_df = pd.concat([train_df,pseudo_df])\ntrain_df.reset_index(drop=True)","metadata":{"papermill":{"duration":0.125975,"end_time":"2022-01-03T13:04:09.385856","exception":false,"start_time":"2022-01-03T13:04:09.259881","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.820065Z","iopub.status.idle":"2022-01-04T14:35:59.820651Z","shell.execute_reply.started":"2022-01-04T14:35:59.82031Z","shell.execute_reply":"2022-01-04T14:35:59.82034Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_size = 1024\ntrain_dataset = WheatDataset(df=train_df, img_size=img_size, mode='train', network='FasterRCNN')\nvalid_dataset = WheatDataset(df=valid_df, img_size=img_size, mode='valid', network='FasterRCNN')","metadata":{"papermill":{"duration":0.232855,"end_time":"2022-01-03T13:04:09.678643","exception":false,"start_time":"2022-01-03T13:04:09.445788","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.822588Z","iopub.status.idle":"2022-01-04T14:35:59.823455Z","shell.execute_reply.started":"2022-01-04T14:35:59.823126Z","shell.execute_reply":"2022-01-04T14:35:59.823157Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batch_size = 8\nnum_workers = 2\ntrain_loader = DataLoader(\n            train_dataset,\n            batch_size=batch_size,\n            sampler=RandomSampler(train_dataset),\n            pin_memory=False,\n            drop_last=False,\n            num_workers=num_workers,\n            collate_fn=collate_fn\n        )\n\nvalid_loader = DataLoader(\n            valid_dataset,\n            batch_size=batch_size,\n            shuffle=False,\n            sampler=SequentialSampler(valid_dataset),\n            pin_memory=False,\n            num_workers=num_workers,\n            collate_fn=collate_fn\n        )","metadata":{"papermill":{"duration":0.073474,"end_time":"2022-01-03T13:04:09.813555","exception":false,"start_time":"2022-01-03T13:04:09.740081","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.825575Z","iopub.status.idle":"2022-01-04T14:35:59.826173Z","shell.execute_reply.started":"2022-01-04T14:35:59.825866Z","shell.execute_reply":"2022-01-04T14:35:59.825895Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = torch.load('../input/wheat5model/wheat5model/modelfold1.h5')\nif torch.cuda.is_available():\n    model = model.cuda()","metadata":{"papermill":{"duration":0.626082,"end_time":"2022-01-03T13:04:10.499931","exception":false,"start_time":"2022-01-03T13:04:09.873849","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.828163Z","iopub.status.idle":"2022-01-04T14:35:59.828744Z","shell.execute_reply.started":"2022-01-04T14:35:59.828421Z","shell.execute_reply":"2022-01-04T14:35:59.82847Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"params = [p for p in model.parameters() if p.requires_grad]\noptimizer = torch.optim.SGD(params, lr=0.0005, momentum=0.9, weight_decay=0.0005)","metadata":{"papermill":{"duration":0.071075,"end_time":"2022-01-03T13:04:10.630853","exception":false,"start_time":"2022-01-03T13:04:10.559778","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.830631Z","iopub.status.idle":"2022-01-04T14:35:59.831191Z","shell.execute_reply.started":"2022-01-04T14:35:59.830885Z","shell.execute_reply":"2022-01-04T14:35:59.830915Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epochs = 10\nap_max = 0\nidx_maxap = 0\n\nloss_hist = AverageMeter()\nfor epoch in range(epochs):\n    loss_hist.reset()\n    model.train()\n\n\n    loop = tqdm(train_loader)\n    for images, targets in loop:\n        ### mixup\n        if random.random() > 0.5:\n            images = torch.stack(images).cuda()\n            shuffle_indices = torch.randperm(images.size(0))\n            indices = torch.arange(images.size(0))\n            lam = np.clip(np.random.beta(1.0, 1.0), 0.35, 0.65)\n            images = lam * images + (1 - lam) * images[shuffle_indices, :]\n            mix_targets = []\n            for i, si in zip(indices, shuffle_indices):\n                if i.item() == si.item():\n                    target = targets[i.item()]\n                else:\n                    target = {\n                        'boxes': torch.cat([targets[i.item()]['boxes'], targets[si.item()]['boxes']]),\n                        'labels': torch.cat([targets[i.item()]['labels'], targets[si.item()]['labels']])\n                    }\n\n                mix_targets.append(target)\n            targets = mix_targets\n        if torch.cuda.is_available():    \n            images = list(image.cuda() for image in images)\n            targets = [{k: v.cuda() for k, v in t.items()} for t in targets]\n\n        optimizer.zero_grad()\n        with torch.set_grad_enabled(True):\n            loss_dict = model(images, targets)\n            losses = sum(loss for loss in loss_dict.values())\n            if losses == 0 or not torch.isfinite(losses):\n                continue\n            losses.backward()\n            optimizer.step()\n            loss_hist.update(losses.detach().item(), len(images))\n    \n    train_loss = loss_hist.avg\n    print(f'Epoch {epoch} train_loss {train_loss}')\n\n    model.eval()\n    all_predictions = []\n    for images, targets in tqdm(valid_loader):\n        images = list(image.cuda() for image in images)\n        with torch.set_grad_enabled(False):\n            outputs = model(images)\n        for t, o in zip(targets, outputs):\n            boxes = o['boxes'].data.cpu().numpy()\n            boxes = boxes.clip(min=0, max=1024-1)\n            scores = o['scores'].data.cpu().numpy()\n            all_predictions.append({\n                'pred_boxes': boxes.astype(int),\n                'scores': scores,\n                'gt_boxes': t['boxes'].cpu().numpy().astype(int)\n            })\n    ap = calculate_final_score(all_predictions, score_threshold=0.5)\n    print('Epoch {} Train loss: {:.5f} | Val AP: {:.5f}'.format(epoch, train_loss, ap))\n\n    if ap > ap_max:\n        ap_max = ap\n        idx_maxap = epoch\n        torch.save(model, f'./model_opt1_ep_{epoch}.h5')","metadata":{"papermill":{"duration":7444.609861,"end_time":"2022-01-03T15:08:15.299864","exception":false,"start_time":"2022-01-03T13:04:10.690003","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.833211Z","iopub.status.idle":"2022-01-04T14:35:59.833806Z","shell.execute_reply.started":"2022-01-04T14:35:59.833472Z","shell.execute_reply":"2022-01-04T14:35:59.833517Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"path_model_opt1 = f'./model_opt1_ep_{idx_maxap}.h5'\n\nmodel = torch.load(path_model_opt1)\nif torch.cuda.is_available():\n    model = model.cuda()\n\nthreshold = 0.5\n\nmodel.eval()\n\npre_boxes = {}\npre_labels = {}\npre_scores = {}\n\nfor images, image_ids in test_data_loader:\n    pre_boxes[list(image_ids)[0]] = []\n    pre_labels[list(image_ids)[0]] = []\n    pre_scores[list(image_ids)[0]] = []\n    images = list(image.to(device) for image in images)\n    for tta_transform in tta_transforms:\n        with torch.set_grad_enabled(False):\n            out = model(tta_transform.fasterrcnn_augment(images.copy()))\n\n        for val in out:\n            boxes = val['boxes'].data.cpu().numpy()\n            scores = val['scores'].data.cpu().numpy()\n\n        boxes = tta_transform.deaugment_boxes(boxes)\n\n        idxs = np.where(scores>threshold)[0]\n        boxes = boxes[idxs]\n        scores = scores[idxs]\n        labels = np.zeros_like(scores)\n\n\n        if boxes.shape[0] > 0:\n            boxes /= float(1024)\n            boxes = boxes.clip(min=0, max=1)\n\n\n\n        pre_boxes[list(image_ids)[0]].append(boxes.tolist())\n        pre_scores[list(image_ids)[0]].append(scores.tolist())\n        pre_labels[list(image_ids)[0]].append(labels.tolist())\n\n        del boxes\n        del scores\n        del labels\n\nsave_dict(pre_boxes, f'model_opt1_pre_boxes.pkl')\nsave_dict(pre_scores, f'model_opt1_pre_scores.pkl')\nsave_dict(pre_labels, f'model_opt1_pre_labels.pkl')\n\ndel pre_boxes\ndel pre_scores\ndel pre_labels\n","metadata":{"papermill":{"duration":10.264328,"end_time":"2022-01-03T15:08:27.090374","exception":false,"start_time":"2022-01-03T15:08:16.826046","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.835654Z","iopub.status.idle":"2022-01-04T14:35:59.836522Z","shell.execute_reply.started":"2022-01-04T14:35:59.836164Z","shell.execute_reply":"2022-01-04T14:35:59.836211Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_opt1_load_boxes = load_dict('model_opt1_pre_boxes.pkl')\nmodel_opt1_load_scores = load_dict('model_opt1_pre_scores.pkl')\nmodel_opt1_load_labels = load_dict('model_opt1_pre_labels.pkl')","metadata":{"papermill":{"duration":1.527616,"end_time":"2022-01-03T15:08:30.830652","exception":false,"start_time":"2022-01-03T15:08:29.303036","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.838514Z","iopub.status.idle":"2022-01-04T14:35:59.83908Z","shell.execute_reply.started":"2022-01-04T14:35:59.838776Z","shell.execute_reply":"2022-01-04T14:35:59.838806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"output_dict = run_wbf(test_df, \n                             model_opt1_load_boxes, model_opt1_load_scores, model_opt1_load_labels,\n                             NMS_THRESH=0.50, BOX_THRESH=0.42, PP_THRESH=0.32)","metadata":{"papermill":{"duration":2.10046,"end_time":"2022-01-03T15:08:34.439706","exception":false,"start_time":"2022-01-03T15:08:32.339246","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.841165Z","iopub.status.idle":"2022-01-04T14:35:59.841736Z","shell.execute_reply.started":"2022-01-04T14:35:59.841409Z","shell.execute_reply":"2022-01-04T14:35:59.841457Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"################## MAKE PSUEDO 2 #############################","metadata":{"papermill":{"duration":1.504656,"end_time":"2022-01-03T15:08:37.602487","exception":false,"start_time":"2022-01-03T15:08:36.097831","status":"completed"},"tags":[]}},{"cell_type":"code","source":"pseudo_df = make_pseudo_dataframe(test_df, output_dict, 1)","metadata":{"papermill":{"duration":1.517969,"end_time":"2022-01-03T15:08:40.647137","exception":false,"start_time":"2022-01-03T15:08:39.129168","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.843589Z","iopub.status.idle":"2022-01-04T14:35:59.844131Z","shell.execute_reply.started":"2022-01-04T14:35:59.84383Z","shell.execute_reply":"2022-01-04T14:35:59.843861Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv('../input/wheat-src/csv/gwd2020.csv')","metadata":{"papermill":{"duration":1.647327,"end_time":"2022-01-03T15:08:43.820073","exception":false,"start_time":"2022-01-03T15:08:42.172746","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.846337Z","iopub.status.idle":"2022-01-04T14:35:59.847418Z","shell.execute_reply.started":"2022-01-04T14:35:59.847115Z","shell.execute_reply":"2022-01-04T14:35:59.847146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fold = 1\nvalid_df = df.loc[df['fold'] == fold]\ntrain_df = df.loc[~df.index.isin(valid_df.index)]\nvalid_df = valid_df.loc[valid_df['isbox']==True].reset_index(drop=True)\n\ntrain_df = pd.concat([train_df,pseudo_df])\ntrain_df.reset_index(drop=True)","metadata":{"papermill":{"duration":1.701517,"end_time":"2022-01-03T15:08:47.296596","exception":false,"start_time":"2022-01-03T15:08:45.595079","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.848938Z","iopub.status.idle":"2022-01-04T14:35:59.849724Z","shell.execute_reply.started":"2022-01-04T14:35:59.849378Z","shell.execute_reply":"2022-01-04T14:35:59.849414Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_size = 1024\ntrain_dataset = WheatDataset(df=train_df, img_size=img_size, mode='train', network='FasterRCNN')\nvalid_dataset = WheatDataset(df=valid_df, img_size=img_size, mode='valid', network='FasterRCNN')","metadata":{"papermill":{"duration":1.677515,"end_time":"2022-01-03T15:08:50.47844","exception":false,"start_time":"2022-01-03T15:08:48.800925","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.851181Z","iopub.status.idle":"2022-01-04T14:35:59.851936Z","shell.execute_reply.started":"2022-01-04T14:35:59.85162Z","shell.execute_reply":"2022-01-04T14:35:59.851657Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batch_size = 8\nnum_workers = 2\ntrain_loader = DataLoader(\n            train_dataset,\n            batch_size=batch_size,\n            sampler=RandomSampler(train_dataset),\n            pin_memory=False,\n            drop_last=False,\n            num_workers=num_workers,\n            collate_fn=collate_fn\n        )\n\nvalid_loader = DataLoader(\n            valid_dataset,\n            batch_size=batch_size,\n            shuffle=False,\n            sampler=SequentialSampler(valid_dataset),\n            pin_memory=False,\n            num_workers=num_workers,\n            collate_fn=collate_fn\n        )","metadata":{"papermill":{"duration":1.522339,"end_time":"2022-01-03T15:08:53.506857","exception":false,"start_time":"2022-01-03T15:08:51.984518","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.853443Z","iopub.status.idle":"2022-01-04T14:35:59.854199Z","shell.execute_reply.started":"2022-01-04T14:35:59.853874Z","shell.execute_reply":"2022-01-04T14:35:59.853908Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = torch.load(path_model_opt1)\nif torch.cuda.is_available():\n    model = model.cuda()","metadata":{"papermill":{"duration":2.578148,"end_time":"2022-01-03T15:08:57.59675","exception":false,"start_time":"2022-01-03T15:08:55.018602","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.855693Z","iopub.status.idle":"2022-01-04T14:35:59.856464Z","shell.execute_reply.started":"2022-01-04T14:35:59.85612Z","shell.execute_reply":"2022-01-04T14:35:59.856155Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"params = [p for p in model.parameters() if p.requires_grad]\noptimizer = torch.optim.SGD(params, lr=0.0005, momentum=0.9, weight_decay=0.0005)","metadata":{"papermill":{"duration":2.16042,"end_time":"2022-01-03T15:09:01.512207","exception":false,"start_time":"2022-01-03T15:08:59.351787","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.857943Z","iopub.status.idle":"2022-01-04T14:35:59.858722Z","shell.execute_reply.started":"2022-01-04T14:35:59.858375Z","shell.execute_reply":"2022-01-04T14:35:59.858411Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"epochs = 6\nap_max = 0\nidx_maxap = 0\n\nloss_hist = AverageMeter()\nfor epoch in range(epochs):\n    loss_hist.reset()\n    model.train()\n\n\n    loop = tqdm(train_loader)\n    for images, targets in loop:\n        ### mixup\n        if random.random() > 0.5:\n            images = torch.stack(images).cuda()\n            shuffle_indices = torch.randperm(images.size(0))\n            indices = torch.arange(images.size(0))\n            lam = np.clip(np.random.beta(1.0, 1.0), 0.35, 0.65)\n            images = lam * images + (1 - lam) * images[shuffle_indices, :]\n            mix_targets = []\n            for i, si in zip(indices, shuffle_indices):\n                if i.item() == si.item():\n                    target = targets[i.item()]\n                else:\n                    target = {\n                        'boxes': torch.cat([targets[i.item()]['boxes'], targets[si.item()]['boxes']]),\n                        'labels': torch.cat([targets[i.item()]['labels'], targets[si.item()]['labels']])\n                    }\n\n                mix_targets.append(target)\n            targets = mix_targets\n        if torch.cuda.is_available():    \n            images = list(image.cuda() for image in images)\n            targets = [{k: v.cuda() for k, v in t.items()} for t in targets]\n\n        optimizer.zero_grad()\n        with torch.set_grad_enabled(True):\n            loss_dict = model(images, targets)\n            losses = sum(loss for loss in loss_dict.values())\n            if losses == 0 or not torch.isfinite(losses):\n                continue\n            losses.backward()\n            optimizer.step()\n            loss_hist.update(losses.detach().item(), len(images))\n    \n    train_loss = loss_hist.avg\n    print(f'Epoch {epoch} train_loss {train_loss}')\n\n    model.eval()\n    all_predictions = []\n    for images, targets in tqdm(valid_loader):\n        images = list(image.cuda() for image in images)\n        with torch.set_grad_enabled(False):\n            outputs = model(images)\n        for t, o in zip(targets, outputs):\n            boxes = o['boxes'].data.cpu().numpy()\n            boxes = boxes.clip(min=0, max=1024-1)\n            scores = o['scores'].data.cpu().numpy()\n            all_predictions.append({\n                'pred_boxes': boxes.astype(int),\n                'scores': scores,\n                'gt_boxes': t['boxes'].cpu().numpy().astype(int)\n            })\n    ap = calculate_final_score(all_predictions, score_threshold=0.5)\n    print('Epoch {} Train loss: {:.5f} | Val AP: {:.5f}'.format(epoch, train_loss, ap))\n\n    if ap > ap_max:\n        ap_max = ap\n        idx_maxap = epoch\n        torch.save(model, f'./model_opt2_ep_{epoch}.h5')","metadata":{"papermill":{"duration":4465.92002,"end_time":"2022-01-03T16:23:28.968432","exception":false,"start_time":"2022-01-03T15:09:03.048412","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.860259Z","iopub.status.idle":"2022-01-04T14:35:59.861025Z","shell.execute_reply.started":"2022-01-04T14:35:59.860695Z","shell.execute_reply":"2022-01-04T14:35:59.860732Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"path_model_opt2 = f'./model_opt2_ep_{idx_maxap}.h5'\n\nmodel = torch.load(path_model_opt2)\nif torch.cuda.is_available():\n    model = model.cuda()\n\nthreshold = 0.5\n\nmodel.eval()\n\npre_boxes = {}\npre_labels = {}\npre_scores = {}\n\nfor images, image_ids in test_data_loader:\n    pre_boxes[list(image_ids)[0]] = []\n    pre_labels[list(image_ids)[0]] = []\n    pre_scores[list(image_ids)[0]] = []\n    images = list(image.to(device) for image in images)\n    for tta_transform in tta_transforms:\n        with torch.set_grad_enabled(False):\n            out = model(tta_transform.fasterrcnn_augment(images.copy()))\n\n        for val in out:\n            boxes = val['boxes'].data.cpu().numpy()\n            scores = val['scores'].data.cpu().numpy()\n\n        boxes = tta_transform.deaugment_boxes(boxes)\n\n        idxs = np.where(scores>threshold)[0]\n        boxes = boxes[idxs]\n        scores = scores[idxs]\n        labels = np.zeros_like(scores)\n\n\n        if boxes.shape[0] > 0:\n            boxes /= float(1024)\n            boxes = boxes.clip(min=0, max=1)\n\n\n\n        pre_boxes[list(image_ids)[0]].append(boxes.tolist())\n        pre_scores[list(image_ids)[0]].append(scores.tolist())\n        pre_labels[list(image_ids)[0]].append(labels.tolist())\n\n        del boxes\n        del scores\n        del labels\n\nsave_dict(pre_boxes, f'model_opt2_pre_boxes.pkl')\nsave_dict(pre_scores, f'model_opt2_pre_scores.pkl')\nsave_dict(pre_labels, f'model_opt2_pre_labels.pkl')\n\ndel pre_boxes\ndel pre_scores\ndel pre_labels\n","metadata":{"papermill":{"duration":11.393349,"end_time":"2022-01-03T16:23:42.791218","exception":false,"start_time":"2022-01-03T16:23:31.397869","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.862582Z","iopub.status.idle":"2022-01-04T14:35:59.863323Z","shell.execute_reply.started":"2022-01-04T14:35:59.863009Z","shell.execute_reply":"2022-01-04T14:35:59.863044Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_opt2_load_boxes = load_dict('model_opt2_pre_boxes.pkl')\nmodel_opt2_load_scores = load_dict('model_opt2_pre_scores.pkl')\nmodel_opt2_load_labels = load_dict('model_opt2_pre_labels.pkl')","metadata":{"papermill":{"duration":2.527917,"end_time":"2022-01-03T16:23:48.120618","exception":false,"start_time":"2022-01-03T16:23:45.592701","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.865577Z","iopub.status.idle":"2022-01-04T14:35:59.866357Z","shell.execute_reply.started":"2022-01-04T14:35:59.866044Z","shell.execute_reply":"2022-01-04T14:35:59.866078Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"output_dict = run_wbf(test_df, \n                             model_opt2_load_boxes, model_opt2_load_scores, model_opt2_load_labels,\n                             NMS_THRESH=0.50, BOX_THRESH=0.44, PP_THRESH=0.34)","metadata":{"papermill":{"duration":2.752679,"end_time":"2022-01-03T16:23:53.301204","exception":false,"start_time":"2022-01-03T16:23:50.548525","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.86789Z","iopub.status.idle":"2022-01-04T14:35:59.868692Z","shell.execute_reply.started":"2022-01-04T14:35:59.868326Z","shell.execute_reply":"2022-01-04T14:35:59.868363Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"############### TẠO FILE SUBMIT LÊN KAGGLE ####################","metadata":{"papermill":{"duration":3.05594,"end_time":"2022-01-03T16:23:59.14674","exception":false,"start_time":"2022-01-03T16:23:56.0908","status":"completed"},"tags":[]}},{"cell_type":"code","source":"results = []\nfor image_id in list(np.unique(test_df.image_id.values)):\n    boxes, scores = output_dict[image_id]\n    if len(boxes) > 0:\n        boxes = boxes.astype(np.int32)\n        #xyxy to xywh\n        boxes[:, 2] = boxes[:, 2] - boxes[:, 0]\n        boxes[:, 3] = boxes[:, 3] - boxes[:, 1]\n        \n    result = {\n        'image_id': image_id,\n        'PredictionString': format_prediction_string(boxes, scores)\n    }\n    results.append(result)\nsub_df = pd.DataFrame(results, columns=['image_id', 'PredictionString'])\nsub_df.to_csv('submission.csv', index=False)\nprint(sub_df.head(20))","metadata":{"papermill":{"duration":2.52682,"end_time":"2022-01-03T16:24:04.083969","exception":false,"start_time":"2022-01-03T16:24:01.557149","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T14:35:59.870181Z","iopub.status.idle":"2022-01-04T14:35:59.870931Z","shell.execute_reply.started":"2022-01-04T14:35:59.870617Z","shell.execute_reply":"2022-01-04T14:35:59.870653Z"},"trusted":true},"execution_count":null,"outputs":[]}]}