arcface,pytorch代码理解记录

    def train(self, conf, epochs):
        self.model.train()
        running_loss = 0.            
        for e in range(epochs):
            print('epoch {} started'.format(e))
            if e == self.milestones[0]:
                self.schedule_lr()
            if e == self.milestones[1]:
                self.schedule_lr()      
            if e == self.milestones[2]:
                self.schedule_lr()
            s = time.time()

            prefetcher = data_prefetcher(self.loader)
            input, target = prefetcher.next()
            i = 0

            while input is not None and len(input)==conf.batch_size:
                i += 1
                print(input)
                imgs = input.to(conf.device)
                labels = target.to(conf.device)
                s1 = time.time()
                print (s1-s)

                self.optimizer.zero_grad()
                embeddings = self.model(imgs)
                thetas = self.head(embeddings, labels)


                # print (thetas.shape, labels)
                loss = conf.ce_loss(thetas, labels)
                loss.backward()
                running_loss += loss.item()
                self.optimizer.step()
class Arcface(Module):
    # implementation of additive margin softmax loss in https://arxiv.org/abs/1801.05599    
    def __init__(self, embedding_size=512, classnum=51332,  s=64., m = 0.5):
        super(Arcface, self).__init__()
        self.classnum = classnum
        self.kernel = Parameter(torch.Tensor(embedding_size, classnum))
        # initial kernel
        self.kernel.data.uniform_(-1, 1).renorm_(2, 1, 1e-5).mul_(1e5) #uniform_(-1, 1)服从均匀分布,mul_对应点相乘
        self.m = m # the margin value, default is 0.5
        self.s = s # scalar value default is 64, see normface https://arxiv.org/abs/1704.06369
        self.cos_m = math.cos(m)
        self.sin_m = math.sin(m)
        self.mm = self.sin_m * m  # issue 1
        self.threshold = math.cos(math.pi - m)
    def forward(self, embbedings, label):
        # weights norm
        nB = len(embbedings)
        kernel_norm = l2_norm(self.kernel, axis=0)
        # cos(theta+m)
        cos_theta = torch.mm(embbedings, kernel_norm)#进行矩阵乘法
#         output = torch.mm(embbedings,kernel_norm)
        cos_theta = cos_theta.clamp(-1,1) # for numerical stability
        cos_theta_2 = torch.pow(cos_theta, 2)
        sin_theta_2 = 1 - cos_theta_2
        sin_theta = torch.sqrt(sin_theta_2)
        cos_theta_m = (cos_theta * self.cos_m - sin_theta * self.sin_m)
        # this condition controls the theta+m should in range [0, pi]
        #      0<=theta+m<=pi
        #     -m<=theta<=pi-m
        cond_v = cos_theta - self.threshold
        cond_mask = cond_v <= 0
        keep_val = (cos_theta - self.mm) # when theta not in [0,pi], use cosface instead
        cos_theta_m[cond_mask] = keep_val[cond_mask]
        output = cos_theta * 1.0 # a little bit hacky way to prevent in_place operation on cos_theta
        idx_ = torch.arange(0, nB, dtype=torch.long)
        output[idx_, label] = cos_theta_m[idx_, label]
        output *= self.s # scale up in order to make softmax work, first introduced in normface
        return output

arcface 损失计算之前先牢记,softmax loss 计算过程

softmax loss 两个计算步骤,先将模型的特征输出(通常是线性的bn 后面的特征结果)
在这里插入图片描述

softmax 计算的是概率,输入是模型的特征输出维度维度是(N,C),C是分类数量(但是,arcfaceloss 这里是模型输出512 特征,通过 arcface head,结构计算,得到一个(512,C)的特征,没有arcface 结构,通常这里的输入特征维度,就是最后的线性分类层。
在这里插入图片描述
softmax loss, 这里的yj 是0或者1, 对应分类label 的是1,只关注 该分类样本的概率
(https://blog.csdn.net/luoxuexiong/article/details/90062937)
在这里插入图片描述但是pytorch 实际使用用的是
loss = nn.CrossEntropyLoss()

This criterion combines :func:nn.LogSoftmax and :func:nn.NLLLoss in one single class
在这里插入图片描述
softmax 概率计算的一个数值更加稳定的计算是 LogSoftmax,
所以 CrossEntropyLoss ,包含了概率计算,和损失函数计算两个步骤,如果概率计算是softmax,计算,那么softmaxLoss == CrossEntropyLoss

然后我们看arcface, arcface head 结构是有学习参数的,就是分类输出线性层的改造,输出是分类个,w 与输入向量x的角度,不是通常的w*x 矩阵乘积

mxnet 是这句话
fc7 = mx.sym.FullyConnected(data=nembedding, weight = _weight, no_bias = True, num_hidden=args.num_classes, name='fc7'

pytorch是这句话
        self.kernel = Parameter(torch.Tensor(embedding_size, classnum))
        self.kernel.data.uniform_(-1, 1).renorm_(2, 1, 1e-5).mul_(1e5) #uniform_(-1, 1)服从均匀分布,mul_对应点相乘
		————————————————
		原文链接:https://blog.csdn.net/jacke121/article/details/104790999/

Parameter并将这个parameter绑定到这个module里面(net.parameter()中就有这个绑定的parameter,所以在参数优化的时候可以进行优化的)变成了模型的一部分,不直接使用一个torch.nn.Linear()可能是因为学习的效果不好(https://blog.csdn.net/qq_36955294/article/details/88117170,我不知道为什么,这里说的理由)

w.renorm(2,0,1e-5).mul(le5) 对w进行归一化 。w.renorm中前两个2,0是代表在对w进行在第0维度的L2范数操作得到归一化结果。1e-5是代表maxnorm ,将大于1e-5的乘以1e5,使得最终归一化到1。
这句话就是归一化到 0-1,之间,到底怎么计算得到的,看官方文档,
在这里插入图片描述
维度是0 计算过程基本是这样 L2 范数
1/(根号(1+4+9) = 0.2673
2/(根号(1+4+9) = 0.5345
4/(根号(16+25+36))=0.2558

维度是1,
4/根号(1+16) = 0.9701,分母是1和4,所有的列元素

arcface对模型的最后一个分类层,的图解,
在这里插入图片描述

在这里插入图片描述(通俗易懂-arcface https://zhuanlan.zhihu.com/p/101059838?from_voters_page=true

在这里插入图片描述
人脸识别:《Arcface》论文详解,对损失层的改进有较为详细的讲解

理解的感觉我觉得类似归一化, 原先是 w*x,作为输入
现在是 cos角度作为输入(值在 -1,1 之间), 做分类,
尽管在余弦范围到角度范围的映射具有一对一的关系,但他们之间仍有不同之处,事实上,实现角度空间内最大化分类界限相对于余弦空间而言具有更加清晰的几何解释性,角空间中的边缘差距也相当于超球面上的弧距。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章