{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Credit NOTE\n\nThis notebook is just Thai comments on my RSNA teammate top kernel, with minor modification to make it (1) simpler, (2) works on CPU and (3) work on 2nd stage data : https://www.kaggle.com/akensert/inceptionv3-prev-resnet50-keras-baseline-model \n\n# เกริ่นนำ\n\nสวัสดีครับเพื่อนๆ หลังจากที่เราได้เล่าปัญหาเรื่องการตรวจสอบเลือดคั่งในสมอง 5 รูปแบบ มุมมองต่อปัญหาทั้งเชิงวิศวกร และเชิงรังสีแพทย์ไปแล้ว (ดูย้อนหลังได้ที่นี่ี http://bit.ly/thaikeras-rsna ) ในโน้ตบุ้คนี้เราจะมาดู โค้ด Keras ที่นำความรู้จากบทความที่ผ่านมา มาสร้างโมเดลต้นแบบกันครับ สำหรับเพื่อนๆ ที่ไม่เคยใช้งาน Kaggle มาก่อนสามารถดูขั้นตอนการเริ่มต้นได้ง่ายๆ ที่นี่ครับ : https://thaikeras.com/2018/setup-kaggle-workshop/\n\nโน้ตบุ้คฉบับนี้เป็นของเพื่อนชาวสวีเดน (@akensert) ที่ร่วมทีมกับทีม ThaiKeras ในการแข่งขัน RSNA ที่ผ่านมา ซึ่งเป็นโน้ตบุ้คที่สร้าง baseline ความแม่นยำสูงสุด อ่านง่ายและเป็นระเบียบ และได้รับการชื่นชมจากเพื่อนๆ ใน Kaggle community เป็นอย่างมากครับ\n\nโดยเนื้อหาในโค้ด จะแบ่งออกเป็น 5 หัวข้อดังต่อไปนี้ครับ (เพื่อนๆ ที่เปิดดูด้วย PC จะสามารถเลือกไปหัวข้อที่ต้องการได้จากเมนูบน Kaggle ด้านซ้ายครับ)\n\n**1. Preprocessing / Windowing -- ** โค้ดในการทำ windowing จากภาพ Dicom ดั่งที่อธิบายอย่างละเอียดในบทความฉบับก่อน\n\n**2. Data Generator -- ** โค้ดในการจัดการข้อมูลทีละ batch เนื่องจากในปัญหาที่ข้อมูลมีจำนวนมหาศาลเช่นนี้ั เราไม่สามารถโหลดข้อมูลขึ้นมาพร้อมๆ กันได้ เราจึงจำเป็นต้องสร้าง Keras Data Generator ขึ้นมาเพื่อจัดการกับข้อมูลทีละ Batch ในระหว่างฝึกสอนโมเดล\n\n**3. Model, Loss and Metric -- ** โค้ดสำหรับสร้างโมเดล รวมทั้ง objective function ในงานนี้\n\n**4. Meta-data (CSV) -- ** โค้ดสำหรับ import และจัดการข้อมูล meta data ที่สำคัญจาก CSV มายู่ใน panda และ numpy\n\n**5. Train and Predict -- ** ใช้โค้ดทั้งหมดเพื่อเริ่มฝึกสอนและทำนายข้อมูล test data\n\nหมายเหตุ เมื่อ Copy notebook เพื่อทดลองโค้ด เพื่อนๆ ต้องเปิด GPU ในเมดูของ Kaggle Notebook ที่อยู่ด้านขวามือนะครับ (จำกัดการใช้งานฟรี 30 ชั่วโมงต่อ 1 สัปดาห์ โดยโน้ตบุ้คนี้รันและเมื่อเซ็ตค่า GPU = On และ EPOCHS = 5 จะใช้เวลารันราวๆ 7-8 ชั่วโมงครับ"},{"metadata":{"trusted":true},"cell_type":"code","source":"EPOCHS = 0 # แก้จำนวน Epochs ที่จะเทรน โดยค่าสูงสุดที่สามารถรันได้ใน Kaggle Notebook (จำกัดชั่วโมงในการรันไม่เกิน 9 ชั่วโมง) คือ EPOCHS = 5","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport pydicom\nimport os\nimport matplotlib.pyplot as plt\nimport collections\nfrom tqdm import tqdm_notebook as tqdm\nfrom datetime import datetime\n\nfrom math import ceil, floor, log\nimport cv2\n\nimport tensorflow as tf\nimport keras\n\nimport sys\n\n# from keras_applications.resnet import ResNet50\nfrom keras_applications.inception_v3 import InceptionV3\n\nfrom sklearn.model_selection import ShuffleSplit\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"ก่อนอื่นกำหนด path ของ train และ test data ครับ"},{"metadata":{"trusted":true},"cell_type":"code","source":"!ls ../input/rsna-intracranial-hemorrhage-detection/rsna-intracranial-hemorrhage-detection","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_images_dir = '../input/rsna-intracranial-hemorrhage-detection/rsna-intracranial-hemorrhage-detection/stage_2_test/'\ntrain_images_dir = '../input/rsna-intracranial-hemorrhage-detection/rsna-intracranial-hemorrhage-detection/stage_2_train/'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!ls {train_images_dir} | wc # นับจำนวนรูปใน training data","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 1. Preprocessing (brain + subudral + soft tissue)\nIdea Credits: [Ryan Epp](https://www.kaggle.com/reppic/gradient-sigmoid-windowing), [David Tang](https://www.kaggle.com/dcstang/see-like-a-radiologist-with-systematic-windowing), [Marco](https://www.kaggle.com/marcovasquez/basic-eda-data-visualization), [Nanashi](https://www.kaggle.com/jesucristo/rsna-introduction-eda-models), [Richard McKinley](https://www.kaggle.com/omission/eda-view-dicom-images-with-correct-windowing)), [appian42's repo](https://github.com/appian42/kaggle-rsna-intracranial-hemorrhage/) (windowing), [Jeremy's kernel](https://www.kaggle.com/jhoward/cleaning-the-data-for-rapid-prototyping-fastai) (cleaning)\n\nในโค้ดข้างล่างเรานิยามฟังก์ชัน `window_image` เพื่อที่จะดึงข้อมูลเฉพาะ window ที่ต้องการ ส่วนฟังก์ชัน `bsb_window` จะทำการเรียก `window_image` 3 ครั้ง เพื่อดึงข้อมูลใน brain window, subdural window และ soft-tissue window ตามลำดับ และ normalize ข้อมูลให้อยู่ในช่วง [0,255] เช่นเดียวกับ format ของรูป 3-channel ปกติ"},{"metadata":{"trusted":true},"cell_type":"code","source":"def window_image(dcm, window_center, window_width):\n    \n    img = dcm.pixel_array * dcm.RescaleSlope + dcm.RescaleIntercept\n    img_min = window_center - window_width // 2\n    img_max = window_center + window_width // 2\n    img = np.clip(img, img_min, img_max)\n\n    return img\n\ndef bsb_window(dcm):\n    brain_img = window_image(dcm, 40, 80)\n    subdural_img = window_image(dcm, 80, 200)\n    soft_img = window_image(dcm, 40, 380)\n    \n    brain_img = (brain_img - 0) / 80\n    subdural_img = (subdural_img - (-20)) / 200\n    soft_img = (soft_img - (-150)) / 380 # soft-tissue window\n    bsb_img = np.array([brain_img, subdural_img, soft_img]).transpose(1,2,0)\n\n    return bsb_img","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"ซึ่งเมื่อเราเรียกฟังก์ชัน `bsb_window` ด้วยไฟล์ dicom เราก็จะได้ 3-dim (กว้าง, ยาว, 3) numpy array กลับมาครับ\n\nฟังก์ชัน `_read` นั้นได้ automate กระบวนการที่จำเป็นทั้งหมดไว้ครับโดย \n\n* อ่าน dicom จากชื่อ path ของไฟล์ที่ต้องการ\n* แปลง dicom เป็นรูปภาพโดยเรียก `bsb_window` และในกรณีที่ไม่มีรูปภาพที่ต้องการก็จะส่ง zero numpy array กลับมาแทน\n* เปลี่ยนขนาดของรูปภาพให้เล็กลงเพื่อให้กระบวนการฝึกสอนโมเดลเร็วขึ้น (ค่า default คือ 256x256 กว้างและยาวตามลำดับ)"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"def _read(path, desired_size=(256, 256)):\n    \"\"\"Will be used in DataGenerator\"\"\"\n    \n    dcm = pydicom.dcmread(path)\n    \n    try:\n        img = bsb_window(dcm)\n    except:\n        img = np.zeros(desired_size)\n    \n    \n    img = cv2.resize(img, desired_size[:2], interpolation=cv2.INTER_LINEAR)\n    \n    return img\n\n# ทดสอบโชว์รูปใน 3 windows\n# ลองเปลี่ยนชื่อไฟล์เหล่านี้ดู: ID_2669954a7, ID_5c8b5d701, ID_52c9913b1\n\nplt.imshow(\n    _read(train_images_dir+'ID_5c8b5d701'+'.dcm', (256, 256)), cmap=plt.cm.bone\n);\n\n# ในรูปนี้มีปรากฏการณ์เลือดคั่งในสมอง 3 รูปแบบด้วยกัน เพื่อนๆ มองออกไหมครับ ?\n#                                     ID  Label\n# 4045566          ID_5c8b5d701_epidural      0\n# 4045567  ID_5c8b5d701_intraparenchymal      1\n# 4045568  ID_5c8b5d701_intraventricular      0\n# 4045569      ID_5c8b5d701_subarachnoid      1\n# 4045570          ID_5c8b5d701_subdural      1\n# 4045571               ID_5c8b5d701_any      1","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 2. Data generators\n\nสร้าง DataGenerator Object จาก class `keras.utils.Sequence` ซึ่งเรื่อง DataGenerator ที่มีประโยชน์มากๆ ในการใช้งานจริงเวลาเรามีข้อมูลปริมาณมหาศาลในมือนี้ ได้ใช้หลายรอบใน workshop ของ ThaiKeras ที่ผ่านมา เดี๋ยวเราจะมีบทความ Series ที่อธิบายเรื่อง Keras อย่างละเอียดในปีนี้ ขอเพื่อนๆ อดใจรอครับ :D\n\nณ ตอนนี้ เพื่อนๆ ทำความเข้าใจเบื้องต้นก่อนว่าการสร้าง DataGenerator จะเป็นการบอกให้ Keras โหลดและจัดการข้อมูลได้ทีละ batch แทนที่จะต้องโหลดทีเดียวทั้ง Dataset ซึ่งไม่สามารถทำได้ใน RAM ที่มีน้อยนิดใน machine ทั่วๆ ไปครับ\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"class DataGenerator(keras.utils.Sequence):\n\n    def __init__(self, list_IDs, labels=None, batch_size=1, img_size=(512, 512, 1), \n                 img_dir=train_images_dir, *args, **kwargs):\n\n        self.list_IDs = list_IDs\n        self.labels = labels\n        self.batch_size = batch_size\n        self.img_size = img_size\n        self.img_dir = img_dir\n        self.on_epoch_end()\n\n    def __len__(self):\n        return int(ceil(len(self.indices) / self.batch_size))\n\n    def __getitem__(self, index):\n        indices = self.indices[index*self.batch_size:(index+1)*self.batch_size]\n        list_IDs_temp = [self.list_IDs[k] for k in indices]\n        \n        if self.labels is not None:\n            X, Y = self.__data_generation(list_IDs_temp)\n            return X, Y\n        else:\n            X = self.__data_generation(list_IDs_temp)\n            return X\n        \n    def on_epoch_end(self):\n        \n        \n        if self.labels is not None: # for training phase we undersample and shuffle\n            # keep probability of any=0 and any=1\n            keep_prob = self.labels.iloc[:, 0].map({0: 0.35, 1: 0.5})\n            keep = (keep_prob > np.random.rand(len(keep_prob)))\n            self.indices = np.arange(len(self.list_IDs))[keep]\n            np.random.shuffle(self.indices)\n        else:\n            self.indices = np.arange(len(self.list_IDs))\n\n    def __data_generation(self, list_IDs_temp):\n        X = np.empty((self.batch_size, *self.img_size))\n        \n        if self.labels is not None: # training phase\n            Y = np.empty((self.batch_size, 6), dtype=np.float32)\n        \n            for i, ID in enumerate(list_IDs_temp):\n                X[i,] = _read(self.img_dir+ID+\".dcm\", self.img_size)\n                Y[i,] = self.labels.loc[ID].values\n        \n            return X, Y\n        \n        else: # test phase\n            for i, ID in enumerate(list_IDs_temp):\n                X[i,] = _read(self.img_dir+ID+\".dcm\", self.img_size)\n            \n            return X","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 3. Metric and Model\n\nในหัวข้อนี้เป็นตัวอย่างการกำหนด loss/metric (3a.) และโมเดล (3b.) โดย loss และ metric นั้นถูกกำหนดจากสมาคม RSNA ที่เป็นจัดเตรียมข้อมูล และกำหนดกติกาการแข่งขัน\n\n### 3a. loss function and metric"},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras import backend as K\n\ndef weighted_log_loss(y_true, y_pred):\n    \"\"\"\n    Can be used as the loss function in model.compile()\n    ---------------------------------------------------\n    \"\"\"\n    \n    class_weights = np.array([2., 1., 1., 1., 1., 1.])\n    \n    eps = K.epsilon()\n    \n    y_pred = K.clip(y_pred, eps, 1.0-eps)\n\n    out = -(         y_true  * K.log(      y_pred) * class_weights\n            + (1.0 - y_true) * K.log(1.0 - y_pred) * class_weights)\n    \n    return K.mean(out, axis=-1)\n\n\ndef _normalized_weighted_average(arr, weights=None):\n    \"\"\"\n    A simple Keras implementation that mimics that of \n    numpy.average(), specifically for this competition\n    \"\"\"\n    \n    if weights is not None:\n        scl = K.sum(weights)\n        weights = K.expand_dims(weights, axis=1)\n        return K.sum(K.dot(arr, weights), axis=1) / scl\n    return K.mean(arr, axis=1)\n\n\ndef weighted_loss(y_true, y_pred):\n    \"\"\"\n    Will be used as the metric in model.compile()\n    ---------------------------------------------\n    \n    Similar to the custom loss function 'weighted_log_loss()' above\n    but with normalized weights, which should be very similar \n    to the official competition metric:\n        https://www.kaggle.com/kambarakun/lb-probe-weights-n-of-positives-scoring\n    and hence:\n        sklearn.metrics.log_loss with sample weights\n    \"\"\"\n    \n    class_weights = K.variable([2., 1., 1., 1., 1., 1.])\n    \n    eps = K.epsilon()\n    \n    y_pred = K.clip(y_pred, eps, 1.0-eps)\n\n    loss = -(        y_true  * K.log(      y_pred)\n            + (1.0 - y_true) * K.log(1.0 - y_pred))\n    \n    loss_samples = _normalized_weighted_average(loss, class_weights)\n    \n    return K.mean(loss_samples)\n\n\ndef weighted_log_loss_metric(trues, preds):\n    \"\"\"\n    Will be used to calculate the log loss \n    of the validation set in PredictionCheckpoint()\n    ------------------------------------------\n    \"\"\"\n    class_weights = [2., 1., 1., 1., 1., 1.]\n    \n    epsilon = 1e-7\n    \n    preds = np.clip(preds, epsilon, 1-epsilon)\n    loss = trues * np.log(preds) + (1 - trues) * np.log(1 - preds)\n    loss_samples = np.average(loss, axis=1, weights=class_weights)\n\n    return - loss_samples.mean()\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 3b. Model\n\nในการสร้างโมเดล Keras เพื่อเรียนรู้และทำนายข้อมูลนี้ จะคล้ายกับ Workshops อื่นๆ ที่ผ่านมาครับ (https://thaikeras.com/2018/setup-kaggle-workshop/) \nโดยในอนาคต (คาดว่าภายในปี 2020) เราจะมี Series บทความที่สอนการสร้างโมเดล Keras อย่างละเอียด step-by-step ทยอยออกมาครับ\n\nสำหรับภาพรวมคือเราสร้างโมเดล image classifier มาตรฐานที่รับ input เป็นรูป (ซึ่งจัดการโดย DataGenerator) และ Output เป็นความน่าจะเป็นที่จะมีเลือดคั่ง 6 ค่าตามที่อธิบายไว้ใน[บทความก่อน](http://bit.ly/thaikeras-rsna)ครับ "},{"metadata":{"trusted":true},"cell_type":"code","source":"\nclass PredictionCheckpoint(keras.callbacks.Callback):\n    \n    def __init__(self, test_df, valid_df, \n                 test_images_dir=test_images_dir, \n                 valid_images_dir=train_images_dir, \n                 batch_size=32, input_size=(224, 224, 3)):\n        \n        self.test_df = test_df\n        self.valid_df = valid_df\n        self.test_images_dir = test_images_dir\n        self.valid_images_dir = valid_images_dir\n        self.batch_size = batch_size\n        self.input_size = input_size\n        \n    def on_train_begin(self, logs={}):\n        self.test_predictions = []\n        self.valid_predictions = []\n        \n    def on_epoch_end(self,batch, logs={}):\n        print('ทำนาย test data...')\n        self.test_predictions.append(\n            self.model.predict_generator(\n                DataGenerator(self.test_df.index, None, self.batch_size, self.input_size, self.test_images_dir), verbose=1)[:len(self.test_df)])\n\nclass MyDeepModel:\n    \n    def __init__(self, engine, input_dims, batch_size=5, num_epochs=4, learning_rate=1e-3, \n                 decay_rate=1.0, decay_steps=1, weights=\"imagenet\", verbose=1):\n        \n        self.engine = engine\n        self.input_dims = input_dims\n        self.batch_size = batch_size\n        self.num_epochs = num_epochs\n        self.learning_rate = learning_rate\n        self.decay_rate = decay_rate\n        self.decay_steps = decay_steps\n        self.weights = weights\n        self.verbose = verbose\n        self._build()\n\n    def _build(self):\n        \n        \n        engine = self.engine(include_top=False, weights=self.weights, input_shape=self.input_dims,\n                             backend = keras.backend, layers = keras.layers,\n                             models = keras.models, utils = keras.utils)\n        \n        x = keras.layers.GlobalAveragePooling2D(name='avg_pool')(engine.output)\n        out = keras.layers.Dense(6, activation=\"sigmoid\", name='dense_output')(x)\n\n        self.model = keras.models.Model(inputs=engine.input, outputs=out)\n\n        self.model.compile(loss=\"binary_crossentropy\", optimizer=keras.optimizers.Adam(), metrics=[weighted_loss])\n    \n\n    def fit_and_predict(self, train_df, valid_df, test_df):\n        \n        # callbacks\n        pred_history = PredictionCheckpoint(test_df, valid_df, input_size=self.input_dims)\n        scheduler = keras.callbacks.LearningRateScheduler(lambda epoch: self.learning_rate * pow(self.decay_rate, floor(epoch / self.decay_steps)))\n        \n        self.model.fit_generator(\n            DataGenerator(\n                train_df.index, \n                train_df, \n                self.batch_size, \n                self.input_dims, \n                train_images_dir\n            ),\n            epochs=self.num_epochs,\n            verbose=self.verbose,\n            use_multiprocessing=True,\n            workers=4,\n            callbacks=[pred_history, scheduler]\n        )\n        \n        return pred_history\n    \n    def save(self, path):\n        self.model.save_weights(path)\n    \n    def load(self, path):\n        self.model.load_weights(path)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 4. Read csv files\n\nอ่านไฟล์ข้อมูล meta-data ของ DICOM ที่สมาคม RSNA และ Kaggle จัดเตรียมไว้ให้ ซึ่งเราจะคัดเฉพาะข้อมูลที่เราต้องการ ซึ่งในที่้คือ ชื่อรูป และ label ว่ามีเลือดคั่งในสมองรูปแบบไหนบ้างเท่านั้น โดยข้อมูลอื่นเช่น ประวัติคนไข้ ต่างๆ จะยังไม่ได้เอามาใช้ ในโมเดลพื้นฐานนี้ครับ\n\nหลังจากนั้นก็จะจัดเรียงให้อยู่ในรูปตารางเพื่อเก็บใน pandas dataframe เพื่อความง่ายในการใช้งาน"},{"metadata":{"trusted":true},"cell_type":"code","source":"def read_testset(filename=\"../input/rsna-intracranial-hemorrhage-detection/rsna-intracranial-hemorrhage-detection/stage_2_sample_submission.csv\"):\n    df = pd.read_csv(filename)\n    df[\"Image\"] = df[\"ID\"].str.slice(stop=12)\n    df[\"Diagnosis\"] = df[\"ID\"].str.slice(start=13)\n    \n    df = df.loc[:, [\"Label\", \"Diagnosis\", \"Image\"]]\n    df = df.set_index(['Image', 'Diagnosis']).unstack(level=-1)\n    \n    return df\n\ndef read_trainset(filename=\"../input/rsna-intracranial-hemorrhage-detection/rsna-intracranial-hemorrhage-detection/stage_2_train.csv\"):\n    df = pd.read_csv(filename)\n    df[\"Image\"] = df[\"ID\"].str.slice(stop=12)\n    df[\"Diagnosis\"] = df[\"ID\"].str.slice(start=13)\n    \n    # เอารูปภาพซ้ำออก\n    duplicates_to_remove = [56340, 56341, 56342, 56343, 56344, 56345, 56346, 56347, 56348, 56349, 56350, 56351, \n                            1171824, 1171825, 1171826, 1171827, 1171828, 1171829, 1171830, 1171831, 1171832, 1171833, \n                            1171834, 1171835, 3705306, 3705307, 3705308, 3705309, 3705310, 3705311, 3705312, 3705313, \n                            3705314, 3705315, 3705316, 3705317, 3842472, 3842473, 3842474, 3842475, 3842476, 3842477, \n                            3842478, 3842479, 3842480, 3842481, 3842482, 3842483]\n    \n    df = df.drop(index=duplicates_to_remove)\n    df = df.reset_index(drop=True)\n    \n    df = df.loc[:, [\"Label\", \"Diagnosis\", \"Image\"]]\n    df = df.set_index(['Image', 'Diagnosis']).unstack(level=-1)\n    \n    return df\n\ndf = read_trainset()\ntest_df = read_testset()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# ตัวอย่าง meta-data ของ training set ที่เราต้องการ\ndf.head(3)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# ตัวอย่าง meta-data ของ test set ซึ่งเราจะเปลี่ยนค่า 0.5 ในตาราง ให้เป็นคำนายเลือดคั่งในสมองจากโมเดลของเราภายหลัง\ntest_df.head(3)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# 5. Train model and predict\n\nKeras นั้นออกแบบให้การฝึกสอนโมเดลสามารถทำได้ภายในบรรทัดเดียวครับ ในที่นี้เราใช้ฟังก์ชัน `fit_and_predict` ที่เราได้เขียนไว้ในหัวข้อ 4 เพื่อเรียนรู้จาก training data และทำนาย test data\n\nโดยเราจะแบ่ง training data ออกเป็นสองกลุ่มคือกลุ่มฝึกสอนจริงๆ (`training_idx`) และกลุ่มที่ใช้ตรวจสอบความแม่นยำ (`valid_idx`) ด้วยฟังก์ชัน `ShuffleSplit` ซึ่งเป็นการแบ่งกลุ่มแบบสุ่มครับ (ดูรายละเอียด https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.ShuffleSplit.html)"},{"metadata":{"trusted":true},"cell_type":"code","source":"# train set (00%) and validation set (10%)\nss = ShuffleSplit(n_splits=10, test_size=0.1, random_state=42).split(df.index)\n\n# lets go for the first fold only\ntrain_idx, valid_idx = next(ss)\n# EPOCHS=0\nif EPOCHS == 0: # just for illustration on CPU\n    train_idx = train_idx[:100]\n    valid_idx = valid_idx[:100]\n    EPOCHS = 1\n\n# obtain model\nmodel = MyDeepModel(engine=InceptionV3, input_dims=(256, 256, 3), batch_size=32, learning_rate=5e-4,\n                    num_epochs=EPOCHS, decay_rate=0.8, decay_steps=1, weights=\"imagenet\", verbose=1)\n\n# obtain test + validation predictions (history.test_predictions, history.valid_predictions)\nhistory = model.fit_and_predict(df.iloc[train_idx], df.iloc[valid_idx], test_df)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"สุดท้ายเราจะนำคำนายของ test data เพื่อทำให้อยู่ในรูป dataframe ที่ผู้จัดการแข่งขันกำหนดครับ"},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df = read_testset()\n\nprint(history.test_predictions)\n\ntest_df.iloc[:, :] = np.mean(history.test_predictions, axis=0)\n\ntest_df = test_df.stack().reset_index()\n\ntest_df.insert(loc=0, column='ID', value=test_df['Image'].astype(str) + \"_\" + test_df['Diagnosis'])\n\ntest_df = test_df.drop([\"Image\", \"Diagnosis\"], axis=1)\n\ntest_df.to_csv('submission.csv', index=False)\ntest_df.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"โมเดลนี้เป็นเพียงโมเดลพื้นฐานเท่านั้น และยังสามารถปรับปรุงให้แม่นยำได้อีกหลายทิศทางครับ โดยเพื่อนๆ สามารถดูไอเดียเพิ่มเติมอีกมากมายได้ที่นี้ครับ https://www.kaggle.com/c/rsna-intracranial-hemorrhage-detection/discussion"},{"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":1}