{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport cv2\nimport os\nimport re\nimport pydicom\nimport warnings\n\nfrom PIL import Image\n\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\n\nimport torch\nimport torchvision\n\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom torchvision.models.detection import FasterRCNN\nfrom torchvision.models.detection.rpn import AnchorGenerator\n\nfrom torch.utils.data import DataLoader, Dataset\nfrom torch.utils.data.sampler import SequentialSampler\n\nfrom matplotlib import pyplot as plt\n\n\nwarnings.filterwarnings(\"ignore\")\n\n\nDIR_INPUT = '/kaggle/input/vinbigdata-chest-xray-abnormalities-detection'\nDIR_TRAIN = f'{DIR_INPUT}/train'\nDIR_TEST = f'{DIR_INPUT}/test'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_DataFrame = pd.read_csv(f'{DIR_INPUT}/train.csv')\n\n\ntrain_DataFrame.fillna(0, inplace=True) # Fill the NAN values with 0\n\ntrain_DataFrame.loc[train_DataFrame[\"class_id\"] == 14, ['x_max', 'y_max']] = 1.0 # make x_max , y_max of no finding = 1\n\n# FasterRCNN handles class_id==0 as the background.\ntrain_DataFrame[\"class_id\"] = train_DataFrame[\"class_id\"] + 1\ntrain_DataFrame.loc[train_DataFrame[\"class_id\"] == 15, [\"class_id\"]] = 0\n\ntrain_DataFrame.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(train_DataFrame.sort_values(by='image_id').head(50))\n\nnum_classes = train_DataFrame[\"class_id\"].nunique()\n\nprint('num_classes = ',num_classes)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"imageID = \"00150343289f317a0ad5629d5b7d9ef9\"\n\ndicom_of_imageID = pydicom.dcmread(f\"{DIR_TRAIN}/{imageID}.dicom\")\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(dicom_of_imageID)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_from_dicom = dicom_of_imageID.pixel_array * dicom_of_imageID.RescaleSlope + dicom_of_imageID.RescaleIntercept\n\nplt.imshow(image_from_dicom, cmap='gray')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"image_ids = train_DataFrame['image_id'].unique()\n\nvalidation_percentage = 0.2\n\nlen_validation_data = int(validation_percentage*len(image_ids))\n\nvalid_ids = image_ids[:len_validation_data]\n\n\ntrain_ids = image_ids[len_validation_data:]\n\nprint(\"number of training data = \",len(train_ids))\nprint(\"number of validation data = \",len(valid_ids))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"valid_DataFrame = train_DataFrame[train_DataFrame['image_id'].isin(valid_ids)]\ntrain_DataFrame = train_DataFrame[train_DataFrame['image_id'].isin(train_ids)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class VinBig_Dataset(Dataset):\n    \n    def __init__(self, DataFrame, Image_Dir, transforms=None):\n        super().__init__()\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):\n        \n        image_id = self.image_IDs[index]\n        records = self.DF[(self.DF['image_id'] == image_id)]\n        records = records.reset_index(drop=True)\n\n        dicom_of_image = pydicom.dcmread(f\"{self.Image_Dir}/{image_id}.dicom\")\n        \n        image = dicom_of_image.pixel_array\n        \n        if \"PhotometricInterpretation\" in dicom_of_image:\n            if dicom_of_image.PhotometricInterpretation == \"MONOCHROME1\":\n                image = np.amax(image) - image\n        \n        intercept = dicom_of_image.RescaleIntercept if \"RescaleIntercept\" in dicom_of_image else 0.0\n        slope = dicom_of_image.RescaleSlope if \"RescaleSlope\" in dicom_of_image else 1.0\n        \n        if slope != 1:\n            image = slope * image.astype(np.float64)\n            image = image.astype(np.int16)\n            \n        image += np.int16(intercept)        \n        \n        image = np.stack([image, image, image])\n        image = image.astype('float32')\n        image = image - image.min()\n        image = image / image.max()\n        image = image * 255.0\n        image = image.transpose(1,2,0)\n       \n        if records.loc[0, \"class_id\"] == 0:\n            records = records.loc[[0], :]\n        \n        boxes = records[['x_min', 'y_min', 'x_max', 'y_max']].values\n        area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])\n        area = torch.as_tensor(area, dtype=torch.float32)\n        labels = torch.tensor(records[\"class_id\"].values, dtype=torch.int64)\n\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['image_id'] = torch.tensor([index])\n        target['area'] = area\n        target['iscrowd'] = iscrowd\n\n        if self.transforms:\n            sample = {\n                'image': image,\n                'bboxes': target['boxes'],\n                'labels': labels\n            }\n            sample = self.transforms(**sample)\n            image = sample['image']\n            \n            target['boxes'] = torch.tensor(sample['bboxes'])\n\n        if target[\"boxes\"].shape[0] == 0:\n            # Albumentation cuts the target (class 14, 1x1px in the corner)\n            target[\"boxes\"] = torch.from_numpy(np.array([[0.0, 0.0, 1.0, 1.0]]))\n            target[\"area\"] = torch.tensor([1.0], dtype=torch.float32)\n            target[\"labels\"] = torch.tensor([0], dtype=torch.int64)\n            \n        return image, target\n    \n    def __len__(self):\n        return self.image_IDs.shape[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Albumentations\ndef get_train_transform():\n    return A.Compose([\n        A.Flip(0.5),\n        A.ShiftScaleRotate(scale_limit=0.1, rotate_limit=45, p=0.25),\n        A.LongestMaxSize(max_size=800, p=1.0),\n\n        # FasterRCNN will normalize.\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']})\n\ndef get_valid_transform():\n    return 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":"model=torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True)\n# get number of input features for the classifier\nin_features = model.roi_heads.box_predictor.cls_score.in_features\n\n# replace the pre-trained 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":"print(model)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def collate_fn(batch):\n    return tuple(zip(*batch))\n\ntrain_dataset = VinBig_Dataset(train_DataFrame, DIR_TRAIN, get_train_transform())\nvalid_dataset = VinBig_Dataset(valid_DataFrame, DIR_TRAIN, get_valid_transform())\n\n\n# split the dataset in train and test set\nindices = torch.randperm(len(train_dataset)).tolist()\n\ntrain_data_loader = DataLoader(\n    train_dataset,\n    batch_size=8,\n    shuffle=True,\n    num_workers=4,\n    collate_fn=collate_fn\n)\n\nvalid_data_loader = DataLoader(\n    valid_dataset,\n    batch_size=8,\n    shuffle=False,\n    num_workers=4,\n    collate_fn=collate_fn\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\nprint(device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"images, targets = next(iter(train_data_loader))\n\nprint(targets)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"images = list(image.to(device) for image in images)\ntargets = [{k: v.to(device) for k, v in t.items()} for t in targets]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"boxes = targets[0]['boxes'].cpu().numpy().astype(np.int32)\nsample = images[0].permute(1,2,0).cpu().numpy()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1, 1, figsize=(16, 8))\n\nfor box in boxes:\n    cv2.rectangle(sample,\n                  (box[0], box[1]),\n                  (box[2], box[3]),\n                  (220, 0, 0), 3)\n    \nax.set_axis_off()\nax.imshow(sample)","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":"model.to(device)\nparams = [p for p in model.parameters() if p.requires_grad]\noptimizer = torch.optim.SGD(params, lr=0.005, momentum=0.9, weight_decay=0.0005)\nlr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=4, gamma=0.1)\n\nnum_epochs = 30","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"loss_hist = Averager()\nitr = 1\n\nfor epoch in range(num_epochs):\n    loss_hist.reset()\n    \n    for images, targets in train_data_loader:\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        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_hist.value}\")\n\n        itr += 1\n        \n        # !!!REMOVE THIS!!!\n        break\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}\")   \n    # print(\"Saving epoch's state...\")\n    # torch.save(model.state_dict(), f\"model_state_epoch_{epoch}.pth\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"torch.save(model.state_dict(), \"model.pth\")\nfrom IPython.display import FileLink\nFileLink(r'model.pth')","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}