# How to write custom loss functions¶

## The simplest possible loss function¶

``````from pytorch_metric_learning.losses import BaseMetricLossFunction
import torch

class BarebonesLoss(BaseMetricLossFunction):
def compute_loss(self, embeddings, labels, indices_tuple, ref_emb, ref_labels):
# perform some calculation #
some_loss = torch.mean(embeddings)

# put into dictionary #
return {
"loss": {
"losses": some_loss,
"indices": None,
}
}
``````

## Compatability with distances and reducers¶

You can make your loss function a lot more powerful by adding support for distance metrics and reducers:

``````from pytorch_metric_learning.losses import BaseMetricLossFunction
from pytorch_metric_learning.reducers import AvgNonZeroReducer
from pytorch_metric_learning.distances import CosineSimilarity
from pytorch_metric_learning.utils import loss_and_miner_utils as lmu
import torch

class FullFeaturedLoss(BaseMetricLossFunction):
def compute_loss(self, embeddings, labels, indices_tuple, ref_emb, ref_labels):
indices_tuple = lmu.convert_to_triplets(indices_tuple, labels)
anchors, positives, negatives = indices_tuple
if len(anchors) == 0:
return self.zero_losses()

mat = self.distance(embeddings)
ap_dists = mat[anchors, positives]
an_dists = mat[anchors, negatives]

# perform some calculations #
losses1 = ap_dists - an_dists
losses2 = ap_dists * 5
losses3 = torch.mean(embeddings)

# put into dictionary #
return {
"loss1": {
"losses": losses1,
"indices": indices_tuple,
"reduction_type": "triplet",
},
"loss2": {
"losses": losses2,
"indices": (anchors, positives),
"reduction_type": "pos_pair",
},
"loss3": {
"losses": losses3,
"indices": None,
},
}

def get_default_reducer(self):
return AvgNonZeroReducer()

def get_default_distance(self):
return CosineSimilarity()

def _sub_loss_names(self):
return ["loss1", "loss2", "loss3"]
``````

• It operates on triplets, so `convert_to_triplets` is used to convert `indices_tuple` to triplet form.
• `self.distance` returns a pairwise distance matrix
• The output of the loss function is a dictionary that contains multiple sub losses. This is why it overrides the `_sub_loss_names` function.
• `get_default_reducer` is overriden to use `AvgNonZeroReducer` by default, rather than `MeanReducer`.
• `get_default_distance` is overriden to use `CosineSimilarity` by default, rather than `LpDistances(p=2)`.

## More on distances¶

To make your loss compatible with inverted distances (like cosine similarity), you can check `self.distance.is_inverted`, and write whatever logic necessary to make your loss make sense in that context.

There are also a few functions in `self.distance` that provide some of this logic, specifically `self.distance.smallest_dist`, `self.distance.largest_dist`, and `self.distance.margin`. The function definitions are pretty straightforward, and you can find them here.

## Using `indices_tuple`¶

This is an optional argument passed in from the outside. (See the overview for an example.) It currently has 3 possible forms:

• `None`
• A tuple of size 4, representing the indices of mined pairs (anchors, positives, anchors, negatives)
• A tuple of size 3, representing the indices of mined triplets (anchors, positives, negatives)

To use `indices_tuple`, use the appropriate conversion function. You don't need to know what type will be passed in, as the conversion function takes care of that:

``````from pytorch_metric_learning.utils import loss_and_miner_utils as lmu

# For a pair based loss
# After conversion, indices_tuple will be a tuple of size 4
indices_tuple = lmu.convert_to_pairs(indices_tuple, labels)

# For a triplet based loss
# After conversion, indices_tuple will be a tuple of size 3
indices_tuple = lmu.convert_to_triplets(indices_tuple, labels)

# For a classification based loss
# miner_weights.shape == labels.shape
# You can use these to weight your loss
miner_weights = lmu.convert_to_weights(indices_tuple, labels, dtype=torch.float32)
``````

## Reduction type¶

The purpose of reduction types is to provide extra information to the reducer, if it needs it. For example, you could write a reducer that behaves differently depending on what kind of loss it receives. Here's a summary of each reduction type:

Reduction type Meaning Shape of "indices"
"triplet" Each entry in "losses" represents a triplet. A tuple of 3 tensors (anchors, positives, negatives), each of size (N,).
"pos_pair" Each entry in "losses" represents a positive pair. A tuple of 2 tensors (anchors, positives), each of size (N,).
"neg_pair" Each entry in "losses" represents a negative pair. A tuple of 2 tensors (anchors, negatives), each of size (N,).
"element" Each entry in "losses" represents something other than a tuple, e.g. an element in a batch. A tensor of size (N,)
"already_reduced" "losses" is a single number, i.e. the loss has already been reduced. Should be `None`

## Some useful examples to look at¶

Here are some existing loss functions that might be useful for reference: