总结
这篇在Arcface loss之后出的,比Arcface多了对正负样本的balance策略
公式里的ap,sp,delta_n,delta_p和代码里一样,sp和sn表示正负样本的Embedding,ap和an是sp和sp前面的加权系数,为了balance正负样本,delta_n和delta_p是为了拉开正负样本的间距
#from https://github.com/TinyZeaMays/CircleLoss/blob/master/circle_loss.py
from typing import Tuple
import torch
from torch import nn, Tensor
def convert_label_to_similarity(normed_feature: Tensor, label: Tensor) -> Tuple[Tensor, Tensor]:
similarity_matrix = normed_feature @ normed_feature.transpose(1, 0)
label_matrix = label.unsqueeze(1) == label.unsqueeze(0)
positive_matrix = label_matrix.triu(diagonal=1) # 主对角线上移一位
negative_matrix = label_matrix.logical_not().triu(diagonal=1)
similarity_matrix = similarity_matrix.view(-1)
positive_matrix = positive_matrix.view(-1)
negative_matrix = negative_matrix.view(-1)
# import pdb; pdb.set_trace();
return similarity_matrix[positive_matrix], similarity_matrix[negative_matrix]
class CircleLoss(nn.Module):
def __init__(self, m: float, gamma: float) -> None:
super(CircleLoss, self).__init__()
self.m = m
self.gamma = gamma
self.soft_plus = nn.Softplus() # \text{Softplus}(x) = \frac{1}{\beta} \log(1 + \exp(\beta x)),可以看成是relu的平滑版本
def forward(self, sp: Tensor, sn: Tensor) -> Tensor:
# 0和1相似,所以sp的shape是size(1);0和1,0和2不相似,所以sn的shape是size(2)
print('sp={} sn={}'.format(sp, sn)) # sp=tensor([0.4792], grad_fn=<IndexBackward0>) sn=tensor([0.7567, 0.7183], grad_fn=<IndexBackward0>)
ap = torch.clamp_min(-sp.detach() + 1 + self.m, min=0.) #ap是sp的衰减因子,非常粗暴的用clip(1-sp+margin)来定
an = torch.clamp_min(sn.detach() + self.m, min=0.) #同理,an是sn的衰减因子
print('ap={} an={}'.format(ap, an)) # ap=tensor([0.3570]) an=tensor([1.1160, 1.2385])
delta_p = 1 - self.m
delta_n = self.m
logit_p = - ap * (sp - delta_p) * self.gamma
logit_n = an * (sn - delta_n) * self.gamma
import pdb; pdb.set_trace();
loss = self.soft_plus(torch.logsumexp(logit_n, dim=0) + torch.logsumexp(logit_p, dim=0))
return loss
if __name__ == "__main__":
feat = nn.functional.normalize(torch.rand(3, 4, requires_grad=True))
# lbl = torch.randint(high=4, size=(3,))
lbl = torch.LongTensor([1,1,0])
inp_sp, inp_sn = convert_label_to_similarity(feat, lbl)
criterion = CircleLoss(m=0.25, gamma=256)
circle_loss = criterion(inp_sp, inp_sn)
print(circle_loss) # 输出 tensor(239.7694, grad_fn=<SoftplusBackward0>)
triplet loss
以上转载自: Triplet loss学习笔记 - 知乎