summaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-rw-r--r--utils/triplet_loss.py35
1 files changed, 35 insertions, 0 deletions
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