{"cells":[{"metadata":{},"cell_type":"markdown","source":"# WORK IN PROGRESS"},{"metadata":{},"cell_type":"markdown","source":"# Imports"},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install pycocotools timm albumentations omegaconf","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!git clone https://github.com/rwightman/efficientdet-pytorch.git","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport numpy as np \nimport pandas as pd\nimport glob\nimport matplotlib.pyplot as plt\nimport plotly.express as px\nimport seaborn as sns\nimport tqdm\nimport torch\nfrom itertools import combinations\nimport albumentations as A\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom albumentations.pytorch.transforms import ToTensorV2\nimport warnings\nwarnings.filterwarnings('ignore')\n\n\nimport pydicom \nimport cv2\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut # voi = value of interest, lut = lookup table","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"DATA_DIR = '/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/'\nTRAIN_DIR = os.path.join(DATA_DIR, 'train')\nTEST_DIR = os.path.join(DATA_DIR, 'test')\n\nLABEL_COLORS = [px.colors.label_rgb(px.colors.convert_to_RGB_255(x)) for x in sns.color_palette(\"Spectral\", 15)]\nLABEL_COLORS","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"LABEL_COLORS_TUPLES = [col[4:-1].split(\",\") for col in LABEL_COLORS]\nLABEL_COLORS_TUPLES = [tuple([int(num) for num in col]) for col in LABEL_COLORS_TUPLES ]\nLABEL_COLORS_TUPLES","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Helper Functions"},{"metadata":{"trusted":true},"cell_type":"code","source":"def read_dicom(path: str, voi_lut=True, fix_monochrome=True) -> np.ndarray:\n    dicom = pydicom.read_file(path)\n    # if voi lut is available, use it to transform raw dicom data to human friendly view\n    if voi_lut:\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n    else:\n        data = dicom.pixel_array\n    #MONOCHROME1 indicates that the greyscale ranges from bright to dark with ascending pixel values, \n    #MONOCHROME2 ranges from dark to bright with ascending pixel values\n    if dicom.PhotometricInterpretation == 'MONOCHROME1' and fix_monochrome:\n        data = np.amax(data) - data # np.amax() -> maximum of flattened array\n    data = data - np.min(data)\n    data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n    return data","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def plot_image(img, title=\"\", figsize=(10,10), cmap=None):\n    plt.figure(figsize=figsize)\n    if cmap:\n        plt.imshow(img, cmap=cmap)\n    else:\n        plt.imshow(img)\n    plt.title(title)\n    plt.axis(False)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_annotations(df: pd.DataFrame, image_id, rad_id=True) -> dict:\n    annotations = {}\n    if isinstance(image_id, str):\n        image_id = [image_id]\n    for im in image_id:\n        annos_df = df[df['image_id'] == im]\n        annos = []\n        for ann_idx in annos_df.index:\n            if annos_df.loc[ann_idx, 'class_id'] != 14:\n                if rad_id:\n                    annos.append([annos_df.loc[ann_idx, 'class_id'], \n                                annos_df.loc[ann_idx, 'x_min'],\n                                 annos_df.loc[ann_idx, 'y_min'],\n                                 annos_df.loc[ann_idx, 'x_max'],\n                                 annos_df.loc[ann_idx, 'y_max'],\n                                 annos_df.loc[ann_idx, 'rad_id']])\n                else:\n                    annos.append([annos_df.loc[ann_idx, 'class_id'], \n                                annos_df.loc[ann_idx, 'x_min'],\n                                 annos_df.loc[ann_idx, 'y_min'],\n                                 annos_df.loc[ann_idx, 'x_max'],\n                                 annos_df.loc[ann_idx, 'y_max']])\n        annotations[im] = annos\n        \n    return annotations","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def draw_boxes(df, img_id, id_to_classes, annotations, rad_id=True, plot_rad=True):\n    \"\"\"Plot image with bounding box annotations\"\"\"\n    img = read_dicom(TRAIN_DIR + \"/\" + img_id + \".dicom\")\n    img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)\n    for idx, annos in annotations.items():\n        for anno in annos:\n            img = cv2.rectangle(img, (int(anno[1]), int(anno[2])), (int(anno[3]), \n                                int(anno[4])), \n                                LABEL_COLORS_TUPLES[anno[0]], 1)\n            if plot_rad and rad_id:\n                label_text = id_to_classes[anno[0]] + f\"({anno[5]})\"\n            else:\n                label_text = id_to_classes[anno[0]]\n            font = cv2.FONT_HERSHEY_SIMPLEX \n            img = cv2.putText(img, label_text, \n                              (int(anno[1]), int(anno[2]) - 5), \n                              font, 1.5, LABEL_COLORS_TUPLES[anno[0]], 2)\n    \n    return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def intersection_over_union(boxes_1, boxes_2, box_format=\"corners\"):\n    \"\"\"\n    Calculate the intersection over union of two bounding boxes\n    Parameters:\n        boxes_1 (tensor): shape (batch_size,4)\n        boxes_2 (tensor): shape (batch_size, 4)\n        box_format (str): midpoint (x,y,w,h) or corners (x1,y1,x2,y2)\n    Returns:\n        tensor: IoU for all inputs\n    \"\"\"\n    \n    if box_format == \"midpoint\":\n        box1_x1 = boxes_1[..., 0:1] - boxes_1[..., 2:3] / 2\n        box1_y1 = boxes_1[..., 1:2] - boxes_1[..., 3:4] / 2\n        box1_x2 = boxes_1[..., 0:1] + boxes_1[..., 2:3] / 2\n        box1_y2 = boxes_1[..., 1:2] + boxes_1[..., 3:4] / 2\n\n        box2_x1 = boxes_2[..., 0:1] - boxes_2[..., 2:3] / 2\n        box2_y1 = boxes_2[..., 1:2] - boxes_2[..., 3:4] / 2\n        box2_x2 = boxes_2[..., 2:3] + boxes_2[..., 2:3] / 2\n        box2_y2 = boxes_2[..., 3:4] + boxes_2[..., 3:4] / 2\n    \n    elif box_format == \"corners\":\n        box1_x1 = boxes_1[..., 0:1] # shape (N,1)\n        box1_y1 = boxes_1[..., 1:2]\n        box1_x2 = boxes_1[..., 2:3]\n        box1_y2 = boxes_1[..., 3:4]\n\n        box2_x1 = boxes_2[..., 0:1] # shape (N,1)\n        box2_y1 = boxes_2[..., 1:2]\n        box2_x2 = boxes_2[..., 2:3]\n        box2_y2 = boxes_2[..., 3:4]\n    \n    # corner points of intersection\n    x1 = torch.max(box1_x1, box2_x1)\n    y1 = torch.max(box1_y1, box2_y1)\n    x2 = torch.max(box1_x2, box2_x2)\n    y2 = torch.max(box1_y2, box2_y2)\n    \n    intersection = (x2 - x1).clamp(0) * (y2 - y1).clamp(0) # clamp is if they don't intersect\n    \n    # union\n    box1_area = abs((box1_x2 - box1_x1) * (box1_y1 - box1_y2))\n    box2_area = abs((box2_x2 - box2_x1) * (box2_y1 - box2_y2))\n    \n    return intersection / (box1_area + box2_area - intersection + 1e-6)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def non_max_suppression(boxes, iou_threshold, threshold, box_format='corners'):\n    # boxes = [[class, confidence/probability, x1, y1, x2, y2], [], ...]\n    assert type(boxes) == list\n    boxes = [box for box in boxes if box[1] > threshold]\n    boxes = sorted(boxes, key=lambda x: x[1], reverse=True)\n    boxes_after_nms = []\n    \n    while boxes:\n        chosen_box = boxes.pop(0)\n        boxes = [box for box in boxes \n                 if box[0] != chosen_box[0] \n                 or intersection_over_union(torch.tensor(chosen_box[2:]), \n                                            torch.tensor(box[2:]), \n                                            box_format=box_format) < iou_threshold]\n    \n        boxes_after_nms.append(chosen_box)\n    \n    return boxes_after_nms","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def merge_similar_annotations(df, image_id: list, threshold=0.75):\n    \"\"\"Function to merge similar annotations by different radiologists\"\"\"\n    annotations_ = {}\n    if isinstance(image_id, str):\n        image_id = [image_id]\n    annotations = get_annotations(df, image_id)\n    for im, annos in annotations.items():\n        a = []\n        class_ids = list(set([anno[0] for anno in annos]))\n        for i in class_ids:\n            anns_ = []\n            anns = [anno for anno in annos if anno[0]==i]\n            if len(anns) > 1:\n                while len(anns) > 1:\n                    #p = list(combinations(list(range(len(anns))), 2))\n                    b_1 = anns.pop(0)\n                    box_1 = torch.tensor(b_1[1:-1]).unsqueeze(0)\n                    similar_boxes = []\n                    for i, ann in enumerate(anns):\n                        box_2 = torch.tensor(ann[1:-1]).unsqueeze(0)\n                        iou = intersection_over_union(box_1, box_2)\n                        if float(iou) >= threshold:\n                            similar_boxes.append(i) \n                    # create a new box from similar boxes\n                    x1 = b_1[1]\n                    y1 = b_1[2]\n                    x2 = b_1[3]\n                    y2 = b_1[4]\n                    rad_id = b_1[5]\n                    for j in similar_boxes:\n                        x1 = min(anns[j][1], x1)\n                        y1 = min(anns[j][2], y1)\n                        x2 = max(anns[j][3], x2)\n                        y2 = max(anns[j][4], y2)\n                        rad_id += f\" {anns[j][5]}\"\n                    new_box = [b_1[0], x1, y1, x2, y2, rad_id]\n                    anns_.append(new_box)\n                    # delete other similar boxes\n                    for idx in sorted(similar_boxes, reverse=True):\n                        del anns[idx]\n\n                a += anns_\n                \n            else:\n                a += anns\n        \n        annotations_[im] = a\n                \n    return annotations_","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def filter_df(df, include_no_findings=True):\n    \"\"\"Reduce train df (delete no findings and merge boxes)\"\"\"\n    no_findings = df[df['class_id'] == 14]\n    no_findings = no_findings.drop_duplicates(subset=['image_id'])\n    print(f\"Num annotations before: {len(df[df['class_id'] != 14])}\")\n    print(f\"{len(no_findings)} images with no findings\")\n    annotations_merged = merge_similar_annotations(df, list(df['image_id'].unique()))\n    data = []\n    for k, val in annotations_merged.items():\n        for v in val:\n            data.append([\n                # image_id, class_name, class_id, rad_id, min_x, min_y, max_x, max_y\n                k, id_to_classes[v[0]], v[0], v[5], v[1], v[2], v[3], v[4]\n            ])\n    new_df = pd.DataFrame(data, columns=df.columns)\n    print(f\"Num annotations after: {len(new_df)} (without no findings images)\")\n    if include_no_findings:\n        print(\"Add no findings to annotations dataframe...\")\n        result = pd.concat([new_df, no_findings], ignore_index=True)\n        print(f\"Length of resulting dataframe: {len(result)}\")\n        return result\n    \n    return new_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def add_size_aspect_ratio(df, drop_nan=False):\n    \"\"\"Add the img height and width as well as the aspect ratio to the dataframe\"\"\"\n    data = pd.DataFrame(df)\n    if drop_nan:\n        data = data[data['class_id'] != 14].reset_index(drop=True)\n    data['img_height'] = 0\n    data['img_width'] = 0\n    data['aspect_ratio'] = 0\n    \n    id_img_size = {}\n    for idx in tqdm.tqdm(list(data['image_id'].unique())):\n        img = read_dicom(os.path.join(TRAIN_DIR, idx + '.dicom'))\n        id_img_size[idx] = {'img_height': img.shape[0], \n                            'img_width': img.shape[1], \n                            'aspect_ratio': img.shape[0] / img.shape[1]}\n    \n    for k, val in id_img_size.items():\n        for idx in data[data['image_id'] == k].index:\n            data.loc[idx, 'img_height'] = val['img_height']\n            data.loc[idx, 'img_width'] = val['img_width']\n            data.loc[idx, 'aspect_ratio'] = val['aspect_ratio']\n    \n    return data","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_train_df(path):\n    df = pd.read_csv(path)\n    print(\"Filter data...\")\n    df = filter_df(df)\n    print(\"Add size and aspect ratios of imgs...\")\n    df = add_size_aspect_ratio(df)\n    print(\"Preprocessing finished\")\n    return df","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Data Exploration"},{"metadata":{"trusted":true},"cell_type":"code","source":"dicoms_train = glob.glob(TRAIN_DIR + '/*.dicom')\nlen(dicoms_train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dicoms_test = glob.glob(TEST_DIR + '/*.dicom')\nlen(dicoms_test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_submission = pd.read_csv(os.path.join(DATA_DIR, 'sample_submission.csv'))\nsample_submission.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train = pd.read_csv(os.path.join(DATA_DIR, 'train.csv'))\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"no_findings = train[train['class_id'] == 14]['image_id']\nlen(no_findings)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"findings = train[train['class_id'] != 14]['image_id']\nlen(findings)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# do we have images, where one radiologist finds something and another don't ? \nno_findings = set(list(no_findings))\nlen(no_findings)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"findings = set(list(findings))\nlen(findings)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"intersection = list(findings & no_findings)\nlen(intersection)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(train['image_id'].unique())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"As you can see we have 67.914 annotations and 15.000 different images in the training dataset which means we can have more than one annotation per image."},{"metadata":{"trusted":true},"cell_type":"code","source":"class_ids = sorted(list(train['class_id'].unique()))\nclass_names = list(train['class_name'])\n\nid_to_classes = {id: list(train.query(f'class_id == {id}')['class_name'])[0] for id in class_ids}\nid_to_classes","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"classes_to_id = {list(train.query(f'class_id == {id}')['class_name'])[0]: id for id in class_ids}\nclasses_to_id","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"id_to_color = {id: LABEL_COLORS[id] for id in class_ids}\nid_to_color_tuples = {id: LABEL_COLORS_TUPLES[id] for id in class_ids}\nid_to_color_tuples","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"And we are dealing with 14 different diseases we want to classify."},{"metadata":{"trusted":true},"cell_type":"code","source":"radiologists = list(train['rad_id'].unique())\nlen(radiologists)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"17 different radiologists produced the findings from the training dataset."},{"metadata":{"trusted":true},"cell_type":"code","source":"class_value_counts = train['class_name'].value_counts().sort_index()\nclass_value_counts","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class_value_counts_norm = train['class_name'].value_counts(normalize=True).sort_index()\nclass_value_counts_norm","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.bar(class_value_counts, \n             color=train['class_name'].value_counts().sort_index().index, \n             opacity=0.9, \n             color_discrete_sequence=LABEL_COLORS, \n             log_y=True, \n             title='Annotations per class',\n             text=class_value_counts)\nfig.update_traces(texttemplate='%{text:.2s}', textposition='outside')\nfig.update_layout(legend_title=None, xaxis_title=\"\", yaxis_title=\"count\")\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"annos_per_img = train.groupby('image_id')[\"class_name\"].unique().apply(lambda x: len(x))\nfig = px.histogram(annos_per_img,\n                   nbins=max(annos_per_img),\n                   labels={'value': 'number of unique abnormalities'}, \n                   title='Annotations per patient', \n                   log_y=True)\n\nfig.update_layout(showlegend=False, \n                  xaxis_title='number of unique abnormalities', \n                  yaxis_title='number of imgs')\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Since radiologists are only human, it can happen that they will come to different conclusions about an image. That is, radiologist X evaluates the image differently than radiologist Y. Unfortunately, we do not have any further information on the radiologists. But we have to keep that in mind. Moreover we have three different opinions per image (three radiologists per image)."},{"metadata":{"trusted":true},"cell_type":"code","source":"test_id = '9a5094b2563a1ef3ff50dc5c7ff71345'\ntest_data = read_dicom(os.path.join(TRAIN_DIR, test_id + '.dicom'))\ntest_data.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"annos = get_annotations(train, '9a5094b2563a1ef3ff50dc5c7ff71345', True)\nannos","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img = draw_boxes(train, '9a5094b2563a1ef3ff50dc5c7ff71345', id_to_classes, annos)\nplot_image(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"boxes = list(annos.values())[0]\nboxes","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#annos = get_annotations(train, list(train['image_id'].unique()))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#new_df = filter_df(train)\n#new_df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#annos_ = merge_similar_annotations(train, '9a5094b2563a1ef3ff50dc5c7ff71345')\n#annos_","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#img = draw_boxes(train, '9a5094b2563a1ef3ff50dc5c7ff71345', id_to_classes, annos_)\n#plot_image(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Modeling\n\nI want to build a pytorch object detection model (first Faster R-CNN, in the long run I will try to use EfficientDet-D7)."},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = create_train_df(os.path.join(DATA_DIR, 'train.csv'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"findings = train_df[train_df['class_id'] != 14]['image_id'].unique()\nno_findings = train_df[train_df['class_id'] == 14]['image_id'].unique()\nlen(findings)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(no_findings)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Create train/val split dfs (80%/20%)\nimgs = train_df['image_id'].unique()\nnp.random.shuffle(imgs)\nfrac = int(0.8*len(imgs))\ntrain = imgs[:frac]\nval = imgs[frac:]\nlen(train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_train = train_df[train_df['image_id'].isin(train)]\ndf_val = train_df[train_df['image_id'].isin(val)]\nlen(df_train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"len(val)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_train_transform():\n    return A.Compose([\n        A.HorizontalFlip(0.3),\n        A.Resize(1024, 1024, interpolation=cv2.INTER_LANCZOS4, p=1.0),\n        ToTensorV2(p=1.0)\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})\n\ndef get_val_transform():\n    return A.Compose([\n        A.Resize(1024, 1024, interpolation=cv2.INTER_LANCZOS4, p=1.0),\n        ToTensorV2(p=1.0)\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class VinBigDataset(Dataset):\n    def __init__(self, root: str, annos_df: pd.DataFrame, mode='train', transform=None):\n        self.root = root\n        self.imgs_dir = os.path.join(self.root, mode)\n        self.annotations = annos_df\n        self.img_ids = annos_df['image_id'].unique()\n        self.transform = transform\n        \n    \n    def __getitem__(self, idx: int):\n        img_id = self.img_ids[idx]\n        annos = self.annotations[self.annotations['image_id'] == img_id]\n        filename = img_id + '.dicom'\n        image = read_dicom(os.path.join(self.imgs_dir, filename))\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB).astype(np.float32)\n        image /= 255.0\n        \n        boxes = annos[['x_min', 'y_min', 'x_max', 'y_max']].values\n        w = boxes[:,2] - boxes[:,0]\n        h = boxes[:,3] - boxes[:,1]\n        \n        area = w * h\n        area = torch.as_tensor(area, dtype=torch.float32)\n\n        labels = annos['class_id'].values\n        labels = torch.as_tensor(labels, dtype=torch.int64)\n        \n        # suppose all instances are not crowd\n        iscrowd = torch.zeros((annos.shape[0],), dtype=torch.int64)\n        \n        target = {}\n        target['boxes'] = boxes\n        target['labels'] = labels\n        target['image_id'] = torch.tensor([idx])\n        target['area'] = area\n        target['iscrowd'] = iscrowd\n\n        if self.transform:\n            sample = {\n                'image': image,\n                'bboxes': target['boxes'],\n                'labels': labels\n            }\n            sample = self.transform(**sample)\n            image = sample['image']\n            \n            target['boxes'] = torch.stack(tuple(map(torch.tensor, zip(*sample['bboxes'])))).permute(1, 0)\n\n        return image, target, img_id\n        \n    \n    def __len__(self) -> int:\n        return self.img_ids.shape[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Averager:   \n    def __init__(self):\n        self.current_total = 0.0\n        self.iterations = 0.0\n\n    def send(self, value):\n        self.current_total += value\n        self.iterations += 1\n\n    @property\n    def value(self):\n        if self.iterations == 0:\n            return 0\n        else:\n            return 1.0 * self.current_total / self.iterations\n\n    def reset(self):\n        self.current_total = 0.0\n        self.iterations = 0.0","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def collate_fn(batch):\n    return tuple(zip(*batch))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True, pretrained_backbone=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\ndevice","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"num_classes = len(df_train['class_id'].unique()) # here no_findings (14) == background class\nnum_classes","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"in_features = model.roi_heads.box_predictor.cls_score.in_features\nin_features","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# replace the pretrained head with a new one \nmodel.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dataset = VinBigDataset(DATA_DIR, df_train, mode='train', transform=get_train_transform())\nlen(train_dataset)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"val_dataset = VinBigDataset(DATA_DIR, df_val, mode='train', transform=get_val_transform())\nlen(val_dataset)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dataloader = DataLoader(train_dataset, batch_size=2, shuffle=False, num_workers=4, collate_fn=collate_fn)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"val_dataloader = DataLoader(val_dataset, batch_size=1, shuffle=False, num_workers=4, collate_fn=collate_fn)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"images, targets, img_ids = next(iter(train_dataloader))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.train()\nmodel.to(device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"params = [p for p in model.parameters() if p.requires_grad]\noptimizer = torch.optim.SGD(params, lr=0.01, momentum=0.9, weight_decay=0.0001)\nlr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.5)\n#lr_scheduler = None\n\nnum_epochs = 3\n\nloss_hist = Averager()\nitr = 1\n\nfor epoch in range(num_epochs):\n    loss_hist.reset()\n    \n    for images, targets, image_ids in train_dataloader:\n        \n        images = list(image.to(device) for image in images)\n        targets = [{k: v.to(device) for k, v in t.items()} for t in targets]\n\n        loss_dict = model(images, targets)\n\n        losses = sum(loss for loss in loss_dict.values())\n        loss_value = losses.item()\n\n        loss_hist.send(loss_value)\n\n        optimizer.zero_grad()\n        losses.backward()\n        optimizer.step()\n    \n        if itr % 100 == 0:\n            print(f\"Iteration #{itr} loss: {loss_value}\")\n\n        itr += 1\n    \n    # update the learning rate\n    if lr_scheduler is not None:\n        lr_scheduler.step()\n\n    print(f\"Epoch #{epoch} loss: {loss_hist.value}\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"torch.save(model.state_dict(), '/kaggle/input/model/fasterrcnn_resnet50_fpn2nd.pth')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df.to_csv('/kaggle/input/my_data/train_df.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Training"}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}