{"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":"import numpy as np \nimport pandas as pd \nfrom keras.preprocessing.image import ImageDataGenerator\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.model_selection import StratifiedKFold\nimport matplotlib.pyplot as plt\nimport cv2\nimport os\nimport re\nimport pydicom\nimport warnings\nfrom PIL import Image\n\nimport albumentations as A\nfrom albumentations.pytorch.transforms import ToTensorV2\nfrom albumentations.core.transforms_interface import ImageOnlyTransform\nimport torch\nimport torchvision\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom torchvision.models.detection import FasterRCNN\nfrom torchvision.models.detection.rpn import AnchorGenerator\nfrom torch.utils.data import DataLoader, Dataset\nfrom torch.utils.data.sampler import SequentialSampler\n\nfrom matplotlib import pyplot as plt\nimport random\npaddingSize= 0\nimport tensorflow as tf\nfrom tensorflow.keras import Model\nfrom tqdm import notebook","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"warnings.filterwarnings(\"ignore\")\nDIR_INPUT = '/kaggle/input/vinbigdata-chest-xray-abnormalities-detection'\nDIR_TRAIN = f'{DIR_INPUT}/train'\nDIR_TEST = f'{DIR_INPUT}/test'\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv(f'{DIR_INPUT}/train.csv')\ntrain_df.fillna(0, inplace=True)\ntrain_df.loc[train_df[\"class_id\"] == 14, ['x_max', 'y_max']] = 1.0\n\n# FasterRCNN handles class_id==0 as the background.\ntrain_df[\"class_id\"] = train_df[\"class_id\"] + 1\ntrain_df.loc[train_df[\"class_id\"] == 15, [\"class_id\"]] = 0\n\nprint(\"df Shape: \"+str(train_df.shape))\nprint(\"No Of Classes: \"+str(train_df[\"class_id\"].nunique()))\ntrain_df.sort_values(by='image_id').head(10)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def label_to_name(id):\n    id = int(id)\n    id = id-1\n    if id == 0:\n        return \"Aortic enlargement\"\n    if id == 1:\n        return \"Atelectasis\"\n    if id == 2:\n        return \"Calcification\"\n    if id == 3:\n        return \"Cardiomegaly\"\n    if id == 4:\n        return \"Consolidation\"\n    if id == 5:\n        return \"ILD\"\n    if id == 6:\n        return \"Infiltration\"\n    if id == 7:\n        return \"Lung Opacity\"\n    if id == 8:\n        return \"Nodule/Mass\"\n    if id == 9:\n        return \"Other lesion\"\n    if id == 10:\n        return \"Pleural effusion\"\n    if id == 11:\n        return \"Pleural thickening\"\n    if id == 12:\n        return \"Pneumothorax\"\n    if id == 13:\n        return \"Pulmonary fibrosis\"\n    else:\n        return str(id)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_ids = train_df['image_id'].unique()\nvalid_ids = image_ids[-10000:]# Tran and Validation Split \ntrain_ids = image_ids[:-10000]\n\n\nvalid_df = train_df[train_df['image_id'].isin(valid_ids)]\ntrain_df = train_df[train_df['image_id'].isin(train_ids)]\n\ntrain_df[\"class_id\"] = train_df[\"class_id\"].apply(lambda x: x+1)\nvalid_df[\"class_id\"] = valid_df[\"class_id\"].apply(lambda x: x+1)\n\ntrain_df.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Clean\n\ntrain_df['area'] = (train_df['x_max'] - train_df['x_min']) * (train_df['y_max'] - train_df['y_min'])\nvalid_df['area'] = (valid_df['x_max'] - valid_df['x_min']) * (valid_df['y_max'] - valid_df['y_min'])\ntrain_df = train_df[train_df['area'] > 1]\nvalid_df = valid_df[valid_df['area'] > 1]\n\ntrain_df = train_df[(train_df['class_id'] > 1) & (train_df['class_id'] < 15)]\nvalid_df = valid_df[(valid_df['class_id'] > 1) & (valid_df['class_id'] < 15)]\n\n\ntrain_df = train_df.drop(['area'], axis = 1)\ntrain_df.shape","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"max(train_df['class_id'])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class VinBigDataset(Dataset): #Class to load Training Data\n    \n    def __init__(self, dataframe, image_dir, transforms=None,stat = 'Train'):\n        super().__init__()\n        \n        self.image_ids = dataframe[\"image_id\"].unique()\n        self.df = dataframe\n        self.image_dir = image_dir\n        self.transforms = transforms\n        self.stat = stat\n        \n    def __getitem__(self, index):\n        if self.stat == 'Train':\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 = pydicom.dcmread(f\"{self.image_dir}/{image_id}.dicom\")\n\n            image = dicom.pixel_array\n\n            if \"PhotometricInterpretation\" in dicom:\n                if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n                    image = np.amax(image) - image\n\n            intercept = dicom.RescaleIntercept if \"RescaleIntercept\" in dicom else 0.0\n            slope = dicom.RescaleSlope if \"RescaleSlope\" in dicom else 1.0\n\n            if slope != 1:\n                image = slope * image.astype(np.float64)\n                image = image.astype(np.int16)\n\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, image_ids\n        \n        else:\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 = pydicom.dcmread(f\"{self.image_dir}/{image_id}.dicom\")\n\n            image = dicom.pixel_array\n\n            intercept = dicom.RescaleIntercept if \"RescaleIntercept\" in dicom else 0.0\n            slope = dicom.RescaleSlope if \"RescaleSlope\" in dicom 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 self.transforms:\n                sample = {\n                    'image': image,\n                }\n                sample = self.transforms(**sample)\n                image = sample['image']\n\n            return image, image_id\n    \n    def __len__(self):\n        return self.image_ids.shape[0]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def dilation(img): # custom image processing function\n    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, tuple(np.random.randint(1, 6, 2)))\n    img = cv2.dilate(img, kernel, iterations=1)\n    return img\n\nclass Dilation(ImageOnlyTransform):\n    def apply(self, img, **params):\n        return dilation(img)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"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        Dilation(),\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']})\n\ndef get_test_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    ])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True)\n\nnum_classes = 15 # 14 Classes + 1 background\n\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)\n\ndevice = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\n\ndef collate_fn(batch):\n    return tuple(zip(*batch))\n\ntrain_dataset = VinBigDataset(train_df, DIR_TRAIN, get_train_transform())\nvalid_dataset = VinBigDataset(valid_df, DIR_TRAIN, get_valid_transform())\n\n\n# split the dataset in train and test set\nindices = torch.randperm(len(train_dataset)).tolist()\n# Create train and validate data loader\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)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Biunding Boxes","metadata":{}},{"cell_type":"code","source":"# Train dataset sample\nimages, targets, image_ids = next(iter(train_data_loader))\nimages = list(image.to(device) for image in images)\ntargets = [{k: v.to(device) for k, v in t.items()} for t in targets]\n\nfor number in random.sample([1,2,3,4,5],5):\n  boxes = targets[number]['boxes'].cpu().numpy().astype(np.int32)\n  img = images[number].permute(1,2,0).cpu().numpy()\n  labels= targets[number]['labels'].cpu().numpy().astype(np.int32)\n  fig, ax = plt.subplots(1, 1, figsize=(16, 8))\n\n  for i in range(len(boxes)):\n      img = cv2.rectangle(img,(boxes[i][0]+paddingSize,boxes[i][1]+paddingSize),(boxes[i][2]+paddingSize,boxes[i][3]+paddingSize),(255,0,0),2)\n      #print(le.inverse_transform([labels[i]-1])[0])\n      #print(label_to_name(labels[i]), (int(boxes[i][0]), int(boxes[i][1])))\n      img = cv2.putText(img, label_to_name(labels[i]), (int(boxes[i][0]), int(boxes[i][1])), cv2.FONT_HERSHEY_TRIPLEX,1, (255,0,0), 2, cv2.LINE_AA)\n\n  ax.set_axis_off()\n  ax.imshow(img)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"try:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print(f'Running on TPU {tpu.master()}')\nexcept ValueError:\n    tpu = None\n\nif tpu:\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nelse:\n    strategy = tf.distribute.get_strategy()\n\nAUTO = tf.data.experimental.AUTOTUNE\nREPLICAS = strategy.num_replicas_in_sync\nprint(f'REPLICAS: {REPLICAS}')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### preparation for Model","metadata":{}},{"cell_type":"code","source":"train  =  pd.read_csv(\"../input/vinbigdata-chest-xray-abnormalities-detection/train.csv\")\ntest = pd.read_csv('../input/vinbigdata-chest-xray-abnormalities-detection/sample_submission.csv')\n\ntrain_dir256 = \"../input/vinbigdata-chest-xray-resized-png-256x256/train\"\ntest_dir256 = \"../input/vinbigdata-chest-xray-resized-png-256x256/test\"\n\n\ntrain['image_png'] = train.image_id+'.png'\ntest['image_png'] = test.image_id+'.png'\n\nIMAGE_SIZE256 = [256, 256] \nBATCH_SIZE = 32  \nEPOCHS = 1\nOPTIMIZER = tf.keras.optimizers.Adam(learning_rate=0.001)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(train.info())\ntrain.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"is_fold_train = train.groupby(\"image_png\")[\"class_id\"].agg(lambda s: \n(s == 14).sum()).reset_index().rename({\n    \"class_id\": \"num_normal_annotations\"}, axis=1)\nis_fold_train.head()\n\ndef change(x):\n    if (x==3):\n        x=1\n    return x\n\nis_fold_train['target'] = is_fold_train['num_normal_annotations'].apply(lambda x: change(x))\nis_fold_train.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"skfolds = StratifiedKFold(n_splits=5, \n                          random_state=42, \n                          shuffle = True)\n    \nfor num_fold, (train_index, val_index) in enumerate(skfolds.split(is_fold_train, is_fold_train.target)):\n    is_fold_train.loc[val_index, 'fold'] = int(num_fold)\n    \nis_fold_train['target'] = is_fold_train.target.astype('str')\n\nprint(is_fold_train.info())\nis_fold_train.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Data Generation of testing and training","metadata":{}},{"cell_type":"code","source":"datagen_train = ImageDataGenerator(\n                        rotation_range=40,          \n                        width_shift_range=0.2,   \n                        height_shift_range=0.2,  \n                        zoom_range=0.2,           \n                        horizontal_flip=True,     \n                        vertical_flip=False\n                                   )     \n\ndatagen_test =  ImageDataGenerator(validation_split = 0.2)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_augmentation = tf.keras.Sequential([\n  tf.keras.layers.experimental.preprocessing.RandomFlip(\"horizontal_and_vertical\"),\n  tf.keras.layers.experimental.preprocessing.RandomRotation(0.2),\n])","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Model","metadata":{}},{"cell_type":"code","source":"with strategy.scope(): \n\n    class MyModel(Model):\n        def __init__(self, appl, training=True):\n            super(MyModel, self).__init__()\n            self.d1_0 = tf.keras.applications.EfficientNetB0(\n                input_shape=(*IMAGE_SIZE256, 3),\n                weights='imagenet', pooling='avg', \n                include_top=False)\n            \n            self.d1_1 = tf.keras.applications.DenseNet121(\n                input_shape=(*IMAGE_SIZE256, 3),\n                weights=None, pooling='max') \n                                                           \n            self.d2 = tf.keras.layers.Dense(150, activation='relu')\n            self.d3 = tf.keras.layers.Dense(2, activation='sigmoid')\n            self.dropout = tf.keras.layers.Dropout(0.15)\n            \n     \n            self.training = training\n            self.appl = appl\n\n       \n        def call(self, x):\n    \n            if self.appl:\n                x = self.d1_0(x)\n            else:\n               \n                x = self.d1_1(x)\n            \n            if self.training:\n                x = self.dropout(x)\n            x = self.d2(x)\n            return self.d3(x)\n    \n    \nmodel1 = MyModel(appl=True)\nmodel2 = MyModel(appl=False)\n\nmodel1.compile(\n        optimizer = OPTIMIZER,\n        loss = \"binary_crossentropy\",\n        metrics = [tf.keras.metrics.BinaryAccuracy()]\n    )\n\nmodel2.compile(\n        optimizer = OPTIMIZER,\n        loss = \"binary_crossentropy\",\n        metrics = [tf.keras.metrics.BinaryAccuracy()]\n    )","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"loss_history = [] \nval_loss_history = []\n\nbinary_accuracy_history = []\nval_binary_accuracy_history = []","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Training Part","metadata":{}},{"cell_type":"code","source":"for fold_n in range(5): \n    print('Fold #{}'.format(fold_n+1))\n    \n    train_data = is_fold_train[is_fold_train.fold != fold_n]\n    val_data = is_fold_train[is_fold_train.fold == fold_n] \n    \n    train_dataset = datagen_train.flow_from_dataframe(\n        train_data,                                              \n        directory = train_dir256,\n        subset = \"training\",\n        x_col = \"image_png\",\n        y_col = \"target\",\n        shuffle=True,\n        batch_size=BATCH_SIZE)\n            \n            \n\n    valid_dataset = datagen_test.flow_from_dataframe(\n        val_data,\n        directory = train_dir256,\n        subset = \"validation\",\n        x_col = \"image_png\",\n        y_col = \"target\",\n        shuffle=True,\n        batch_size=BATCH_SIZE)\n    \n    model_fit = model1.fit(train_dataset, validation_data=valid_dataset, epochs=EPOCHS) \n    \n    loss_history.append(model_fit.history['loss'])\n    val_loss_history.append(model_fit.history['val_loss'])\n    binary_accuracy_history.append(model_fit.history['binary_accuracy'])\n    val_binary_accuracy_history.append(model_fit.history['val_binary_accuracy'])\n    \n    \nlh = [item for sublist in loss_history for item in sublist]\nvlh = [item for sublist in val_loss_history for item in sublist]\n\nbah = [item for sublist in binary_accuracy_history for item in sublist]\nvbah = [item for sublist in val_binary_accuracy_history for item in sublist]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.plot(lh, label='train')\nplt.plot(vlh, label='test')\nplt.title('loss')\nplt.legend()\nplt.show()\nplt.plot(bah, label='train')\nplt.plot(vbah, label='test')\nplt.title('binary_accuracy')\nplt.legend()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"execution_count":null,"outputs":[]}]}