{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"#import\nimport glob, pylab, pandas as pd\nimport pydicom, numpy as np\nimport os \nimport sys","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport keras\nfrom keras.applications import DenseNet121\nfrom keras.callbacks import Callback, ModelCheckpoint, ReduceLROnPlateau\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.models import Sequential\nfrom keras.optimizers import Adam, Nadam\nfrom keras import layers\nimport tensorflow as tf\nfrom tqdm import tqdm\nimport cv2\nfrom sklearn.metrics import roc_auc_score, roc_curve","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install --upgrade imgaug\nimport imgaug.augmenters as iaa","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data_dir = \"../input/rsna-pneumonia-detection-challenge/\"\ntrain_image_dir = os.path.join(data_dir, \"stage_2_train_images\")\ntest_image_dir = os.path.join(data_dir, \"stage_2_test_images\")\nmodel_dir = \"../output/kaggle/working/\"","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"df = pd.read_csv(os.path.join(data_dir, 'stage_2_train_labels.csv'))\nprint(df.iloc[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#parse data\ndef parse_data(df):\n    \"\"\"\n    Method to read a CSV file (Pandas dataframe) and parse the \n    data into the following nested dictionary:\n\n      parsed = {\n        \n        'patientId-00': {\n            'dicom': path/to/dicom/file,\n            'label': either 0 or 1 for normal or pnuemonia, \n            'boxes': list of box(es)\n        },\n        'patientId-01': {\n            'dicom': path/to/dicom/file,\n            'label': either 0 or 1 for normal or pnuemonia, \n            'boxes': list of box(es)\n        }, ...\n\n      }\n\n    \"\"\"\n    # --- Define lambda to extract coords in list [y, x, height, width]\n    extract_box = lambda row: [row['y'], row['x'], row['height'], row['width']]\n\n    parsed = {}\n    for n, row in df.iterrows():\n        # --- Initialize patient entry into parsed \n        pid = row['patientId']\n        if pid not in parsed:\n            parsed[pid] = {\n                'dicom': '../input/rsna-pneumonia-detection-challenge/stage_2_train_images/%s.dcm' % pid,\n                'label': row['Target'],\n                'boxes': []}\n\n        # --- Add box if opacity is present\n        if parsed[pid]['label'] == 1:\n            parsed[pid]['boxes'].append(extract_box(row))\n\n    return parsed","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"parsed = parse_data(df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#visualize box\ndef draw(data):\n    \"\"\"\n    Method to draw single patient with bounding box(es) if present \n\n    \"\"\"\n    # --- Open DICOM file\n    d = pydicom.read_file(data['dicom'])\n    im = d.pixel_array\n\n    # --- Convert from single-channel grayscale to 3-channel RGB\n    im = np.stack([im] * 3, axis=2)\n\n    # --- Add boxes with random color if present\n    for box in data['boxes']:\n        rgb = np.floor(np.random.rand(3) * 256).astype('int')\n        im = overlay_box(im=im, box=box, rgb=rgb, stroke=6)\n\n    pylab.imshow(im, cmap=pylab.cm.gist_gray)\n    pylab.axis('off')\n\ndef overlay_box(im, box, rgb, stroke=1):\n    \"\"\"\n    Method to overlay single box on image\n\n    \"\"\"\n    # --- Convert coordinates to integers\n    box = [int(b) for b in box]\n    \n    # --- Extract coordinates\n    y1, x1, height, width = box\n    y2 = y1 + height\n    x2 = x1 + width\n\n    im[y1:y1 + stroke, x1:x2] = rgb\n    im[y2:y2 + stroke, x1:x2] = rgb\n    im[y1:y2, x1:x1 + stroke] = rgb\n    im[y1:y2, x2:x2 + stroke] = rgb\n\n    return im","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"draw(parsed['00436515-870c-4b36-a041-de91049b9ab4'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# classes\ndf_detailed = pd.read_csv(os.path.join(data_dir, 'stage_2_detailed_class_info.csv'))\nsummary = {}\nfor n, row in df_detailed.iterrows():\n    if row['class'] not in summary:\n        summary[row['class']] = 0\n    summary[row['class']] += 1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#count\nprint(\"numbers of train samples:\", len([name for name in os.listdir(train_image_dir)]))\nprint(\"numbers of test samples:\", len([name for name in os.listdir(test_image_dir)]))\nprint(\"numbers of train labels:\", df.shape[0])\nprint(\"numbers of train detailed labels:\", df_detailed.shape[0])\nprint(\"classes:\", summary)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#number of unique df\ndf_unique = df[\"patientId\"].unique()\ndf_unique.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#visualize numbers of patients with variable targets\ndf_target_num = df.groupby(\"patientId\").agg(\"sum\")\ndf_target_num.reset_index(inplace=True)\ndf_target_num[\"Target\"].value_counts()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#create dataset for classifier\ndf_target_num[\"hasMask\"] = df_target_num[\"Target\"] != 0\ndf_target_num[\"hasMask\"].value_counts()\n# df_target_num.loc[df_target_num[\"hasMask\"], \"hasMask\"] = \"Yes\"\n# df_target_num.loc[df_target_num[\"hasMask\"] == False, \"hasMask\"] = \"No\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_target_num.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\ntrain_df, valid_df = train_test_split(df_target_num, test_size=0.15, stratify=df_target_num[\"hasMask\"])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_test = pd.DataFrame([name for name in os.listdir(test_image_dir)], columns=[\"patientId\"])\ndf_test.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# #read data for passing to generator\n# def load_image(data, directory):\n#     d = pydicom.read_file(directory + \"/\" + data + \".dcm\")\n#     im = d.pixel_array\n\n#     # --- Convert from single-channel grayscale to 3-channel RGB\n#     im = np.stack([im] * 3, axis=2)\n#     return im\n# train_data = [load_image(d, train_image_dir) for d in df_unique]\n# test_data = [load_image(d, test_image_dir) for d in df_unique]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#create datagenerator\n\n\n# def create_train_generator():\n#     return ImageDataGenerator(\n#         zoom_range=0.1,\n#         fill_mode=\"constant\",#One of {\"constant\", \"nearest\", \"reflect\" or \"wrap\"}. Points outside the boundaries of the input are filled\n#         cval=0.,#Float or Int. Value used for points outside the boundaries when fill_mode = \"constant\".\n#         rotation_range=10,\n#         height_shift_range=0.1,\n#         width_shift_range=0.1,\n#         horizontal_flip=True,\n#         vertical_flip=True,\n#         rescale=1/255.,\n#         validation_split=0.15        \n#     )\n\n# def create_train_flow(datagen, df, seed, **dflow_args):\n#     flow = datagen.flow_from_directory(\n#         directory=train_image_dir,\n#         class_mode = 'sparse',\n#         seed = seed,\n#         **dflow_args\n#     )\n#     flow.filenames = train_image_dir + \"/\" + df[\"patientId\"] + \".dcm\"\n#     print(flow.filenames.values[1])\n#     print(df[\"patientId\"].values[1])\n#     flow.classes = df[\"hasMask\"]\n#     flow.samples = df.shape[0]\n#     flow.n = df.shape[0]\n#     flow._set_index_array()\n#     #flow.directory = '' # since we have the full path\n#     print('Reinserting dataframe: {} images'.format(df.shape[0]))\n#     return flow\n\n\n# def create_train_flow(datagen, subset, seed):\n#     return datagen.flow_from_dataframe(\n#         df_target_num,\n#         directory=os.path.join(data_dir, \"stage_2_train_images\"),\n#         x_col=\"patientId\",\n#         y_col=\"hasMask\",\n#         color_mode='grayscale',\n#         class_mode=\"sparse\",\n#         target_size=(256, 256),\n#         batch_size=BATCH_SIZE,\n#         subset=subset,\n#         seed=seed\n#     )\n\n# def create_test_flow():\n#     return ImageDataGenerator(rescale=1/255.).flow_from_dataframe(\n#         df_test,\n#         directory=os.path.join(data_dir, \"stage_2_test_images\"),\n#         x_col=\"patientId\",\n#         color_mode='grayscale',\n#         class_mode=None,\n#         target_size=(256, 256),\n#         batch_size=BATCH_SIZE,\n#         shuffle=False\n#     )\n\n# data_generator = create_train_generator()\n# train_gen = create_train_flow(data_generator, train_df, None, color_mode='rgb', batch_size=BATCH_SIZE)\n#val_gen = create_train_flow(data_generator, df_target_num, \"validation\", None, color_mode='rgb', batch_size=BATCH_SIZE)\n# test_gen = create_test_flow()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# t_x, t_y = next(train_gen)\n# print(t_x.shape, t_y.shape)\n# fig, m_axs = plt.subplots(2, 4, figsize = (16, 8))\n# for (c_x, c_y, c_ax) in zip(t_x, t_y, m_axs.flatten()):\n#     c_ax.imshow(c_x[:,:,0], cmap = 'bone')\n#     c_ax.set_title('%s' % class_enc.classes_[np.argmax(c_y)])\n#     c_ax.axis('off')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#create generator\nclass DataGenerator(keras.utils.Sequence):\n    def __init__(self, df, id_col, label_col, image_dir, batch_size=32,\n                 img_h=256, img_w=512, phase='train', shuffle=True):\n        \n        self.list_ids = df[id_col].values\n        self.list_ids = [id1 + \".dcm\" for id1 in self.list_ids]\n        self.labels = {(row[1][id_col] + \".dcm\"):row[1][label_col] for row in df[[id_col, label_col]].iterrows()}\n        self.image_dir = image_dir\n        self.batch_size = batch_size\n        self.img_h = img_h\n        self.img_w = img_w\n        self.phase = phase\n        self.shuffle = shuffle\n        self.on_epoch_end()\n    def __len__(self):\n        'denotes the number of batches per epoch'\n        return int(np.floor(len(self.list_ids)) / self.batch_size)\n    \n    def __getitem__(self, index):\n        'generate one batch of data'\n        indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]\n        # get list of IDs\n        list_ids_temp = [self.list_ids[k] for k in indexes]\n        # generate data\n        X, y = self.__data_generation(list_ids_temp)\n        # return data \n        return X, y\n    \n    def on_epoch_end(self):\n        'update ended after each epoch'\n        self.indexes = np.arange(len(self.list_ids))\n        if self.shuffle:\n            np.random.shuffle(self.indexes)\n            \n    def __data_generation(self, list_ids_temp):\n        'generate data containing batch_size samples'\n        X = np.empty((self.batch_size, self.img_h, self.img_w, 3))\n        y = np.empty((self.batch_size, 1))\n        \n        for idx, id in enumerate(list_ids_temp):\n            file_path =  os.path.join(self.image_dir, id)\n            image = pydicom.read_file(file_path)\n            image = image.pixel_array\n\n            image_resized = cv2.resize(image, (self.img_w, self.img_h))\n            \n            \n            image_resized = np.array(image_resized, dtype=np.float64)\n#             image_resized /= 255.0\n            \n            # standardization of the image\n            image_resized -= image_resized.mean()\n            image_resized /= image_resized.std()#标准化和归一化看图像还是有区别的，这个要尝试两种方式\n            \n            X[idx,] = np.expand_dims(image_resized, axis=2)\n            y[idx,] = self.labels.get(id)\n        \n        #image augmentation\n        if self.phase == \"train\":\n            aug_seq = iaa.Sequential([\n                iaa.Fliplr(0.5),\n                iaa.Flipud(0.5),\n#                 iaa.Affine(\n#                     scale={\"x\": (0.9, 1.0), \"y\": (0.1, 0.1)}               \n#                 )\n            ]\n            )\n            X = aug_seq(images=X)\n        \n            \n        return X, y","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img_h = 256\nimg_w = 256\nbatch_size = 16\n\ntrain_params = {'img_h': img_h,\n          'img_w': img_w,\n          'image_dir': train_image_dir,\n          'batch_size': batch_size,\n          'phase':\"train\",\n          'shuffle': True}\nval_params = {'img_h': img_h,\n          'img_w': img_w,\n          'image_dir': train_image_dir,\n          'batch_size': batch_size,\n          'phase':\"validation\",\n          'shuffle': False}\n\n# Get Generators\ntraining_generator = DataGenerator(train_df, \"patientId\", \"hasMask\", **train_params)\nvalid_generator = DataGenerator(valid_df, \"patientId\", \"hasMask\", **val_params)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#check data generator\n# x_test, y_test = training_generator.__getitem__(0)\n# print(x_test.shape, y_test.shape)\n# pylab.imshow(x_test[8])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# try augmentation\n# pid2 = train_df[\"patientId\"].values\n# def load_batch(ids):\n#     ids = ids + \".dcm\"\n#     img2 = pydicom.read_file(os.path.join(train_image_dir, ids))\n#     img2 = img2.pixel_array\n#     img2 = np.stack([img2] * 3, -1)\n#     return img2\n\n# img2 = np.array([load_batch(id1) for id1 in pid2[:5]])\n# print(img2.shape)\n\n# seq2 = iaa.Sequential([\n#     iaa.Crop(px=(50, 16), keep_size=False),\n#     iaa.Fliplr(0.5),\n#     iaa.GaussianBlur(sigma=(0, 20.0)),\n#     iaa.CropAndPad(\n#             percent=(-0.05, 0.1),\n#             pad_cval=(0, 255)\n#         ),\n#     iaa.AverageBlur(k=(2, 7)),\n# ])\n# imgseq2 = seq2(images=img2)\n# pylab.imshow(imgseq2[0])\n# pylab.imshow(img2[0])\n# del img2, pid2, seq2, imgseq2","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#build model\ndef build_model(clf_model, drop_rate, lr):\n    if clf_model == \"Densenet121\":\n        model_name = DenseNet121\n    base_model = model_name(\n        include_top=False,\n        input_shape=(256, 256, 3),\n        weights=\"imagenet\"\n    )\n    \n    model = Sequential()\n    model.add(base_model)\n    model.add(layers.GlobalAveragePooling2D())\n    model.add(layers.BatchNormalization())\n    model.add(layers.Dropout(drop_rate))\n    model.add(layers.Dense(512, activation=\"relu\"))\n    model.add(layers.BatchNormalization())\n    model.add(layers.Dropout(drop_rate))\n    model.add(layers.Dense(1, activation=\"sigmoid\"))\n    \n    #freeze pretrained model\n#     for ly in base_model.layers:\n#         ly.trainable = False\n    \n    model.compile(\n        loss=\"binary_crossentropy\",\n        optimizer=Adam(lr),\n        metrics=['accuracy']\n    )\n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#hyperparameters\nlr = 1e-3\ndrop_rate = 0.5\ncoarse_gs = 2\nclf_model = \"Densenet121\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class myHistory(keras.callbacks.Callback):\n    def on_train_begin(self, logs={}):\n        self.losses = []\n        self.acc = []\n\n    def on_batch_end(self, batch, logs={}):\n        self.losses.append(logs.get('loss'))\n        self.acc.append(logs.get('accuracy'))\n\nmyhistory = myHistory()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#define callback\ntotal_steps = train_df.shape[0]/batch_size\ncheckpoint = ModelCheckpoint(\n    model_dir+clf_model+\".h5\",\n    monitor=\"val_acc\",\n    verbose=1,\n    save_best_only=True,\n    save_weights_only=False,\n    mode=\"auto\"\n)\n\n# model = build_model(clf_model, drop_rate, 0.0001)\n# history = model.fit_generator(\n#     training_generator,\n#     validation_data=valid_generator,\n#     steps_per_epoch=total_steps*0.85*0.2,\n#     epochs=1,\n#     callbacks=[checkpoint, myhistory],\n#     use_multiprocessing=True,\n#     workers=4\n# )\n\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history_list_gs = pd.DataFrame(columns=[\"round\", \"learning_rate\", \"drop_rate\", \"history\", \"myhistory\"])\nfor ci in tqdm(range(coarse_gs)):\n    lr1 = 10**(np.random.uniform(-4,-2))\n    dr1 = np.random.uniform(0.01, 0.99)\n    \n    model = build_model(clf_model, dr1, lr1)\n    history = model.fit_generator(\n        training_generator,\n        validation_data=valid_generator,\n        steps_per_epoch=total_steps*0.85*0.05,\n        validation_steps=total_steps*0.15*0.05,\n        use_multiprocessing=True,\n        epochs=2,\n        callbacks=[checkpoint,myhistory]\n    )\n    print(\"round:{}, learning_rate:{}, drop_rate:{}\".format(ci+1, lr1, dr1))\n    print(\"history:{}\".format(history.history))\n    history_list_gs.loc[ci, \"round\"] = ci + 1\n    history_list_gs.loc[ci, \"learning_rate\"] = lr1\n    history_list_gs.loc[ci, \"drop_rate\"] = dr1\n    history_list_gs.loc[ci, \"history\"] = history\n    history_list_gs.loc[ci, \"myhistory\"] = myhistory","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# history_df = pd.DataFrame(history.history)\n# history_df[['loss', 'val_loss']].plot()\n# history_df[['accuracy', 'val_accuracy']].plot()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(3,2, figsize=(16,12))\nfor row in history_list_gs.iterrows():\n    row1 = row[1]\n    lr = row1[\"learning_rate\"]\n    dr = row1[\"drop_rate\"]\n    history = row1[\"history\"].history\n    myhistory = row1[\"myhistory\"]\n    color = np.random.rand(3,)\n    label = \"lr:{:.4f}-dr:{:.2f}\".format(lr,dr)\n    \n    \n    for (metric, ax1) in zip(history.keys(), ax.flatten()[:4]):\n        ax1.set_title(metric)\n        ax1.plot(history[metric], color=color, label=label)\n        ax1.legend()\n    \n    ax.flatten()[4].set_title(\"losses_iteration\")\n    ax.flatten()[4].plot(myhistory.losses, color=color, label=label)\n    ax.flatten()[4].legend()\n    ax.flatten()[5].set_title(\"acc_iteration\")\n    ax.flatten()[5].plot(myhistory.acc, color=color, label=label)\n    ax.flatten()[5].legend()\n    ","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}