forked from jxgu1016/MNIST_center_loss_pytorch
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CenterLoss.py
66 lines (57 loc) · 2.5 KB
/
CenterLoss.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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import torch
import torch.nn as nn
from torch.autograd.function import Function
class CenterLoss(nn.Module):
def __init__(self, num_classes, feat_dim, size_average=True):
super(CenterLoss, self).__init__()
self.centers = nn.Parameter(torch.randn(num_classes, feat_dim))
self.centerlossfunc = CenterlossFunc.apply
self.feat_dim = feat_dim
self.size_average = size_average
def forward(self, label, feat):
batch_size = feat.size(0)
feat = feat.view(batch_size, -1)
# To check the dim of centers and features
if feat.size(1) != self.feat_dim:
raise ValueError("Center's dim: {0} should be equal to input feature's \
dim: {1}".format(self.feat_dim,feat.size(1)))
batch_size_tensor = feat.new_empty(1).fill_(batch_size if self.size_average else 1)
loss = self.centerlossfunc(feat, label, self.centers, batch_size_tensor)
return loss
class CenterlossFunc(Function):
@staticmethod
def forward(ctx, feature, label, centers, batch_size):
ctx.save_for_backward(feature, label, centers, batch_size)
centers_batch = centers.index_select(0, label.long())
return (feature - centers_batch).pow(2).sum() / 2.0 / batch_size
@staticmethod
def backward(ctx, grad_output):
feature, label, centers, batch_size = ctx.saved_tensors
centers_batch = centers.index_select(0, label.long())
diff = centers_batch - feature
# init every iteration
counts = centers.new_ones(centers.size(0))
ones = centers.new_ones(label.size(0))
grad_centers = centers.new_zeros(centers.size())
counts = counts.scatter_add_(0, label.long(), ones)
grad_centers.scatter_add_(0, label.unsqueeze(1).expand(feature.size()).long(), diff)
grad_centers = grad_centers/counts.view(-1, 1)
return - grad_output * diff / batch_size, None, grad_centers / batch_size, None
def main(test_cuda=False):
print('-'*80)
device = torch.device("cuda" if test_cuda else "cpu")
ct = CenterLoss(10,2,size_average=True).to(device)
y = torch.Tensor([0,0,2,1]).to(device)
feat = torch.zeros(4,2).to(device).requires_grad_()
print (list(ct.parameters()))
print (ct.centers.grad)
out = ct(y,feat)
print(out.item())
out.backward()
print(ct.centers.grad)
print(feat.grad)
if __name__ == '__main__':
torch.manual_seed(999)
main(test_cuda=False)
if torch.cuda.is_available():
main(test_cuda=True)