def forward(self, inputs: torch.Tensor, targets: torch.Tensor): """ Computes the Tversky loss based on https://arxiv.org/pdf/1706.05721.pdf Note that PyTorch optimizers minimize a loss. In this case, we would like to maximize the dice loss so we return the negated dice loss. Args: inputs (:obj:`torch.Tensor`) : A tensor of shape (B, C, ..). The model prediction on which the loss has to be computed. targets (:obj:`torch.Tensor`) : A tensor of shape (B, C, ..). The ground truth. Returns: :obj:`torch.Tensor`: The Tversky loss for each class or reduced according to reduction method. """ if not inputs.size() == targets.size(): raise ValueError( "'Inputs' and 'Targets' must have the same shape.") inputs = flatten(inputs) targets = flatten(targets).float() ones = torch.Tensor().new_ones((inputs.size()), dtype=torch.float, device=inputs.device) P_G = (inputs * targets).sum(-1) if self.weight is not None: P_G = self.weight * P_G P_NG = (inputs * (ones - targets)).sum(-1) NP_G = ((ones - inputs) * targets).sum(-1) ones = torch.Tensor().new_ones((inputs.size(0), ), dtype=torch.float, device=inputs.device) tversky = P_G / (P_G + self._alpha * P_NG + self._beta * NP_G + EPSILON) tversky_loss = ones - tversky if self._ignore_index != -100: def ignore_index_fn(tversky_vector): try: indices = list(range(len(tversky_vector))) indices.remove(self._ignore_index) return tversky_vector[indices] except ValueError as e: raise IndexError( "'ignore_index' must be non-negative, and lower than the number of classes in confusion matrix, but {} was given. " .format(self._ignore_index)) tversky_loss = MetricsLambda(ignore_index_fn, tversky_loss).compute() if self.reduction == "mean": tversky_loss = tversky_loss.mean() return tversky_loss
def forward(self, inputs: torch.Tensor, targets: torch.Tensor): """ Computes the Sørensen–Dice loss. Note that PyTorch optimizers minimize a loss. In this case, we would like to maximize the dice loss so we return the negated dice loss. Args: inputs (:obj:`torch.Tensor`) : A tensor of shape (B, C, ..). The model prediction on which the loss has to be computed. targets (:obj:`torch.Tensor`) : A tensor of shape (B, C, ..). The ground truth. Returns: :obj:`torch.Tensor`: The Sørensen–Dice loss for each class or reduced according to reduction method. """ if not inputs.size() == targets.size(): raise ValueError( "'Inputs' and 'Targets' must have the same shape.") inputs = flatten(inputs) targets = flatten(targets).float() # Compute per channel Dice Coefficient intersection = (inputs * targets).sum(-1) if self.weight is not None: intersection = self.weight * intersection cardinality = (inputs + targets).sum(-1) ones = torch.Tensor().new_ones((inputs.size(0), ), dtype=torch.float, device=inputs.device) dice = ones - (2.0 * intersection / cardinality.clamp(min=EPSILON)) if self._ignore_index != -100: def ignore_index_fn(dice_vector): try: indices = list(range(len(dice_vector))) indices.remove(self._ignore_index) return dice_vector[indices] except ValueError as e: raise IndexError( "'ignore_index' must be non-negative, and lower than the number of classes in confusion matrix, but {} was given. " .format(self._ignore_index)) dice = MetricsLambda(ignore_index_fn, dice).compute() if self.reduction == "mean": dice = dice.mean() return dice
def create_dice_metric(self, cm: ConfusionMatrix): """ Computes the Sørensen–Dice Coefficient (https://en.wikipedia.org/wiki/Sørensen–Dice_coefficient) Args: cm (:obj:`ignite.metrics.ConfusionMatrix`): A confusion matrix representing the classification of data. Returns: array or float: The Sørensen–Dice Coefficient for each class or the mean Sørensen–Dice Coefficient. """ # Increase floating point precision cm = cm.type(torch.float64) dice = 2 * cm.diag() / (cm.sum(dim=1) + cm.sum(dim=0) + EPSILON) if self._ignore_index != -100: def remove_index(dice_vector): try: indices = list(range(len(dice_vector))) indices.remove(self._ignore_index) return dice_vector[indices] except ValueError as e: raise IndexError( "'ignore_index' must be non-negative, and lower than the number of classes in confusion matrix, but {} was given. " .format(self._ignore_index)) dice = MetricsLambda(remove_index, dice) if self._weight is not None: def multiply_weights(dice_vector): return self._weight * dice_vector dice = MetricsLambda(multiply_weights, dice) if self._reduction == "mean": dice = dice.mean() return dice