Source code for unagi.utils.metric_utils

#!/usr/bin/python3

from typing import Any, Callable

import tensorflow as tf
from tensorflow.python.keras import backend as K


[docs]class ModelMetrics: """Util Class to hold all metric methods for model evaluation. Static methods are used to calculate metrics without instantiating the class. """
[docs] @staticmethod def dice_coef(y_true: tf.Tensor, y_pred: tf.Tensor) -> float: """Count Sorensen-Dice coefficient for output and ground-truth image. Parameters ---------- y_true: tf.Tensor tensor of true pixel values y_pred: tf.Tensor tensor of predicted pixel values Returns ------- float dice coefficient calculated on predicted and input class values. Example ------- unagi.utils.metric_utils.ModelMetrics.dice_coef(y_true, y_pred) """ y_true_f = K.flatten(y_true) y_pred_f = K.flatten(y_pred) intersection = K.sum(y_true_f * y_pred_f) dice = (2.0 * intersection + 1.0) / (K.sum(y_true_f) + K.sum(y_pred_f) + 1.0) return dice
[docs] @staticmethod def dice_coef_loss(y_true: tf.Tensor, y_pred: tf.Tensor) -> float: """Loss of Sorensen-Dice coefficient for output and ground-truth image. Parameters ---------- y_true: tf.Tensor tensor of true pixel values y_pred: tf.Tensor tensor of predicted pixel values Returns ------- float dice loss calculated from dice coefficient. See Also -------- dice_coef() Example ------- unagi.utils.metric_utils.ModelMetrics.dice_coef_loss(y_true, y_pred) """ return 1 - ModelMetrics.dice_coef(y_true, y_pred)
[docs] @staticmethod def jacard_coef(y_true: tf.Tensor, y_pred: tf.Tensor) -> float: """Count Jaccard coefficient for output and ground-truth image. Parameters ---------- y_true: tf.Tensor tensor of true pixel values y_pred: tf.Tensor tensor of predicted pixel values Returns ------- float Jaccard coefficient calculated on predicted and input class values. Example ------- unagi.utils.metric_utils.ModelMetrics.jacard_coef(y_true, y_pred) """ y_true_f = K.flatten(y_true) y_pred_f = K.flatten(y_pred) intersection = K.sum(y_true_f * y_pred_f) return (intersection + 1.0) / ( K.sum(y_true_f) + K.sum(y_pred_f) - intersection + 1.0 )
[docs] @staticmethod def jacard_coef_loss(y_true: tf.Tensor, y_pred: tf.Tensor) -> float: """Calculates loss based on Jaccard coefficient for output and ground-truth image. Parameters ---------- y_true: tf.Tensor tensor of true pixel values y_pred: tf.Tensor tensor of predicted pixel values Returns ------- float Jaccard loss calculated from Jaccard coefficient. See Also -------- jacard_coef() Example ------- unagi.utils.metric_utils.ModelMetrics.jacard_coef_loss(y_true, y_pred) """ return 1 - ModelMetrics.jacard_coef(y_true, y_pred)
[docs] @staticmethod def weighted_cross_entropy(pos_weight: float = 0.2) -> Callable[[Any, Any], float]: """Calculates weighted cross entropy loss on predicted pixels. Parameters ---------- y_true: tf.Tensor tensor of true pixel values y_pred: tf.Tensor tensor of predicted pixel values Returns ------- Callable[[Any, Any], float] loss value calculated based on the weight factor References ---------- [1] keras implementation : lars76.github.io/neural-networks/object-detection/losses-for-segmentation/ Example ------- unagi.utils.metric_utils.ModelMetrics.weighted_cross_entropy(y_true, y_pred) """ def convert_to_logits(y_pred: tf.Tensor) -> tf.Tensor: y_pred = tf.clip_by_value( y_pred, tf.keras.backend.epsilon(), 1 - tf.keras.backend.epsilon() ) return tf.math.log(y_pred / (1 - y_pred)) def loss(y_true: tf.Tensor, y_pred: tf.Tensor) -> float: y_pred = convert_to_logits(y_pred) loss = tf.nn.weighted_cross_entropy_with_logits( logits=y_pred, labels=y_true, pos_weight=pos_weight ) # or reduce_sum and/or axis=-1 return tf.reduce_mean(input_tensor=loss * (1 - pos_weight)) return loss
[docs] @staticmethod def focal_loss( alpha: float = 0.25, gamma: float = 2.0 ) -> Callable[[Any, Any], float]: """Calculates focal loss on predicted pixels. Parameters ---------- alpha: float tensor of true pixel values gamma: float tensor of predicted pixel values Returns ------- Callable[[Any, Any], float] focal loss value calculated predicted pixels. References ---------- [1] keras implementation : lars76.github.io/neural-networks/object-detection/losses-for-segmentation/ [2] oiginal paper : Focal Loss for Dense Object Detection https://arxiv.org/abs/1708.02002 Example ------- unagi.utils.metric_utils.ModelMetrics.focal_loss() """ def focal_loss_with_logits( logits: tf.Tensor, targets: tf.Tensor, alpha: float, gamma: float, y_pred: tf.Tensor, ) -> tf.Tensor: """Calculates focal loss based on aplha and gamma.""" weight_a = alpha * (1 - y_pred) ** gamma * targets weight_b = (1 - alpha) * y_pred**gamma * (1 - targets) return (tf.math.log1p(tf.exp(-tf.abs(logits))) + tf.nn.relu(-logits)) * ( weight_a + weight_b ) + logits * weight_b def loss(y_true: tf.Tensor, y_pred: tf.Tensor) -> float: """Calculates final focal loss value.""" y_pred = tf.clip_by_value( y_pred, tf.keras.backend.epsilon(), 1 - tf.keras.backend.epsilon() ) logits = tf.math.log(y_pred / (1 - y_pred)) loss = focal_loss_with_logits( logits=logits, targets=y_true, alpha=alpha, gamma=gamma, y_pred=y_pred ) # or reduce_sum and/or axis=-1 return tf.reduce_mean(input_tensor=loss) return loss
[docs] @staticmethod def tversky(y_true: tf.Tensor, y_pred: tf.Tensor) -> float: """Calculates tversky index based on predicted pixels. Parameters ---------- y_true: tf.Tensor tensor of true pixel values y_pred: tf.Tensor tensor of predicted pixel valuesass Returns ------- float tversky index value calculated on predicted pixels. Example ------- unagi.utils.metric_utils.ModelMetrics.tversky(y_true, y_pred) """ smooth = 1 y_true_pos = K.flatten(y_true) y_pred_pos = K.flatten(y_pred) true_pos = K.sum(y_true_pos * y_pred_pos) false_neg = K.sum(y_true_pos * (1 - y_pred_pos)) false_pos = K.sum((1 - y_true_pos) * y_pred_pos) alpha = 0.7 return (true_pos + smooth) / ( true_pos + alpha * false_neg + (1 - alpha) * false_pos + smooth )
[docs] @staticmethod def tversky_loss(y_true: tf.Tensor, y_pred: tf.Tensor) -> float: """Calculates tversky loss based on tversky index for predicted pixels. Parameters ---------- y_true: tf.Tensor tensor of true pixel values y_pred: tf.Tensor tensor of predicted pixel valuesass Returns ------- float tversky loss value calculated on tversky index for predicted pixels. Example ------- unagi.utils.metric_utils.ModelMetrics.tversky_loss(y_true, y_pred) """ return 1 - ModelMetrics.tversky(y_true, y_pred)
[docs] @staticmethod def focal_tversky(y_true: tf.Tensor, y_pred: tf.Tensor) -> float: """Calculates focal-tversky loss based on tversky index for predicted pixels. Parameters ---------- y_true: tf.Tensor tensor of true pixel values y_pred: tf.Tensor tensor of predicted pixel valuesass Returns ------- float focal-tversky loss value calculated on tversky index for predicted pixels. References ---------- [1] keras implementation : https://github.com/nabsabraham/focal-tversky-unet [2] oiginal paper : A Novel Focal Tversky loss function with improved Attention U-Net for lesion segmentation. https://arxiv.org/abs/1810.07842 Example ------- unagi.utils.metric_utils.ModelMetrics.focal_tversky(y_true, y_pred) """ pt_1 = ModelMetrics.tversky(y_true, y_pred) gamma = 0.75 return K.pow((1 - pt_1), gamma)