{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-output":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-19T09:16:41.467746Z","iopub.execute_input":"2021-11-19T09:16:41.468557Z","iopub.status.idle":"2021-11-19T09:17:37.143942Z","shell.execute_reply.started":"2021-11-19T09:16:41.468461Z","shell.execute_reply":"2021-11-19T09:17:37.143224Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Importing the required libraries**","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport torch\nimport torch.utils.data\nfrom PIL import Image\nimport pandas as pd\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom torchvision.models.detection import FasterRCNN\nfrom sklearn.model_selection import train_test_split\nfrom torchvision.models.detection.rpn import AnchorGenerator\nfrom torch.utils.data.sampler import SequentialSampler\nfrom pydicom import dcmread\n\nimport torch\nfrom torchvision import transforms\nfrom torchvision import transforms as T\nimport torch.nn as nn\nimport torchvision\nfrom torch.utils import data\nfrom torch.utils.data import Dataset, DataLoader\n\nimport plotly.express as px\nimport plotly.graph_objects as go\n\nimport matplotlib.patches as patches\nimport matplotlib.pyplot as plt\nimport pydicom as pyd\nfrom tqdm import tqdm\n\nimport cv2\nimport re\nimport time\nimport matplotlib\nimport os\n\nfrom albumentations.pytorch.transforms import ToTensorV2\nfrom tqdm import tqdm\nimport albumentations as A\n\nfrom albumentations import (\n    Resize,\n    HorizontalFlip, IAAPerspective, ShiftScaleRotate, CLAHE, RandomRotate90,\n    Transpose, ShiftScaleRotate, Blur, OpticalDistortion, GridDistortion, HueSaturationValue,\n    IAAAdditiveGaussianNoise, GaussNoise, MotionBlur, MedianBlur, IAAPiecewiseAffine,\n    IAASharpen, IAAEmboss, RandomBrightnessContrast, Flip, OneOf, Compose\n)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-19T09:54:09.118114Z","iopub.execute_input":"2021-11-19T09:54:09.119066Z","iopub.status.idle":"2021-11-19T09:54:14.295367Z","shell.execute_reply.started":"2021-11-19T09:54:09.119022Z","shell.execute_reply":"2021-11-19T09:54:14.294531Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Reading the input files**","metadata":{}},{"cell_type":"code","source":"images_path = '../input/rsna-pneumonia-detection-challenge/stage_2_train_images'\ntrain_labels_df = pd.read_csv('../input/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv')\nlabel_meta_data = pd.read_csv('../input/rsna-pneumonia-detection-challenge/stage_2_detailed_class_info.csv')","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:17:42.850556Z","iopub.execute_input":"2021-11-19T09:17:42.851120Z","iopub.status.idle":"2021-11-19T09:17:42.963045Z","shell.execute_reply.started":"2021-11-19T09:17:42.851087Z","shell.execute_reply":"2021-11-19T09:17:42.962111Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_labels_df","metadata":{"execution":{"iopub.status.busy":"2021-11-18T14:14:37.516083Z","iopub.execute_input":"2021-11-18T14:14:37.516622Z","iopub.status.idle":"2021-11-18T14:14:37.542733Z","shell.execute_reply.started":"2021-11-18T14:14:37.516567Z","shell.execute_reply":"2021-11-18T14:14:37.542032Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"label_meta_data","metadata":{"execution":{"iopub.status.busy":"2021-11-18T14:14:37.544294Z","iopub.execute_input":"2021-11-18T14:14:37.544794Z","iopub.status.idle":"2021-11-18T14:14:37.55719Z","shell.execute_reply.started":"2021-11-18T14:14:37.544757Z","shell.execute_reply":"2021-11-18T14:14:37.55653Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(np.unique(label_meta_data['patientId']))","metadata":{"execution":{"iopub.status.busy":"2021-11-18T14:14:37.558236Z","iopub.execute_input":"2021-11-18T14:14:37.558549Z","iopub.status.idle":"2021-11-18T14:14:37.598103Z","shell.execute_reply.started":"2021-11-18T14:14:37.558517Z","shell.execute_reply":"2021-11-18T14:14:37.597387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"label_meta_data.shape","metadata":{"execution":{"iopub.status.busy":"2021-11-18T14:14:37.599166Z","iopub.execute_input":"2021-11-18T14:14:37.59948Z","iopub.status.idle":"2021-11-18T14:14:37.604853Z","shell.execute_reply.started":"2021-11-18T14:14:37.599446Z","shell.execute_reply":"2021-11-18T14:14:37.603886Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Target Distribution**","metadata":{}},{"cell_type":"code","source":"unique_patientId = train_labels_df.drop_duplicates(subset = ['patientId'])\nunique_patientId","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:04.563492Z","iopub.execute_input":"2021-11-19T09:18:04.563825Z","iopub.status.idle":"2021-11-19T09:18:04.623187Z","shell.execute_reply.started":"2021-11-19T09:18:04.563787Z","shell.execute_reply":"2021-11-19T09:18:04.622400Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"target = (\n    unique_patientId['Target']\n    .value_counts()\n    .to_frame()\n    .reset_index()\n    .rename(columns={'index':'Target', 'Target':'Count'})\n    .replace([0,1], ['Normal', 'Lung Opacity']) \n    .groupby('Target')\n    .sum()\n    .reset_index()    \n          )   \n\nfig = go.Figure(data=[go.Pie(labels=target['Target'], \n                             values=target['Count'])])\n\nfig.update_traces(hoverinfo='percent+value', \n                  textinfo='label', \n                  textfont_size=20,\n                  marker=dict(colors=['#8cb074', '#5a7c47'], line=dict(color='white', width=5)))\n\nfig.update_layout(showlegend=False, \n                  title_text=\"Target Distribution\",\n                  title_x=0.5,\n                  font=dict(family=\"Hiragino Kaku Gothic Pro, sans-serif\", size=20, color='#000000'))\n\n\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:05.940343Z","iopub.execute_input":"2021-11-19T09:18:05.940929Z","iopub.status.idle":"2021-11-19T09:18:06.047993Z","shell.execute_reply.started":"2021-11-19T09:18:05.940887Z","shell.execute_reply":"2021-11-19T09:18:06.047144Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Selecting instances with inflammation**","metadata":{}},{"cell_type":"code","source":"print('Original dataframe shape:', train_labels_df.shape)\n\ntrain_labels_df_pos = pd.DataFrame(columns=['patientId', 'x', 'y', 'width', 'height'])\n\nk = 0\nfor i in range(len(train_labels_df)):\n    if train_labels_df.loc[i]['Target'] == 1:\n        train_labels_df_pos.loc[k] = train_labels_df.loc[i]\n        k += 1\n\nprint('Positive instances dataframe shape:', train_labels_df_pos.shape)\ntrain_paths = [os.path.join(images_path, image[0]) for image in train_labels_df_pos.values]","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:10.129779Z","iopub.execute_input":"2021-11-19T09:18:10.130042Z","iopub.status.idle":"2021-11-19T09:18:38.020646Z","shell.execute_reply.started":"2021-11-19T09:18:10.130013Z","shell.execute_reply":"2021-11-19T09:18:38.019032Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_labels_df_pos.head()","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:38.022305Z","iopub.execute_input":"2021-11-19T09:18:38.022718Z","iopub.status.idle":"2021-11-19T09:18:38.037508Z","shell.execute_reply.started":"2021-11-19T09:18:38.022657Z","shell.execute_reply":"2021-11-19T09:18:38.036567Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Visualization of the images and the areas of inflammation**","metadata":{}},{"cell_type":"code","source":"def imshow(num_to_show=9):\n    \n    plt.figure(figsize=(20,20))\n    \n    for i in range(num_to_show):\n        plt.subplot(3, 3, i+1)\n        plt.grid(False)\n        plt.xticks([])\n        plt.yticks([])\n        \n        img_dcm = dcmread(f'{train_paths[i+20]}.dcm')\n        img_np = img_dcm.pixel_array\n        plt.imshow(img_np, cmap='bone')\n\nimshow()","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:38.039344Z","iopub.execute_input":"2021-11-19T09:18:38.039845Z","iopub.status.idle":"2021-11-19T09:18:40.165760Z","shell.execute_reply.started":"2021-11-19T09:18:38.039808Z","shell.execute_reply":"2021-11-19T09:18:40.165079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def show_image_with_bboxes(num_to_show=9):\n    plt.figure(figsize=(20,20))\n    \n    for i in range(num_to_show):\n        plt.subplot(3, 3, i+1)\n        plt.grid(False)\n        plt.xticks([])\n        plt.yticks([])\n        \n        id_= np.random.choice(train_labels_df_pos['patientId'].values)\n\n        current_axis = plt.gca()\n        img=pyd.read_file(os.path.join(images_path,id_+'.dcm')).pixel_array\n        plt.imshow(img,cmap='bone')\n\n\n        current_axis = plt.gca()\n        boxes=train_labels_df_pos[['x','y','width','height']][train_labels_df_pos['patientId']==id_].values\n\n        for box in boxes:\n            x=box[0]\n            y=box[1]\n            w=box[2]\n            h=box[3]\n            current_axis.add_patch(plt.Rectangle((x, y), w, h, color='red', fill=False, linewidth=3)) \n        \nshow_image_with_bboxes()","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:40.167530Z","iopub.execute_input":"2021-11-19T09:18:40.168279Z","iopub.status.idle":"2021-11-19T09:18:42.496182Z","shell.execute_reply.started":"2021-11-19T09:18:40.168243Z","shell.execute_reply":"2021-11-19T09:18:42.495380Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def parse_one_annot(data, patient_id):\n    boxes_array = data[data[\"patientId\"] == patient_id][[\"x\", \"y\", \"width\", \"height\"]].values\n#     print(boxes_array.dtype)\n    return boxes_array","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:42.497657Z","iopub.execute_input":"2021-11-19T09:18:42.497901Z","iopub.status.idle":"2021-11-19T09:18:42.502570Z","shell.execute_reply.started":"2021-11-19T09:18:42.497869Z","shell.execute_reply":"2021-11-19T09:18:42.501676Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def show_one_with_bbox(id_):\n    plt.figure(figsize=(20,20))\n    current_axis = plt.gca()\n    img=pyd.read_file(os.path.join(images_path,id_+'.dcm')).pixel_array\n    plt.imshow(img,cmap='bone')\n\n    current_axis = plt.gca()\n    boxes=train_labels_df_pos[['x','y','width','height']][train_labels_df_pos['patientId']==id_].values\n    for box in boxes:\n        x=box[0]\n        y=box[1]\n        w=box[2]\n        h=box[3]\n        current_axis.add_patch(plt.Rectangle((x, y), w, h, color='red', fill=False, linewidth=3)) ","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:42.504221Z","iopub.execute_input":"2021-11-19T09:18:42.504578Z","iopub.status.idle":"2021-11-19T09:18:42.514168Z","shell.execute_reply.started":"2021-11-19T09:18:42.504536Z","shell.execute_reply":"2021-11-19T09:18:42.513375Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"id_= np.random.choice(train_labels_df_pos['patientId'].values)\nprint('Id', id_)\nprint('Bboxes', parse_one_annot(train_labels_df_pos, id_))\nshow_one_with_bbox(id_)","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:42.515549Z","iopub.execute_input":"2021-11-19T09:18:42.515784Z","iopub.status.idle":"2021-11-19T09:18:43.490746Z","shell.execute_reply.started":"2021-11-19T09:18:42.515754Z","shell.execute_reply":"2021-11-19T09:18:43.490054Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Preparing data for training. Dataset class. Tranformations**","metadata":{}},{"cell_type":"code","source":"class PneumoniaDataset(Dataset):\n    def __init__(self, dataframe, image_dir, transforms=None):\n        super().__init__()\n\n        self.image_ids = dataframe['patientId'].unique()\n        self.df = dataframe\n        self.image_dir = image_dir\n        self.transforms = transforms\n        \n    def __getitem__(self, index):\n        # load images and bounding boxes   \n        image_id = self.image_ids[index]\n        records = self.df[self.df['patientId'] == image_id]\n        \n#         img_path = os.path.join(self.image_dir, image_id)\n#         img=pyd.read_file(os.path.join(img_path+'.dcm')).pixel_array\n        img = cv2.imread(f'{self.image_dir}/{image_id}.jpg', cv2.IMREAD_COLOR)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astype(np.float32)\n#         img = img/255\n        img /= 255.0\n        \n        boxes = records[['x', 'y', 'width', 'height']].values\n        boxes[:, 2] = boxes[:, 0] + boxes[:, 2]\n        boxes[:, 3] = boxes[:, 1] + boxes[:, 3]\n        \n        area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])\n        area = torch.as_tensor(area, dtype=torch.float32)\n        \n        # there is only one class\n        labels = torch.ones((records.shape[0],), dtype=torch.int64)\n        # suppose all instances are not crowd\n        iscrowd = torch.zeros((records.shape[0],), dtype=torch.int64)\n        \n        target = {}    \n        target['boxes'] = boxes\n        target['labels'] = labels\n        target['patientId'] = torch.tensor([index])\n        target['area'] = area\n        target['iscrowd'] = iscrowd\n                      \n        if self.transforms:\n            sample = {\n                'image': img,\n                'bboxes': target['boxes'],\n                'labels': labels\n            }\n            sample = self.transforms(**sample)\n            img = sample['image']\n            \n            target['boxes'] = torch.stack(tuple(map(torch.FloatTensor, zip(*sample['bboxes'])))).permute(1, 0)\n            \n        return img, target\n    \n\n    def __len__(self):\n        return self.image_ids.shape[0]","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:43.491999Z","iopub.execute_input":"2021-11-19T09:18:43.492356Z","iopub.status.idle":"2021-11-19T09:18:43.509476Z","shell.execute_reply.started":"2021-11-19T09:18:43.492311Z","shell.execute_reply":"2021-11-19T09:18:43.508834Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Albumentations\ndef get_train_transform():\n    return A.Compose([\n        Resize(300,  300),\n        A.Flip(0.5),\n        A.RandomRotate90(0.5),\n        MotionBlur(p=0.2),\n        MedianBlur(blur_limit=3, p=0.1),\n        Blur(blur_limit=3, p=0.1),\n        ToTensorV2(p=1.0)\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})\n\ndef get_valid_transform():\n    return A.Compose([\n        ToTensorV2(p=1.0)\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:43.510971Z","iopub.execute_input":"2021-11-19T09:18:43.511381Z","iopub.status.idle":"2021-11-19T09:18:43.524245Z","shell.execute_reply.started":"2021-11-19T09:18:43.511345Z","shell.execute_reply":"2021-11-19T09:18:43.523304Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Train and Validation split**","metadata":{}},{"cell_type":"code","source":"input_folder = '../input/rsna-pneumonia-detection-2018/input'\nimages_folder = f\"{input_folder}/images/\"\n\nimage_ids = train_labels_df_pos['patientId'].unique()\nvalid_ids = image_ids[-300:]\ntrain_ids = image_ids[:-300]\nprint(f\"Training instance: {len(train_ids)}\")\nprint(f\"Validation instances: {len(valid_ids)}\")\n\nvalid_df = train_labels_df_pos[train_labels_df_pos['patientId'].isin(valid_ids)]\ntrain_df = train_labels_df_pos[train_labels_df_pos['patientId'].isin(train_ids)]\n\nprint('Train dataframe shape:', train_df.shape)\nprint('Valid dataframe shape:', valid_df.shape)\n    \ntrain_dataset = PneumoniaDataset(train_df, images_folder, get_train_transform())\nvalid_dataset = PneumoniaDataset(valid_df, images_folder, get_valid_transform())\nprint('train_dataset and valid_dataset are loaded :)')   \nprint(\"We have: {} training examples and {} validation examples\".format(len(train_dataset), len(valid_dataset)))","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:43.527199Z","iopub.execute_input":"2021-11-19T09:18:43.527530Z","iopub.status.idle":"2021-11-19T09:18:43.549011Z","shell.execute_reply.started":"2021-11-19T09:18:43.527493Z","shell.execute_reply":"2021-11-19T09:18:43.548174Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Data Loaders**","metadata":{}},{"cell_type":"code","source":"def collate_fn(batch):\n    return tuple(zip(*batch))","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:43.550462Z","iopub.execute_input":"2021-11-19T09:18:43.550747Z","iopub.status.idle":"2021-11-19T09:18:43.555727Z","shell.execute_reply.started":"2021-11-19T09:18:43.550711Z","shell.execute_reply":"2021-11-19T09:18:43.554985Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data_loader = DataLoader(train_dataset, batch_size=8, shuffle=False, num_workers=2, collate_fn=collate_fn)\nvalid_data_loader = DataLoader(valid_dataset, batch_size=8, shuffle=False, num_workers=2, collate_fn=collate_fn)","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:43.557267Z","iopub.execute_input":"2021-11-19T09:18:43.557800Z","iopub.status.idle":"2021-11-19T09:18:43.564894Z","shell.execute_reply.started":"2021-11-19T09:18:43.557760Z","shell.execute_reply":"2021-11-19T09:18:43.564121Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Helper functions**","metadata":{}},{"cell_type":"code","source":"def calculate_image_precision(gts, preds, thresholds = (0.5, ), form = 'coco') -> float:\n    # https://www.kaggle.com/sadmanaraf/wheat-detection-using-faster-rcnn-train\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\ndef calculate_iou(gt, pr, form='pascal_voc') -> float:\n    # https://www.kaggle.com/sadmanaraf/wheat-detection-using-faster-rcnn-train\n    \"\"\"Calculates the Intersection over Union.\n\n    Args:\n        gt: (np.ndarray[Union[int, float]]) coordinates of the ground-truth box\n        pr: (np.ndarray[Union[int, float]]) coordinates of the prdected box\n        form: (str) gt/pred coordinates format\n            - pascal_voc: [xmin, ymin, xmax, ymax]\n            - coco: [xmin, ymin, w, h]\n    Returns:\n        (float) Intersection over union (0.0 <= iou <= 1.0)\n    \"\"\"\n    if form == 'coco':\n        gt = gt.copy()\n        pr = pr.copy()\n\n        gt[2] = gt[0] + gt[2]\n        gt[3] = gt[1] + gt[3]\n        pr[2] = pr[0] + pr[2]\n        pr[3] = pr[1] + pr[3]\n\n    # Calculate overlap area\n    dx = min(gt[2], pr[2]) - max(gt[0], pr[0]) + 1\n    \n    if dx < 0:\n        return 0.0\n    dy = min(gt[3], pr[3]) - max(gt[1], pr[1]) + 1\n\n    if dy < 0:\n        return 0.0\n\n    overlap_area = dx * dy\n\n    # Calculate union area\n    union_area = (\n            (gt[2] - gt[0] + 1) * (gt[3] - gt[1] + 1) +\n            (pr[2] - pr[0] + 1) * (pr[3] - pr[1] + 1) -\n            overlap_area\n    )\n\n    return overlap_area / union_area\n\n\ndef find_best_match(gts, pred, pred_idx, threshold = 0.5, form = 'pascal_voc', ious=None) -> int:\n    # https://www.kaggle.com/sadmanaraf/wheat-detection-using-faster-rcnn-train\n    \"\"\"Returns the index of the 'best match' between the\n    ground-truth boxes and the prediction. The 'best match'\n    is the highest IoU. (0.0 IoUs are ignored).\n\n    Args:\n        gts: (List[List[Union[int, float]]]) Coordinates of the available ground-truth boxes\n        pred: (List[Union[int, float]]) Coordinates of the predicted box\n        pred_idx: (int) Index of the current predicted box\n        threshold: (float) Threshold\n        form: (str) Format of the coordinates\n        ious: (np.ndarray) len(gts) x len(preds) matrix for storing calculated ious.\n\n    Return:\n        (int) Index of the best match GT box (-1 if no match above threshold)\n    \"\"\"\n    best_match_iou = -np.inf\n    best_match_idx = -1\n    for gt_idx in range(len(gts)):\n        \n        if gts[gt_idx][0] < 0:\n            # Already matched GT-box\n            continue\n        \n        iou = -1 if ious is None else ious[gt_idx][pred_idx]\n\n        if iou < 0:\n            iou = calculate_iou(gts[gt_idx], pred, form=form)\n            \n            if ious is not None:\n                ious[gt_idx][pred_idx] = iou\n\n        if iou < threshold:\n            continue\n\n        if iou > best_match_iou:\n            best_match_iou = iou\n            best_match_idx = gt_idx\n\n    return best_match_idx\n\ndef calculate_precision(gts, preds, threshold = 0.5, form = 'coco', ious=None) -> float:\n    # https://www.kaggle.com/sadmanaraf/wheat-detection-using-faster-rcnn-train\n    \"\"\"Calculates precision for GT - prediction pairs at one threshold.\n\n    Args:\n        gts: (List[List[Union[int, float]]]) Coordinates of the available ground-truth boxes\n        preds: (List[List[Union[int, float]]]) Coordinates of the predicted boxes,\n               sorted by confidence value (descending)\n        threshold: (float) Threshold\n        form: (str) Format of the coordinates\n        ious: (np.ndarray) len(gts) x len(preds) matrix for storing calculated ious.\n\n    Return:\n        (float) Precision\n    \"\"\"\n    n = len(preds)\n    tp = 0\n    fp = 0\n    \n    for pred_idx in range(n):\n\n        best_match_gt_idx = find_best_match(gts, preds[pred_idx], pred_idx,\n                                            threshold=threshold, form=form, ious=ious)\n\n        if best_match_gt_idx >= 0:\n            # True positive: The predicted box matches a gt box with an IoU above the threshold.\n            tp += 1\n            # Remove the matched GT box\n            gts[best_match_gt_idx] = -1\n        else:\n            # No match\n            # False positive: indicates a predicted box had no associated gt box.\n            fp += 1\n\n    # False negative: indicates a gt box had no associated predicted box.\n    fn = (gts.sum(axis=1) > 0).sum()\n\n    return tp / (tp + fp + fn)\n","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-19T09:18:43.567553Z","iopub.execute_input":"2021-11-19T09:18:43.568300Z","iopub.status.idle":"2021-11-19T09:18:43.590189Z","shell.execute_reply.started":"2021-11-19T09:18:43.568274Z","shell.execute_reply":"2021-11-19T09:18:43.589363Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-19T09:18:43.591579Z","iopub.execute_input":"2021-11-19T09:18:43.591870Z","iopub.status.idle":"2021-11-19T09:18:43.603214Z","shell.execute_reply.started":"2021-11-19T09:18:43.591833Z","shell.execute_reply":"2021-11-19T09:18:43.602368Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Train and Validate functions**","metadata":{}},{"cell_type":"code","source":"def train(dataloader, lr_scheduler, model, optimizer, \n          device, epoch, loss_hist, itr):\n    model.train()\n    start = time.time()\n    loss_hist.reset()\n    for images, targets in 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\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\"Epoch #{epoch} iteration #{itr} loss: {loss_value}\")\n\n        itr += 1\n    \n    end = time.time()\n    return loss_hist, end, start","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:43.604675Z","iopub.execute_input":"2021-11-19T09:18:43.604946Z","iopub.status.idle":"2021-11-19T09:18:43.615004Z","shell.execute_reply.started":"2021-11-19T09:18:43.604909Z","shell.execute_reply":"2021-11-19T09:18:43.614249Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def validate(dataloader, model, device, iou_thresholds):\n    valid_image_precision = []\n    model.eval()\n    with torch.no_grad():\n        for images, targets in 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            outputs = model(images)\n            \n    for i, image in enumerate(images):\n        boxes = outputs[i]['boxes'].data.cpu().numpy()\n        scores = outputs[i]['scores'].data.cpu().numpy()\n        gt_boxes = targets[i]['boxes'].cpu().numpy()\n        preds_sorted_idx = np.argsort(scores)[::-1]\n        preds_sorted = boxes[preds_sorted_idx]\n        image_precision = calculate_image_precision(preds_sorted,\n                                                        gt_boxes,\n                                                        thresholds=iou_thresholds,\n                                                        form='coco')\n        valid_image_precision.append(image_precision)\n\n    valid_prec = np.mean(valid_image_precision)\n    return valid_prec","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:18:43.615979Z","iopub.execute_input":"2021-11-19T09:18:43.616649Z","iopub.status.idle":"2021-11-19T09:18:43.628773Z","shell.execute_reply.started":"2021-11-19T09:18:43.616613Z","shell.execute_reply":"2021-11-19T09:18:43.628007Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Load the model**","metadata":{}},{"cell_type":"code","source":"def get_model():\n    # load an object detection model pre-trained on COCO\n    model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True, min_size=1024)\n    # one class is pneumonia, and the other is background\n    num_classes = 2\n    # get the number of input features for the classifier\n    in_features = model.roi_heads.box_predictor.cls_score.in_features\n    # replace the pre-trained head with a new on\n    model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)\n   \n    return model","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:54:47.532435Z","iopub.execute_input":"2021-11-19T09:54:47.532727Z","iopub.status.idle":"2021-11-19T09:54:47.538492Z","shell.execute_reply.started":"2021-11-19T09:54:47.532692Z","shell.execute_reply":"2021-11-19T09:54:47.537482Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Train the model**","metadata":{}},{"cell_type":"code","source":"torch.cuda.is_available()","metadata":{"execution":{"iopub.status.busy":"2021-11-18T14:17:42.288098Z","iopub.execute_input":"2021-11-18T14:17:42.28835Z","iopub.status.idle":"2021-11-18T14:17:42.340886Z","shell.execute_reply.started":"2021-11-18T14:17:42.288322Z","shell.execute_reply":"2021-11-18T14:17:42.340098Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\n\n# learning parameters\nnum_epochs = 30\nlr = 0.001\nbatch_size = 8\n\nmodel = get_model().to(device)\nparams = [p for p in model.parameters() if p.requires_grad]\noptimizer = torch.optim.SGD(params, lr=lr, momentum=0.9, weight_decay=0.0005)\nlr_scheduler = None\n\n# initialize the Averager\nloss_hist = Averager()\niou_thresholds = [x for x in np.arange(0.5, 0.76, 0.05)]","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:19:06.639535Z","iopub.execute_input":"2021-11-19T09:19:06.639813Z","iopub.status.idle":"2021-11-19T09:19:07.370583Z","shell.execute_reply.started":"2021-11-19T09:19:06.639782Z","shell.execute_reply":"2021-11-19T09:19:07.369816Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_loss = []\nprecision = []\nfor epoch in range(num_epochs):\n    itr = 1\n    train_loss_hist, end, start = train(train_data_loader, lr_scheduler,\n                                        model, optimizer, device,\n                                        epoch, loss_hist, itr)\n    valid_prec = validate(valid_data_loader, model, device, iou_thresholds)\n    print(f\"Took {(end-start)/60:.3f} minutes for epoch# {epoch} to train\")\n    print(f\"Epoch #{epoch} Train loss: {train_loss_hist.value}\")  \n    print(f\"Epoch #{epoch} Validation Precision: {valid_prec}\")  \n    train_loss.append(train_loss_hist.value)\n    precision.append(valid_prec)\n    \n    # update the learning rate\n    if lr_scheduler is not None:\n        lr_scheduler.step()\n\n","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:19:09.152974Z","iopub.execute_input":"2021-11-19T09:19:09.153228Z","iopub.status.idle":"2021-11-19T09:36:01.091727Z","shell.execute_reply.started":"2021-11-19T09:19:09.153200Z","shell.execute_reply":"2021-11-19T09:36:01.090783Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Saving**","metadata":{}},{"cell_type":"code","source":"torch.save(model.state_dict(), 'fasterrcnn_resnet50_fpn_pneumonia_detection.pth')","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:46:37.273283Z","iopub.execute_input":"2021-11-19T09:46:37.274068Z","iopub.status.idle":"2021-11-19T09:46:37.582225Z","shell.execute_reply.started":"2021-11-19T09:46:37.274026Z","shell.execute_reply":"2021-11-19T09:46:37.581387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Plot Loss and Precision**","metadata":{}},{"cell_type":"code","source":"# plot the training loss\nplt.figure()\nplt.plot(train_loss, label='Training loss')\nplt.legend()\nplt.show()\n\n# plot the validation precision\nplt.figure()\nplt.plot(precision, label='Validation precision')\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-11-18T14:15:11.392559Z","iopub.status.idle":"2021-11-18T14:15:11.392977Z","shell.execute_reply.started":"2021-11-18T14:15:11.392758Z","shell.execute_reply":"2021-11-18T14:15:11.392779Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Test model and make predictions**","metadata":{}},{"cell_type":"code","source":"#uncomment next cells in case of testing\n#commented due to lack of memory","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\n\n# input_folder = '../input/rsna-pneumonia-detection-2018/input'\n\n# images_test_path = f\"{input_folder}/samples\"\n# test_images = os.listdir(images_test_path)\n# print(f\"Validation instances: {len(test_images)}\")","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # load a model; pre-trained on COCO\n# model = get_model()\n\n# # os.makedirs('../validation_predictions', exist_ok=True)\n# model.load_state_dict(torch.load('./fasterrcnn_resnet50_fpn_pneumonia_detection.pth'))\n# model.to(device)","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:54:52.288796Z","iopub.execute_input":"2021-11-19T09:54:52.289663Z","iopub.status.idle":"2021-11-19T09:54:56.557731Z","shell.execute_reply.started":"2021-11-19T09:54:52.289623Z","shell.execute_reply":"2021-11-19T09:54:56.557012Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# def format_prediction_string(boxes, scores):\n#     pred_strings = []\n#     for j in zip(scores, boxes):\n#         pred_strings.append(\"{0:.4f} {1} {2} {3} {4}\".format(j[0], \n#                                                              int(j[1][0]), int(j[1][1]), \n#                                                              int(j[1][2]), int(j[1][3])))\n\n#     return \" \".join(pred_strings)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-19T09:55:01.666409Z","iopub.execute_input":"2021-11-19T09:55:01.667224Z","iopub.status.idle":"2021-11-19T09:55:01.673063Z","shell.execute_reply.started":"2021-11-19T09:55:01.667182Z","shell.execute_reply":"2021-11-19T09:55:01.672301Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# detection_threshold = 0.8\n# img_num = 0\n# results = []\n# model.eval()\n# with torch.no_grad():\n#     for i, image in tqdm(enumerate(test_images), total=len(test_images)):\n\n#         orig_image = cv2.imread(f\"{images_test_path}/{test_images[i]}\", cv2.IMREAD_COLOR)\n#         image = cv2.cvtColor(orig_image, cv2.COLOR_BGR2RGB).astype(np.float32)\n#         image /= 255.0\n#         image = np.transpose(image, (2, 0, 1)).astype(np.float)\n#         image = torch.tensor(image, dtype=torch.float).cuda()\n#         image = torch.unsqueeze(image, 0)\n\n#         model.eval()\n#         cpu_device = torch.device(\"cpu\")\n\n#         outputs = model(image)\n        \n#         outputs = [{k: v.to(cpu_device) for k, v in t.items()} for t in outputs]\n#         if len(outputs[0]['boxes']) != 0:\n#             for counter in range(len(outputs[0]['boxes'])):\n#                 boxes = outputs[0]['boxes'].data.cpu().numpy()\n#                 scores = outputs[0]['scores'].data.cpu().numpy()\n#                 boxes = boxes[scores >= detection_threshold].astype(np.int32)\n#                 draw_boxes = boxes.copy()\n#                 boxes[:, 2] = boxes[:, 2] - boxes[:, 0]\n#                 boxes[:, 3] = boxes[:, 3] - boxes[:, 1]\n                \n#             for box in draw_boxes:\n#                 cv2.rectangle(orig_image,\n#                             (int(box[0]), int(box[1])),\n#                             (int(box[2]), int(box[3])),\n#                             (0, 0, 255), 3)\n        \n#             plt.imshow(cv2.cvtColor(orig_image, cv2.COLOR_BGR2RGB))\n#             plt.axis('off')\n#             plt.savefig(f\"{test_images[i]}\")\n#             plt.close()\n                \n#             result = {\n#                 'patientId': test_images[i].split('.')[0],\n#                 'PredictionString': format_prediction_string(boxes, scores)\n#             }\n#             results.append(result)\n#         else:\n#             result = {\n#                 'patientId': test_images[i].split('.')[0],\n#                 'PredictionString': None\n#             }\n#             results.append(result)\n\n# sub_df = pd.DataFrame(results, columns=['patientId', 'PredictionString'])\n# print(sub_df.head())\n# sub_df.to_csv('submission.csv', index=False)","metadata":{"execution":{"iopub.status.busy":"2021-11-19T09:55:06.291310Z","iopub.execute_input":"2021-11-19T09:55:06.291819Z"},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]}]}