Shortcuts

Source code for torchtraining.loss

import typing

import torch
from torch.nn.modules.loss import _Loss

from . import functional

# DICE


# Dice loss


[docs]class BinaryFocal(_Loss): """Binary focal loss working with raw output from network (logits). See original research paper: `Focal Loss for Dense Object Detection <https://arxiv.org/abs/1708.02002>`__ Underplays loss of easy examples while leaving loss of harder examples for neural network mostly intact (dampened way less). The higher the gamma parameter, the greater the "focusing" effect. Arguments --------- gamma: float Scale of focal loss effect. To obtain binary crossentropy set it to 0.0. `0.5 - 2.5` range was used in original research paper and seemed robust. weight: Tensor, optional Manual rescaling weight, if provided it's repeated to match input tensor shape pos_weight: Tensor, optional Weight of positive examples. Must be a vector with length equal to the number of classes. In general `pos_weight` should be decreased slightly as `gamma` is increased (for `gamma=2`, `pos_weight=0.25` was found to work best in original paper). reduction: typing.Callable(torch.Tensor) -> torch.Tensor, optional Specifies the reduction to apply to the output. If user wants no reduction he should use: `lambda loss: loss`. If user wants a summation he should use: `torch.sum`. By default, `lambda loss: loss.sum() / loss.shape[0]` is used (mean across examples). """ def __init__( self, gamma: float, weight=None, pos_weight=None, reduction: typing.Callable[[torch.Tensor], torch.Tensor] = None, ): super().__init__() self.gamma = gamma self.weight = weight self.pos_weight = pos_weight self.reduction = reduction
[docs] def forward(self, outputs: torch.Tensor, targets: torch.Tensor) -> torch.Tensor: """ Arguments --------- outputs: torch.Tensor :math:`(N, *)` where :math:`*` means, any number of additional dimensions. Usually of shape :math:`(N, H, W)`, where :math:`H` is image height and :math:`W` is it's width. targets: torch.Tensor :math:`(N, *)`, same shape as the input. Returns ------- torch.Tensor If :attr:`reduction` is not specified then `mean` across sample is taken. Otherwise whatever shape `reduction` returns. """ return functional.loss.binary_focal_loss( outputs, targets, self.gamma, self.weight, self.pos_weight, self.reduction, )
[docs]class MulticlassFocal(_Loss): r"""Multiclass focal loss working with raw output from network (logits). See original research paper: `Focal Loss for Dense Object Detection <https://arxiv.org/abs/1708.02002>`__ Underplays loss of easy examples while leaving loss of harder examples for neural network mostly intact (dampened way less). The higher the gamma parameter, the greater the "focusing" effect. Arguments --------- gamma: float Scale of focal loss effect. To obtain binary crossentropy set it to 0.0. `0.5 - 2.5` range was used in original research paper and seemed robust. weight: Tensor, optional Manual rescaling weight, if provided it's repeated to match input tensor shape. ignore_index int, optional Specifies a target value that is ignored and does not contribute to the input gradient. When :attr:`size_average` is ``True``, the loss is averaged over non-ignored targets. reduction: typing.Callable(torch.Tensor) -> torch.Tensor, optional Specifies the reduction to apply to the output. If user wants no reduction he should use: `lambda loss: loss`. If user wants a summation he should use: `torch.sum`. By default, `lambda loss: loss.sum() / loss.shape[0]` is used (mean across examples). """ def __init__( self, gamma: float, weight=None, ignore_index: int = -100, reduction: typing.Callable[[torch.Tensor], torch.Tensor] = None, ): super().__init__() self.gamma = gamma self.weight = weight self.ignore_index = ignore_index self.reduction = reduction
[docs] def forward(self, outputs: torch.Tensor, targets: torch.Tensor) -> torch.Tensor: """ Arguments --------- outputs: torch.Tensor :math:`(N, C)` where `C = number of classes`, or :math:`(N, C, d_1, d_2, ..., d_K)` with :math:`K \geq 1` in the case of `K`-dimensional loss. Usually of shape :math:`(N, C, H, W)`, where :math:`H` is image height and :math:`W` is it's width. targets: torch.Tensor :math:`(N)` where each value is :math:`0 \leq \text{targets}[i] \leq C-1`, or :math:`(N, d_1, d_2, ..., d_K)` with :math:`K \geq 1` in the case of K-dimensional loss. Usually of shape :math:`(N, H, W)`, where :math:`H` is image height and :math:`W` is it's width and elements are of specified `C` classes. Returns ------- torch.Tensor If :attr:`reduction` is not specified then `mean` across sample is taken. Otherwise whatever shape `reduction` returns. """ return functional.loss.multiclass_focal_loss( outputs, targets, self.gamma, self.weight, self.ignore_index, self.reduction )
# Inspired by: https://stackoverflow.com/questions/55681502/label-smoothing-in-pytorch # and adjusted to fit torchtraining API
[docs]class SmoothCrossEntropy(_Loss): r"""Run cross entropy with non-integer labels smoothed by `alpha`. See `When Does Label Smoothing Help? <https://arxiv.org/abs/1906.02629>`__ for more details `targets` will be transformed to one-hot encoding and modified according to formula: .. math:: y = y(1 - \alpha) + \frac{\alpha}{C} where :math:`C` is total number of classes. Arguments --------- alpha: float Smoothing parameter in the range `[0, 1)`. weight: Tensor, optional Manual rescaling weight, if provided it's repeated to match input tensor shape. Default: `None` (no weighting) ignore_index int, optional Specifies a target value that is ignored and does not contribute to the input gradient. When :attr:`size_average` is ``True``, the loss is averaged over non-ignored targets. Default: `-100` reduction: typing.Callable(torch.Tensor) -> torch.Tensor, optional Specifies the reduction to apply to the output. If user wants no reduction he should use: `lambda loss: loss`. If user wants a summation he should use: `torch.sum`. By default, `lambda loss: loss.sum() / loss.shape[0]` is used (mean across examples). """ def __init__( self, alpha: float, weight=None, ignore_index: int = -100, reduction: typing.Callable[[torch.Tensor], torch.Tensor] = None, ): if not 0 <= alpha < 1: raise ValueError("smoothing alpha should be in [0, 1) range.") super().__init__() self.alpha = alpha self.weight = weight self.ignore_index = ignore_index self.reduction = reduction
[docs] def forward(self, outputs: torch.Tensor, targets: torch.Tensor) -> torch.Tensor: """ Arguments --------- outputs: torch.Tensor :math:`(N, C)` where `C = number of classes`, or :math:`(N, C, d_1, d_2, ..., d_K)` with :math:`K \geq 1` in the case of `K`-dimensional loss. targets: torch.Tensor :math:`(N)` where each value is :math:`0 \leq \text{targets}[i] \leq C-1`, or :math:`(N, d_1, d_2, ..., d_K)` with :math:`K \geq 1` in the case of K-dimensional loss. Returns ------- torch.Tensor If :attr:`reduction` is not specified then `mean` across sample is taken. Otherwise whatever shape `reduction` returns. """ return functional.loss.smooth_cross_entropy( outputs, targets, self.alpha, self.weight, self.ignore_index, self.reduction )
[docs]class SmoothBinaryCrossEntropy(_Loss): r"""Run binary cross entropy with booleans smoothed by `alpha`. See `When Does Label Smoothing Help? <https://arxiv.org/abs/1906.02629>`__ for more details `targets` will be transformed to one-hot encoding and modified according to formula: .. math:: y = y(1 - \alpha) + \frac{\alpha}{2} where :math:`2` is total number of classes in binary case. Arguments --------- alpha: float Smoothing parameter in the range `[0, 1)`. weight: Tensor, optional Manual rescaling weight, if provided it's repeated to match input tensor shape. Default: `None` (no weighting) pos_weight: Tensor, optional Weight of positive examples. Must be a vector with length equal to the number of classes. In general `pos_weight` should be decreased slightly as `gamma` is increased (for `gamma=2`, `pos_weight=0.25` was found to work best in original paper). reduction: typing.Callable(torch.Tensor) -> torch.Tensor, optional Specifies the reduction to apply to the output. If user wants no reduction he should use: `lambda loss: loss`. If user wants a summation he should use: `torch.sum`. By default, `lambda loss: loss.sum() / loss.shape[0]` is used (mean across examples). """ def __init__( self, alpha: float, weight=None, pos_weight: int = None, reduction: typing.Callable[[torch.Tensor], torch.Tensor] = None, ): if not 0 <= alpha < 1: raise ValueError("smoothing alpha should be in [0, 1) range.") super().__init__() self.alpha = alpha self.weight = weight self.pos_weight = pos_weight self.reduction = reduction
[docs] def forward(self, outputs: torch.Tensor, targets: torch.Tensor) -> torch.Tensor: """ Arguments --------- outputs: torch.Tensor :math:`(N, *)` where :math:`*` means, any number of additional dimensions targets: torch.Tensor :math:`(N, *)`, same shape as the input Returns ------- torch.Tensor If :attr:`reduction` is not specified then `mean` across sample is taken. Otherwise whatever shape `reduction` returns. """ return functional.loss.smooth_binary_cross_entropy( outputs, targets, self.alpha, self.weight, self.pos_weight, self.reduction )
[docs]class QuadrupletLoss(_Loss): r"""Quadruplet loss pushing away samples belonging to different classes. See original research paper `Beyond triplet loss: a deep quadruplet network for person re-identification <https://arxiv.org/abs/1704.01719>`__ for more information. It is an extension of `torch.nn.TripletMarginLoss`, where samples from two different `negative` (`negative` and `negative2`) classes should be pushed further away in space than those belonging to the same class (`anchor` and `positive`) The loss function for each sample in the mini-batch is: .. math:: L(a, p, n) = \max \{d(a, p) - d(a, n) + {\rm alpha1}, 0\} + \max \{d(a, p) - d(n, n2) + {\rm alpha2}, 0\} Arguments --------- alpha1: float, optional Margin of standard `triplet` loss. Default: `1.0` alpha2: float, optional Margin of second part of loss (pushing negative1 and negative2 samples more than positive and anchor). Default: `0.5` metric: Callable(torch.Tensor, torch.Tensor) -> torch.Tensor, optional Metric used to rate distance between samples. Fully Connected neural network with one output and `sigmoid` could be used (as in original paper) or anything else adhering to API. Default: Euclidean distance. weight: Tensor, optional Manual rescaling weight, if provided it's repeated to match input tensor shape. Default: `None` (no weighting) reduction: typing.Callable(torch.Tensor) -> torch.Tensor, optional Specifies the reduction to apply to the output. If user wants no reduction he should use: `lambda loss: loss`. If user wants a summation he should use: `torch.sum`. By default, `lambda loss: loss.sum() / loss.shape[0]` is used (mean across examples). """ def __init__( self, alpha1: float = 1.0, alpha2: float = 0.5, metric: typing.Callable[ [torch.Tensor, torch.Tensor], torch.Tensor ] = torch.nn.functional.pairwise_distance, weight=None, reduction: str = "sum", ): super().__init__() self.alpha1 = alpha1 self.alpha2 = alpha2 self.metric = metric self.weight = weight self.reduction = reduction
[docs] def forward( self, anchor: torch.Tensor, positive: torch.Tensor, negative: torch.Tensor, negative2: torch.Tensor, ) -> torch.Tensor: """ Arguments --------- anchor: torch.Tensor :math:`(N, *)` where :math:`*` means, any number of additional dimensions For images usually of shape :math:`(N, C, H, W)`. positive: torch.Tensor Same as `anchor` negative: torch.Tensor Same as `anchor` negative2: torch.Tensor Same as `anchor` Returns ------- torch.Tensor If :attr:`reduction` is not specified then `mean` across sample is taken. Otherwise whatever shape `reduction` returns. """ return functional.loss.quadruplet( anchor, positive, negative, negative2, self.alpha1, self.alpha2, self.metric, self.weight, self.reduction, )