{"cells":[{"metadata":{"id":"MySRJxnbmVr-"},"cell_type":"markdown","source":"# **Pneumonia Detection**","execution_count":null},{"metadata":{"id":"z0iGewD2FDoO","trusted":true},"cell_type":"code","source":"#!pip install tensorflow==2.0.0\n#!pip install keras==2.3.1","execution_count":null,"outputs":[]},{"metadata":{"id":"m-n_fZEMyddM","outputId":"5397e8c1-57b4-4125-b43d-a0d9263562f9","trusted":true},"cell_type":"code","source":"import pandas as pd \nimport tensorflow as tf\nimport numpy as np\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport tqdm\nfrom tqdm import tqdm_notebook\nfrom matplotlib.patches import Rectangle\nimport seaborn as sns\n!pip install pydicom\nimport pydicom as dcm\n%matplotlib inline \nIS_LOCAL = False\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 5GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{"id":"Zmkh_kre12-c"},"cell_type":"markdown","source":"# **Load Datasets**","execution_count":null},{"metadata":{"id":"aZMo4BIBz6BZ","trusted":true},"cell_type":"code","source":"detailclassinfo_df = pd.read_csv('../input/rsna-pneumonia-detection-challenge/stage_2_detailed_class_info.csv')\ntrainlabels_df = pd.read_csv('../input/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv')","execution_count":null,"outputs":[]},{"metadata":{"id":"qqifXEBw4_ov","outputId":"20b617f7-fa9f-4cc7-8238-da75f7906165","trusted":true},"cell_type":"code","source":"detailclassinfo_df.head()","execution_count":null,"outputs":[]},{"metadata":{"id":"Jx5gfMzZ5Gsq","outputId":"49c1eaed-496f-4cb6-c6c2-3ca30b627b1c","trusted":true},"cell_type":"code","source":"trainlabels_df.head()","execution_count":null,"outputs":[]},{"metadata":{"id":"zq6WCLu4JpX-","outputId":"d44c1350-c1dd-462b-aab1-338ce34ac6d2","trusted":true},"cell_type":"code","source":"print('Shape of Detailed Class Information: {}'.format(detailclassinfo_df.shape))\nprint('Shape of Train Labels: {}'.format(trainlabels_df.shape))","execution_count":null,"outputs":[]},{"metadata":{"id":"iGu3Hn5l6QjJ"},"cell_type":"markdown","source":"# **Exploratory Data Analysis**","execution_count":null},{"metadata":{"id":"QTF-3qZIFLld"},"cell_type":"markdown","source":"**Data Preprocessing**","execution_count":null},{"metadata":{"id":"pmyKRbXYLxU2"},"cell_type":"markdown","source":"Merging Datasets","execution_count":null},{"metadata":{"id":"2pXgsLcNL0U9","outputId":"c99e30d2-1aab-42c2-82c8-3fcda93719d1","trusted":true},"cell_type":"code","source":"merge_train_df = trainlabels_df.merge(detailclassinfo_df, left_on='patientId', right_on='patientId', how='inner')\nmerge_train_df.sample(5)","execution_count":null,"outputs":[]},{"metadata":{"id":"fAJE_cAuBwT2","outputId":"66a572c3-a0ea-4bd5-f595-58a98987a62b","trusted":true},"cell_type":"code","source":"merge_train_df = merge_train_df. drop_duplicates()\nmerge_train_df.shape","execution_count":null,"outputs":[]},{"metadata":{"id":"1bB4NZ4nAhMd"},"cell_type":"markdown","source":"**Summary on the values, types and null values**","execution_count":null},{"metadata":{"id":"P1-U8E9OCPJT","outputId":"a44922f5-e8d2-4103-9283-53fe6444deb7","trusted":true},"cell_type":"code","source":"merge_train_df.isnull().sum()","execution_count":null,"outputs":[]},{"metadata":{"id":"AY53gArSotTw"},"cell_type":"markdown","source":"**Inference**: Bounding box parameters x, y , width and height are having Null/NaN values","execution_count":null},{"metadata":{"id":"Wisr102TEpdc"},"cell_type":"markdown","source":"**Distribution of classes**","execution_count":null},{"metadata":{"id":"jkvUTynOCxon","outputId":"4a00e096-89c5-444d-e5ea-2da44aa39ef6","trusted":true},"cell_type":"code","source":"detailclassinfo_df.groupby([\"class\"]).count().transpose().style.background_gradient(cmap='Wistia',axis=1)","execution_count":null,"outputs":[]},{"metadata":{"id":"f9CIAyHSCkFf","outputId":"95f26aab-1689-4de1-9565-898cd4bf2eed","trusted":true},"cell_type":"code","source":"merge_train_df.groupby([\"class\"]).count().transpose().style.background_gradient(cmap='Wistia',axis=1)","execution_count":null,"outputs":[]},{"metadata":{"id":"gx0IgwHhEt2a"},"cell_type":"markdown","source":"**Inference**: 9555 patients are in class of 'Lung Opacity', 11821 are in 'No Lung Opacity / Not Normal' and 8851 are in Normal category.","execution_count":null},{"metadata":{"id":"UNseE0ZDDS5t","outputId":"40a517f0-264a-461a-f158-2f7edf7204b2","trusted":true},"cell_type":"code","source":"f, ax = plt.subplots(1,1, figsize=(6,4))\ntotal = float(len(merge_train_df))\nsns.countplot(merge_train_df['class'],order = merge_train_df['class'].value_counts().index, palette='Set1')\nfor p in ax.patches:\n    height = p.get_height()\n    ax.text(p.get_x()+p.get_width()/2.,\n            height + 3,\n            '{:1.2f}%'.format(100*height/total),\n            ha=\"center\") \nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"id":"y35jKMkzDmtu"},"cell_type":"markdown","source":"**Insight**: The classes \"No Lung Opacity / Not Normal\", \"Lung Opacity\" and \"Normal\" are in the proportions of 39%, 32% and 29% respectively","execution_count":null},{"metadata":{"id":"_9vGuyyuMRoe"},"cell_type":"markdown","source":"**Distribution of Labels (Positive and Negative)**","execution_count":null},{"metadata":{"id":"OgZJkn78pyEl","outputId":"e3c7967b-9c9f-4d28-f301-baf29209452c","trusted":true},"cell_type":"code","source":"f, ax = plt.subplots(1,1, figsize=(6,4))\nsns.countplot(x = \"Target\", data= merge_train_df, hue=\"class\", palette='Set1')","execution_count":null,"outputs":[]},{"metadata":{"id":"DABJrBo1HuoY","outputId":"8d119f36-af78-46dc-a441-92db7892bfbc","trusted":true},"cell_type":"code","source":"plt.style.use('ggplot')\nf, ax = plt.subplots(1,1, figsize=(6,4))\ntotale = float(len(merge_train_df))\npd.value_counts(merge_train_df[\"Target\"]).plot(kind='bar', position=0.5, rot=0)\nfor p in ax.patches:\n    height = p.get_height()\n    ax.text(p.get_x()+p.get_width()/2.,\n            height + 3,\n            '{:1.2f}%'.format(100*height/total),\n            ha=\"center\") \nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"id":"6gK_kdnFMCcI","outputId":"4f2a26f2-e340-402c-bfeb-9aa2c278aae2","trusted":true},"cell_type":"code","source":"# Number of positive targets\nprint(round((9555 / (9555 + 20672)) * 100, 2), '% of the patients are positive')","execution_count":null,"outputs":[]},{"metadata":{"id":"GgSOvQsY_uUk","outputId":"fc1065d0-ed86-4b10-9da3-cdbbfab36ecf","trusted":true},"cell_type":"code","source":"merge_train_df.groupby([\"Target\"]).count()","execution_count":null,"outputs":[]},{"metadata":{"id":"5LY1cWbW0JQP"},"cell_type":"markdown","source":"**Insight**: \n\n1. Patients with Target = 0 (no pathology detected) are either of class: Normal or class: No Lung Opacity / Not Normal don't have bounding box coordinates (x, y, w, h) parameters.\n2. **31.61%** of the patients belongs to **Lung Opacity** class.","execution_count":null},{"metadata":{"id":"EWY_3--iGwtI"},"cell_type":"markdown","source":"**Handling Null using KNN Imputation method**","execution_count":null},{"metadata":{"id":"hoqlpHv9G2OT","outputId":"ca1f9008-0c5c-4fbc-e3af-cffd310df4ea","trusted":true},"cell_type":"code","source":"merge_bb_df = merge_train_df.drop(columns = [\"patientId\", \"class\"])\n\n## Impute NaN with KNN mean\nfrom sklearn.impute import KNNImputer\nimputer = KNNImputer(n_neighbors=2)\nmerge_bb_imputed_df = imputer.fit_transform(merge_bb_df)\n\n## Converted array to dataframe\nmerge_bb_final = pd.DataFrame(data=merge_bb_imputed_df, columns=[\"x\", \"y\", \"width\", \"height\", \"Target\"])\nmerge_bb_final[\"Target\"] = merge_bb_final[\"Target\"].astype('int64')\nmerge_bb_final.head(5)","execution_count":null,"outputs":[]},{"metadata":{"id":"HoWieGCbsJ73","outputId":"f7f89646-eea6-49d9-ff58-f9addc961dd0","trusted":true},"cell_type":"code","source":"# Seggregating Opacity data from imputed data\nopacity_bb_final = merge_bb_final[(merge_bb_final['Target']== 1)]\nopacity_bb_final.head()","execution_count":null,"outputs":[]},{"metadata":{"id":"8yA4SYwOsks5"},"cell_type":"markdown","source":"**Checking Data Distribution**","execution_count":null},{"metadata":{"id":"6ctzIDfQM2vx","outputId":"90b2278c-f566-4e05-cc4a-c65529f2e125","trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(2,2,figsize=(12,12))\nsns.distplot(opacity_bb_final['x'],kde=True,bins=50, color=\"grey\", ax=ax[0,0])\nsns.distplot(opacity_bb_final['y'],kde=True,bins=50, color=\"orange\", ax=ax[0,1])\nsns.distplot(opacity_bb_final['width'],kde=True,bins=50, color=\"blue\", ax=ax[1,0])\nsns.distplot(opacity_bb_final['height'],kde=True,bins=50, color=\"brown\", ax=ax[1,1])\nlocs, labels = plt.xticks()\nplt.tick_params(axis='both', which='major', labelsize=12)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"id":"H5sNSDHMstca"},"cell_type":"markdown","source":"**Finding Correlation**","execution_count":null},{"metadata":{"id":"56wYNO3XsxMh","outputId":"5b3f067d-3829-4da1-99b6-e2169bd85712","trusted":true},"cell_type":"code","source":"# Correlation\nplt.figure(figsize=(7,5))\nsns.heatmap(opacity_bb_final.corr(),linewidths=0.1,vmax=1.0, \n            square=True,  linecolor='white', annot=True)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"id":"bG4ejZfuTVj1","outputId":"7279e455-5195-469c-f705-bf544b0b0c34","trusted":true},"cell_type":"code","source":"sns.jointplot(x = 'width', y = 'height', data = opacity_bb_final, kind= 'reg')","execution_count":null,"outputs":[]},{"metadata":{"id":"zwyDMKchSnYs"},"cell_type":"markdown","source":"**Inference**: There is a fine correlation between width and height variables.","execution_count":null},{"metadata":{"id":"PsAolXBEt1k8"},"cell_type":"markdown","source":"**Lung Opacity Representation** (2000 samples)","execution_count":null},{"metadata":{"id":"qvNhmk3vOx3n","outputId":"f452624d-bcc1-4757-e153-e65401f5eb8c","trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(1,1,figsize=(7,7))\ntarget_sample = opacity_bb_final.sample(2000)\ntarget_sample['xc'] = target_sample['x'] + target_sample['width'] / 2\ntarget_sample['yc'] = target_sample['y'] + target_sample['height'] / 2\nplt.title(\"Lung Opacity representation for 2000 data samples\")\ntarget_sample.plot.scatter(x='xc', y='yc', xlim=(0,1024), ylim=(0,1024), ax=ax, alpha=0.8, marker=\".\", color=\"black\")\nfor i, crt_sample in target_sample.iterrows():\n    ax.add_patch(Rectangle(xy=(crt_sample['x'], crt_sample['y']),\n                width=crt_sample['width'],height=crt_sample['height'],alpha=3.5e-3, color=\"green\"))\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"id":"_cNIgE4NuHa7"},"cell_type":"markdown","source":"**Split Data sets** (Train= 80%, Test= 20%)","execution_count":null},{"metadata":{"id":"PU9b-0z9t-WS","outputId":"1d65ce21-c5ac-4e3d-9ee9-5842d53eb61e","trusted":true},"cell_type":"code","source":"tmp = merge_train_df[[\"patientId\", \"class\"]]\ntrain_labels_data = pd.merge(tmp, merge_bb_final, how= 'inner', left_index=True, right_index=True)\ntrain_labels_data.head()","execution_count":null,"outputs":[]},{"metadata":{"id":"ZBeLNWu8ucbK","outputId":"bd9f8412-991a-48df-883f-71156eb309f7","trusted":true},"cell_type":"code","source":"X = train_labels_data.drop(columns= [\"patientId\", \"Target\", \"class\"])\ny = train_labels_data[[\"Target\"]]\nX.shape, y.shape","execution_count":null,"outputs":[]},{"metadata":{"id":"jQMp7_JIuilA","outputId":"77a7d407-6df7-465a-d1e3-c941eccd9f3d","trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix, classification_report, accuracy_score, average_precision_score\nfrom sklearn.metrics import roc_auc_score, auc, plot_confusion_matrix, plot_roc_curve, roc_curve\n\nX_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state =7)\nprint('Training data shape:', (X_train.shape, y_train.shape))\nprint('Test data shape:', (X_test.shape, y_test.shape))","execution_count":null,"outputs":[]},{"metadata":{"id":"ciQG85dyvmol"},"cell_type":"markdown","source":"# **Classification using SVC**","execution_count":null},{"metadata":{"id":"5Z4Gg1EUvu8U","outputId":"9f9a52fb-405b-4f77-a170-d206bd0c4d4c","trusted":true},"cell_type":"code","source":"from sklearn.svm import SVC\nsvm_model = SVC(kernel = 'rbf', C = 1.0).fit(X_train, y_train)\ny_preds = svm_model.predict(X_test)\nprint('Accuracy score:', accuracy_score(y_test, y_preds))\nprint('Average Precision Score:', average_precision_score(y_test, y_preds))","execution_count":null,"outputs":[]},{"metadata":{"id":"v-czYpm6xljs","outputId":"9a2d9e46-17f9-4c98-9b19-820be279aec3","trusted":true},"cell_type":"code","source":"print('Score: \"{:.2%}\"'.format(svm_model.score(X, y)))","execution_count":null,"outputs":[]},{"metadata":{"id":"VyJD5BL9wPFK","outputId":"e452f8dc-d668-46e1-b2d8-3a4b89cdbd95","trusted":true},"cell_type":"code","source":"print('Classification report: \\n')\nprint(classification_report(y_test, y_preds))","execution_count":null,"outputs":[]},{"metadata":{"id":"fFq15f6CwWxZ","outputId":"94098f0a-0df8-446a-ef2b-65ed1f4ef11f","trusted":true},"cell_type":"code","source":"plot_confusion_matrix(svm_model, X_test, y_test, values_format='d', cmap ='plasma')\nplt.title('Confusion Matrix')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"id":"257rbTmdw5RK","outputId":"60df56bb-0457-47f1-9019-e37ff0a323ac","trusted":true},"cell_type":"code","source":"plot_roc_curve(svm_model, X_test, y_test, name= 'ROC-TP/FP')","execution_count":null,"outputs":[]},{"metadata":{"id":"u5-9Ldjo1mQU","outputId":"b6f353ba-0a83-45da-a968-ed9412dc2ba1","trusted":true},"cell_type":"code","source":"fpr, tpr, thresholds = roc_curve(y_test, y_preds)\n\n# calculate AUC\nauc = roc_auc_score(y_test, y_preds)\nprint('AUC: %.3f' % auc)","execution_count":null,"outputs":[]},{"metadata":{"id":"VlmU_ImmOQ7b"},"cell_type":"markdown","source":"# **Using Pydicom to analyze .dcm images**\n\nMedical images are stored in DICOM file format (.DCM). \nIn python pydicom library is used to manipulate these kind of files.","execution_count":null},{"metadata":{"id":"NaUbxSk8PHGz"},"cell_type":"markdown","source":"**Load DICOM data**","execution_count":null},{"metadata":{"id":"UFbJKw4jTE9Q","outputId":"e448da3f-c363-4ed1-f617-181319bbf4c6","trusted":true},"cell_type":"code","source":"patientId = trainlabels_df['patientId'][0]\ndcm_file = '../input/rsna-pneumonia-detection-challenge/stage_2_train_images/%s.dcm' % patientId\ndcm_data = dcm.read_file(dcm_file)\nprint(dcm_data)","execution_count":null,"outputs":[]},{"metadata":{"id":"GN7TDz3XQDV7","outputId":"1f0516d7-1ae0-44e6-e1c1-0a5b72a5228a","trusted":true},"cell_type":"code","source":"im = dcm_data.pixel_array\nprint(type(im))\nprint(im.dtype)\nprint(im.shape)","execution_count":null,"outputs":[]},{"metadata":{"id":"YKERdag5TmVs"},"cell_type":"markdown","source":"**Visualize sample image**","execution_count":null},{"metadata":{"id":"j2kVhW2ATLRf","outputId":"774fc608-cdae-4798-a7b4-bee68711ec68","trusted":true},"cell_type":"code","source":"import pylab\npylab.imshow(im, cmap=pylab.cm.gist_gray)\npylab.axis('off')\n","execution_count":null,"outputs":[]},{"metadata":{"id":"6_UbiiE5YrSh"},"cell_type":"markdown","source":"# **Plot bounding boxes over Images**","execution_count":null},{"metadata":{"id":"fnlDlQpbUPW8","trusted":true},"cell_type":"code","source":"def 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\n\nparsed = parse_data(trainlabels_df)\n\ndef draw(data):\n    \"\"\"\n    Method to draw single patient with bounding box(es) if present \n\n    \"\"\"\n    # --- Open DICOM file\n    d = dcm.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        rgb = [0, 0, 255] # Just use blue\n        im = overlay_box(im=im, box=box, rgb=rgb, stroke=15)\n\n    plt.imshow(im, cmap=plt.cm.gist_gray)\n    plt.axis('off')\n\ndef overlay_box(im, box, rgb, stroke=2):\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":{"id":"LcTj9yksjEOY"},"cell_type":"markdown","source":"**Visualize Train Labels dataset images**","execution_count":null},{"metadata":{"id":"LHMERVpXYKTo","outputId":"fb459e41-8724-4cfa-bda4-1eccee7c3c3c","trusted":true},"cell_type":"code","source":"plt.style.use('default')\nfig=plt.figure(figsize=(8, 8))\ncolumns = 5; rows = 4\nfor i in range(1, columns*rows +1):\n    fig.add_subplot(rows, columns, i)\n    draw(parsed[trainlabels_df['patientId'].unique()[i]])\n    fig.add_subplot","execution_count":null,"outputs":[]},{"metadata":{"id":"57Wyv_e-epC_"},"cell_type":"markdown","source":"**Visualize Detail Class dataset images**","execution_count":null},{"metadata":{"id":"tvJ1Vi7pdVqc","trusted":true},"cell_type":"code","source":"opacity = detailclassinfo_df \\\n    .loc[detailclassinfo_df['class'] == 'Lung Opacity'] \\\n    .reset_index()","execution_count":null,"outputs":[]},{"metadata":{"id":"wL39cc-Ce_x4","trusted":true},"cell_type":"code","source":"not_normal = detailclassinfo_df \\\n    .loc[detailclassinfo_df['class'] == 'No Lung Opacity / Not Normal'] \\\n    .reset_index()","execution_count":null,"outputs":[]},{"metadata":{"id":"jLrNpFO8fEeA","trusted":true},"cell_type":"code","source":"normal = detailclassinfo_df \\\n    .loc[detailclassinfo_df['class'] == 'Normal'] \\\n    .reset_index()","execution_count":null,"outputs":[]},{"metadata":{"id":"RLmsGM0Qfg-x"},"cell_type":"markdown","source":"**1. Lung Opacity Samples**","execution_count":null},{"metadata":{"id":"YaK2kgxxfXvE","outputId":"55c055d0-c966-4e88-e72e-03d387d9cec0","trusted":true},"cell_type":"code","source":"plt.style.use('default')\nfig=plt.figure(figsize=(6, 6))\ncolumns = 4; rows = 4\nfor i in range(1, columns*rows +1):\n    fig.add_subplot(rows, columns, i)\n    draw(parsed[opacity['patientId'].unique()[i]])","execution_count":null,"outputs":[]},{"metadata":{"id":"WkLil9hNgFCK"},"cell_type":"markdown","source":"**2. Normal Samples**","execution_count":null},{"metadata":{"id":"ftlOUfAnf2g6","outputId":"0f7392d6-cbc9-45b2-e62f-0f91c5f13ec0","trusted":true},"cell_type":"code","source":"plt.style.use('default')\nfig=plt.figure(figsize=(6, 6))\ncolumns = 4; rows = 4\nfor i in range(1, columns*rows +1):\n    fig.add_subplot(rows, columns, i)\n    draw(parsed[normal['patientId'].unique()[i]])","execution_count":null,"outputs":[]},{"metadata":{"id":"RINmpxavghwN"},"cell_type":"markdown","source":"**3. No Lung Opacity/Not Normal Samples**","execution_count":null},{"metadata":{"id":"a6V8rh-0gLyX","outputId":"bf0cd94f-b1f3-4237-8a18-79faa1037fbf","trusted":true},"cell_type":"code","source":"plt.style.use('default')\nfig=plt.figure(figsize=(6, 6))\ncolumns = 4; rows = 4\nfor i in range(1, columns*rows +1):\n    fig.add_subplot(rows, columns, i)\n    draw(parsed[not_normal['patientId'].unique()[i]])","execution_count":null,"outputs":[]},{"metadata":{"id":"0N_KZN1Fg0ms","outputId":"ad1b3f65-e8ba-4521-e69e-db624901b344","trusted":true},"cell_type":"code","source":"fig=plt.figure(figsize=(20, 10))\ncolumns = 3; rows = 1\nfig.add_subplot(rows, columns, 1).set_title(\"Normal\", fontsize=30)\ndraw(parsed[normal['patientId'].unique()[0]])\nfig.add_subplot(rows, columns, 2).set_title(\"Not Normal\", fontsize=30)\n# ax2.set_title(\"Not Normal\", fontsize=30)\ndraw(parsed[not_normal['patientId'].unique()[0]])\nfig.add_subplot(rows, columns, 3).set_title(\"Opacity\", fontsize=30)\n# ax3.set_title(\"Opacity\", fontsize=30)\ndraw(parsed[opacity['patientId'].unique()[0]])","execution_count":null,"outputs":[]},{"metadata":{"id":"IlvxnhaWgNvK"},"cell_type":"markdown","source":"# **Image Segmentation**","execution_count":null},{"metadata":{"id":"2ZN7zvHMjetg"},"cell_type":"markdown","source":"**Image segmentation** creates a pixel-wise mask for each object in the image. This technique gives us a far more granular understanding of the object(s) in the image.","execution_count":null},{"metadata":{"id":"bJHu_ttPjBht"},"cell_type":"markdown","source":"Class generatortransfer:\n\nThe dataset is too large to fit into memory, so we need to create a generator that loads data on the fly.\n\n**I/p**: filenames, batch_size and other parameters.\n\n**O/p**: A random batch of numpy images and numpy masks.","execution_count":null},{"metadata":{"id":"I96jfqOSgtik"},"cell_type":"markdown","source":"# **Model Selection**","execution_count":null},{"metadata":{"id":"iFe9LmuWWg5G","trusted":true},"cell_type":"code","source":"import os\nimport csv\nimport random\nimport pydicom\nimport numpy as np\nimport pandas as pd\nfrom skimage import measure\nfrom skimage.transform import resize\n\nimport tensorflow as tf\nfrom tensorflow import keras","execution_count":null,"outputs":[]},{"metadata":{"id":"7yzNN7s9igEd"},"cell_type":"markdown","source":"**Load opacity locations**","execution_count":null},{"metadata":{"id":"gpR4EzqKWgwo","trusted":true},"cell_type":"code","source":"opacity_locations = {}\n# load table\nwith open(os.path.join('../input/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv'), mode='r') as infile:\n    # open reader\n    reader = csv.reader(infile)\n    # skip header\n    next(reader, None)\n    # loop through rows\n    for rows in reader:\n        # retrieve information\n        filename = rows[0]\n        location = rows[1:5]\n        lungopacity = rows[5]\n        # if row contains lungopacity add label to dictionary\n        # which contains a list of lungopacity locations per filename\n        if lungopacity == '1':\n            # convert string to float to int\n            location = [int(float(i)) for i in location]\n            # save lungopacity location in dictionary\n            if filename in opacity_locations:\n                opacity_locations[filename].append(location)\n            else:\n                opacity_locations[filename] = [location]","execution_count":null,"outputs":[]},{"metadata":{"id":"JnAnLzFmippI"},"cell_type":"markdown","source":"**Load File names**","execution_count":null},{"metadata":{"id":"XZUY3Uv1WgsT","outputId":"a482953f-3904-4b12-ad0b-73cf53ff578b","trusted":true},"cell_type":"code","source":"folder = '../input/rsna-pneumonia-detection-challenge/stage_2_train_images'\nfilenames = os.listdir(folder)\nprint('Number of Train images:', len(filenames))\nprint('First 5 samples: \\n')\nfilenames[0:5]","execution_count":null,"outputs":[]},{"metadata":{"id":"Vtqp-CoO6RpB","trusted":true},"cell_type":"code","source":"#folder = '/content/drive/My Drive/GL_Capstone/stage_2_train_images'\n#filenames = os.listdir(folder)\nrandom.shuffle(filenames)\n# split into train and validation filenames\nn_valid_samples = 5336\nn_train_samples = len(filenames) - n_valid_samples\ntrain_filenames = filenames[n_valid_samples:]\nvalid_filenames = filenames[:n_valid_samples]\nprint('Total file samples:', len(filenames))\nprint('Train samples (80%):', len(train_filenames))\nprint('Valid samples (20%):', len(valid_filenames))","execution_count":null,"outputs":[]},{"metadata":{"id":"KokTOccS6gww"},"cell_type":"markdown","source":"**Downsampling Train samples**","execution_count":null},{"metadata":{"id":"OPLqayP_6fMk","trusted":true},"cell_type":"code","source":"n_downsamples = 5336\ntrain_dsamps = train_filenames[:n_downsamples]\nprint('Training Downsamples:', len(train_dsamps))","execution_count":null,"outputs":[]},{"metadata":{"id":"AxINpeSWEmjO"},"cell_type":"markdown","source":"# **Transfer Learning**","execution_count":null},{"metadata":{"id":"RaVv8UDBIcEK","trusted":true},"cell_type":"code","source":"!pip install opencv-python\nimport cv2\nkeras = tf.compat.v1.keras\n#Sequence = keras.utils.Sequence","execution_count":null,"outputs":[]},{"metadata":{"id":"H2gkwbdQQy6S"},"cell_type":"markdown","source":"**Define class generatortransfer**","execution_count":null},{"metadata":{"id":"SHcAaaH0Eq2u","trusted":true},"cell_type":"code","source":"class generatortransfer(keras.utils.Sequence):\n    \n    def __init__(self, folder, filenames, opacity_locations=None, batch_size=32, image_size=320, shuffle=True, augment=False, predict=False):\n        self.folder = folder\n        self.filenames = filenames\n        self.opacity_locations = opacity_locations\n        self.batch_size = batch_size\n        self.image_size = image_size\n        self.shuffle = shuffle\n        self.augment = augment\n        self.predict = predict\n        self.on_epoch_end()\n        \n    def __load__(self, filename):\n        # load dicom file as numpy array\n        img = pydicom.dcmread(os.path.join(self.folder, filename)).pixel_array\n        # create empty mask\n        msk = np.zeros(img.shape)\n        # get filename without extension\n        filename = filename.split('.')[0]\n        # if image contains lung opacity\n        if filename in opacity_locations:\n            # loop through opacity\n            for location in opacity_locations[filename]:\n                # add 1's at the location of the lung opacity\n                x, y, w, h = location\n                msk[y:y+h, x:x+w] = 1\n        # if augment then horizontal flip half the time\n        if self.augment and random.random() > 0.5:\n            img = np.fliplr(img)\n            msk = np.fliplr(msk)\n        # resize both image and mask\n        #img = resize(img, (self.image_size, self.image_size), mode='reflect')\n        msk = resize(msk, (self.image_size, self.image_size), mode='reflect') > 0.5\n        # add trailing channel dimension\n        msk = np.expand_dims(msk, -1)\n         #Converting Image from GrayScale to RGB \n        if len(img.shape) != 3 or img.shape[2] != 3:\n            img = np.stack((img,) * 3, -1)\n            img = cv2.resize(img, dsize=(self.image_size, self.image_size), interpolation=cv2.INTER_CUBIC)\n        return img, msk\n    \n    def __loadpredict__(self, filename):\n        # load dicom file as numpy array\n        img = pydicom.dcmread(os.path.join(self.folder, filename)).pixel_array\n        # resize image\n        #img = resize(img, (self.image_size, self.image_size), mode='reflect')\n        #Converting Image from GrayScale to RGB \n        if len(img.shape) != 3 or img.shape[2] != 3:\n          img = np.stack((img,) * 3, -1)\n          img = cv2.resize(img, dsize=(self.image_size, self.image_size), interpolation=cv2.INTER_CUBIC)\n        return img\n        \n    def __getitem__(self, index):\n        # select batch\n        filenames = self.filenames[index*self.batch_size:(index+1)*self.batch_size]\n        # predict mode: return images and filenames\n        if self.predict:\n            # load files\n            imgs = [self.__loadpredict__(filename) for filename in filenames]\n            # create numpy batch\n            imgs = np.array(imgs)\n            return imgs, filenames\n        # train mode: return images and masks\n        else:\n            # load files\n            items = [self.__load__(filename) for filename in filenames]\n            # unzip images and masks\n            imgs, msks = zip(*items)\n            # create numpy batch\n            imgs = np.array(imgs)\n            msks = np.array(msks)\n            return imgs, msks\n        \n    def on_epoch_end(self):\n        if self.shuffle:\n            random.shuffle(self.filenames)\n        \n    def __len__(self):\n        if self.predict:\n            # return everything\n            return int(np.ceil(len(self.filenames) / self.batch_size))\n        else:\n            # return full batches only\n            return int(len(self.filenames) / self.batch_size)","execution_count":null,"outputs":[]},{"metadata":{"id":"Ab1g5Mpy-GWn","trusted":true},"cell_type":"code","source":"img_width = 128\nimg_height = 128\nIMAGE_SIZE=128\nkernel =3\nnum_of_classes =2\nBATCH_SIZE = 16\nSHUFFLE_BUFFER_SIZE=1000\n#input_shape = (img_width, img_height, 3)","execution_count":null,"outputs":[]},{"metadata":{"id":"Lwvc3w2W94uJ","trusted":true},"cell_type":"code","source":"# create training and validation data\nfolder = '../input/rsna-pneumonia-detection-challenge/stage_2_train_images'\ntrain_trans = generatortransfer(folder, train_filenames, opacity_locations, batch_size=BATCH_SIZE, image_size=IMAGE_SIZE, shuffle=True, augment=False, predict=False)\nvalid_trans = generatortransfer(folder, valid_filenames, opacity_locations, batch_size=BATCH_SIZE, image_size=IMAGE_SIZE, shuffle=False, predict=False)\n#train_downsamples = generatortransfer(folder, tune_train_samples, opacity_locations, batch_size=BATCH_SIZE, image_size=IMAGE_SIZE, shuffle=False, predict=False)","execution_count":null,"outputs":[]},{"metadata":{"id":"ARLLB1LG-Tza","trusted":true},"cell_type":"code","source":"import keras\nfrom tensorflow.keras import Sequential, backend as K\n#from tensorflow.keras.applications.mobilenet import MobileNet\nfrom tensorflow.keras.applications.resnet50 import ResNet50\nfrom tensorflow.keras.layers import Concatenate, UpSampling2D\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Dense\n\nmodel = Sequential()\nmodel.add(ResNet50(input_shape= (img_width, img_height, 3), include_top=False, weights='imagenet'))\nmodel.add(Dense(1024, activation='relu'))\nmodel.add(UpSampling2D())\nmodel.add(Dense(512, activation='relu'))\nmodel.add(UpSampling2D())\nmodel.add(Dense(256, activation='relu'))\nmodel.add(UpSampling2D())\nmodel.add(Dense(64, activation='relu'))\nmodel.add(UpSampling2D())\nmodel.add(Dense(8, activation='relu'))\nmodel.add(UpSampling2D())\nmodel.add(Dense(1, activation='sigmoid'))\n# Say not to train first layer (ResNet) model. It is already trained\nmodel.layers[0].trainable = False\nprint(model.summary())","execution_count":null,"outputs":[]},{"metadata":{"id":"8BXa-lufJhyd","trusted":true},"cell_type":"code","source":"model.compile(optimizer='Adam',\n              loss='binary_crossentropy',\n              metrics=['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"id":"80YGx70LKMhI","outputId":"7c1e5ee1-f46d-4815-d022-e9373f3603b2","trusted":true},"cell_type":"code","source":"history = model.fit(train_trans, epochs=5, steps_per_epoch =10, shuffle=True)","execution_count":null,"outputs":[]},{"metadata":{"id":"4XDvtGqEDjFo","outputId":"16e850d0-529d-4ef1-a52a-759502b50b45","trusted":true},"cell_type":"code","source":"model.evaluate(valid_trans)","execution_count":null,"outputs":[]},{"metadata":{"id":"q6KN8BpGPLbw"},"cell_type":"markdown","source":"# **Hyper Parameter Tuning**","execution_count":null},{"metadata":{"id":"O53E6QcNVkOf"},"cell_type":"markdown","source":"I have tried couple of times to use GridSearchCV in order to find out the optimal parameters, but the session ran for longer time and the runtime was disconnected and finally lead to lost of changes.","execution_count":null},{"metadata":{"id":"_bZTngROSWFa","outputId":"87de2f3e-574c-4e4e-ea10-cbc04bd303af","trusted":true},"cell_type":"code","source":"import tensorflow as tf\nprint(tf.__version__)","execution_count":null,"outputs":[]},{"metadata":{"id":"SAu3ZTjUkMfd","trusted":true},"cell_type":"code","source":"#!pip install tensorflow==1.14.0\n#!pip install keras==2.0","execution_count":null,"outputs":[]},{"metadata":{"id":"jTd1B__tIlIp"},"cell_type":"markdown","source":"![image.png](data:image/png;base64,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)","execution_count":null},{"metadata":{"id":"6dzmZ00jKXd9"},"cell_type":"markdown","source":"**IoU Mean**","execution_count":null},{"metadata":{"id":"Ej2xagypj3VI","trusted":true},"cell_type":"code","source":"# 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, axis=[1, 2, 3])\n    union = tf.reduce_sum(y_true, axis=[1, 2, 3]) + tf.reduce_sum(y_pred, axis=[1, 2, 3])\n    smooth = tf.ones(tf.shape(intersect))\n    return tf.reduce_mean((intersect + smooth) / (union - intersect + smooth))","execution_count":null,"outputs":[]},{"metadata":{"id":"5qju2OcSQGMY"},"cell_type":"markdown","source":"**Call Backs (Earlystop, ModelCheckpoint)**","execution_count":null},{"metadata":{"id":"prVqHWcXQC7V","outputId":"b0d3f157-c8d9-4fbd-d9ea-6c914a98859c","trusted":true},"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau\n## Earlystopping\nearlystop = EarlyStopping(monitor='val_loss', patience=3)\n\n## Model Check point\nfilepath=\"/kaggle/working/LOHPT-{epoch:02d}-{val_accuracy:.2f}.hdf5\"\ncheckpoint = ModelCheckpoint(filepath, monitor='val_loss', verbose=1, save_best_only=True, mode='min', save_weights_only = True)\n\n## Reduce learning rate when metric has stopped improving\nreduceLROnPlat = ReduceLROnPlateau(monitor='val_loss', factor=0.8, \n                                   patience=2, verbose=1, mode='auto', \n                                   epsilon=0.0001, cooldown=5, min_lr=0.0001)","execution_count":null,"outputs":[]},{"metadata":{"id":"WT1pKxjrmPz0"},"cell_type":"markdown","source":" **Define Parameters**","execution_count":null},{"metadata":{"id":"BTAiPKYlQct7","trusted":true},"cell_type":"code","source":"BATCH_SIZE = 16\nIMAGE_SIZE = 128","execution_count":null,"outputs":[]},{"metadata":{"id":"iVqsVuBJnmJ0","outputId":"6a19bca8-a693-434a-902e-b2d61b5b1e63","trusted":true},"cell_type":"code","source":"import tensorflow as tf\nimport keras\nfrom tensorflow.keras import Sequential, backend as K\n#from tensorflow.keras.applications.mobilenet import MobileNet\nfrom tensorflow.keras.applications.resnet50 import ResNet50\nfrom tensorflow.keras.layers import Concatenate, UpSampling2D\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Dense\n\nhpt_model = Sequential()\nhpt_model.add(ResNet50(input_shape= (img_width, img_height, 3), include_top=False, weights='imagenet'))\nhpt_model.add(Dense(1024, activation='relu'))\nhpt_model.add(UpSampling2D())\nhpt_model.add(Dense(512, activation='relu'))\nhpt_model.add(UpSampling2D())\nhpt_model.add(Dense(256, activation='relu'))\nhpt_model.add(UpSampling2D())\nhpt_model.add(Dense(64, activation='relu'))\nhpt_model.add(UpSampling2D())\nhpt_model.add(Dense(8, activation='relu'))\nhpt_model.add(UpSampling2D())\nhpt_model.add(Dense(1, activation='sigmoid'))\n# Say not to train first layer (ResNet) model. It is already trained\nhpt_model.layers[0].trainable = False\nprint(hpt_model.summary())","execution_count":null,"outputs":[]},{"metadata":{"id":"dd7l4nPymUDv"},"cell_type":"markdown","source":"**Compilation**","execution_count":null},{"metadata":{"id":"N8Hd2r98kW3y","trusted":true},"cell_type":"code","source":"hpt_model.compile(optimizer= 'Adam',\n              loss='binary_crossentropy',\n              metrics=['accuracy', mean_iou])","execution_count":null,"outputs":[]},{"metadata":{"id":"1NEIDyvpnDLq"},"cell_type":"markdown","source":"**Fitting Model**","execution_count":null},{"metadata":{"id":"QEFymVb30Gc6","outputId":"900d47b2-dd55-4f2c-d37e-96c0da869a63","trusted":true},"cell_type":"code","source":"#history = hpt_model.fit(train_trans, validation_data= valid_trans, \n                        #epochs=10, callbacks=[earlystop, checkpoint, reduceLROnPlat],\n                        #steps_per_epoch=10, shuffle=True)","execution_count":null,"outputs":[]},{"metadata":{"id":"4jc0vKYmmIW0"},"cell_type":"markdown","source":"# **Plots for Accuracy, Loss, IoU Mean**","execution_count":null},{"metadata":{"id":"yI7AID_kQ0an"},"cell_type":"markdown","source":"**Accuracy**","execution_count":null},{"metadata":{"id":"BPZ_9mGsPxIa","trusted":true},"cell_type":"code","source":"acc = history.history['accuracy']\nval_acc = history.history['val_accuracy']\nepochs = range(1, len(acc) + 1)\nplt.plot(epochs, acc, 'y', label='Training Accuracy')\nplt.plot(epochs, val_acc, 'r', label='Validation Accuracy')\nplt.title('Training and Validation Accuracy Graph')\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"id":"_1DqXoxdQ4ea"},"cell_type":"markdown","source":"**Loss**","execution_count":null},{"metadata":{"id":"pKA4LR6wQGlC","trusted":true},"cell_type":"code","source":"Train_Loss = history.history['loss']\nVal_Loss = history.history['val_loss']\nEpochs = range(1, len(Train_Loss) + 1)\nplt.plot(Epochs, Train_Loss, 'r', label='Training Loss')\nplt.plot(Epochs, Val_Loss, 'g', label='Validation Loss')\nplt.title('Training and Validation Loss Graph')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"id":"KC48sMylQ8N0"},"cell_type":"markdown","source":"**iou_mean**","execution_count":null},{"metadata":{"id":"8f1-DcyFQ_H9","trusted":true},"cell_type":"code","source":"train_iou = history.history['mean_iou']\nval_iou = history.history['val_mean_iou']\nepochs = range(1, len(train_iou) + 1)\nplt.plot(epochs, train_iou, 'r', label='Training iou')\nplt.plot(epochs, val_iou, 'g', label='Validation iou')\nplt.title('Training and Validation IOU Graph')\nplt.xlabel('Epochs')\nplt.ylabel('IOU')\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"id":"4Fl24SPQN_0A"},"cell_type":"markdown","source":"**Load the model weights**","execution_count":null},{"metadata":{"id":"y89Li2GOqa28","outputId":"0ed8d3b0-02a6-474b-83d3-2ef42a922257","trusted":true},"cell_type":"code","source":"model.save_weights('/kaggle/working/RESNET50-05-0.97.hdf5')","execution_count":null,"outputs":[]},{"metadata":{"id":"a0bD0DPmML7e"},"cell_type":"markdown","source":"# **Predict Test images**","execution_count":null},{"metadata":{"id":"HlVdFOnyMQqW","outputId":"db7f4546-cc2f-42ab-db79-372b5a97bac5","trusted":true},"cell_type":"code","source":"# load and shuffle filenames\n#folder = '../input/stage_2_test_images'\nfolder = '../input/rsna-pneumonia-detection-challenge/stage_2_test_images'\ntest_filenames = os.listdir(folder)\nprint('n test samples:', len(test_filenames))","execution_count":null,"outputs":[]},{"metadata":{"id":"c09jJHTAoM9_","trusted":true},"cell_type":"code","source":"# create test generator with predict flag set to True\ntest_trans = generatortransfer(folder, test_filenames, None, batch_size=16, image_size=IMAGE_SIZE, shuffle=False, predict=True)","execution_count":null,"outputs":[]},{"metadata":{"id":"RJUaXMxgBtIg","trusted":true},"cell_type":"code","source":"test_dicom_dir = '../input/rsna-pneumonia-detection-challenge/stage_2_test_images'","execution_count":null,"outputs":[]},{"metadata":{"id":"E1nFZS8pBMn5","trusted":true},"cell_type":"code","source":"import glob\ndef get_dicom_fps(dicom_dir):\n    dicom_fps = glob.glob(dicom_dir+'/'+'*.dcm')\n    return list(set(dicom_fps))","execution_count":null,"outputs":[]},{"metadata":{"id":"IPllzQaSBhzW","trusted":true},"cell_type":"code","source":"# Get filenames of test dataset DICOM images\ntest_image_fps = get_dicom_fps(test_dicom_dir)","execution_count":null,"outputs":[]},{"metadata":{"id":"YrLJWdi0WrOX","trusted":true},"cell_type":"code","source":"# Make predictions on test images, write out sample submission \ndef predict(image_fps, filepath='/content/sample_submission.csv', min_conf=0.98): \n    \n    # assume square image\n    \n    with open(filepath, 'w') as file:\n      for image_id in tqdm_notebook(image_fps): \n        ds = dcm.read_file(image_id)\n        image = ds.pixel_array\n          \n        # If grayscale. Convert to RGB for consistency.\n        #if len(image.shape) != 3 or image.shape[2] != 3:\n\n        image = np.stack((image,) * 3, -1)\n        #img = cv2.resize(image, dsize=(128, 128), interpolation=cv2.INTER_CUBIC)\n\n\n        #patient_id = os.path.splitext(os.path.basename(image_fps))[0]\n        patient_id = image_fps\n        results = hpt_model.predict([image])\n        r = results[0]\n\n        out_str = \"\"\n        out_str += patient_id \n        assert( len(r['rois']) == len(r['class_ids']) == len(r['scores']) )\n        if len(r['rois']) == 0: \n            pass\n        else: \n            num_instances = len(r['rois'])\n            out_str += \",\"\n            for i in range(num_instances): \n                if r['scores'][i] > min_conf: \n                    out_str += ' '\n                    out_str += str(round(r['scores'][i], 2))\n                    out_str += ' '\n\n                    # x1, y1, width, height \n                    x1 = r['rois'][i][1]\n                    y1 = r['rois'][i][0]\n                    width = r['rois'][i][3] - x1 \n                    height = r['rois'][i][2] - y1 \n                    bboxes_str = \"{} {} {} {}\".format(x1, y1, \\\n                                                      width, height)    \n                    out_str += bboxes_str\n\n        filepath.write(out_str+\"\\n\")","execution_count":null,"outputs":[]},{"metadata":{"id":"B7GA2GjlJQxU","trusted":true},"cell_type":"code","source":"# predict only the first 50 entries\nsample_submission_fp = '/kaggle/working/sample_submission.csv'\npredict(test_image_fps[:50], filepath=sample_submission_fp)","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}