{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport sys\nimport torch\nimport torch.nn.functional as F\nimport torch.nn as nn\nimport torch.utils.data as data\nimport torchvision\nimport torchvision.models as models\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nimport sklearn.utils as utils\nimport sklearn.metrics as metrics\nfrom torchvision import transforms as T\nfrom torchvision.models import resnet50\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nimport cv2\nimport pydicom\nfrom sklearn.utils import shuffle\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\nimport pandas as pd","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = pd.read_csv('/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/train.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def agg_min(x):\n    if np.isnan(x.values[0]):\n        return [0]\n    else:\n        return list(x)\ndef agg_max(x):\n    if np.isnan(x.values[0]):\n        return [1]\n    else:\n        return list(x)\ndef agg_class(x):\n    if x.values[0] == 14:\n        return [0]\n    else:\n        return list(x.values+1)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = df.groupby('image_id').agg({'x_min': agg_min, 'x_max': agg_max, 'y_min': agg_min, 'y_max': agg_max, 'class_id': agg_class})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = df.reset_index(drop=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Use IOU score to select one of multiple overlapping boxes."},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"\ndef iou(box1, box2):\n    x1_min, y1_min, x1_max, y1_max = box1\n    x2_min, y2_min, x2_max, y2_max = box2\n    if (min(x1_max, x2_max) - max(x1_min, x2_min)) <= 0 or (min(y1_max, y2_max) - max(y1_min, y2_min)) <= 0:\n        return 0\n    intersection = (min(x1_max, x2_max) - max(x1_min, x2_min)) * (min(y1_max, y2_max) - max(y1_min, y2_min))\n    union = (x1_max - x1_min) * (y1_max - y1_min) + (x2_max - x2_min) * (y2_max - y2_min) - intersection\n    return intersection / (union + 1e-5)\n\ndef get_similarity(x):\n    similarities = np.zeros((x.shape[0], x.shape[0]))\n    for i in range(x.shape[0]):\n        for j in range(i, x.shape[0]):\n            similarities[i, j] = iou(x[i], x[j])\n    return similarities\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df2 = []\nfor row_i in range(len(df)):\n    row = df.iloc[row_i]\n    x = np.array(row[['x_min', 'y_min', 'x_max', 'y_max', 'class_id']].values.tolist()).transpose()\n    x_mins, y_mins, x_maxs, y_maxs, class_ids = [], [], [], [], []\n    if len(x) > 0:\n        similarities = get_similarity(x[:, :-1])\n        x_i = x[0]\n        x_mins.append(x_i[0])\n        y_mins.append(x_i[1])\n        x_maxs.append(x_i[2])\n        y_maxs.append(x_i[3])\n        class_ids.append(x_i[4])\n        for i in range(1, len(x)):\n            if np.all(similarities[:i, i] < 0.6):\n                x_i = x[i]\n                x_mins.append(x_i[0])\n                y_mins.append(x_i[1])\n                x_maxs.append(x_i[2])\n                y_maxs.append(x_i[3])\n                class_ids.append(x_i[4])\n    df2.append([row['image_id'], x_mins, y_mins, x_maxs, y_maxs, class_ids])\n    if row_i % 1000 == 0: print(row_i)\n\ndf2 = pd.DataFrame(df2, columns=['image_id', 'x_min', 'y_min', 'x_max', 'y_max', 'class_id'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# df['image_path'] = '/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/train/' + df['image_id'] + '.dicom'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Dicom Images\n# class Dataset(torch.utils.data.Dataset):\n#     def __init__(self, df, transforms):\n#         self.transforms = transforms\n#         self.df = df\n\n#     def __getitem__(self, idx):\n#         img_path = self.df.iloc[idx]['image_path']\n#         boxes = np.array(self.df.iloc[idx][['x_min', 'y_min', 'x_max', 'y_max']].values.tolist()).transpose()\n#         dicom = pydicom.dcmread(img_path)#\n#         dicom.BitsStored = 16\n#         img = dicom.pixel_array.astype('float32')\n#         if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n#             img = np.amax(img) - img\n#         intercept = int(dicom.RescaleIntercept) if \"RescaleIntercept\" in dicom else 0.0\n#         slope = float(dicom.RescaleSlope) if \"RescaleSlope\" in dicom else 1.0\n#         img = (img * slope).astype('int16') + intercept\n#         img = np.stack([img, img, img])\n#         img = (img - img.min()) / (img.max() - img.min()) * 255.\n#         img = img.transpose(1, 2, 0)\n#         labels = self.df.iloc[idx]['class_id']\n#         target = {}\n#         boxes = torch.as_tensor(boxes, dtype=torch.float32)\n#         if len(boxes) == 0:\n#             boxes = torch.tensor([[0., 0., 1., 1.]], dtype=torch.float32)\n#             labels = [0]\n#         area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])\n#         target[\"boxes\"] = boxes #/ 1024.\n#         target[\"labels\"] = torch.as_tensor(labels, dtype=torch.int64)\n#         target[\"area\"] = torch.as_tensor(area, dtype=torch.float32)\n#         sample = {\n#                     'image': img,\n#                     'bboxes': target['boxes'],\n#                     'labels': labels\n#                 }\n#         sample = self.transforms(**sample)\n#         img = sample['image']\n#         target['boxes'] = torch.tensor(sample['bboxes'])\n#         return img, target\n\n#     def __len__(self):\n#         return len(self.df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df['image_path'] = '/kaggle/input/vinbigdata-train-jpg/' + df['image_id'] + '.jpg'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# JPG Images\nclass Dataset(torch.utils.data.Dataset):\n    def __init__(self, df, transforms):\n        self.transforms = transforms\n        self.df = df\n\n    def __getitem__(self, idx):\n        img_path = self.df.iloc[idx]['image_path']\n        boxes = np.array(self.df.iloc[idx][['x_min', 'y_min', 'x_max', 'y_max']].values.tolist()).transpose()\n        img = np.array(Image.open(img_path))\n        labels = self.df.iloc[idx]['class_id']\n        target = {}\n        boxes = torch.as_tensor(boxes, dtype=torch.float32)\n        if len(boxes) == 0:\n            boxes = torch.tensor([[0., 0., 1., 1.]], dtype=torch.float32)\n            labels = [0]\n        area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])\n        target[\"boxes\"] = boxes #/ 1024.\n        target[\"labels\"] = torch.as_tensor(labels, dtype=torch.int64)\n        target[\"area\"] = torch.as_tensor(area, dtype=torch.float32)\n        sample = {\n                    'image': img,\n                    'bboxes': target['boxes'],\n                    'labels': labels\n                }\n        sample = self.transforms(**sample)\n        img = sample['image']\n        target['boxes'] = torch.tensor(sample['bboxes'])\n        return img, target\n\n    def __len__(self):\n        return len(self.df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df = shuffle(df,random_state=42)\ndf_train = df[:12000]\ndf_val = df[12000:]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\ntrain_transforms = A.Compose([\n            A.HorizontalFlip(0.5),\n            A.Normalize(mean=(0, 0, 0), std=(1, 1, 1), max_pixel_value=255.0, p=1.0),\n            ToTensorV2(p=1.0)\n    ], bbox_params={'format': 'pascal_voc', 'label_fields': ['labels']})\nvalid_transforms = A.Compose([\n            A.Normalize(mean=(0, 0, 0), std=(1, 1, 1), max_pixel_value=255.0, 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":"train_dataset = Dataset(df_train, train_transforms)\nval_dataset = Dataset(df_val, valid_transforms)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def collate_fn(batch):\n    images = [image for (image, _) in batch]\n    targets = [target for (_, target) in batch]\n    return images, targets","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data_loader = torch.utils.data.DataLoader(\n                                         train_dataset,\n                                         batch_size=8,\n                                         shuffle=True,\n                                         collate_fn=collate_fn,\n                                         num_workers=2\n                                         )\nvalid_data_loader = torch.utils.data.DataLoader(\n                                         val_dataset,\n                                         batch_size=8,\n                                         shuffle=True,\n                                         collate_fn=collate_fn,\n                                         num_workers=2\n                                         )","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = 'cuda:0' if torch.cuda.is_available() else 'cpu'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True)\nnum_classes = 15\nin_features = model.roi_heads.box_predictor.cls_score.in_features\nmodel.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)\nmodel = model.to(device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"opt  = torch.optim.Adam(model.parameters(), lr=0.00001)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"EPOCHS = 0","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"It is very slow to train in kaggle kernels as the number of CPUs are only 2, GPU remains underutilized I believe. Better to train elsewhere or use lower resolution images."},{"metadata":{"trusted":true},"cell_type":"code","source":"import multiprocessing\nmultiprocessing.cpu_count()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for epoch in range(EPOCHS):\n        for b, (x, y) in enumerate(train_data_loader):\n            x = [xi.to(device) for xi in x]\n            y = [{k: v.to(device) for k, v in yi.items()} for yi in y]\n            opt.zero_grad()\n            loss_dict = model(x, y)\n            loss = loss_dict['loss_box_reg'] + loss_dict['loss_objectness'] + loss_dict['loss_classifier'] + loss_dict['loss_rpn_box_reg']#sum(loss for loss in loss_dict_reduced.values())\n            loss.backward()\n            opt.step()\n            if b % 10 == 0:\n                print('Epoch {}/{}, batch {}/{}, loss {:.4f}, loss classifier {:.4f}, loss box reg {:.4f}, loss objectness {:.4f}, loss rpn box reg {:.4f}'\\\n                      .format(epoch+1, EPOCHS, b+1, len(train_data_loader), loss.item(), loss_dict['loss_classifier'].item(),\n                        loss_dict['loss_box_reg'].item(), loss_dict['loss_objectness'].item(), loss_dict['loss_rpn_box_reg'].item()))\n            if b % 1000 == 0:\n                #model.eval()\n                val_loss = {'loss_box_reg': 0, 'loss_classifier': 0, 'loss_objectness': 0, 'loss_rpn_box_reg': 0}\n                for _, (x, y) in enumerate(valid_data_loader):\n                    x = [xi.to(device) for xi in x]\n                    y = [{k: v.to(device) for k, v in yi.items()} for yi in y]\n                    loss_dict = model(x, y)\n                    loss = loss_dict['loss_box_reg'] + loss_dict['loss_objectness'] + loss_dict['loss_classifier'] + loss_dict['loss_rpn_box_reg']#sum(loss for loss in loss_dict_reduced.values())\n                    loss = loss.detach()\n                    val_loss = {\n                            'loss_box_reg': val_loss['loss_box_reg'] + loss_dict['loss_box_reg'].item(),\n                            'loss_classifier': val_loss['loss_classifier'] + loss_dict['loss_classifier'].item(),\n                            'loss_objectness': val_loss['loss_objectness'] + loss_dict['loss_objectness'].item(),\n                            'loss_rpn_box_reg': val_loss['loss_rpn_box_reg'] + loss_dict['loss_rpn_box_reg'].item(),\n\n                    }\n\n                    val_loss = {loss_key: loss / len(valid_data_loader) for loss_key, loss in val_loss.items()}\n                print('loss classifier {:.4f}, loss box reg {:.4f}, loss objectness {:.4f}, loss rpn box reg {:.4f} '\\\n                      .format(val_loss['loss_classifier'], val_loss['loss_box_reg'], val_loss['loss_objectness'], val_loss['loss_rpn_box_reg']))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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}