From ab29067d6469473481cc73fe42bcaf69d7633a83 Mon Sep 17 00:00:00 2001 From: Jordan Gong Date: Tue, 5 Jan 2021 20:20:06 +0800 Subject: Implement Batch All Triplet Loss --- utils/triplet_loss.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 utils/triplet_loss.py (limited to 'utils/triplet_loss.py') diff --git a/utils/triplet_loss.py b/utils/triplet_loss.py new file mode 100644 index 0000000..242be45 --- /dev/null +++ b/utils/triplet_loss.py @@ -0,0 +1,35 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + + +class BatchAllTripletLoss(nn.Module): + def __init__(self, margin: float = 0.2): + super().__init__() + self.margin = margin + + def forward(self, x, y): + # Duplicate labels for each part + p, n, c = x.size() + y = y.repeat(p, 1) + + # Euclidean distance p x n x n + x_squared_sum = torch.sum(x ** 2, dim=2) + x1_squared_sum = x_squared_sum.unsqueeze(1) + x2_squared_sum = x_squared_sum.unsqueeze(2) + x1_times_x2_sum = x @ x.transpose(1, 2) + dist = torch.sqrt(x1_squared_sum - 2 * x1_times_x2_sum + x2_squared_sum) + + hard_positive_mask = y.unsqueeze(1) == y.unsqueeze(2) + hard_negative_mask = y.unsqueeze(1) != y.unsqueeze(2) + all_hard_positive = dist[hard_positive_mask].view(p, n, -1, 1) + all_hard_negative = dist[hard_negative_mask].view(p, n, 1, -1) + positive_negative_dist = all_hard_positive - all_hard_negative + all_loss = F.relu(self.margin + positive_negative_dist).view(p, -1) + + # Non-zero parted mean + parted_loss_mean = all_loss.sum(1) / (all_loss != 0).sum(1) + parted_loss_mean[parted_loss_mean == float('Inf')] = 0 + + loss = parted_loss_mean.mean() + return loss -- cgit v1.2.3