{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"! pip install -q git+https://github.com/keras-team/keras-cv","metadata":{"execution":{"iopub.status.busy":"2023-10-05T17:04:37.949502Z","iopub.execute_input":"2023-10-05T17:04:37.950241Z","iopub.status.idle":"2023-10-05T17:05:00.693271Z","shell.execute_reply.started":"2023-10-05T17:04:37.950184Z","shell.execute_reply":"2023-10-05T17:05:00.69209Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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","metadata":{"execution":{"iopub.status.busy":"2023-10-05T17:05:00.696022Z","iopub.execute_input":"2023-10-05T17:05:00.696356Z","iopub.status.idle":"2023-10-05T17:05:07.512165Z","shell.execute_reply.started":"2023-10-05T17:05:00.69633Z","shell.execute_reply":"2023-10-05T17:05:07.511233Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Config:\n    SEED = 42\n    IMAGE_SIZE = [256, 256]\n    BATCH_SIZE = 64\n    EPOCHS = 10\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":"2023-10-05T17:05:07.513626Z","iopub.execute_input":"2023-10-05T17:05:07.51508Z","iopub.status.idle":"2023-10-05T17:05:07.520544Z","shell.execute_reply.started":"2023-10-05T17:05:07.515044Z","shell.execute_reply":"2023-10-05T17:05:07.519774Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"keras.utils.set_random_seed(seed=config.SEED)","metadata":{"execution":{"iopub.status.busy":"2023-10-05T17:05:07.52198Z","iopub.execute_input":"2023-10-05T17:05:07.522377Z","iopub.status.idle":"2023-10-05T17:05:07.544606Z","shell.execute_reply.started":"2023-10-05T17:05:07.52234Z","shell.execute_reply":"2023-10-05T17:05:07.543285Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BASE_PATH = f\"/kaggle/input/rsna-atd-512x512-png-v2-dataset\"","metadata":{"execution":{"iopub.status.busy":"2023-10-05T17:05:07.548762Z","iopub.execute_input":"2023-10-05T17:05:07.550365Z","iopub.status.idle":"2023-10-05T17:05:07.557262Z","shell.execute_reply.started":"2023-10-05T17:05:07.550317Z","shell.execute_reply":"2023-10-05T17:05:07.556068Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# train\ndataframe = pd.read_csv(f\"{BASE_PATH}/train.csv\")\ndataframe[\"image_path\"] = f\"{BASE_PATH}/train_images\"\\\n                    + \"/\" + dataframe.patient_id.astype(str)\\\n                    + \"/\" + dataframe.series_id.astype(str)\\\n                    + \"/\" + dataframe.instance_number.astype(str) +\".png\"\ndataframe = dataframe.drop_duplicates()\n\ndataframe.head()","metadata":{"execution":{"iopub.status.busy":"2023-10-05T17:05:07.558744Z","iopub.execute_input":"2023-10-05T17:05:07.559556Z","iopub.status.idle":"2023-10-05T17:05:07.681515Z","shell.execute_reply.started":"2023-10-05T17:05:07.559522Z","shell.execute_reply":"2023-10-05T17:05:07.680261Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Function to handle the split for each group\ndef split_group(group, test_size=0.2):\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 dataframe.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)","metadata":{"execution":{"iopub.status.busy":"2023-10-05T17:05:07.682945Z","iopub.execute_input":"2023-10-05T17:05:07.683807Z","iopub.status.idle":"2023-10-05T17:05:07.774999Z","shell.execute_reply.started":"2023-10-05T17:05:07.683776Z","shell.execute_reply":"2023-10-05T17:05:07.773931Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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\ndef apply_augmentation(images, labels):\n    augmenter = keras_cv.layers.Augmenter(\n        [\n            keras_cv.layers.RandomFlip(mode=\"horizontal_and_vertical\"),\n            keras_cv.layers.RandomCutout(height_factor=0.2, width_factor=0.2),\n            \n        ]\n    )\n    return (augmenter(images), labels)\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","metadata":{"execution":{"iopub.status.busy":"2023-10-05T17:05:07.776777Z","iopub.execute_input":"2023-10-05T17:05:07.777098Z","iopub.status.idle":"2023-10-05T17:05:07.785206Z","shell.execute_reply.started":"2023-10-05T17:05:07.777071Z","shell.execute_reply":"2023-10-05T17:05:07.784214Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"2023-10-05T17:05:07.786561Z","iopub.execute_input":"2023-10-05T17:05:07.786879Z","iopub.status.idle":"2023-10-05T17:05:17.520339Z","shell.execute_reply.started":"2023-10-05T17:05:07.786853Z","shell.execute_reply":"2023-10-05T17:05:17.518719Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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":"2023-10-05T17:05:17.522388Z","iopub.execute_input":"2023-10-05T17:05:17.524183Z","iopub.status.idle":"2023-10-05T17:05:18.169563Z","shell.execute_reply.started":"2023-10-05T17:05:17.524115Z","shell.execute_reply":"2023-10-05T17:05:18.168312Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import keras\nimport tensorflow as tf\nfrom keras.layers import GlobalAveragePooling2D, Dense, Input\n\n# Define a custom callback for cosine decay\nclass CosineDecayWithWarmup(keras.callbacks.Callback):\n    def __init__(self, initial_learning_rate, decay_steps, warmup_steps, alpha=0.0):\n        super(CosineDecayWithWarmup, self).__init__()\n        self.initial_learning_rate = initial_learning_rate\n        self.decay_steps = decay_steps\n        self.warmup_steps = warmup_steps\n        self.alpha = alpha\n\n    def on_epoch_begin(self, epoch, logs=None):\n        if epoch < self.warmup_steps:\n            lr = (self.initial_learning_rate - self.alpha) * (epoch / self.warmup_steps) + self.alpha\n        else:\n            lr = self.alpha + 0.5 * (self.initial_learning_rate - self.alpha) * (\n                1 + tf.cos((epoch - self.warmup_steps) / (self.decay_steps - self.warmup_steps) * 3.14159265)\n            )\n        keras.backend.set_value(self.model.optimizer.lr, lr)\n\ndef build_model(warmup_steps, decay_steps):\n    # Define Input\n    inputs = Input(shape=config.IMAGE_SIZE + [3,], batch_size=config.BATCH_SIZE)\n    \n    # Define Backbone (DenseNet121 as an example)\n    backbone = tf.keras.applications.DenseNet121(\n        include_top=False,  # Exclude the top (classification) layer\n        weights='imagenet',  # You can specify None if you don't want to use pre-trained weights\n        input_tensor=inputs,\n    )\n    x = backbone.output\n    \n    # GAP to get the activation maps\n    gap = GlobalAveragePooling2D()\n    x = gap(x)\n\n    # Define 'necks' for each head\n    x_bowel = Dense(32, activation='relu')(x)\n    x_extra = Dense(32, activation='relu')(x)\n    x_liver = Dense(32, activation='relu')(x)\n    x_kidney = Dense(32, activation='relu')(x)\n    x_spleen = Dense(32, activation='relu')(x)\n\n    # Define heads\n    out_bowel = Dense(1, name='bowel', activation='sigmoid')(x_bowel)\n    out_extra = Dense(1, name='extra', activation='sigmoid')(x_extra)\n    out_liver = Dense(3, name='liver', activation='softmax')(x_liver)\n    out_kidney = Dense(3, name='kidney', activation='softmax')(x_kidney)\n    out_spleen = Dense(3, name='spleen', activation='softmax')(x_spleen)\n    \n    # Concatenate the outputs\n    outputs = [out_bowel, out_extra, out_liver, out_kidney, out_spleen]\n\n    # Create model\n    print(\"[INFO] Building the model...\")\n    model = keras.Model(inputs=inputs, outputs=outputs)\n\n    # Compile the model\n    optimizer = keras.optimizers.Adam(learning_rate=1e-4)  # Set the initial learning rate here\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    metrics = {\n        \"bowel\": [\"accuracy\"],\n        \"extra\": [\"accuracy\"],\n        \"liver\": [\"accuracy\"],\n        \"kidney\": [\"accuracy\"],\n        \"spleen\": [\"accuracy\"],\n    }\n    print(\"[INFO] Compiling the model...\")\n    model.compile(\n        optimizer=optimizer,\n        loss=loss,\n        metrics=metrics\n    )\n    \n    return model\n\n# Define the number of warmup and decay steps\nwarmup_steps = 100\ndecay_steps = 1000\n\n# Create the model\nmodel = build_model(warmup_steps, decay_steps)\n\n# Define the custom learning rate schedule callback\ncosine_decay_callback = CosineDecayWithWarmup(\n    initial_learning_rate=1e-4,\n    decay_steps=decay_steps,\n    warmup_steps=warmup_steps,\n)\n\n# Train the model\nprint(\"[INFO] Training...\")\n","metadata":{"execution":{"iopub.status.busy":"2023-10-05T17:14:28.44343Z","iopub.execute_input":"2023-10-05T17:14:28.445542Z","iopub.status.idle":"2023-10-05T17:14:33.260863Z","shell.execute_reply.started":"2023-10-05T17:14:28.445469Z","shell.execute_reply":"2023-10-05T17:14:33.259472Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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\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":"2023-10-05T17:14:38.083111Z","iopub.execute_input":"2023-10-05T17:14:38.084041Z","iopub.status.idle":"2023-10-05T17:14:40.369259Z","shell.execute_reply.started":"2023-10-05T17:14:38.083983Z","shell.execute_reply":"2023-10-05T17:14:40.367655Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# build the model\nprint(\"[INFO] Building the model...\")\nmodel = build_model(warmup_steps, decay_steps)\n\n# train\nprint(\"[INFO] Training...\")\nhistory = model.fit(\n    train_ds,\n    epochs=config.EPOCHS,\n    validation_data=val_ds,\n)","metadata":{"execution":{"iopub.status.busy":"2023-10-05T17:14:42.770337Z","iopub.execute_input":"2023-10-05T17:14:42.771193Z"},"trusted":true},"execution_count":null,"outputs":[]},{"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_count":null,"outputs":[]},{"cell_type":"code","source":"plt.plot(history.history[\"loss\"], label=\"loss\")\nplt.plot(history.history[\"val_loss\"], label=\"val loss\")\nplt.legend()\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"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_count":null,"outputs":[]},{"cell_type":"code","source":"# Save the model\nmodel.save(\"rsna-atd.keras\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}