MCPcopy Create free account
hub / github.com/HobbitLong/SupContrast / forward

Method forward

losses.py:21–106  ·  view source on GitHub ↗

Compute loss for model. If both `labels` and `mask` are None, it degenerates to SimCLR unsupervised loss: https://arxiv.org/pdf/2002.05709.pdf Args: features: hidden vector of shape [bsz, n_views, ...]. labels: ground truth of shape [bsz].

(self, features, labels=None, mask=None)

Source from the content-addressed store, hash-verified

19 self.base_temperature = base_temperature
20
21 def forward(self, features, labels=None, mask=None):
22 """Compute loss for model. If both `labels` and `mask` are None,
23 it degenerates to SimCLR unsupervised loss:
24 https://arxiv.org/pdf/2002.05709.pdf
25
26 Args:
27 features: hidden vector of shape [bsz, n_views, ...].
28 labels: ground truth of shape [bsz].
29 mask: contrastive mask of shape [bsz, bsz], mask_{i,j}=1 if sample j
30 has the same class as sample i. Can be asymmetric.
31 Returns:
32 A loss scalar.
33 """
34 device = (torch.device('cuda')
35 if features.is_cuda
36 else torch.device('cpu'))
37
38 if len(features.shape) < 3:
39 raise ValueError('`features` needs to be [bsz, n_views, ...],'
40 'at least 3 dimensions are required')
41 if len(features.shape) > 3:
42 features = features.view(features.shape[0], features.shape[1], -1)
43
44 batch_size = features.shape[0]
45 if labels is not None and mask is not None:
46 raise ValueError('Cannot define both `labels` and `mask`')
47 elif labels is None and mask is None:
48 mask = torch.eye(batch_size, dtype=torch.float32).to(device)
49 elif labels is not None:
50 labels = labels.contiguous().view(-1, 1)
51 if labels.shape[0] != batch_size:
52 raise ValueError('Num of labels does not match num of features')
53 mask = torch.eq(labels, labels.T).float().to(device)
54 else:
55 mask = mask.float().to(device)
56
57 contrast_count = features.shape[1]
58 contrast_feature = torch.cat(torch.unbind(features, dim=1), dim=0)
59 if self.contrast_mode == 'one':
60 anchor_feature = features[:, 0]
61 anchor_count = 1
62 elif self.contrast_mode == 'all':
63 anchor_feature = contrast_feature
64 anchor_count = contrast_count
65 else:
66 raise ValueError('Unknown mode: {}'.format(self.contrast_mode))
67
68 # compute logits
69 anchor_dot_contrast = torch.div(
70 torch.matmul(anchor_feature, contrast_feature.T),
71 self.temperature)
72 # for numerical stability
73 logits_max, _ = torch.max(anchor_dot_contrast, dim=1, keepdim=True)
74 logits = anchor_dot_contrast - logits_max.detach()
75
76 # tile mask
77 mask = mask.repeat(anchor_count, contrast_count)
78 # mask-out self-contrast cases

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected