''' Reference: https://github.com/hshustc/CVPR19_Incremental_Learning/blob/master/cifar100-class-incremental/modified_linear.py ''' import math import torch from torch import nn from torch.nn import functional as F class SimpleLinear(nn.Module): ''' Reference: https://github.com/pytorch/pytorch/blob/master/torch/nn/modules/linear.py ''' def __init__(self, in_features, out_features, bias=True): super(SimpleLinear, self).__init__() self.in_features = in_features self.out_features = out_features self.weight = nn.Parameter(torch.Tensor(out_features, in_features)) if bias: self.bias = nn.Parameter(torch.Tensor(out_features)) else: self.register_parameter('bias', None) self.reset_parameters() def reset_parameters(self): nn.init.kaiming_uniform_(self.weight, nonlinearity='linear') nn.init.constant_(self.bias, 0) def forward(self, input): return {'logits': F.linear(input, self.weight, self.bias)} class CosineLinear(nn.Module): def __init__(self, in_features, out_features, nb_proxy=1, to_reduce=False, sigma=True): super(CosineLinear, self).__init__() self.in_features = in_features self.out_features = out_features * nb_proxy self.nb_proxy = nb_proxy self.to_reduce = to_reduce self.weight = nn.Parameter(torch.Tensor(self.out_features, in_features)) if sigma: self.sigma = nn.Parameter(torch.Tensor(1)) else: self.register_parameter('sigma', None) self.reset_parameters() def reset_parameters(self): stdv = 1. / math.sqrt(self.weight.size(1)) self.weight.data.uniform_(-stdv, stdv) if self.sigma is not None: self.sigma.data.fill_(1) def forward(self, input): out = F.linear(F.normalize(input, p=2, dim=1), F.normalize(self.weight, p=2, dim=1)) if self.to_reduce: # Reduce_proxy out = reduce_proxies(out, self.nb_proxy) if self.sigma is not None: out = self.sigma * out return {'logits': out} class SplitCosineLinear(nn.Module): def __init__(self, in_features, out_features1, out_features2, nb_proxy=1, sigma=True): super(SplitCosineLinear, self).__init__() self.in_features = in_features self.out_features = (out_features1 + out_features2) * nb_proxy self.nb_proxy = nb_proxy self.fc1 = CosineLinear(in_features, out_features1, nb_proxy, False, False) self.fc2 = CosineLinear(in_features, out_features2, nb_proxy, False, False) if sigma: self.sigma = nn.Parameter(torch.Tensor(1)) self.sigma.data.fill_(1) else: self.register_parameter('sigma', None) def forward(self, x): out1 = self.fc1(x) out2 = self.fc2(x) out = torch.cat((out1['logits'], out2['logits']), dim=1) # concatenate along the channel # Reduce_proxy out = reduce_proxies(out, self.nb_proxy) if self.sigma is not None: out = self.sigma * out return { 'old_scores': reduce_proxies(out1['logits'], self.nb_proxy), 'new_scores': reduce_proxies(out2['logits'], self.nb_proxy), 'logits': out } def reduce_proxies(out, nb_proxy): if nb_proxy == 1: return out bs = out.shape[0] nb_classes = out.shape[1] / nb_proxy assert nb_classes.is_integer(), 'Shape error' nb_classes = int(nb_classes) simi_per_class = out.view(bs, nb_classes, nb_proxy) attentions = F.softmax(simi_per_class, dim=-1) return (attentions * simi_per_class).sum(-1) ''' class CosineLinear(nn.Module): def __init__(self, in_features, out_features, sigma=True): super(CosineLinear, self).__init__() self.in_features = in_features self.out_features = out_features self.weight = nn.Parameter(torch.Tensor(out_features, in_features)) if sigma: self.sigma = nn.Parameter(torch.Tensor(1)) else: self.register_parameter('sigma', None) self.reset_parameters() def reset_parameters(self): stdv = 1. / math.sqrt(self.weight.size(1)) self.weight.data.uniform_(-stdv, stdv) if self.sigma is not None: self.sigma.data.fill_(1) def forward(self, input): out = F.linear(F.normalize(input, p=2, dim=1), F.normalize(self.weight, p=2, dim=1)) if self.sigma is not None: out = self.sigma * out return {'logits': out} class SplitCosineLinear(nn.Module): def __init__(self, in_features, out_features1, out_features2, sigma=True): super(SplitCosineLinear, self).__init__() self.in_features = in_features self.out_features = out_features1 + out_features2 self.fc1 = CosineLinear(in_features, out_features1, False) self.fc2 = CosineLinear(in_features, out_features2, False) if sigma: self.sigma = nn.Parameter(torch.Tensor(1)) self.sigma.data.fill_(1) else: self.register_parameter('sigma', None) def forward(self, x): out1 = self.fc1(x) out2 = self.fc2(x) out = torch.cat((out1['logits'], out2['logits']), dim=1) # concatenate along the channel if self.sigma is not None: out = self.sigma * out return { 'old_scores': out1['logits'], 'new_scores': out2['logits'], 'logits': out } '''