{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":52254,"databundleVersionId":6863140,"sourceType":"competition"}],"dockerImageVersionId":30559,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Image Preprocessing and Global Dependancies ","metadata":{}},{"cell_type":"markdown","source":"https://www.kaggle.com/code/younghef/rsna-abdominal-trauma-detection-via-u-net","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport pydicom\nimport io\nimport tensorflow as tf\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix\nfrom tensorflow.keras.applications import DenseNet121\nfrom tensorflow.keras.metrics import BinaryAccuracy, Precision, Recall, AUC, MeanSquaredError\n\n# Load the files into Pandas dataframes\nimage_level_labels_df = pd.read_csv('/kaggle/input/rsna-2023-abdominal-trauma-detection/image_level_labels.csv')\ntrain_series_meta_df = pd.read_csv('/kaggle/input/rsna-2023-abdominal-trauma-detection/train_series_meta.csv')\ntest_series_meta_df = pd.read_csv('/kaggle/input/rsna-2023-abdominal-trauma-detection/test_series_meta.csv') \ntrain_df = pd.read_csv('/kaggle/input/rsna-2023-abdominal-trauma-detection/train.csv')\n\n\n# Define the global image sizes\nIMG_HEIGHT, IMG_WIDTH = 256, 256\n\n#creating a function for reading these DICOM files into arrays\ndef process_dicom(file_content):\n    file_like = io.BytesIO(file_content.numpy()) # creating a BytesIO object for putting binary image data\n    dicom_data = pydicom.dcmread(file_like)      # using pydicom.dcmread to parse the DICOM image to this dicom_data variable\n    image = dicom_data.pixel_array               # extracting the pixel array from the image\n    image = np.expand_dims(image, axis=-1)       # adding an extra dimension to the image, so even 2D images will work together with the 3D images in the dataset\n    image = tf.image.resize(image, [256, 256])   # resizing all to a uniform 256 x 256 px. Images were reported to all be 512 x 512 but this helps with processing load\n    image = tf.cast(image, tf.float32) / 255.0   # normalizing to [0,1], as the pixel grey values are [0,255] \n    return image\n\n\n#vars for arrays \nimage_paths = []\nlabels = []\n\n#targets into an array var\ninjury_columns = [\n    \"bowel_healthy\", \"bowel_injury\", \"extravasation_healthy\", \"extravasation_injury\", \n    \"kidney_healthy\", \"kidney_low\", \"kidney_high\", \"liver_healthy\", \n    \"liver_low\", \"liver_high\", \"spleen_healthy\", \"spleen_low\", \"spleen_high\", \"any_injury\"\n]\n\n#iteratively appending the image labels from the label dataframe to array\nfor _, row in image_level_labels_df.iterrows():\n    patient_id = int(row['patient_id'])\n    series_id = int(row['series_id'])\n    instance_number = row['instance_number']\n    image_path = os.path.join(\"train_images\", str(patient_id), str(series_id), f\"{instance_number}.DCM\")\n    image_paths.append(image_path)\n    \n    injury_values = train_df[train_df[\"patient_id\"] == patient_id][injury_columns].iloc[0].values.tolist()\n    labels.append(injury_values)\n\n# Zip the paths and labels (something to ensure the array sizes remain their expected shape after the validation split)\ndata = list(zip(image_paths, labels))\n\n# Split the data into training and validation sets\ntrain_data, val_data = train_test_split(data, test_size=0.2, random_state=42)\n\n# Unzip the data back into separate lists\ntrain_paths, train_labels = zip(*train_data)\nval_paths, val_labels = zip(*val_data)\n\n# Convert the unzipped labels back into numpy arrays\ntrain_labels = np.array(train_labels, dtype=np.float32)\nval_labels = np.array(val_labels, dtype=np.float32)\n\n#loading the labels to to image  \ndef load_image_and_label(image_path, label):\n    file_content = tf.io.read_file(image_path)\n    image = process_dicom(file_content)\n    return image, label\n\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-11-26T20:35:52.551822Z","iopub.execute_input":"2023-11-26T20:35:52.552076Z","iopub.status.idle":"2023-11-26T20:36:14.218434Z","shell.execute_reply.started":"2023-11-26T20:35:52.552052Z","shell.execute_reply":"2023-11-26T20:36:14.217548Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Shape Checking\nprint(\"Training Paths:\", len(train_paths))\nprint(\"Training Labels:\", train_labels.shape)\n\nprint(\"Validation Paths:\", len(val_paths))\nprint(\"Validation Labels:\", val_labels.shape)","metadata":{"execution":{"iopub.status.busy":"2023-11-26T20:36:14.220419Z","iopub.execute_input":"2023-11-26T20:36:14.220729Z","iopub.status.idle":"2023-11-26T20:36:14.226277Z","shell.execute_reply.started":"2023-11-26T20:36:14.220704Z","shell.execute_reply":"2023-11-26T20:36:14.225423Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Architecture","metadata":{}},{"cell_type":"code","source":"#Using GRU with an attention mechanism to process the spatial image features via the defining function below \n#framework for the attention block function inspired  \"Deep Learning with TensorFlow 2 and Keras 2nd ed.\" by Gulli, Kapoor, and Pal. \n\n#defining the attention weights first, as it needs to be setup prior to the full model build\ndef attention_block(inputs, context):\n    attention_weights = tf.keras.layers.Dense(1, activation='tanh')(inputs)\n    attention_weights = tf.keras.layers.Flatten()(attention_weights)\n    attention_weights = tf.keras.activations.softmax(attention_weights, axis=1)\n    attention_weights = tf.keras.layers.RepeatVector(context.shape[2])(attention_weights)\n    attention_weights = tf.keras.layers.Permute([2, 1])(attention_weights)\n    context_mul_attention = tf.keras.layers.Multiply()([context, attention_weights])\n    return context_mul_attention\n\n#Building U-Net encoder \n#framework for U-Net inspired by  \"Deep Learning with TensorFlow 2 and Keras 2nd ed.\" by Gulli, Kapoor, and Pal.\n\n# This funciton is creating the entire U-Net + GRU (w attention weights defined above) into a the varible \"model\"\ndef combined_unet_gru(input_shape, num_classes):\n    inputs = tf.keras.layers.Input(input_shape)\n    \n    # U-Net Encoder\n    c1 = tf.keras.layers.Conv2D(16, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(inputs)\n    p1 = tf.keras.layers.MaxPooling2D((2, 2))(c1)\n\n    c2 = tf.keras.layers.Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_normal', padding='same')(p1)\n    p2 = tf.keras.layers.MaxPooling2D((2, 2))(c2)\n    \n    # Flatten the U-Net outputs to use as sequential data to be fed into the GRU\n    flattened = tf.keras.layers.Flatten()(p2)\n    reshaped = tf.keras.layers.Reshape((-1, flattened.shape[1]))(flattened)\n    \n    # GRU with Attention\n    gru_out = tf.keras.layers.GRU(64, return_sequences=True)(reshaped)\n    attention_out = attention_block(reshaped, gru_out)\n    gru_out_with_attention = tf.keras.layers.Concatenate(axis=2)([gru_out, attention_out])\n    gru_final_out = tf.keras.layers.GRU(32)(gru_out_with_attention)\n    \n    # Classifier\n    outputs = tf.keras.layers.Dense(num_classes, activation='sigmoid')(gru_final_out)\n    \n    #Rolling it all into one compunded model\n    model = tf.keras.Model(inputs=[inputs], outputs=[outputs])\n\n    return model\n\ninput_shape = (IMG_HEIGHT, IMG_WIDTH, 1)\nnum_classes = 14\n\nmodel = combined_unet_gru(input_shape, num_classes)\n\n# Creating the model training parameters\nadadelta_optimizer = tf.keras.optimizers.Adadelta(learning_rate=1.0, rho=0.95)\nmodel.compile(\n    optimizer=adadelta_optimizer,\n    loss='categorical_crossentropy',\n    metrics=[\n        BinaryAccuracy(name='accuracy'),\n        Precision(name='precision'),\n        Recall(name='recall'),\n        AUC(name='auc'),\n        MeanSquaredError(name='mean_squared_error')\n    ]\n)\n#demonstrate the model full structure\nmodel.summary()\n","metadata":{"execution":{"iopub.status.busy":"2023-11-26T20:36:58.104228Z","iopub.execute_input":"2023-11-26T20:36:58.104942Z","iopub.status.idle":"2023-11-26T20:36:58.655443Z","shell.execute_reply.started":"2023-11-26T20:36:58.104903Z","shell.execute_reply":"2023-11-26T20:36:58.654545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Final dataset structurization ","metadata":{}},{"cell_type":"code","source":"#loading labels/image merge into full dataset\n\n#This function wraps non-tensor values for image and level into a TensorFlow compatible numpy array. Outputting TWO float32 tensors \ndef load_image_and_label_function(item1, item2):\n    return tf.numpy_function(\n        load_image_and_label, [item1, item2], [tf.float32, tf.float32]) \n\n#defining this \"last ditch effort\" function to filter out any images or labels that aren't valid (There was a problem DICOM image in the dataset). via a Kaggle help forum \ndef filter_function(img, lbl):\n    img_condition = tf.math.reduce_all(tf.math.not_equal(img, -1))  \n    lbl_condition = tf.math.reduce_all(tf.math.not_equal(lbl, -1))\n    return tf.math.logical_and(img_condition, lbl_condition)\n\n#Function to create the master TensorFlow training data set\ndef create_dataset(image_paths, labels, batch_size):\n    image_paths = tf.convert_to_tensor(image_paths, dtype=tf.string)      #Converting the previously defined image_path list into a string tensor\n    labels = tf.convert_to_tensor(labels, dtype=tf.float32)               #Converting the labels into a float32 tensor\n    \n    dataset = tf.data.Dataset.from_tensor_slices((image_paths, labels))                                       #dataset created via tensor slices of image path and labels\n    dataset = dataset.map(load_image_and_label_function, num_parallel_calls=tf.data.experimental.AUTOTUNE)    #Mapping the image and label function output to a map \n    dataset = dataset.filter(filter_function)                                                                 #applying the bad image filter defined earlier\n    dataset = dataset.shuffle(buffer_size=1000).batch(batch_size, drop_remainder=True)                        #batching and shuffling up the dataset\n    dataset = dataset.prefetch(buffer_size=tf.data.experimental.AUTOTUNE)                                     #prefetching the dataset\n    \n    return dataset\n\n#parsing final model parameters together for training execution\nbatch_size = 128\ntrain_dataset = create_dataset(train_paths, train_labels, batch_size)\nval_dataset = create_dataset(val_paths, val_labels, batch_size)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-11-26T20:36:47.213957Z","iopub.execute_input":"2023-11-26T20:36:47.214305Z","iopub.status.idle":"2023-11-26T20:36:47.291302Z","shell.execute_reply.started":"2023-11-26T20:36:47.214276Z","shell.execute_reply":"2023-11-26T20:36:47.290125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Training","metadata":{}},{"cell_type":"code","source":"# Training the model\nhistory = model.fit(train_dataset, validation_data=val_dataset, epochs=20)# This line of code parses the previously established model parameters (i.e. validation data set var), establishes new bounds, like epoch count and early stopping sensitivity, and executes the training process  \n\nhistory = model.fit(\n    train_dataset,\n    validation_data=val_dataset,\n    epochs=40,\n    callbacks=[\n        tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5),            #Early Stoppage with a patience setting of 5 epochs\n        tf.keras.callbacks.ModelCheckpoint('model_best.h5', monitor='val_accuracy', save_best_only=True),    #Exporting the model\n        ]\n)\n\n#prints the validation accuracy and accuacy for every epoch on a single line\nprint(history.history['accuracy'])\nprint(history.history['val_accuracy'])","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-11-26T20:36:18.745674Z","iopub.status.idle":"2023-11-26T20:36:18.745995Z","shell.execute_reply.started":"2023-11-26T20:36:18.745839Z","shell.execute_reply":"2023-11-26T20:36:18.745854Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Post Training Analysis","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import ConfusionMatrixDisplay, accuracy_score, classification_report, precision_recall_curve, roc_curve, auc\nfrom tensorflow.math import confusion_matrix\nimport matplotlib.pyplot as plt\n\ntest_data = val_dataset  \n\n# Make predictions on the test dataset\ny_true = []\ny_pred_probs = []\n\nfor images, labels in test_data:\n    predictions = model.predict(images)\n    y_true.extend(tf.argmax(labels, axis=1).numpy())\n    y_pred_probs.extend(tf.argmax(predictions, axis=1).numpy())\n\n# Convert prediction probabilities to class predictions\ny_pred = [1 if prob >= 0.5 else 0 for prob in y_pred_probs]\n\n# Create confusion matrix\nconf_matrix = confusion_matrix(y_true, y_pred)\nprint(\"Confusion Matrix:\")\nprint(conf_matrix)\n\n# Calculate accuracy score\nacc_score = accuracy_score(y_true, y_pred)\nprint(\"Accuracy Score:\", acc_score)\n\n# Convert EagerTensor to NumPy array\nconf_matrix_np = np.array(conf_matrix)\n\n# Calculate min and max using NumPy functions\nthresh = (np.max(conf_matrix_np) + np.min(conf_matrix_np)) / 2.0\n\n# Display confusion matrix\nconf_matrix_display = ConfusionMatrixDisplay(conf_matrix_np, display_labels=[0, 1])\nconf_matrix_display.plot(cmap='Blues', values_format='d')\nplt.title('Confusion Matrix')\nplt.show()\n\n# Calculate precision and recall\nprecision, recall, _ = precision_recall_curve(y_true, y_pred_probs)\n\n# Plot Precision-Recall curve\nplt.figure(figsize=(8, 8))\nplt.plot(recall, precision, color='darkorange', lw=2)\nplt.xlabel('Recall')\nplt.ylabel('Precision')\nplt.title('Precision-Recall Curve')\nplt.show()\n\n# Calculate ROC curve\nfpr, tpr, _ = roc_curve(y_true, y_pred_probs)\n\n# Calculate AUC\nroc_auc = auc(fpr, tpr)\n\n# Plot ROC curve\nplt.figure(figsize=(8, 8))\nplt.plot(fpr, tpr, color='darkorange', lw=2, label=f'AUC = {roc_auc:.2f}')\nplt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')\nplt.xlabel('False Positive Rate')\nplt.ylabel('True Positive Rate')\nplt.title('Receiver Operating Characteristic (ROC) Curve')\nplt.legend(loc='lower right')\nplt.show()","metadata":{},"execution_count":null,"outputs":[]}]}