TorchLean API

NN.Spec.Layers.Loss

Loss functions (spec layer) #

This file defines a small collection of common losses (and their gradients) in a way that is:

In PyTorch you'll often see two layers:

TorchLean's spec layer mirrors that idea: most definitions are written as an elementwise formula followed by a global mean over the shape.

inductive Spec.LossType :

Enumeration of supported loss families used by configuration records.

Instances For
    structure Spec.Loss :

    Loss configuration record that names the selected loss family.

    • lossType : LossType

      Selected loss family for this configuration.

    Instances For

      Configuration selecting mean-squared-error loss.

      Instances For

        Configuration selecting mean-absolute-error loss.

        Instances For

          Configuration selecting Huber loss.

          Instances For

            Cross-entropy loss configuration.

            Instances For

              Configuration selecting hinge loss.

              Instances For

                Poisson loss configuration.

                Instances For

                  Cosine similarity loss configuration.

                  Instances For

                    Log-cosh loss configuration.

                    Instances For
                      def Spec.toScalarSpec {α : Type} [Context α] {s : Shape} :
                      Tensor α sα

                      Sum all tensor elements into a single scalar.

                      Instances For

                        Denominator for totalized mean reductions over a shape.

                        For nonempty shapes this is the real element count. For empty shapes the mathematical mean is undefined; TorchLean's scalar-polymorphic spec layer is total, so it uses denominator 1 and the empty sum contributes 0.

                        Instances For
                          def Spec.meanOver {α : Type} [Context α] {s : Shape} (x : α) :
                          α

                          Mean of a scalar that conceptually came from a tensor with shape s.

                          Instances For

                            Number of independent last-axis slices in a tensor shape.

                            Classification losses sum over the innermost class axis and apply mean over the remaining axes. Thus a class vector has one slice, a matrix of shape (batch, classes) has batch slices, and higher-rank inputs use the product of every axis except the last. Scalars are treated as one single-coordinate slice.

                            Instances For

                              Totalized denominator for a mean over last-axis slices.

                              Instances For
                                def Spec.meanOverLastAxisSlices {α : Type} [Context α] {s : Shape} (x : α) :
                                α

                                Divide a last-axis-summed classification loss by its number of independent slices.

                                Instances For
                                  def Spec.mseSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) :
                                  α

                                  Mean squared error: average of $(\mathtt{predicted}-\mathtt{target})^2$.

                                  Instances For
                                    def Spec.mseDerivSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) :
                                    Tensor α s

                                    Derivative of mse_spec w.r.t. predicted.

                                    Instances For
                                      def Spec.maeSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) :
                                      α

                                      Mean absolute error: average of |predicted - target|.

                                      Instances For
                                        def Spec.maeDerivSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) :
                                        Tensor α s

                                        Derivative of mae_spec w.r.t. predicted (subgradient via sign).

                                        Instances For
                                          def Spec.huberSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) (delta : α := 1) :
                                          α

                                          Huber loss with transition parameter delta.

                                          Elementwise, for residual $d=\mathtt{pred}-\mathtt{target}$:

                                          • if $\lvert d\rvert<\delta$: $\tfrac12d^2$
                                          • otherwise: $\delta(\lvert d\rvert-\tfrac12\delta)$

                                          Then we take a mean over all elements.

                                          This is PyTorch's HuberLoss convention. It differs from SmoothL1Loss by a factor of delta. The Huber interpretation requires delta > 0.

                                          Instances For
                                            def Spec.huberDerivSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) (delta : α := 1) :
                                            Tensor α s

                                            Derivative of huber_spec w.r.t. predicted.

                                            Instances For
                                              def Spec.crossEntropySpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) (epsilon : α := Numbers.epsilon) :
                                              α

                                              Cross-entropy between distributions (probabilities).

                                              This is closest to PyTorch when you already have probabilities q (e.g. after a softmax) and a probability target p (e.g. one-hot or label-smoothed), and you want:

                                              $$ \operatorname{CE}(p,q) =-\operatorname{mean}_r\sum_c p_{rc}\log q_{rc}, $$

                                              where c is the last (class) axis and r ranges over all remaining axes. A lone class vector is one distribution and is not divided by its number of classes.

                                              PyTorch's F.cross_entropy typically takes logits and does log_softmax + NLLLoss; that is a different API surface than this "probabilities in, scalar out" spec.

                                              Instances For
                                                def Spec.crossEntropyDerivSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) (epsilon : α := Numbers.epsilon) :
                                                Tensor α s

                                                Derivative of cross_entropy_spec w.r.t. predicted.

                                                Instances For
                                                  def Spec.crossEntropyLogitsSpec {α : Type} [Context α] {s : Shape} (logits target : Tensor α s) :
                                                  α

                                                  Cross-entropy on logits (stable log-softmax form).

                                                  This matches the common PyTorch decomposition:

                                                  $$ \operatorname{cross\_entropy}(\mathtt{logits},\mathtt{target}) =-\operatorname{mean}_r\sum_c \mathtt{target}_{rc}\operatorname{logsoftmax}(\mathtt{logits})_{rc}, $$

                                                  where the last axis c contains classes and r ranges over the remaining axes. This is PyTorch's reduction="mean" convention for one-hot or soft distribution targets.

                                                  Unlike crossEntropySpec, this takes logits and uses Activation.logSoftmaxSpec for numerical stability.

                                                  Note: this spec assumes each last-axis target slice is a probability distribution (sums to 1), as in one-hot or label-smoothed targets.

                                                  Instances For
                                                    def Spec.crossEntropyLogitsDerivSpec {α : Type} [Context α] {s : Shape} (logits target : Tensor α s) :
                                                    Tensor α s

                                                    Derivative of cross_entropy_logits_spec w.r.t. logits.

                                                    Instances For
                                                      def Spec.hingeSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) :
                                                      α

                                                      Hinge loss (binary margin loss), elementwise then mean-reduced:

                                                      $\operatorname{hinge}(x,y)=\operatorname{mean}_i\max(0,1-y_i x_i)$.

                                                      This matches the usual SVM-style hinge loss. (PyTorch exposes similar behavior via margin-style losses such as HingeEmbeddingLoss / MultiMarginLoss, but the exact signature differs.)

                                                      Instances For
                                                        def Spec.hingeDerivSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) :
                                                        Tensor α s

                                                        Derivative/subgradient of hinge_spec w.r.t. predicted.

                                                        Instances For
                                                          def Spec.poissonSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) :
                                                          α

                                                          Poisson negative log-likelihood (log-input form), elementwise then mean-reduced:

                                                          If predicted represents log(rate) and target is a nonnegative count, then (up to an additive constant that does not affect gradients):

                                                          $\operatorname{loss}_i=\exp(\mathtt{pred}_i)-\mathtt{target}_i\mathtt{pred}_i$.

                                                          This corresponds to PyTorch's PoissonNLLLoss(log_input=true, full=false) at the math level.

                                                          Instances For
                                                            def Spec.poissonDerivSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) :
                                                            Tensor α s

                                                            Derivative of poisson_spec w.r.t. predicted.

                                                            Instances For
                                                              def Spec.cosineSimilaritySpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) (epsilon : α := Numbers.epsilon) :
                                                              α

                                                              Cosine similarity loss: 1 - cos(predicted, target) (reduced-to-scalar).

                                                              Instances For
                                                                def Spec.cosineSimilarityDerivSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) (epsilon : α := Numbers.epsilon) :
                                                                Tensor α s

                                                                Derivative of cosine_similarity_spec w.r.t. predicted.

                                                                If $\cos=(p\mathbin{\cdot}t)/(\lVert p\rVert\lVert t\rVert)$ and $\operatorname{loss}=1-\cos$, then (for nonzero norms):

                                                                $$ \frac{\partial\operatorname{loss}}{\partial p} =\frac{p\mathbin{\cdot}t}{\lVert p\rVert^2\lVert t\rVert}p -\frac{1}{\lVert p\rVert\lVert t\rVert}t. $$

                                                                We use epsilon to avoid division by zero (similar to common "eps" handling in PyTorch code).

                                                                Instances For
                                                                  def Spec.logCoshSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) :
                                                                  α

                                                                  Log-cosh loss (reduced-to-scalar): log(cosh(predicted - target)).

                                                                  Instances For
                                                                    def Spec.logCoshDerivSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) :
                                                                    Tensor α s

                                                                    Derivative of log_cosh_spec w.r.t. predicted.

                                                                    Instances For
                                                                      def Spec.binaryCrossEntropySpec {α : Type} [Context α] (predicted target : α) (epsilon : α := Numbers.epsilon) :
                                                                      α

                                                                      Binary cross-entropy on scalars (probabilities), with clipping to avoid log(0).

                                                                      This matches the core formula behind PyTorch's BCELoss when predicted is already a probability (not a logit):

                                                                      $$ \operatorname{BCE}(p,y) =-\left(y\log p+(1-y)\log(1-p)\right). $$

                                                                      Assumption: target is in [0, 1]. We do not clip the target; we only clip predicted.

                                                                      Instances For
                                                                        def Spec.binaryCrossEntropyDerivSpec {α : Type} [Context α] (predicted target : α) (epsilon : α := Numbers.epsilon) :
                                                                        α

                                                                        Selected derivative of binary_cross_entropy_spec w.r.t. predicted.

                                                                        The clipped forward function is not differentiable at epsilon or 1 - epsilon; this definition chooses zero at those two kinks and on the clipped exterior branches.

                                                                        Instances For
                                                                          def Spec.binaryCrossEntropyTensorSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) (epsilon : α := Numbers.epsilon) :
                                                                          α

                                                                          Tensor BCE (probabilities), elementwise then mean-reduced.

                                                                          Instances For
                                                                            def Spec.binaryCrossEntropyTensorDerivSpec {α : Type} [Context α] {s : Shape} (predicted target : Tensor α s) (epsilon : α := Numbers.epsilon) :
                                                                            Tensor α s

                                                                            Derivative of binary_cross_entropy_tensor_spec w.r.t. predicted.

                                                                            Instances For