{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":52254,"databundleVersionId":9674523,"sourceType":"competition"},{"sourceId":6211844,"sourceType":"datasetVersion","datasetId":3567114},{"sourceId":6091,"sourceType":"modelInstanceVersion","modelInstanceId":4623,"modelId":2800}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Setup and Imports","metadata":{}},{"cell_type":"code","source":"! pip install -q git+https://github.com/keras-team/keras-cv","metadata":{"_kg_hide-output":true,"_kg_hide-input":false,"execution":{"iopub.status.busy":"2024-12-10T22:47:46.758035Z","iopub.execute_input":"2024-12-10T22:47:46.758542Z","iopub.status.idle":"2024-12-10T22:48:17.679879Z","shell.execute_reply.started":"2024-12-10T22:47:46.758488Z","shell.execute_reply":"2024-12-10T22:48:17.678379Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n# You can use `tensorflow`, `pytorch`, `jax` here\n# KerasCore makes the notebook backend agnostic :)\nos.environ[\"KERAS_BACKEND\"] = \"tensorflow\"\n\nimport keras_cv\nimport keras_core as keras\nfrom keras_core import layers\n\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom matplotlib import pyplot as plt\nfrom sklearn.model_selection import train_test_split\n\nimport cv2\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_modality_lut\nimport gc\nfrom tqdm.notebook import tqdm\nfrom glob import glob\nfrom joblib import Parallel, delayed","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:17.683Z","iopub.execute_input":"2024-12-10T22:48:17.683506Z","iopub.status.idle":"2024-12-10T22:48:41.419793Z","shell.execute_reply.started":"2024-12-10T22:48:17.683453Z","shell.execute_reply":"2024-12-10T22:48:41.418274Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Config:\n    SEED = 42\n    IMAGE_SIZE = [256, 256]\n    RESIZE_DIM = 256\n    BATCH_SIZE = 64\n    EPOCHS = 20\n    TARGET_COLS  = [\n        \"bowel_injury\", \"extravasation_injury\",\n        \"kidney_healthy\", \"kidney_low\", \"kidney_high\",\n        \"liver_healthy\", \"liver_low\", \"liver_high\",\n        \"spleen_healthy\", \"spleen_low\", \"spleen_high\",\n    ]\n    AUTOTUNE = tf.data.AUTOTUNE\n\nconfig = Config()","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:41.421349Z","iopub.execute_input":"2024-12-10T22:48:41.422311Z","iopub.status.idle":"2024-12-10T22:48:41.428841Z","shell.execute_reply.started":"2024-12-10T22:48:41.422266Z","shell.execute_reply":"2024-12-10T22:48:41.427754Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Reproducibility\nWe would want this notebook to have reproducible results. Here we set the seed for all the random algorithms so that we can reproduce the experiments each time exactly the same way.","metadata":{}},{"cell_type":"code","source":"keras.utils.set_random_seed(seed=config.SEED)","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:41.431721Z","iopub.execute_input":"2024-12-10T22:48:41.432098Z","iopub.status.idle":"2024-12-10T22:48:41.44655Z","shell.execute_reply.started":"2024-12-10T22:48:41.432065Z","shell.execute_reply":"2024-12-10T22:48:41.44539Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Files Path","metadata":{}},{"cell_type":"code","source":"BASE_PATH = f\"/kaggle/input/rsna-atd-512x512-png-v2-dataset\"\nRSNA_BASE_PATH = f\"/kaggle/input/rsna-2023-abdominal-trauma-detection\"\nIMAGE_DIR = \"/tmp/dataset/rsna-atd\"\nTEST_IMAGE_DIR = \"/tmp/dataset/test\"\nSTRIDE = 10","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2024-12-10T22:48:41.447815Z","iopub.execute_input":"2024-12-10T22:48:41.448118Z","iopub.status.idle":"2024-12-10T22:48:41.459952Z","shell.execute_reply.started":"2024-12-10T22:48:41.44809Z","shell.execute_reply":"2024-12-10T22:48:41.458547Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Generating Label\nApplied crossproduct to get labels for images","metadata":{}},{"cell_type":"code","source":"meta_df_1 = pd.read_csv(f\"{RSNA_BASE_PATH}/train_2024.csv\")\nmeta_df_2 = pd.read_csv(f\"{RSNA_BASE_PATH}/train_series_meta.csv\")\n\n# Perform cross product (Cartesian product) on 'patient_id'\n# Renaming columns to avoid column name conflicts\nmeta_df_1 = meta_df_1.rename(columns=lambda x: f\"{x}\" if x != 'patient_id' else x)\nmeta_df_2 = meta_df_2.rename(columns=lambda x: f\"{x}\" if x != 'patient_id' else x)\n\n# Cartesian product using merge without specifying the 'on' argument but using all patient_ids\nmeta_df = pd.merge(meta_df_1, meta_df_2, on=\"patient_id\", how=\"outer\")\n\n# Checking if patients are repeated by finding the number of unique patient IDs\nnum_rows = meta_df.shape[0]\nunique_patients = meta_df[\"patient_id\"].nunique()\n\n# Display the results\nprint(f\"Total number of rows after cross product: {num_rows}\")\nprint(f\"Number of unique patients: {unique_patients}\")\n\n# Display the first few rows of the cross product dataframe to verify\nmeta_df.head()","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:41.46184Z","iopub.execute_input":"2024-12-10T22:48:41.462295Z","iopub.status.idle":"2024-12-10T22:48:41.549478Z","shell.execute_reply.started":"2024-12-10T22:48:41.462248Z","shell.execute_reply":"2024-12-10T22:48:41.548387Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Here, you can apply n pateint for testing.","metadata":{}},{"cell_type":"code","source":"# # Limit to only n unique patients\n# limited_patient_ids = meta_df[\"patient_id\"].drop_duplicates().sample(n=10, random_state=42)\n# meta_df_limited = meta_df[meta_df[\"patient_id\"].isin(limited_patient_ids)]\n\n# # Checking the number of unique patients in the limited DataFrame\n# limited_num_rows = meta_df_limited.shape[0]\n# limited_unique_patients = meta_df_limited[\"patient_id\"].nunique()\n\n# # Display the results for verification\n# print(f\"Total number of rows after limiting to 1000 patients: {limited_num_rows}\")\n# print(f\"Number of unique patients in the limited DataFrame: {limited_unique_patients}\")\n\n# # Display the first few rows of the limited DataFrame\n# meta_df_limited.head()\n\n# meta_df = meta_df_limited","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:48:41.550905Z","iopub.execute_input":"2024-12-10T22:48:41.551327Z","iopub.status.idle":"2024-12-10T22:48:41.567983Z","shell.execute_reply.started":"2024-12-10T22:48:41.551283Z","shell.execute_reply":"2024-12-10T22:48:41.56695Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Image Prepocessing\nadd a dicom image path column in the label, which we will be using to ask the path to convertion of PNG","metadata":{}},{"cell_type":"code","source":"%%capture\nfrom tqdm import tqdm\nfrom glob import glob\n\nmeta_df[\"dicom_folder\"] = RSNA_BASE_PATH + \"/\" + \"train_images\" \\\n                          + \"/\" + meta_df.patient_id.astype(str) \\\n                          + \"/\" + meta_df.series_id.astype(str)\n\ntrain_folders = meta_df.dicom_folder.tolist()\ntrain_paths = []\nfor folder in tqdm(train_folders):\n    train_paths += sorted(glob(os.path.join(folder, \"*dcm\")))[::STRIDE]","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:41.569337Z","iopub.execute_input":"2024-12-10T22:48:41.569696Z","iopub.status.idle":"2024-12-10T22:48:41.97001Z","shell.execute_reply.started":"2024-12-10T22:48:41.569637Z","shell.execute_reply":"2024-12-10T22:48:41.968972Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"combines the columns to get all the lables and image paths as one table","metadata":{}},{"cell_type":"code","source":"df = pd.DataFrame(train_paths, columns=[\"dicom_path\"])\ndf[\"patient_id\"] = df.dicom_path.map(lambda x: x.split(\"/\")[-3]).astype(int)\ndf[\"series_id\"] = df.dicom_path.map(lambda x: x.split(\"/\")[-2]).astype(int)\ndf[\"instance_number\"] = df.dicom_path.map(lambda x: x.split(\"/\")[-1].replace(\".dcm\",\"\")).astype(int)\n\ndf[\"image_path\"] = f\"{IMAGE_DIR}/train_images\"\\\n                    + \"/\" + df.patient_id.astype(str)\\\n                    + \"/\" + df.series_id.astype(str)\\\n                    + \"/\" + df.instance_number.astype(str) +\".png\"\n\n# Perform the merge on both 'patient_id' and 'series_id', adding suffixes to handle duplicate columns\ndf_merged = pd.merge(meta_df, df, on=[\"patient_id\", \"series_id\"], how=\"outer\", suffixes=('_meta', '_df'))\n\n# Iterate through columns and drop duplicates (keep the column from 'df' and drop '_meta')\nfor col in df_merged.columns:\n    if col.endswith('_meta') and col.replace('_meta', '') in df_merged.columns:\n        df_merged.drop(columns=[col], inplace=True)\n\n# Rename columns back to remove the '_df' suffix\ndf_merged.columns = df_merged.columns.str.replace('_df', '')\n\ndf_merged = df_merged.iloc[:, :-6].join(df_merged.iloc[:, -3:])\n\n# Display the first few rows to verify the merge result\ndf_merged.head()","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:41.971331Z","iopub.execute_input":"2024-12-10T22:48:41.971714Z","iopub.status.idle":"2024-12-10T22:48:42.008283Z","shell.execute_reply.started":"2024-12-10T22:48:41.971658Z","shell.execute_reply":"2024-12-10T22:48:42.007217Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Checking if patients are repeated by finding the number of unique patient IDs\nnum_rows = df_merged.shape[0]\nunique_patients = df_merged[\"patient_id\"].nunique()\n\nprint(f\"{num_rows=}\")\nprint(f\"{unique_patients=}\")","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:42.01147Z","iopub.execute_input":"2024-12-10T22:48:42.011848Z","iopub.status.idle":"2024-12-10T22:48:42.017982Z","shell.execute_reply.started":"2024-12-10T22:48:42.011816Z","shell.execute_reply":"2024-12-10T22:48:42.017002Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!rm -r {IMAGE_DIR}\nos.makedirs(f\"{IMAGE_DIR}/train_images\", exist_ok=True)\nos.makedirs(f\"{IMAGE_DIR}/test_images\", exist_ok=True)","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:42.019227Z","iopub.execute_input":"2024-12-10T22:48:42.019577Z","iopub.status.idle":"2024-12-10T22:48:43.232183Z","shell.execute_reply.started":"2024-12-10T22:48:42.019516Z","shell.execute_reply":"2024-12-10T22:48:43.230762Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Fuctions to convert the images to PNG from DICOM","metadata":{}},{"cell_type":"code","source":"def standardize_pixel_array(dcm):\n    # Correct DICOM pixel_array if PixelRepresentation == 1.\n    pixel_array = dcm.pixel_array\n    if dcm.PixelRepresentation == 1:\n        bit_shift = dcm.BitsAllocated - dcm.BitsStored\n        dtype = pixel_array.dtype \n        new_array = (pixel_array << bit_shift).astype(dtype) >>  bit_shift\n        pixel_array = pydicom.pixel_data_handlers.util.apply_modality_lut(new_array, dcm)\n    return pixel_array\n\ndef read_xray(path, fix_monochrome=True):\n    dicom = pydicom.dcmread(path)\n    data = standardize_pixel_array(dicom)\n    data = data - np.min(data)\n    data = data / (np.max(data) + 1e-5)\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = 1.0 - data\n    return data\n\ndef resize_and_save(file_path):\n    img = read_xray(file_path)\n    h, w = img.shape[:2]  # orig hw\n    img = cv2.resize(img, (config.RESIZE_DIM, config.RESIZE_DIM), cv2.INTER_LINEAR)\n    img = (img * 255).astype(np.uint8)\n    \n    sub_path = file_path.split(\"/\",4)[-1].split(\".dcm\")[0] + \".png\"\n    infos = sub_path.split(\"/\")\n    sub_path = file_path.split(\"/\",4)[-1].split(\".dcm\")[0] + \".png\"\n    infos = sub_path.split(\"/\")\n    pid = infos[-3]\n    sid = infos[-2]\n    iid = infos[-1]; iid = iid.replace(\".png\",\"\")\n    new_path = os.path.join(IMAGE_DIR, sub_path)\n    os.makedirs(new_path.rsplit(\"/\",1)[0], exist_ok=True)\n    cv2.imwrite(new_path, img)\n    return","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:43.23423Z","iopub.execute_input":"2024-12-10T22:48:43.234693Z","iopub.status.idle":"2024-12-10T22:48:43.247636Z","shell.execute_reply.started":"2024-12-10T22:48:43.234637Z","shell.execute_reply":"2024-12-10T22:48:43.246363Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"converting DICOM to PNG in temporary folders","metadata":{}},{"cell_type":"code","source":"%%time\n\nfile_paths = df_merged.dicom_path.tolist()\n_ = Parallel(n_jobs=2, backend=\"threading\")(\n    delayed(resize_and_save)(file_path) for file_path in tqdm(file_paths, leave=True, position=0)\n)\n\ndel _; gc.collect()","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:43.249213Z","iopub.execute_input":"2024-12-10T22:48:43.249817Z","iopub.status.idle":"2024-12-10T22:48:54.637909Z","shell.execute_reply.started":"2024-12-10T22:48:43.249747Z","shell.execute_reply":"2024-12-10T22:48:54.636357Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data Spliting\nSplit the training set into train and validation","metadata":{}},{"cell_type":"code","source":"# Function to handle the split for each group\ndef split_group(group, test_size=0.3):\n    if len(group) == 1:\n        return (group, pd.DataFrame()) if np.random.rand() < test_size else (pd.DataFrame(), group)\n    else:\n        return train_test_split(group, test_size=test_size, random_state=42)\n\n# Initialize the train and validation datasets\ntrain_data = pd.DataFrame()\nval_data = pd.DataFrame()\n\n# Iterate through the groups and split them, handling single-sample groups\nfor _, group in df_merged.groupby(config.TARGET_COLS):\n    train_group, val_group = split_group(group)\n    train_data = pd.concat([train_data, train_group], ignore_index=True)\n    val_data = pd.concat([val_data, val_group], ignore_index=True)\n    \ntrain_data.shape, val_data.shape\n\n","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:54.639757Z","iopub.execute_input":"2024-12-10T22:48:54.640206Z","iopub.status.idle":"2024-12-10T22:48:54.67288Z","shell.execute_reply.started":"2024-12-10T22:48:54.640167Z","shell.execute_reply":"2024-12-10T22:48:54.671624Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Augmentation","metadata":{}},{"cell_type":"code","source":"def decode_image_and_label(image_path, label):\n    file_bytes = tf.io.read_file(image_path)\n    image = tf.io.decode_png(file_bytes, channels=3, dtype=tf.uint8)\n    image = tf.image.resize(image, config.IMAGE_SIZE, method=\"bilinear\")\n    image = tf.cast(image, tf.float32) / 255.0\n    \n    label = tf.cast(label, tf.float32)\n    #         bowel       fluid       kidney      liver       spleen\n    labels = (label[0:1], label[1:2], label[2:5], label[5:8], label[8:11])\n    \n    return (image, labels)\n\n\naugmenter = keras.Sequential([\n    layers.RandomFlip(mode='horizontal_and_vertical'),\n    layers.RandomRotation(factor=0.2),\n    layers.RandomZoom(height_factor=0.2, width_factor=0.2),\n    layers.RandomContrast(0.2)\n])\n\ndef apply_augmentation(images, labels):\n    # Use the already created augmenter here\n    augmented_images = augmenter(images)\n    return augmented_images, labels\n\n\n\n\ndef build_dataset(image_paths, labels):\n    ds = (\n        tf.data.Dataset.from_tensor_slices((image_paths, labels))\n        .map(decode_image_and_label, num_parallel_calls=config.AUTOTUNE)\n        .shuffle(config.BATCH_SIZE * 10)\n        .batch(config.BATCH_SIZE)\n        .map(apply_augmentation, num_parallel_calls=config.AUTOTUNE)\n        .prefetch(config.AUTOTUNE)\n    )\n    return ds\n","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:54.674865Z","iopub.execute_input":"2024-12-10T22:48:54.675439Z","iopub.status.idle":"2024-12-10T22:48:54.75501Z","shell.execute_reply.started":"2024-12-10T22:48:54.675376Z","shell.execute_reply":"2024-12-10T22:48:54.753577Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"building the dataset","metadata":{}},{"cell_type":"code","source":"paths  = train_data.image_path.tolist()\nlabels = train_data[config.TARGET_COLS].values\n\nds = build_dataset(image_paths=paths, labels=labels)\nimages, labels = next(iter(ds))\nimages.shape, [label.shape for label in labels]","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:48:54.756427Z","iopub.execute_input":"2024-12-10T22:48:54.756859Z","iopub.status.idle":"2024-12-10T22:49:09.28129Z","shell.execute_reply.started":"2024-12-10T22:48:54.756817Z","shell.execute_reply":"2024-12-10T22:49:09.280127Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# No more customizing your plots by hand, KerasCV has your back ;)\nkeras_cv.visualization.plot_image_gallery(\n    images=images,\n    value_range=(0, 1),\n    rows=2,\n    cols=2,\n)","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:49:09.283133Z","iopub.execute_input":"2024-12-10T22:49:09.284257Z","iopub.status.idle":"2024-12-10T22:49:09.884691Z","shell.execute_reply.started":"2024-12-10T22:49:09.284205Z","shell.execute_reply":"2024-12-10T22:49:09.883376Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model Architecture","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\n\ndef build_model(warmup_steps, decay_steps):\n    # Define Input\n    inputs = keras.Input(shape=(256, 256, 3), batch_size=config.BATCH_SIZE)\n\n    # Preprocessing Layer for EfficientNetV2\n    preprocess = tf.keras.applications.efficientnet_v2.preprocess_input\n    x = preprocess(inputs)\n    \n    # Define Backbone using EfficientNetV2S\n    base_model = tf.keras.applications.EfficientNetV2S(\n        weights='imagenet',  # Load pretrained weights from ImageNet\n        include_top=False,   # No classification head\n        input_shape=(256, 256, 3)\n    )\n    base_model.trainable = True  # Set to False if freezing is needed\n    x = base_model(x)\n\n    # Global Average Pooling\n    gap = layers.GlobalAveragePooling2D()(x)\n    \n    # Adding complexity to each \"neck\"\n    def complex_neck(inputs):\n        x = layers.Dense(64, activation='silu')(inputs)\n        x = layers.BatchNormalization()(x)\n        x = layers.Dropout(0.3)(x)\n        x = layers.Dense(32, activation='silu')(x)\n        x = layers.BatchNormalization()(x)\n        x = layers.Dropout(0.2)(x)\n        return x\n\n    # Define 'necks' for each head\n    x_bowel = complex_neck(gap)\n    x_extra = complex_neck(gap)\n    x_liver = complex_neck(gap)\n    x_kidney = complex_neck(gap)\n    x_spleen = complex_neck(gap)\n    \n    # Define heads for each target output\n    out_bowel = layers.Dense(1, name='bowel', activation='sigmoid')(x_bowel)\n    out_extra = layers.Dense(1, name='extra', activation='sigmoid')(x_extra)\n    out_liver = layers.Dense(3, name='liver', activation='softmax')(x_liver)\n    out_kidney = layers.Dense(3, name='kidney', activation='softmax')(x_kidney)\n    out_spleen = layers.Dense(3, name='spleen', activation='softmax')(x_spleen)\n    \n    # Combine all outputs\n    outputs = [out_bowel, out_extra, out_liver, out_kidney, out_spleen]\n    \n    # Create the Model\n    model = keras.Model(inputs=inputs, outputs=outputs)\n    \n    # Custom learning rate schedule class (unchanged)\n    @tf.keras.utils.register_keras_serializable()\n    class WarmUpCosineDecaySchedule(tf.keras.optimizers.schedules.LearningRateSchedule):\n        def __init__(self, initial_lr, warmup_steps, decay_steps):\n            self.initial_lr = initial_lr\n            self.warmup_steps = warmup_steps\n            self.decay_steps = decay_steps\n\n        def __call__(self, step):\n            lr = tf.cond(\n                step < self.warmup_steps,\n                lambda: self.initial_lr * (tf.cast(step, tf.float32) / tf.cast(self.warmup_steps, tf.float32)),\n                lambda: self.initial_lr * 0.5 * (1 + tf.cos(tf.constant(np.pi, dtype=tf.float32) * \n                       (tf.cast(step, tf.float32) - tf.cast(self.warmup_steps, tf.float32)) / \n                       tf.cast((self.decay_steps - self.warmup_steps), tf.float32)))\n            )\n            return lr\n\n        def get_config(self):\n            return {\n                \"initial_lr\": self.initial_lr,\n                \"warmup_steps\": self.warmup_steps,\n                \"decay_steps\": self.decay_steps,\n            }\n\n    # Set initial learning rate and schedule\n    initial_learning_rate = 1e-4\n    lr_schedule = WarmUpCosineDecaySchedule(\n        initial_lr=initial_learning_rate,\n        warmup_steps=warmup_steps,\n        decay_steps=decay_steps\n    )\n    \n    # Define Optimizer\n    optimizer = keras.optimizers.Adam(learning_rate=lr_schedule)\n    \n    # Define Losses\n    loss = {\n        \"bowel\": keras.losses.BinaryCrossentropy(),\n        \"extra\": keras.losses.BinaryCrossentropy(),\n        \"liver\": keras.losses.CategoricalCrossentropy(),\n        \"kidney\": keras.losses.CategoricalCrossentropy(),\n        \"spleen\": keras.losses.CategoricalCrossentropy(),\n    }\n    \n    # Define Metrics\n    metrics = {\n        \"bowel\": [\"accuracy\"],\n        \"extra\": [\"accuracy\"],\n        \"liver\": [\"accuracy\"],\n        \"kidney\": [\"accuracy\"],\n        \"spleen\": [\"accuracy\"],\n    }\n\n    # Compile the Model\n    model.compile(\n        optimizer=optimizer,\n        loss=loss,\n        metrics=metrics\n    )\n    \n    return model\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:09.887012Z","iopub.execute_input":"2024-12-10T22:49:09.88758Z","iopub.status.idle":"2024-12-10T22:49:09.911897Z","shell.execute_reply.started":"2024-12-10T22:49:09.887511Z","shell.execute_reply":"2024-12-10T22:49:09.910368Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# The datasets processing","metadata":{}},{"cell_type":"code","source":"# get image_paths and labels\nprint(\"[INFO] Building the dataset...\")\ntrain_paths = train_data.image_path.values; train_labels = train_data[config.TARGET_COLS].values.astype(np.float32)\nvalid_paths = val_data.image_path.values; valid_labels = val_data[config.TARGET_COLS].values.astype(np.float32)\n\n# train and valid dataset\ntrain_ds = build_dataset(image_paths=train_paths, labels=train_labels)\nval_ds = build_dataset(image_paths=valid_paths, labels=valid_labels)\n\n# Calculate total steps, warmup, and decay steps\ntotal_train_steps = train_ds.cardinality().numpy() * config.BATCH_SIZE * config.EPOCHS\nwarmup_steps = int(total_train_steps * 0.10)\ndecay_steps = total_train_steps - warmup_steps\n\nprint(f\"{total_train_steps=}\")\nprint(f\"{warmup_steps=}\")\nprint(f\"{decay_steps=}\")","metadata":{"execution":{"iopub.status.busy":"2024-12-10T22:49:09.91354Z","iopub.execute_input":"2024-12-10T22:49:09.914052Z","iopub.status.idle":"2024-12-10T22:49:11.738496Z","shell.execute_reply.started":"2024-12-10T22:49:09.91401Z","shell.execute_reply":"2024-12-10T22:49:11.736744Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"used in confusion matrix function call back","metadata":{}},{"cell_type":"code","source":"def pred_decode_image(image_path):\n    file_bytes = tf.io.read_file(image_path)\n    image = tf.io.decode_png(file_bytes, channels=3, dtype=tf.uint8)\n    image = tf.image.resize(image, config.IMAGE_SIZE, method=\"bilinear\")\n    image = tf.cast(image, tf.float32) / 255.0\n    return image\n\ndef pred_build_dataset(image_paths):\n    ds = (\n        tf.data.Dataset.from_tensor_slices(image_paths)\n        .map(pred_decode_image, num_parallel_calls=config.AUTOTUNE)\n        .shuffle(config.BATCH_SIZE * 10)\n        .batch(config.BATCH_SIZE)\n        .prefetch(config.AUTOTUNE)\n    )\n    return ds","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.740233Z","iopub.execute_input":"2024-12-10T22:49:11.744195Z","iopub.status.idle":"2024-12-10T22:49:11.759631Z","shell.execute_reply.started":"2024-12-10T22:49:11.744107Z","shell.execute_reply":"2024-12-10T22:49:11.758441Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def post_proc(pred):\n    \"\"\"\n    Maps 11 model outputs to 13 target columns.\n    \"\"\"\n    proc_pred = np.empty((pred.shape[0], 13), dtype=\"float32\")\n\n    # bowel (binary: injury, healthy)\n    proc_pred[:, 0] = pred[:, 0]  # bowel_injury\n    proc_pred[:, 1] = 1 - proc_pred[:, 0]  # bowel_healthy\n\n    # extravasation (binary: injury, healthy)\n    proc_pred[:, 2] = pred[:, 1]  # extravasation_injury\n    proc_pred[:, 3] = 1 - proc_pred[:, 2]  # extravasation_healthy\n\n    # kidney (multi-class: healthy, low, high)\n    proc_pred[:, 4:7] = pred[:, 2:5]\n\n    # liver (multi-class: healthy, low, high)\n    proc_pred[:, 7:10] = pred[:, 5:8]\n\n    # spleen (multi-class: healthy, low, high)\n    proc_pred[:, 10:13] = pred[:, 8:11]\n\n    return proc_pred","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-11T00:19:35.986704Z","iopub.execute_input":"2024-12-11T00:19:35.988845Z","iopub.status.idle":"2024-12-11T00:19:36.00095Z","shell.execute_reply.started":"2024-12-11T00:19:35.988775Z","shell.execute_reply":"2024-12-11T00:19:35.999339Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(valid_labels[0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-11T00:19:38.605131Z","iopub.execute_input":"2024-12-11T00:19:38.605576Z","iopub.status.idle":"2024-12-11T00:19:38.613362Z","shell.execute_reply.started":"2024-12-11T00:19:38.605539Z","shell.execute_reply":"2024-12-11T00:19:38.612113Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Confusion Matrix with Sensitivity, Recall, and F1-Score","metadata":{}},{"cell_type":"code","source":"import os\nfrom sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay, precision_score, recall_score, f1_score\n\nclass MetricsCallback(tf.keras.callbacks.Callback):\n    def __init__(self, valid_paths, valid_labels, pred_build_dataset, target_cols, threshold=0.5, output_dir=\"metrics_output\"):\n        \"\"\"\n        Args:\n            valid_paths (list): List of image paths for validation.\n            valid_labels (numpy array): Corresponding labels for the validation paths.\n            pred_build_dataset (function): Function to build the dataset for prediction.\n            target_cols (list): List of target column names.\n            threshold (float): Threshold for binary classification.\n            output_dir (str): Directory to save metrics outputs.\n        \"\"\"\n        super().__init__()\n        self.valid_paths = valid_paths\n        self.valid_labels = valid_labels\n        self.pred_build_dataset = pred_build_dataset\n        self.target_cols = target_cols\n        self.threshold = threshold\n        self.output_dir = output_dir\n\n        # Create output directory if it doesn't exist\n        os.makedirs(output_dir, exist_ok=True)\n\n    def on_epoch_end(self, epoch, logs=None):\n        # Build dataset from valid_paths\n        dval = self.pred_build_dataset(self.valid_paths)\n\n        # Predict with the model\n        preds = self.model.predict(dval)\n\n        # Ensure predictions are concatenated correctly for multi-output models\n        if isinstance(preds, list):\n            preds = np.concatenate(preds, axis=-1)\n\n        # Post-process predictions to align with target columns\n        preds = post_proc(preds)\n\n        # Check shape consistency\n        if preds.shape[0] != len(self.valid_labels):\n            raise ValueError(\n                f\"Mismatch between number of predictions ({preds.shape[0]}) and labels ({len(self.valid_labels)}).\"\n            )\n\n        # Initialize binary predictions\n        binary_preds = preds.copy()\n\n        # Set binary predictions for kidney, liver, spleen\n        for organ, indices in zip(\n            [\"kidney\", \"liver\", \"spleen\"], \n            [(4, 5, 6), (7, 8, 9), (10, 11, 12)]  # Multi-class indices for each organ\n        ):\n            organ_preds = preds[:, indices]  # Extract predictions for the organ\n            max_indices = np.argmax(organ_preds, axis=1)  # Get the index of the largest value\n            binary_preds[:, indices] = 0  # Set all values to 0\n            binary_preds[np.arange(len(preds)), indices[0] + max_indices] = 1  # Set max value to 1\n\n        # Threshold predictions for binary targets (e.g., bowel, extravasation)\n        binary_preds[:, :4] = (preds[:, :4] > self.threshold).astype(int)\n\n        # Open a file to save predictions for this epoch\n        epoch_output_dir = os.path.join(self.output_dir, f\"epoch_{epoch+1}\")\n        os.makedirs(epoch_output_dir, exist_ok=True)\n        predictions_file = os.path.join(epoch_output_dir, f\"predictions_epoch_{epoch+1}.txt\")\n\n        with open(predictions_file, \"w\") as f:\n            # Compute confusion matrix and metrics for each target column\n            for i, target in enumerate(self.target_cols):\n                y_true = self.valid_labels[:, i]\n                y_pred = binary_preds[:, i]\n\n                # Save predictions and ground truth\n                f.write(f\"Target: {target}\\n\")\n                f.write(f\"Raw Predictions: {preds[:, i].tolist()}\\n\")\n                f.write(f\"Binary Predictions: {y_pred.tolist()}\\n\")\n                f.write(f\"Ground Truth: {y_true.tolist()}\\n\\n\")\n\n                # Compute confusion matrix\n                cm_labels = [0, 1]\n                cm = confusion_matrix(y_true, y_pred, labels=cm_labels)\n\n                # Save confusion matrix plot\n                disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=cm_labels)\n                disp.plot(cmap=plt.cm.Blues)\n                plt.title(f'Confusion Matrix for {target} (Epoch {epoch + 1})')\n\n                cm_plot_file = os.path.join(epoch_output_dir, f\"confusion_matrix_{target}.png\")\n                plt.savefig(cm_plot_file)\n                plt.close()\n\n                # Compute precision, recall, and F1-score\n                precision = precision_score(y_true, y_pred, average=\"binary\")\n                recall = recall_score(y_true, y_pred, average=\"binary\")\n                f1 = f1_score(y_true, y_pred, average=\"binary\")\n\n                # Save metrics to the file\n                f.write(f\"{target} - Precision: {precision:.4f}, Recall: {recall:.4f}, F1-Score: {f1:.4f}\\n\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-11T00:29:19.245236Z","iopub.execute_input":"2024-12-11T00:29:19.245745Z","iopub.status.idle":"2024-12-11T00:29:19.264044Z","shell.execute_reply.started":"2024-12-11T00:29:19.245701Z","shell.execute_reply":"2024-12-11T00:29:19.262496Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"update the lables for bowel and extravastion in the validation dataset so that the prediction result matches with the number of columns in the label.","metadata":{}},{"cell_type":"code","source":"def update_valid_labels(valid_labels):\n    \"\"\"\n    Update the valid_labels to include both healthy and injury labels for bowel and extravasation.\n    \"\"\"\n    updated_labels = np.zeros((valid_labels.shape[0], 13), dtype=\"float32\")\n    \n    # bowel (binary: injury, healthy)\n    updated_labels[:, 0] = valid_labels[:, 0]  # bowel_injury\n    updated_labels[:, 1] = 1 - updated_labels[:, 0]  # bowel_healthy\n\n    # extravasation (binary: injury, healthy)\n    updated_labels[:, 2] = valid_labels[:, 1]  # extravasation_injury\n    updated_labels[:, 3] = 1 - updated_labels[:, 2]  # extravasation_healthy\n\n    # kidney (multi-class: healthy, low, high)\n    updated_labels[:, 4:7] = valid_labels[:, 2:5]\n\n    # liver (multi-class: healthy, low, high)\n    updated_labels[:, 7:10] = valid_labels[:, 5:8]\n\n    # spleen (multi-class: healthy, low, high)\n    updated_labels[:, 10:13] = valid_labels[:, 8:11]\n\n    return updated_labels","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-11T00:19:58.845721Z","iopub.execute_input":"2024-12-11T00:19:58.846168Z","iopub.status.idle":"2024-12-11T00:19:58.854706Z","shell.execute_reply.started":"2024-12-11T00:19:58.846133Z","shell.execute_reply":"2024-12-11T00:19:58.853289Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"set matris callback","metadata":{}},{"cell_type":"code","source":"# Define the target columns (both healthy and injury for bowel and extravasation)\nTARGET_COLS = [\n    \"bowel_healthy\", \"bowel_injury\",  # Healthy and injury for bowel\n    \"extravasation_healthy\", \"extravasation_injury\",  # Healthy and injury for extravasation\n    \"kidney_healthy\", \"kidney_low\", \"kidney_high\",\n    \"liver_healthy\", \"liver_low\", \"liver_high\",\n    \"spleen_healthy\", \"spleen_low\", \"spleen_high\",\n]\n\n# Assuming valid_labels has 11 columns, we update it to match the 13 target columns\nvalid_labels_updated = update_valid_labels(valid_labels)\n\n# Initialize the metrics callback\nmetrics_callback = MetricsCallback(\n    valid_paths=valid_paths,\n    valid_labels=valid_labels_updated,\n    pred_build_dataset=pred_build_dataset,  # Replace with your function\n    target_cols=TARGET_COLS,\n    threshold=0.5\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-11T00:29:46.150975Z","iopub.execute_input":"2024-12-11T00:29:46.151433Z","iopub.status.idle":"2024-12-11T00:29:46.159594Z","shell.execute_reply.started":"2024-12-11T00:29:46.151395Z","shell.execute_reply":"2024-12-11T00:29:46.15839Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"updated label of validation dataset","metadata":{}},{"cell_type":"code","source":"print(valid_labels_updated[0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-11T00:29:49.374788Z","iopub.execute_input":"2024-12-11T00:29:49.375412Z","iopub.status.idle":"2024-12-11T00:29:49.383008Z","shell.execute_reply.started":"2024-12-11T00:29:49.375356Z","shell.execute_reply":"2024-12-11T00:29:49.381847Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Train the Model","metadata":{}},{"cell_type":"code","source":"# Build the model\nprint(\"[INFO] Building the model...\")\nmodel = build_model(warmup_steps, decay_steps)\n\n\n# Model checkpointing to save the best model\ncheckpoint_cb = keras.callbacks.ModelCheckpoint(\n    filepath='best_model.keras',\n    save_best_only=True,  # Only save the model with the best validation loss\n    monitor='val_loss',   # Monitor validation loss\n    mode='min',           # Minimize the validation loss\n    verbose=1             # Print a message when saving the model\n)\n\n# Early stopping to prevent overfitting\nearly_stopping = keras.callbacks.EarlyStopping(\n    monitor='val_loss', \n    patience=5,  # Stop training if validation loss doesn't improve for 5 epochs\n    restore_best_weights=True\n)\n\n# Train the model without class_weight\nhistory = model.fit(\n    train_ds,\n    epochs=config.EPOCHS,\n    validation_data=val_ds,\n    callbacks=[checkpoint_cb, metrics_callback]  # Use early stopping and checkpoint\n)","metadata":{"_kg_hide-input":true,"_kg_hide-output":false,"execution":{"iopub.status.busy":"2024-12-11T00:29:51.199999Z","iopub.execute_input":"2024-12-11T00:29:51.200445Z","iopub.status.idle":"2024-12-11T00:42:50.204895Z","shell.execute_reply.started":"2024-12-11T00:29:51.200409Z","shell.execute_reply":"2024-12-11T00:42:50.203452Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Visualize the training plots","metadata":{}},{"cell_type":"code","source":"# Create a 3x2 grid for the subplots\nfig, axes = plt.subplots(5, 1, figsize=(5, 15))\n\n# Flatten axes to iterate through them\naxes = axes.flatten()\n\n# Iterate through the metrics and plot them\nfor i, name in enumerate([\"bowel\", \"extra\", \"kidney\", \"liver\", \"spleen\"]):\n    # Plot training accuracy\n    axes[i].plot(history.history[name + '_accuracy'], label='Training ' + name)\n    # Plot validation accuracy\n    axes[i].plot(history.history['val_' + name + '_accuracy'], label='Validation ' + name)\n    axes[i].set_title(name)\n    axes[i].set_xlabel('Epoch')\n    axes[i].set_ylabel('Accuracy')\n    axes[i].legend()\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.864908Z","iopub.status.idle":"2024-12-10T22:49:11.865643Z","shell.execute_reply.started":"2024-12-10T22:49:11.865266Z","shell.execute_reply":"2024-12-10T22:49:11.865295Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# store best results\nbest_epoch = np.argmin(history.history['val_loss'])\nbest_loss = history.history['val_loss'][best_epoch]\nbest_acc_bowel = history.history['val_bowel_accuracy'][best_epoch]\nbest_acc_extra = history.history['val_extra_accuracy'][best_epoch]\nbest_acc_liver = history.history['val_liver_accuracy'][best_epoch]\nbest_acc_kidney = history.history['val_kidney_accuracy'][best_epoch]\nbest_acc_spleen = history.history['val_spleen_accuracy'][best_epoch]\n\n# Find mean accuracy\nbest_acc = np.mean(\n    [best_acc_bowel,\n     best_acc_extra,\n     best_acc_liver,\n     best_acc_kidney,\n     best_acc_spleen\n])\n\n\nprint(f'>>>> BEST Loss  : {best_loss:.3f}\\n>>>> BEST Acc   : {best_acc:.3f}\\n>>>> BEST Epoch : {best_epoch}\\n')\nprint('ORGAN Acc:')\nprint(f'  >>>> {\"Bowel\".ljust(15)} : {best_acc_bowel:.3f}')\nprint(f'  >>>> {\"Extravasation\".ljust(15)} : {best_acc_extra:.3f}')\nprint(f'  >>>> {\"Liver\".ljust(15)} : {best_acc_liver:.3f}')\nprint(f'  >>>> {\"Kidney\".ljust(15)} : {best_acc_kidney:.3f}')\nprint(f'  >>>> {\"Spleen\".ljust(15)} : {best_acc_spleen:.3f}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.867518Z","iopub.status.idle":"2024-12-10T22:49:11.868117Z","shell.execute_reply.started":"2024-12-10T22:49:11.867853Z","shell.execute_reply":"2024-12-10T22:49:11.86788Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Store the model for inference","metadata":{}},{"cell_type":"code","source":"# Define output paths\noutput_dir = '/kaggle/working/output'\nos.makedirs(output_dir, exist_ok=True)\n\n# Save performance metrics to a CSV file\nresults_file = os.path.join(output_dir, 'performance_metrics.csv')\nresults_df = pd.DataFrame({\n    \"Epoch\": [best_epoch],\n    \"Best_Loss\": [best_loss],\n    \"Best_Acc\": [best_acc],\n    \"Bowel_Accuracy\": [best_acc_bowel],\n    \"Extravasation_Accuracy\": [best_acc_extra],\n    \"Liver_Accuracy\": [best_acc_liver],\n    \"Kidney_Accuracy\": [best_acc_kidney],\n    \"Spleen_Accuracy\": [best_acc_spleen],\n})\nresults_df.to_csv(results_file, index=False)\n\n# Save loss and accuracy plot\nplt.figure(figsize=(10, 6))\nplt.plot(history.history[\"loss\"], label=\"Training Loss\")\nplt.plot(history.history[\"val_loss\"], label=\"Validation Loss\")\nplt.title(\"Loss Over Epochs\")\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.grid(True)\nplt.savefig(os.path.join(output_dir, 'loss_plot.png'))\n\nplt.figure(figsize=(10, 6))\nplt.plot(history.history['bowel_accuracy'], label='Bowel Accuracy')\nplt.plot(history.history['extra_accuracy'], label='Extravasation Accuracy')\nplt.plot(history.history['liver_accuracy'], label='Liver Accuracy')\nplt.plot(history.history['kidney_accuracy'], label='Kidney Accuracy')\nplt.plot(history.history['spleen_accuracy'], label='Spleen Accuracy')\nplt.title(f\"Organ Accuracy Over Epochs (Train)\")\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Accuracy\")\nplt.legend()\nplt.grid(True)\nplt.savefig(os.path.join(output_dir, 'organ_accuracy_plot_train.png'))\n\nplt.figure(figsize=(10, 6))\nplt.plot(history.history['val_bowel_accuracy'], label='Bowel Accuracy')\nplt.plot(history.history['val_extra_accuracy'], label='Extravasation Accuracy')\nplt.plot(history.history['val_liver_accuracy'], label='Liver Accuracy')\nplt.plot(history.history['val_kidney_accuracy'], label='Kidney Accuracy')\nplt.plot(history.history['val_spleen_accuracy'], label='Spleen Accuracy')\nplt.title(f\"Organ Accuracy Over Epochs (Validation)\")\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Accuracy\")\nplt.legend()\nplt.grid(True)\nplt.savefig(os.path.join(output_dir, 'organ_accuracy_plot_val.png'))\n\nprint(f'All results saved to: {output_dir}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.869882Z","iopub.status.idle":"2024-12-10T22:49:11.870423Z","shell.execute_reply.started":"2024-12-10T22:49:11.87016Z","shell.execute_reply":"2024-12-10T22:49:11.870186Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"saving the best model","metadata":{}},{"cell_type":"code","source":"model.save('best_model.keras')\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.872716Z","iopub.status.idle":"2024-12-10T22:49:11.873233Z","shell.execute_reply.started":"2024-12-10T22:49:11.872968Z","shell.execute_reply":"2024-12-10T22:49:11.872993Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Inference (Prediction)\nSimilar process as in trainig the model and creating confusion matrix","metadata":{}},{"cell_type":"code","source":"test_meta_df = pd.read_csv(f\"{RSNA_BASE_PATH}/test_series_meta.csv\")\n\n# Checking if patients are repeated by finding the number of unique patient IDs\nnum_rows = test_meta_df.shape[0]\nunique_patients = test_meta_df[\"patient_id\"].nunique()\n\nprint(f\"{num_rows=}\")\nprint(f\"{unique_patients=}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.875166Z","iopub.status.idle":"2024-12-10T22:49:11.875772Z","shell.execute_reply.started":"2024-12-10T22:49:11.875437Z","shell.execute_reply":"2024-12-10T22:49:11.875506Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_meta_df[\"dicom_folder\"] = RSNA_BASE_PATH + \"/\" + \"test_images\"\\\n                                    + \"/\" + test_meta_df.patient_id.astype(str)\\\n                                    + \"/\" + test_meta_df.series_id.astype(str)\n\ntest_folders = test_meta_df.dicom_folder.tolist()\ntest_paths = []\nfor folder in tqdm(test_folders):\n    test_paths += sorted(glob(os.path.join(folder, \"*dcm\")))[::STRIDE]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.878148Z","iopub.status.idle":"2024-12-10T22:49:11.87865Z","shell.execute_reply.started":"2024-12-10T22:49:11.878388Z","shell.execute_reply":"2024-12-10T22:49:11.878412Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df = pd.DataFrame(test_paths, columns=[\"dicom_path\"])\ntest_df[\"patient_id\"] = test_df.dicom_path.map(lambda x: x.split(\"/\")[-3]).astype(int)\ntest_df[\"series_id\"] = test_df.dicom_path.map(lambda x: x.split(\"/\")[-2]).astype(int)\ntest_df[\"instance_number\"] = test_df.dicom_path.map(lambda x: x.split(\"/\")[-1].replace(\".dcm\",\"\")).astype(int)\n\ntest_df[\"image_path\"] = f\"{IMAGE_DIR}/test_images\"\\\n                    + \"/\" + test_df.patient_id.astype(str)\\\n                    + \"/\" + test_df.series_id.astype(str)\\\n                    + \"/\" + test_df.instance_number.astype(str) +\".png\"\n\ntest_df.head(2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.879985Z","iopub.status.idle":"2024-12-10T22:49:11.880485Z","shell.execute_reply.started":"2024-12-10T22:49:11.880237Z","shell.execute_reply":"2024-12-10T22:49:11.880263Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Checking if patients are repeated by finding the number of unique patient IDs\nnum_rows = test_df.shape[0]\nunique_patients = test_df[\"patient_id\"].nunique()\n\nprint(f\"{num_rows=}\")\nprint(f\"{unique_patients=}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.882707Z","iopub.status.idle":"2024-12-10T22:49:11.883301Z","shell.execute_reply.started":"2024-12-10T22:49:11.88301Z","shell.execute_reply":"2024-12-10T22:49:11.883037Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!rm -r {IMAGE_DIR}\nos.makedirs(f\"{TEST_IMAGE_DIR}/train_images\", exist_ok=True)\nos.makedirs(f\"{TEST_IMAGE_DIR}/test_images\", exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.884956Z","iopub.status.idle":"2024-12-10T22:49:11.885483Z","shell.execute_reply.started":"2024-12-10T22:49:11.885206Z","shell.execute_reply":"2024-12-10T22:49:11.885234Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time\n\nfile_paths = test_df.dicom_path.tolist()\n_ = Parallel(n_jobs=2, backend=\"threading\")(\n    delayed(resize_and_save)(file_path) for file_path in tqdm(file_paths, leave=True, position=0)\n)\n\ndel _; gc.collect()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.887217Z","iopub.status.idle":"2024-12-10T22:49:11.887761Z","shell.execute_reply.started":"2024-12-10T22:49:11.887468Z","shell.execute_reply":"2024-12-10T22:49:11.887493Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def decode_image(image_path):\n    file_bytes = tf.io.read_file(image_path)\n    image = tf.io.decode_png(file_bytes, channels=3, dtype=tf.uint8)\n    image = tf.image.resize(image, config.IMAGE_SIZE, method=\"bilinear\")\n    image = tf.cast(image, tf.float32) / 255.0\n    return image\n\ndef build_dataset(image_paths):\n    ds = (\n        tf.data.Dataset.from_tensor_slices(image_paths)\n        .map(decode_image, num_parallel_calls=config.AUTOTUNE)\n        .shuffle(config.BATCH_SIZE * 10)\n        .batch(config.BATCH_SIZE)\n        .prefetch(config.AUTOTUNE)\n    )\n    return ds","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.889396Z","iopub.status.idle":"2024-12-10T22:49:11.889946Z","shell.execute_reply.started":"2024-12-10T22:49:11.889638Z","shell.execute_reply":"2024-12-10T22:49:11.889689Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"paths  = test_df.image_path.tolist()\n\nds = build_dataset(paths)\nimages = next(iter(ds))\n\nimages.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.891174Z","iopub.status.idle":"2024-12-10T22:49:11.891649Z","shell.execute_reply.started":"2024-12-10T22:49:11.891404Z","shell.execute_reply":"2024-12-10T22:49:11.891441Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"keras_cv.visualization.plot_image_gallery(\n    images=images,\n    value_range=(0, 1),\n    rows=1,\n    cols=3,\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.893837Z","iopub.status.idle":"2024-12-10T22:49:11.89435Z","shell.execute_reply.started":"2024-12-10T22:49:11.894083Z","shell.execute_reply":"2024-12-10T22:49:11.894108Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def post_proc(pred):\n    proc_pred = np.empty((pred.shape[0], 2*2 + 3*3), dtype=\"float32\")\n\n    # bowel, extravasation\n    proc_pred[:, 0] = pred[:, 0]\n    proc_pred[:, 1] = 1 - proc_pred[:, 0]\n    proc_pred[:, 2] = pred[:, 1]\n    proc_pred[:, 3] = 1 - proc_pred[:, 2]\n    \n    # liver, kidney, sneel\n    proc_pred[:, 4:7] = pred[:, 2:5]\n    proc_pred[:, 7:10] = pred[:, 5:8]\n    proc_pred[:, 10:13] = pred[:, 8:11]\n\n    return proc_pred","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.898102Z","iopub.status.idle":"2024-12-10T22:49:11.898646Z","shell.execute_reply.started":"2024-12-10T22:49:11.898375Z","shell.execute_reply":"2024-12-10T22:49:11.898404Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Getting unique patient IDs from test dataset\npatient_ids = test_df[\"patient_id\"].unique()\n\n# Initializing array to store predictions\npatient_preds = np.zeros(\n    shape=(len(patient_ids), 2*2 + 3*3),\n    dtype=\"float32\"\n)\n\n# Iterating over each patient\nfor pidx, patient_id in tqdm(enumerate(patient_ids), total=len(patient_ids), desc=\"Patients \"):\n    print(f\"Patient ID: {patient_id}\")\n    \n    # Query the dataframe for a particular patient\n    patient_df = test_df.query(\"patient_id == @patient_id\")\n    \n    # Getting image paths for a patient\n    patient_paths = patient_df.image_path.tolist()\n\n    # Building dataset for prediction\n    dtest = build_dataset(patient_paths)\n    \n    # Predicting with the model\n    pred = model.predict(dtest)\n    pred = np.concatenate(pred, axis=-1).astype(\"float32\")\n    pred = pred[:len(patient_paths), :]\n    pred = np.mean(pred.reshape(1, len(patient_paths), 11), axis=0)\n    pred = np.max(pred, axis=0, keepdims=True)\n    \n    patient_preds[pidx, :] += post_proc(pred)[0]\n    \n\n    # Deleting variables to free up memory \n    del patient_df, patient_paths, dtest, pred; gc.collect()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.901077Z","iopub.status.idle":"2024-12-10T22:49:11.901602Z","shell.execute_reply.started":"2024-12-10T22:49:11.901342Z","shell.execute_reply":"2024-12-10T22:49:11.901368Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TARGET_COLS  = [\n        \"bowel_healthy\", \"bowel_injury\",  \"extravasation_healthy\", \"extravasation_injury\",\n        \"kidney_healthy\", \"kidney_low\", \"kidney_high\",\n        \"liver_healthy\", \"liver_low\", \"liver_high\",\n        \"spleen_healthy\", \"spleen_low\", \"spleen_high\",\n    ]\n\n# Create Submission\npred_df = pd.DataFrame({\"patient_id\":patient_ids,})\npred_df[TARGET_COLS] = patient_preds.astype(\"float32\")\n\n# Align with sample submission\nsub_df = pd.read_csv(f\"{RSNA_BASE_PATH}/sample_submission.csv\")\nsub_df = sub_df[[\"patient_id\"]]\nsub_df = sub_df.merge(pred_df, on=\"patient_id\", how=\"left\")\n\nsub_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-12-10T22:49:11.90922Z","iopub.status.idle":"2024-12-10T22:49:11.912291Z","shell.execute_reply.started":"2024-12-10T22:49:11.911913Z","shell.execute_reply":"2024-12-10T22:49:11.911956Z"}},"outputs":[],"execution_count":null}]}