这两个作品分别出自2018年的CVPR和2018年的ICLR,属于撞车作品。

    CosFace中的loss名为large margin loss,与AM-softmax基本相同。

    CosFace与AM-softmax在归一化权值W与归一化特征x的基础上,采用固定尺度因子s=30,把乘性margin改成了加性margin,使训练难度大幅降低。

    large margin当中的夹角,是权值W和特征x之间的夹角,并不是不同类别之间的夹角,loss函数也完全没有涉及不同类别特征向量之间的夹角约束。

    Large margin的优化目标是让权值向量W和特征向量f之间的夹角更小。例如,假如一个类别有1000张图像,经CNN特征映射后得到1000个特征向量,而权值向量W是每个类别只有一个,large margin loss要求这1000个特征向量和这1个权值向量的夹角非常小,也就是说,优化让1000个特征向量都向权值向量W的方向靠拢。

    Large margin在优化上的困难主要是由乘性margin引起的。

    首先,乘性 margin 把 cos 函数的单调区间压小了,如果某个类别正好落到了目标区域之外,那么通过乘法是无法让其重新进入目标区域的,导致优化困难。

    其次,乘性 margin 所造成的 margin 实际上是不均匀的,依赖于 两个类别的权重W之间的夹角。如果这两个 class 本身挨得很近,那么他们的 margin 就小,这两个class也变得不可优化。

    换成加性 margin 之后,large margin loss具备了单调下降的特性,上述提到的两个乘性 margin 的弊端自然就消失了。

    CosFace的Large Margin Cosine Loss如下图所示:

    Screenshot from 2020-03-09 18-36-201583800336829.png

    1. # CosFace
    2. class AddMarginProduct(nn.Module):
    3. """
    4. Implement of large margin cosine distance: :
    5. Args:
    6. in_features: size of each input sample
    7. out_features: size of each output sample
    8. s: norm of input feature
    9. m: margin
    10. cos(theta) - m
    11. """
    12. def __init__(self, in_features, out_features, s=30.0, m=0.40):
    13. super(AddMarginProduct, self).__init__()
    14. self.in_features = in_features
    15. self.out_features = out_features
    16. self.s = s
    17. self.m = m
    18. self.weight = Parameter(torch.FloatTensor(out_features, in_features))
    19. nn.init.xavier_uniform_(self.weight)
    20. def forward(self, input, label):
    21. # --------------------------- cos(theta) & phi(theta) ---------------------------
    22. cosine = F.linear(F.normalize(input), F.normalize(self.weight))
    23. phi = cosine - self.m
    24. # --------------------------- convert label to one-hot ---------------------------
    25. one_hot = torch.zeros(cosine.size(), device='cuda')
    26. # one_hot = one_hot.cuda() if cosine.is_cuda else one_hot
    27. one_hot.scatter_(1, label.view(-1, 1).long(), 1)
    28. # -------------torch.where(out_i = {x_i if condition_i else y_i) -------------
    29. output = (one_hot * phi) + ((1.0 - one_hot) * cosine)
    30. # you can use torch.where if your torch.__version__ is 0.4
    31. output *= self.s
    32. # print(output)
    33. return output
    34. def __repr__(self):
    35. return self.__class__.__name__ + '(' \
    36. + 'in_features=' + str(self.in_features) \
    37. + ', out_features=' + str(self.out_features) \
    38. + ', s=' + str(self.s) \
    39. + ', m=' + str(self.m) + ')'

    CosFace:
    AM-softmax: