{"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":"%matplotlib inline\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport os","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-09-05T07:27:36.909168Z","iopub.execute_input":"2021-09-05T07:27:36.909428Z","iopub.status.idle":"2021-09-05T07:27:36.928085Z","shell.execute_reply.started":"2021-09-05T07:27:36.909376Z","shell.execute_reply":"2021-09-05T07:27:36.927413Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# params we will probably want to do some hyperparameter optimization later\nBASE_MODEL= 'VGG16'\nIMG_SIZE = (224, 224)\nBATCH_SIZE = 24\nDENSE_COUNT = 128 \nDROPOUT = 0.5 \nLEARN_RATE = 1e-4 \nTRAIN_SAMPLES = 6000\nTEST_SAMPLES = 600\nUSE_ATTN = False ","metadata":{"_uuid":"b148e50a8ba9440c2b4ee582496dbf63608cb92c","execution":{"iopub.status.busy":"2021-09-05T07:46:41.959245Z","iopub.execute_input":"2021-09-05T07:46:41.959521Z","iopub.status.idle":"2021-09-05T07:46:41.969108Z","shell.execute_reply.started":"2021-09-05T07:46:41.959469Z","shell.execute_reply":"2021-09-05T07:46:41.96844Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"image_bbox_df = pd.read_csv('../input/lung-opacity-overview/image_bbox_full.csv')\nimage_bbox_df['path'] = image_bbox_df['path'].map(lambda x: \n                                                  x.replace('input', \n                                                            'input/rsna-pneumonia-detection-challenge'))\nprint(image_bbox_df.shape[0], 'images')\nimage_bbox_df.sample(3)","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","execution":{"iopub.status.busy":"2021-09-05T07:46:44.627934Z","iopub.execute_input":"2021-09-05T07:46:44.628216Z","iopub.status.idle":"2021-09-05T07:46:44.748017Z","shell.execute_reply.started":"2021-09-05T07:46:44.62816Z","shell.execute_reply":"2021-09-05T07:46:44.747175Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# get the labels in the right format\nfrom sklearn.preprocessing import LabelEncoder, OneHotEncoder\nclass_enc = LabelEncoder()\nimage_bbox_df['class_idx'] = class_enc.fit_transform(image_bbox_df['class'])\noh_enc = OneHotEncoder(sparse=False)\nimage_bbox_df['class_vec'] = oh_enc.fit_transform(\n    image_bbox_df['class_idx'].values.reshape(-1, 1)).tolist() \nimage_bbox_df.sample(3)","metadata":{"_uuid":"f4925492adb4f55f01794709cb751a42ca5c2177","execution":{"iopub.status.busy":"2021-09-05T07:46:47.742563Z","iopub.execute_input":"2021-09-05T07:46:47.742878Z","iopub.status.idle":"2021-09-05T07:46:47.804921Z","shell.execute_reply.started":"2021-09-05T07:46:47.742828Z","shell.execute_reply":"2021-09-05T07:46:47.804108Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nimage_df = image_bbox_df.groupby('patientId').apply(lambda x: x.sample(1))\nraw_train_df, valid_df = train_test_split(image_df, test_size=0.25, random_state=2018,\n                                    stratify=image_df['class'])\nprint(raw_train_df.shape, 'training data')\nprint(valid_df.shape, 'validation data')","metadata":{"_uuid":"c12bfd5115610163c2b63ece8fa8845bb7792327","execution":{"iopub.status.busy":"2021-09-05T07:46:52.905989Z","iopub.execute_input":"2021-09-05T07:46:52.906268Z","iopub.status.idle":"2021-09-05T07:47:20.387357Z","shell.execute_reply.started":"2021-09-05T07:46:52.906218Z","shell.execute_reply":"2021-09-05T07:47:20.38653Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import keras.preprocessing.image as KPImage\nfrom PIL import Image\nimport pydicom\ndef read_dicom_image(in_path):\n    img_arr = pydicom.read_file(in_path).pixel_array\n    return img_arr/img_arr.max()\n    \nclass medical_pil():\n    @staticmethod\n    def open(in_path):\n        if '.dcm' in in_path:\n            c_slice = read_dicom_image(in_path)\n            int_slice =  (255*c_slice).clip(0, 255).astype(np.uint8) \n            return Image.fromarray(int_slice)\n        else:\n            return Image.open(in_path)\n    fromarray = Image.fromarray\nKPImage.pil_image = medical_pil","metadata":{"_uuid":"9548edfc1a318edfcd2a387468a5b7950a376cc5","execution":{"iopub.status.busy":"2021-09-05T07:47:20.388641Z","iopub.execute_input":"2021-09-05T07:47:20.389131Z","iopub.status.idle":"2021-09-05T07:47:20.407221Z","shell.execute_reply.started":"2021-09-05T07:47:20.389077Z","shell.execute_reply":"2021-09-05T07:47:20.406566Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras_preprocessing.image import ImageDataGenerator\nfrom keras.applications.vgg16 import VGG16 as PTModel, preprocess_input","metadata":{"_uuid":"b655a151895a67cb66b41ccf0bf97c5cd80cc0f2","execution":{"iopub.status.busy":"2021-09-05T07:47:20.408388Z","iopub.execute_input":"2021-09-05T07:47:20.408794Z","iopub.status.idle":"2021-09-05T07:47:20.435772Z","shell.execute_reply.started":"2021-09-05T07:47:20.408666Z","shell.execute_reply":"2021-09-05T07:47:20.435073Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_gen_args = dict(horizontal_flip = True, \n                              vertical_flip = False, \n                              height_shift_range = 0.05, \n                              width_shift_range = 0.02, \n                              rotation_range = 3, zoom_range = 0.05,\n                               preprocessing_function=preprocess_input)\nimg_gen = ImageDataGenerator(**img_gen_args)","metadata":{"_uuid":"a068b664c8bb465938fa3974c7b6e6120bf0860e","execution":{"iopub.status.busy":"2021-09-05T07:47:20.437367Z","iopub.execute_input":"2021-09-05T07:47:20.437951Z","iopub.status.idle":"2021-09-05T07:47:20.446902Z","shell.execute_reply.started":"2021-09-05T07:47:20.437881Z","shell.execute_reply":"2021-09-05T07:47:20.445951Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def flow_from_dataframe(img_data_gen, in_df, path_col, y_col, seed = None, **dflow_args):\n    base_dir = os.path.dirname(in_df[path_col].values[0])\n    print('## Ignore next message from keras, values are replaced anyways: seed: {}'.format(seed))\n    df_gen = img_data_gen.flow_from_directory(base_dir, \n                                     class_mode = 'sparse',\n                                              seed = seed,\n                                    **dflow_args)\n    df_gen.filenames = in_df[path_col].values\n    df_gen.classes = np.stack(in_df[y_col].values,0)\n    df_gen.samples = in_df.shape[0]\n    df_gen.n = in_df.shape[0]\n    df_gen._set_index_array()\n    df_gen.directory = '' # since we have the full path\n    print('Reinserting dataframe: {} images'.format(in_df.shape[0]))\n    return df_gen","metadata":{"_uuid":"9a3983f1be91084ba8c04441280efb18290814f2","execution":{"iopub.status.busy":"2021-09-05T07:47:28.283032Z","iopub.execute_input":"2021-09-05T07:47:28.283343Z","iopub.status.idle":"2021-09-05T07:47:28.301096Z","shell.execute_reply.started":"2021-09-05T07:47:28.283288Z","shell.execute_reply":"2021-09-05T07:47:28.300332Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_gen = flow_from_dataframe(img_gen, train_df, \n                             path_col = 'path',\n                            y_col = 'class_vec', \n                            target_size = IMG_SIZE,\n                             color_mode = 'rgb',\n                            batch_size = BATCH_SIZE)\n\nvalid_gen = flow_from_dataframe(img_gen, valid_df, \n                             path_col = 'path',\n                            y_col = 'class_vec', \n                            target_size = IMG_SIZE,\n                             color_mode = 'rgb',\n                            batch_size = 256) # we can use much larger batches for evaluation\n# used a fixed dataset for evaluating the algorithm\nvalid_X, valid_Y = next(flow_from_dataframe(img_gen, \n                               valid_df, \n                             path_col = 'path',\n                            y_col = 'class_vec', \n                            target_size = IMG_SIZE,\n                             color_mode = 'rgb',\n                            batch_size = TEST_SAMPLES)) # one big batch","metadata":{"_uuid":"720a67aaa3a8f4c9d5d50752c3f18f4e54dc3af0","execution":{"iopub.status.busy":"2021-09-05T07:47:33.516259Z","iopub.execute_input":"2021-09-05T07:47:33.51654Z","iopub.status.idle":"2021-09-05T07:48:04.334481Z","shell.execute_reply.started":"2021-09-05T07:47:33.516488Z","shell.execute_reply":"2021-09-05T07:48:04.3337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"t_x, t_y = next(train_gen)","metadata":{"_uuid":"26f5733df5b2c77a0c7e1a75b495744b0f8b34a4","execution":{"iopub.status.busy":"2021-09-05T07:48:04.33535Z","iopub.execute_input":"2021-09-05T07:48:04.335693Z","iopub.status.idle":"2021-09-05T07:48:04.959258Z","shell.execute_reply.started":"2021-09-05T07:48:04.335642Z","shell.execute_reply":"2021-09-05T07:48:04.958606Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"base_pretrained_model = PTModel(input_shape =  t_x.shape[1:], \n                              include_top = False, weights = 'imagenet')\nbase_pretrained_model.trainable = False","metadata":{"_uuid":"7ec309826c33787294dd1ba2cac5e4e65bab1755","execution":{"iopub.status.busy":"2021-09-05T07:48:30.913276Z","iopub.execute_input":"2021-09-05T07:48:30.913582Z","iopub.status.idle":"2021-09-05T07:48:34.252325Z","shell.execute_reply.started":"2021-09-05T07:48:30.91352Z","shell.execute_reply":"2021-09-05T07:48:34.251622Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras.layers import GlobalAveragePooling2D, Dense, Dropout, Flatten, Input, Conv2D, multiply, LocallyConnected2D, Lambda, AvgPool2D\nfrom keras.models import Model\nfrom keras.optimizers import Adam\npt_features = Input(base_pretrained_model.get_output_shape_at(0)[1:], name = 'feature_input')\npt_depth = base_pretrained_model.get_output_shape_at(0)[-1]\nfrom keras.layers import BatchNormalization\nbn_features = BatchNormalization()(pt_features)\ngap = GlobalAveragePooling2D()(bn_features)\n\ngap_dr = Dropout(DROPOUT)(gap)\ndr_steps = Dropout(DROPOUT)(Dense(DENSE_COUNT, activation = 'elu')(gap_dr))\nout_layer = Dense(t_y.shape[1], activation = 'softmax')(dr_steps)\n\nattn_model = Model(inputs = [pt_features], \n                   outputs = [out_layer], name = 'trained_model')\n\nattn_model.summary()","metadata":{"_uuid":"b0f2055383236d17f0d5455ecc2af5de922fc3b8","execution":{"iopub.status.busy":"2021-09-05T07:48:56.468275Z","iopub.execute_input":"2021-09-05T07:48:56.46857Z","iopub.status.idle":"2021-09-05T07:48:56.629945Z","shell.execute_reply.started":"2021-09-05T07:48:56.468519Z","shell.execute_reply":"2021-09-05T07:48:56.629114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# define iou or jaccard loss function\ndef iou_loss(y_true, y_pred):\n    y_true = tf.reshape(y_true, [-1])\n    y_pred = tf.reshape(y_pred, [-1])\n    intersection = tf.reduce_sum(y_true * y_pred)\n    score = (intersection + 1.) / (tf.reduce_sum(y_true) + tf.reduce_sum(y_pred) - intersection + 1.)\n    return 1 - score\n\n# combine bce loss and iou loss\ndef iou_bce_loss(y_true, y_pred):\n    return 0.5 * keras.losses.binary_crossentropy(y_true, y_pred) + 0.5 * iou_loss(y_true, y_pred)\n\n# mean iou as a metric\ndef mean_iou(y_true, y_pred):\n    y_pred = tf.round(y_pred)\n    intersect = tf.reduce_sum(y_true * y_pred)\n    union = tf.reduce_sum(y_true) + tf.reduce_sum(y_pred)\n    smooth = tf.ones(tf.shape(intersect))\n    return tf.reduce_mean((intersect + smooth) / (union - intersect + smooth))\n","metadata":{"execution":{"iopub.status.busy":"2021-09-05T08:00:09.735321Z","iopub.execute_input":"2021-09-05T08:00:09.735622Z","iopub.status.idle":"2021-09-05T08:00:09.759645Z","shell.execute_reply.started":"2021-09-05T08:00:09.735553Z","shell.execute_reply":"2021-09-05T08:00:09.758856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow import keras\nfrom keras.models import Sequential\nfrom keras.optimizers import Adam\nimport tensorflow as tf\npneu_model = Sequential(name = 'combined_model')\nbase_pretrained_model.trainable = False\npneu_model.add(base_pretrained_model)\npneu_model.add(attn_model)\npneu_model.compile(optimizer = Adam(lr = LEARN_RATE), loss=iou_bce_loss,\n                           metrics = ['accuracy', mean_iou])\n#pneu_model.summary()","metadata":{"_uuid":"74103ef71c30a9725949fbeed864174cbe62c69d","execution":{"iopub.status.busy":"2021-09-05T08:00:11.316438Z","iopub.execute_input":"2021-09-05T08:00:11.316754Z","iopub.status.idle":"2021-09-05T08:00:11.525353Z","shell.execute_reply.started":"2021-09-05T08:00:11.316701Z","shell.execute_reply":"2021-09-05T08:00:11.524723Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras.callbacks import ModelCheckpoint, EarlyStopping\n\ncheckpoint = ModelCheckpoint(\"model-{val_loss:.2f}.h5\", monitor=\"val_loss\", verbose=1, \n                             save_best_only=True, save_weights_only=True)\n\nstop = EarlyStopping(monitor=\"val_loss\", patience=2)","metadata":{"execution":{"iopub.status.busy":"2021-09-05T08:00:57.417356Z","iopub.execute_input":"2021-09-05T08:00:57.417748Z","iopub.status.idle":"2021-09-05T08:00:57.428524Z","shell.execute_reply.started":"2021-09-05T08:00:57.41769Z","shell.execute_reply":"2021-09-05T08:00:57.427717Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_gen.batch_size = BATCH_SIZE\npneu_model.fit_generator(train_gen, \n                         validation_data = (valid_X, valid_Y), \n                         epochs=10, \n                         callbacks=callbacks_list,\n                         workers=2)","metadata":{"_uuid":"58347af9669fed1e5308ac90a6ce06b3579761c5","execution":{"iopub.status.busy":"2021-09-05T08:13:41.933824Z","iopub.execute_input":"2021-09-05T08:13:41.93414Z","iopub.status.idle":"2021-09-05T08:41:55.631812Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pred_Y = pneu_model.predict(valid_X, \n                          batch_size = BATCH_SIZE, \n                          verbose = True)","metadata":{"_uuid":"f67dd5726e24517401ff0724f2aa07b1787cf791","trusted":true},"execution_count":null,"outputs":[]}]}