{"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"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":52254,"databundleVersionId":6307054,"sourceType":"competition"},{"sourceId":6211844,"sourceType":"datasetVersion","datasetId":3567114}],"dockerImageVersionId":30527,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"Aswaf's notebooks have been used as reference . \n\nhttps://www.kaggle.com/code/awsaf49/rsna-atd-cnn-tpu-train\n\nhttps://www.kaggle.com/code/awsaf49/rsna-atd-cnn-tpu-infer","metadata":{}},{"cell_type":"code","source":"! pip install -q keras-cv wandb keras-core","metadata":{"_kg_hide-output":true,"_kg_hide-input":false,"execution":{"iopub.status.busy":"2023-08-15T20:05:41.999876Z","iopub.execute_input":"2023-08-15T20:05:42.0003Z","iopub.status.idle":"2023-08-15T20:05:58.967415Z","shell.execute_reply.started":"2023-08-15T20:05:42.000266Z","shell.execute_reply":"2023-08-15T20:05:58.966054Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import wandb\nfrom kaggle_secrets import UserSecretsClient\nfrom datetime import datetime","metadata":{"execution":{"iopub.status.busy":"2023-08-15T17:41:59.96038Z","iopub.execute_input":"2023-08-15T17:41:59.960943Z","iopub.status.idle":"2023-08-15T17:42:01.12589Z","shell.execute_reply.started":"2023-08-15T17:41:59.960897Z","shell.execute_reply":"2023-08-15T17:42:01.124519Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Setup user secrets for login\nuser_secrets = UserSecretsClient()\nwandb_api = user_secrets.get_secret(\"api_key\") ","metadata":{"execution":{"iopub.status.busy":"2023-08-15T17:42:11.720632Z","iopub.execute_input":"2023-08-15T17:42:11.721194Z","iopub.status.idle":"2023-08-15T17:42:13.324899Z","shell.execute_reply.started":"2023-08-15T17:42:11.721156Z","shell.execute_reply":"2023-08-15T17:42:13.32331Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Login\nwandb.login(key = wandb_api)\n\nrun = wandb.init(project = \"RSNA1\",\n                 name = f\"Run_{datetime.now().strftime('%d%m%Y%H%M%S')}\", \n                 notes = \"add some features\",\n                 tags = [],\n                 config = dict(competition = 'RSNA',\n                               _wandb_kernel = 'tensorgirl',\n                               batch_size = 32,\n                               epochs = 30,\n                               learning_rate = 0.005)\n)\n\nconfig = wandb.config","metadata":{"execution":{"iopub.status.busy":"2023-08-15T17:42:18.760881Z","iopub.execute_input":"2023-08-15T17:42:18.762034Z","iopub.status.idle":"2023-08-15T17:42:59.458958Z","shell.execute_reply.started":"2023-08-15T17:42:18.761991Z","shell.execute_reply":"2023-08-15T17:42:59.457Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#import wandb\n#wandb.login(key=api_key)","metadata":{"execution":{"iopub.status.busy":"2023-08-13T13:59:50.225149Z","iopub.execute_input":"2023-08-13T13:59:50.225548Z","iopub.status.idle":"2023-08-13T13:59:50.230324Z","shell.execute_reply.started":"2023-08-13T13:59:50.225518Z","shell.execute_reply":"2023-08-13T13:59:50.229147Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\n\nos.environ[\"KERAS_BACKEND\"] = \"tensorflow\"\nimport keras_cv\n# import keras_core as keras\n# from keras_core import layers\n\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\n\nimport tensorflow.keras as keras\nfrom matplotlib import pyplot as plt\nfrom sklearn.model_selection import train_test_split","metadata":{"execution":{"iopub.status.busy":"2023-08-15T17:43:08.554371Z","iopub.execute_input":"2023-08-15T17:43:08.554944Z","iopub.status.idle":"2023-08-15T17:43:08.565713Z","shell.execute_reply.started":"2023-08-15T17:43:08.5549Z","shell.execute_reply":"2023-08-15T17:43:08.56387Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Config:\n    SEED = 42\n    IMAGE_SIZE = [256, 256]\n    BATCH_SIZE = 16\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-08-15T17:46:34.285945Z","iopub.execute_input":"2023-08-15T17:46:34.286525Z","iopub.status.idle":"2023-08-15T17:46:35.644647Z","shell.execute_reply.started":"2023-08-15T17:46:34.286476Z","shell.execute_reply":"2023-08-15T17:46:35.643308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BASE_PATH = f\"/kaggle/input/rsna-atd-512x512-png-v2-dataset\"","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-08-15T17:43:21.865485Z","iopub.execute_input":"2023-08-15T17:43:21.866024Z","iopub.status.idle":"2023-08-15T17:43:21.872696Z","shell.execute_reply.started":"2023-08-15T17:43:21.865982Z","shell.execute_reply":"2023-08-15T17:43:21.871207Z"},"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(2)","metadata":{"execution":{"iopub.status.busy":"2023-08-15T17:43:26.149404Z","iopub.execute_input":"2023-08-15T17:43:26.149864Z","iopub.status.idle":"2023-08-15T17:43:26.312005Z","shell.execute_reply.started":"2023-08-15T17:43:26.149829Z","shell.execute_reply":"2023-08-15T17:43:26.31085Z"},"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-08-15T17:43:32.501253Z","iopub.execute_input":"2023-08-15T17:43:32.501786Z","iopub.status.idle":"2023-08-15T17:43:32.636603Z","shell.execute_reply.started":"2023-08-15T17:43:32.501744Z","shell.execute_reply":"2023-08-15T17:43:32.635244Z"},"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 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        .prefetch(config.AUTOTUNE)\n    )\n    return ds\n\ndef build_dataset_wandb(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        .prefetch(config.AUTOTUNE)\n    )\n    return ds","metadata":{"execution":{"iopub.status.busy":"2023-08-15T17:58:14.387182Z","iopub.execute_input":"2023-08-15T17:58:14.38776Z","iopub.status.idle":"2023-08-15T17:58:15.572126Z","shell.execute_reply.started":"2023-08-15T17:58:14.387714Z","shell.execute_reply":"2023-08-15T17:58:15.569522Z"},"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)\nunbatched_ds = build_dataset_wandb(image_paths=paths, labels=labels)\nimages, labels = next(iter(unbatched_ds))\nimages.shape, [label.shape for label in labels]","metadata":{"execution":{"iopub.status.busy":"2023-08-15T17:58:21.940396Z","iopub.execute_input":"2023-08-15T17:58:21.940825Z","iopub.status.idle":"2023-08-15T17:58:23.931409Z","shell.execute_reply.started":"2023-08-15T17:58:21.940786Z","shell.execute_reply":"2023-08-15T17:58:23.929876Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Wandb tables","metadata":{}},{"cell_type":"code","source":"run = wandb.init(project = 'rsna',\n                 config = {},\n                 save_code = True,\n                 \n)\ncolumns = ['image','bowel','fluid','kidney','liver','spleen']\ntable = wandb.Table(columns=columns)","metadata":{"execution":{"iopub.status.busy":"2023-08-15T17:58:29.325378Z","iopub.execute_input":"2023-08-15T17:58:29.325963Z","iopub.status.idle":"2023-08-15T17:59:09.80499Z","shell.execute_reply.started":"2023-08-15T17:58:29.325921Z","shell.execute_reply":"2023-08-15T17:59:09.8037Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm import tqdm\nfor img, lab in tqdm(unbatched_ds):\n        table.add_data(wandb.Image(img.numpy().astype(int)),lab[0].numpy().astype(int),lab[1].numpy().astype(int),lab[2].numpy().astype(int),lab[3].numpy().astype(int),lab[4].numpy().astype(int))","metadata":{"execution":{"iopub.status.busy":"2023-08-15T17:59:20.170099Z","iopub.execute_input":"2023-08-15T17:59:20.170677Z","iopub.status.idle":"2023-08-15T18:00:19.988778Z","shell.execute_reply.started":"2023-08-15T17:59:20.170628Z","shell.execute_reply":"2023-08-15T18:00:19.987309Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"wandb.log({\"Table\":table})\nrun.finish()","metadata":{"execution":{"iopub.status.busy":"2023-08-15T18:01:49.695579Z","iopub.execute_input":"2023-08-15T18:01:49.696041Z","iopub.status.idle":"2023-08-15T18:02:03.670861Z","shell.execute_reply.started":"2023-08-15T18:01:49.696004Z","shell.execute_reply":"2023-08-15T18:02:03.669614Z"},"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-08-15T18:02:13.665812Z","iopub.execute_input":"2023-08-15T18:02:13.666302Z","iopub.status.idle":"2023-08-15T18:02:13.905802Z","shell.execute_reply.started":"2023-08-15T18:02:13.666262Z","shell.execute_reply":"2023-08-15T18:02:13.904151Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"run = wandb.init(project = 'rsna',\n                 config = config,\n                 save_code = True,\n                 name='yolov8'\n                 \n)","metadata":{"execution":{"iopub.status.busy":"2023-08-15T18:02:29.78041Z","iopub.execute_input":"2023-08-15T18:02:29.780859Z","iopub.status.idle":"2023-08-15T18:03:02.953435Z","shell.execute_reply.started":"2023-08-15T18:02:29.780826Z","shell.execute_reply":"2023-08-15T18:03:02.951808Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from wandb.keras import WandbCallback, WandbMetricsLogger","metadata":{"execution":{"iopub.status.busy":"2023-08-15T18:03:48.630492Z","iopub.execute_input":"2023-08-15T18:03:48.630938Z","iopub.status.idle":"2023-08-15T18:03:49.719771Z","shell.execute_reply.started":"2023-08-15T18:03:48.630904Z","shell.execute_reply":"2023-08-15T18:03:49.717824Z"},"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\n\nprint(f\"{total_train_steps=}\")","metadata":{"execution":{"iopub.status.busy":"2023-08-15T18:03:56.130521Z","iopub.execute_input":"2023-08-15T18:03:56.130977Z","iopub.status.idle":"2023-08-15T18:03:57.202691Z","shell.execute_reply.started":"2023-08-15T18:03:56.13094Z","shell.execute_reply":"2023-08-15T18:03:57.201323Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Yolov8","metadata":{}},{"cell_type":"code","source":"def build_model():\n    # Define Input\n    inputs = keras.Input(shape=[256,256,3],batch_size=config.BATCH_SIZE)\n    # Define Backbone\n    backbone = keras_cv.models.YOLOV8Backbone.from_preset(\n    \"yolo_v8_xs_backbone_coco\"\n)\n#     backbone.include_rescaling = False\n    x = backbone(inputs)\n    # GAP to get the activation maps\n    gap = keras.layers.GlobalAveragePooling2D(name='global')\n    x = gap(x)\n\n    # Define 'necks' for each head\n    x_bowel = keras.layers.Dense(32, activation='silu')(x)\n    x_extra = keras.layers.Dense(32, activation='silu')(x)\n    x_liver = keras.layers.Dense(32, activation='silu')(x)\n    x_kidney = keras.layers.Dense(32, activation='silu')(x)\n    x_spleen = keras.layers.Dense(32, activation='silu')(x)\n\n    # Define heads\n    out_bowel = keras.layers.Dense(1, name='bowel', activation='sigmoid')(x_bowel) # use sigmoid to convert predictions to [0-1]\n    out_extra = keras.layers.Dense(1, name='extra', activation='sigmoid')(x_extra) # use sigmoid to convert predictions to [0-1]\n    out_liver = keras.layers.Dense(3, name='liver', activation='softmax')(x_liver) # use softmax for the liver head\n    out_kidney = keras.layers.Dense(3, name='kidney', activation='softmax')(x_kidney) # use softmax for the kidney head\n    out_spleen = keras.layers.Dense(3, name='spleen', activation='softmax')(x_spleen) # use softmax for the spleen head\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   \n    # Compile the model\n    optimizer = keras.optimizers.Adam(learning_rate=1e-3)\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    model.summary()\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2023-08-15T18:04:05.501308Z","iopub.execute_input":"2023-08-15T18:04:05.501812Z","iopub.status.idle":"2023-08-15T18:04:06.450712Z","shell.execute_reply.started":"2023-08-15T18:04:05.501772Z","shell.execute_reply":"2023-08-15T18:04:06.449126Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# build the model\n\nprint(\"[INFO] Building the model...\")\nmodel = build_model()\ncall2 = wandb.keras.WandbModelCheckpoint(filepath='model.h5', \n                                               monitor='val_loss',\n                                               verbose=1,\n                                               save_best_only=True,\n                                               save_weights_only=False,\n                                               mode='min',)\n# train\nprint(\"[INFO] Training...\")\n\nhistory = model.fit(\n    train_ds,\n    epochs=5,\n    validation_data=val_ds,\n    verbose=1,\n    callbacks=[WandbCallback(),call2]\n)","metadata":{"_kg_hide-input":true,"_kg_hide-output":false,"execution":{"iopub.status.busy":"2023-08-15T18:06:31.59374Z","iopub.execute_input":"2023-08-15T18:06:31.59415Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"artifact = wandb.Artifact(name='yolo_model', type='model')\nartifact.add_file('/kaggle/working/model.h5')\nrun.log_artifact(artifact)\nrun.finish()","metadata":{"execution":{"iopub.status.busy":"2023-08-15T18:06:14.828539Z","iopub.execute_input":"2023-08-15T18:06:14.828989Z","iopub.status.idle":"2023-08-15T18:06:16.260396Z","shell.execute_reply.started":"2023-08-15T18:06:14.828954Z","shell.execute_reply":"2023-08-15T18:06:16.258669Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def build_model(lr):\n    # Define Input\n    inputs = keras.Input(shape=[256,256,3],batch_size=config.BATCH_SIZE)\n    # Define Backbone\n    backbone = keras_cv.models.YOLOV8Backbone.from_preset(\n    \"yolo_v8_xs_backbone_coco\"\n)\n#     backbone.include_rescaling = False\n    x = backbone(inputs)\n    # GAP to get the activation maps\n    gap = keras.layers.GlobalAveragePooling2D(name='global')\n    x = gap(x)\n\n    # Define 'necks' for each head\n    x_bowel = keras.layers.Dense(32, activation='silu')(x)\n    x_extra = keras.layers.Dense(32, activation='silu')(x)\n    x_liver = keras.layers.Dense(32, activation='silu')(x)\n    x_kidney = keras.layers.Dense(32, activation='silu')(x)\n    x_spleen = keras.layers.Dense(32, activation='silu')(x)\n\n    # Define heads\n    out_bowel = keras.layers.Dense(1, name='bowel', activation='sigmoid')(x_bowel) # use sigmoid to convert predictions to [0-1]\n    out_extra = keras.layers.Dense(1, name='extra', activation='sigmoid')(x_extra) # use sigmoid to convert predictions to [0-1]\n    out_liver = keras.layers.Dense(3, name='liver', activation='softmax')(x_liver) # use softmax for the liver head\n    out_kidney = keras.layers.Dense(3, name='kidney', activation='softmax')(x_kidney) # use softmax for the kidney head\n    out_spleen = keras.layers.Dense(3, name='spleen', activation='softmax')(x_spleen) # use softmax for the spleen head\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   \n    # Compile the model\n    optimizer = keras.optimizers.Adam(learning_rate=lr)\n    loss = {\"bowel\":keras.losses.BinaryCrossentropy(),\"extra\":keras.losses.BinaryCrossentropy(),\"liver\":keras.losses.CategoricalCrossentropy(),\"kidney\":keras.losses.CategoricalCrossentropy(),\"spleen\":keras.losses.CategoricalCrossentropy(),}\n    metrics = {\"bowel\":[\"accuracy\"],\"extra\":[\"accuracy\"],\"liver\":[\"accuracy\"],\"kidney\":[\"accuracy\"],\"spleen\":[\"accuracy\"],}\n    print(\"[INFO] Compiling the model...\")\n    model.compile(\n     optimizer=optimizer,\n      loss=loss,\n      metrics=metrics\n    )\n    model.summary()\n    \n    return model","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def build_model(lr):\n    # Define Input\n    inputs = keras.Input(shape=[256,256,3],batch_size=config.BATCH_SIZE)\n    # Define Backbone\n    backbone = keras_cv.models.YOLOV8Backbone.from_preset(\n    \"yolo_v8_xs_backbone_coco\"\n)\n#     backbone.include_rescaling = False\n    x = backbone(inputs)\n    # GAP to get the activation maps\n    gap = keras.layers.GlobalAveragePooling2D(name='global')\n    x = gap(x)\n\n    # Define 'necks' for each head\n    x_bowel = keras.layers.Dense(32, activation='silu')(x)\n    x_extra = keras.layers.Dense(32, activation='silu')(x)\n    x_liver = keras.layers.Dense(32, activation='silu')(x)\n    x_kidney = keras.layers.Dense(32, activation='silu')(x)\n    x_spleen = keras.layers.Dense(32, activation='silu')(x)\n\n    # Define heads\n    out_bowel = keras.layers.Dense(1, name='bowel', activation='sigmoid')(x_bowel) # use sigmoid to convert predictions to [0-1]\n    out_extra = keras.layers.Dense(1, name='extra', activation='sigmoid')(x_extra) # use sigmoid to convert predictions to [0-1]\n    out_liver = keras.layers.Dense(3, name='liver', activation='softmax')(x_liver) # use softmax for the liver head\n    out_kidney = keras.layers.Dense(3, name='kidney', activation='softmax')(x_kidney) # use softmax for the kidney head\n    out_spleen = keras.layers.Dense(3, name='spleen', activation='softmax')(x_spleen) # use softmax for the spleen head\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   \n    # Compile the model\n    optimizer = keras.optimizers.Adam(learning_rate=lr)\n    loss = {\"bowel\":keras.losses.BinaryCrossentropy(),\"extra\":keras.losses.BinaryCrossentropy(),\"liver\":keras.losses.CategoricalCrossentropy(),\"kidney\":keras.losses.CategoricalCrossentropy(),\"spleen\":keras.losses.CategoricalCrossentropy(),}\n    metrics = {\"bowel\":[\"accuracy\"],\"extra\":[\"accuracy\"],\"liver\":[\"accuracy\"],\"kidney\":[\"accuracy\"],\"spleen\":[\"accuracy\"],}\n    print(\"[INFO] Compiling the model...\")\n    model.compile(\n     optimizer=optimizer,\n      loss=loss,\n      metrics=metrics\n    )\n    model.summary()\n    \n    return model","metadata":{},"execution_count":null,"outputs":[]}]}