-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathcircle_loss.py
47 lines (39 loc) · 1.74 KB
/
circle_loss.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import torch
from torch import nn
import torch.nn.functional as F
class CircleLoss(nn.Module):
def __init__(self, scale=32, margin=0.25, similarity='cos', **kwargs):
super(CircleLoss, self).__init__()
self.scale = scale
self.margin = margin
self.similarity = similarity
def forward(self, feats, labels):
assert feats.size(0) == labels.size(0), \
f"feats.size(0): {feats.size(0)} is not equal to labels.size(0): {labels.size(0)}"
m = labels.size(0)
mask = labels.expand(m, m).t().eq(labels.expand(m, m)).float()
pos_mask = mask.triu(diagonal=1)
neg_mask = (mask - 1).abs_().triu(diagonal=1)
if self.similarity == 'dot':
sim_mat = torch.matmul(feats, torch.t(feats))
elif self.similarity == 'cos':
feats = F.normalize(feats)
sim_mat = feats.mm(feats.t())
else:
raise ValueError('This similarity is not implemented.')
pos_pair_ = sim_mat[pos_mask == 1]
neg_pair_ = sim_mat[neg_mask == 1]
alpha_p = torch.relu(-pos_pair_ + 1 + self.margin)
alpha_n = torch.relu(neg_pair_ + self.margin)
margin_p = 1 - self.margin
margin_n = self.margin
loss_p = torch.sum(torch.exp(-self.scale * alpha_p * (pos_pair_ - margin_p)))
loss_n = torch.sum(torch.exp(self.scale * alpha_n * (neg_pair_ - margin_n)))
loss = torch.log(1 + loss_p * loss_n)
return loss
if __name__ == '__main__':
batch_size = 10
feats = torch.rand(batch_size, 1028)
labels = torch.randint(high=10, dtype=torch.long, size=(batch_size,))
circleloss = CircleLoss(similarity='cos')
print(circleloss(feats, labels))