From c5fc62c41109d4f4d4688d214b14dcf8b82036cd Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 10 Mar 2023 20:24:13 +0000 Subject: [PATCH] dlr CHANGE: Add optional log(cosh(dice_loss)) Ref https://doi.org/10.1109/cibcb48159.2020.9277638 --- aimodel/slurm-TEST-deeplabv3p-rainfall.job | 3 ++- aimodel/src/deeplabv3_plus_test_rainfall.py | 3 ++- .../src/lib/ai/components/LossCrossEntropyDice.py | 14 ++++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/aimodel/slurm-TEST-deeplabv3p-rainfall.job b/aimodel/slurm-TEST-deeplabv3p-rainfall.job index 180012a..31bad59 100755 --- a/aimodel/slurm-TEST-deeplabv3p-rainfall.job +++ b/aimodel/slurm-TEST-deeplabv3p-rainfall.job @@ -37,6 +37,7 @@ show_help() { echo -e " NO_REMOVE_ISOLATED_PIXELS Set to any value to avoid the engine from removing isolated pixels - that is, water pixels with no other surrounding pixels, either side to side to diagonally." >&2; echo -e " EPOCHS The number of epochs to train for." >&2; echo -e " LOSS The loss function to use. Default: cross-entropy (possible values: cross-entropy, cross-entropy-dice)." >&2; + echo -e " DICE_LOG_COSH When in cross-entropy-dice mode, in addition do loss = cel + log(cosh(dice_loss)) instead of just loss = cel + dice_loss." >&2; echo -e " PATH_CHECKPOINT The path to a checkcpoint to load. If specified, a model will be loaded instead of being trained." >&2; echo -e " LEARNING_RATE The learning rate to use. Default: 0.001." >&2; echo -e " PREDICT_COUNT The number of items from the (SCRAMBLED) dataset to make a prediction for." >&2; @@ -69,7 +70,7 @@ echo -e ">>> DIR_OUTPUT: ${DIR_OUTPUT}"; echo -e ">>> Additional args: ${ARGS}"; export PATH=$HOME/software/bin:$PATH; -export IMAGE_SIZE BATCH_SIZE DIR_RAINFALLWATER PATH_HEIGHTMAP PATH_COLOURMAP STEPS_PER_EPOCH DIR_OUTPUT PATH_CHECKPOINT EPOCHS PREDICT_COUNT NO_REMOVE_ISOLATED_PIXELS LOSS LEARNING_RATE; +export IMAGE_SIZE BATCH_SIZE DIR_RAINFALLWATER PATH_HEIGHTMAP PATH_COLOURMAP STEPS_PER_EPOCH DIR_OUTPUT PATH_CHECKPOINT EPOCHS PREDICT_COUNT NO_REMOVE_ISOLATED_PIXELS LOSS LEARNING_RATE DICE_LOG_COSH; echo ">>> Installing requirements"; conda run -n py38 pip install -q -r requirements.txt; diff --git a/aimodel/src/deeplabv3_plus_test_rainfall.py b/aimodel/src/deeplabv3_plus_test_rainfall.py index 7a6964f..9ca7b6d 100755 --- a/aimodel/src/deeplabv3_plus_test_rainfall.py +++ b/aimodel/src/deeplabv3_plus_test_rainfall.py @@ -47,6 +47,7 @@ STEPS_PER_EPOCH = int(os.environ["STEPS_PER_EPOCH"]) if "STEPS_PER_EPOCH" in os. REMOVE_ISOLATED_PIXELS = False if "NO_REMOVE_ISOLATED_PIXELS" in os.environ else True EPOCHS = int(os.environ["EPOCHS"]) if "EPOCHS" in os.environ else 50 LOSS = os.environ["LOSS"] if "LOSS" in os.environ else "cross-entropy-dice" +DICE_LOG_COSH = True if "DICE_LOG_COSH" in os.environ else False LEARNING_RATE = float(os.environ["LEARNING_RATE"]) if "LEARNING_RATE" in os.environ else 0.001 DIR_OUTPUT=os.environ["DIR_OUTPUT"] if "DIR_OUTPUT" in os.environ else f"output/{datetime.utcnow().date().isoformat()}_deeplabv3plus_rainfall_TEST" @@ -184,7 +185,7 @@ else: if PATH_CHECKPOINT is None: loss_fn = None if LOSS == "cross-entropy-dice": - loss_fn = LossCrossEntropyDice() + loss_fn = LossCrossEntropyDice(log_cosh=DICE_LOG_COSH) elif LOSS == "cross-entropy": loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) else: diff --git a/aimodel/src/lib/ai/components/LossCrossEntropyDice.py b/aimodel/src/lib/ai/components/LossCrossEntropyDice.py index f411fb0..25cee09 100644 --- a/aimodel/src/lib/ai/components/LossCrossEntropyDice.py +++ b/aimodel/src/lib/ai/components/LossCrossEntropyDice.py @@ -16,7 +16,6 @@ def dice_loss(y_true, y_pred): y_pred = tf.math.sigmoid(y_pred) numerator = 2 * tf.reduce_sum(y_true * y_pred) denominator = tf.reduce_sum(y_true + y_pred) - return 1 - numerator / denominator class LossCrossEntropyDice(tf.keras.losses.Loss): @@ -24,20 +23,27 @@ class LossCrossEntropyDice(tf.keras.losses.Loss): Combines the two with mean. The ground truth labels should sparse, NOT one-hot. The predictions should be one-hot, NOT sparse. @source https://lars76.github.io/2018/09/27/loss-functions-for-segmentation.html#9 + log_cosh (bool): Whether to do log(cosh(dice_loss)) instead of just dice_loss on its own. Ref https://doi.org/10.1109/cibcb48159.2020.9277638 """ - def __init__(self, **kwargs): + def __init__(self, log_cosh=True, **kwargs): super(LossCrossEntropyDice, self).__init__(**kwargs) + + self.param_log_cosh = log_cosh def call(self, y_true, y_pred): y_true = tf.cast(y_true, tf.float32) y_true = tf.one_hot(tf.cast(y_true, dtype=tf.int32), 2) # Input is sparse - o = tf.nn.sigmoid_cross_entropy_with_logits(y_true, y_pred) + dice_loss(y_true, y_pred) + + cel = tf.nn.sigmoid_cross_entropy_with_logits(y_true, y_pred) + dice = dice_loss(y_true, y_pred) + + o = cel + dice return tf.reduce_mean(o) def get_config(self): config = super(LossCrossEntropyDice, self).get_config() config.update({ - + "log_cosh": self.param_log_cosh }) return config