# 人脸识别损失函数的汇总 | Pytorch版本实现

## Softmax

### Pytorch代码实现

```class Linear(nn.Module):
def __init__(self):
super(Linear, self).__init__()
self.weight = nn.Parameter(torch.Tensor(2, 10))  # (input,output)
nn.init.xavier_uniform_(self.weight)

def forward(self, x, label):
out = x.mm(self.weight)
loss = F.cross_entropy(out, label)
return out, loss```

emmm...现实生活中根本没人会这么写好吧！明明就有现成的Linear层啊喂！

## Modified Softmax

### Pytorch代码实现

```class Modified(nn.Module):
def __init__(self):
super(Modified, self).__init__()
self.weight = nn.Parameter(torch.Tensor(2,10))#(input,output)
nn.init.xavier_uniform_(self.weight)
self.weight.data.uniform_(-1,1).renorm_(2,1,1e-5).mul_(1e5)
#因为renorm采用的是maxnorm，所以先缩小再放大以防止norm结果小于1

def forward(self, x):
w=self.weight
ww=w.renorm(2,1,1e-5).mul(1e5)
out = x.mm(ww)
return out```

## NormFace

### Pytorch代码实现

```class NormFace(nn.Module):
def __init__(self):
super(NormFace, self).__init__()
self.weight = nn.Parameter(torch.Tensor(2, 10))  # (input,output)
nn.init.xavier_uniform_(self.weight)
self.weight.data.uniform_(-1, 1).renorm_(2, 1, 1e-5).mul_(1e5)
self.s = 16
# 因为renorm采用的是maxnorm，所以先缩小再放大以防止norm结果小于1

def forward(self, x, label):
cosine = F.normalize(x).mm(F.normalize(self.weight, dim=0))
loss = F.cross_entropy(self.s * cosine, label)
return cosine, loss```

## SphereFace：A-softmax

### Pytorch代码实现

```class SphereFace(nn.Module):
def __init__(self, m=4):
super(SphereFace, self).__init__()
self.weight = nn.Parameter(torch.Tensor(2, 10))  # (input,output)
nn.init.xavier_uniform_(self.weight)
self.weight.data.renorm_(2, 1, 1e-5).mul_(1e5)
self.m = m
self.mlambda = [  # calculate cos(mx)
lambda x: x ** 0,
lambda x: x ** 1,
lambda x: 2 * x ** 2 - 1,
lambda x: 4 * x ** 3 - 3 * x,
lambda x: 8 * x ** 4 - 8 * x ** 2 + 1,
lambda x: 16 * x ** 5 - 20 * x ** 3 + 5 * x
]
self.it = 0
self.LambdaMin = 3
self.LambdaMax = 30000.0
self.gamma = 0

def forward(self, input, label):
# 注意，在原始的A-softmax中是不对x进行标准化的,
# 标准化可以提升性能，也会增加收敛难度，A-softmax本来就很难收敛

cos_theta = F.normalize(input).mm(F.normalize(self.weight, dim=0))
cos_theta = cos_theta.clamp(-1, 1)  # 防止出现异常
# 以上计算出了传统意义上的cos_theta，但为了cos(m*theta)的单调递减，需要使用phi_theta

cos_m_theta = self.mlambda[self.m](cos_theta)
# 计算theta，依据theta的区间把k的取值定下来
theta = cos_theta.data.acos()
k = (self.m * theta / 3.1415926).floor()
phi_theta = ((-1) ** k) * cos_m_theta - 2 * k

x_norm = input.pow(2).sum(1).pow(0.5)  # 这个地方决定x带不带模长，不带就要乘s
x_cos_theta = cos_theta * x_norm.view(-1, 1)
x_phi_theta = phi_theta * x_norm.view(-1, 1)

############ 以上计算target logit，下面构造loss，退火训练#####
self.it += 1  # 用来调整lambda
target = label.view(-1, 1)  # (B,1)

onehot = torch.zeros(target.shape[0], 10).cuda().scatter_(1, target, 1)

lamb = max(self.LambdaMin, self.LambdaMax / (1 + 0.2 * self.it))

output = x_cos_theta * 1.0  # 如果不乘可能会有数值错误？
output[onehot.byte()] -= x_cos_theta[onehot.byte()] * (1.0 + 0) / (1 + lamb)
output[onehot.byte()] += x_phi_theta[onehot.byte()] * (1.0 + 0) / (1 + lamb)
# 到这一步可以等同于原来的Wx+b=y的输出了，

# 到这里使用了Focal Loss，如果直接使用cross_Entropy的话似乎效果会减弱许多
log = F.log_softmax(output, 1)
log = log.gather(1, target)

log = log.view(-1)
pt = log.data.exp()
loss = -1 * (1 - pt) ** self.gamma * log

loss = loss.mean()
# loss = F.cross_entropy(x_cos_theta,target.view(-1))#换成crossEntropy效果会差
return output, loss```

## InsightFace(ArcSoftmax)

### Pytorch代码实现

```class ArcMarginProduct(nn.Module):
def __init__(self, s=32, m=0.5):
super(ArcMarginProduct, self).__init__()
self.in_feature = 2
self.out_feature = 10
self.s = s
self.m = m
self.weight = nn.Parameter(torch.Tensor(2, 10))  # (input,output)
nn.init.xavier_uniform_(self.weight)
self.weight.data.renorm_(2, 1, 1e-5).mul_(1e5)

self.cos_m = math.cos(m)
self.sin_m = math.sin(m)
# 为了保证cos(theta+m)在0-pi单调递减：
self.th = math.cos(3.1415926 - m)
self.mm = math.sin(3.1415926 - m) * m

def forward(self, x, label):
cosine = F.normalize(x).mm(F.normalize(self.weight, dim=0))
cosine = cosine.clamp(-1, 1)  # 数值稳定
sine = torch.sqrt(torch.max(1.0 - torch.pow(cosine, 2), torch.ones(cosine.shape).cuda() * 1e-7))  # 数值稳定

##print(self.sin_m)
phi = cosine * self.cos_m - sine * self.sin_m  # 两角和公式
# # 为了保证cos(theta+m)在0-pi单调递减：
# phi = torch.where((cosine - self.th) > 0, phi, cosine - self.mm)#必要性未知
#
one_hot = torch.zeros_like(cosine)
one_hot.scatter_(1, label.view(-1, 1), 1)
output = (one_hot * phi) + ((1.0 - one_hot) * cosine)

output = output * self.s
loss = F.cross_entropy(output, label)

return output, loss```

### 可视化

ArcSoftmax需要更久的训练，这个收敛还不够充分...颜值堪忧，另外ArcSoftmax经常出现类别在特征空间分布不均匀的情况，这个也有点费解，难道在训FR模型的时候先用softmax然后慢慢加margin有奇效？SphereFace那种退火的训练方式效果好会不会和这个有关呢...

## Center Loss

### Pytorch代码实现

```class centerloss(nn.Module):
def __init__(self):
super(centerloss, self).__init__()
self.center = nn.Parameter(10 * torch.randn(10, 2))
self.lamda = 0.2
self.weight = nn.Parameter(torch.Tensor(2, 10))  # (input,output)
nn.init.xavier_uniform_(self.weight)

def forward(self, feature, label):
batch_size = label.size()[0]
nCenter = self.center.index_select(dim=0, index=label)
distance = feature.dist(nCenter)
centerloss = (1 / 2.0 / batch_size) * distance
out = feature.mm(self.weight)
ceLoss = F.cross_entropy(out, label)
return out, ceLoss + self.lamda * centerloss```

### 总结

Wang M, Deng W. Deep face recognition: A survey[J]. arXiv preprint arXiv:1804.06655, 2018.

0 条评论

• ### 【项目实践】中英文文字检测与识别项目（CTPN+CRNN+CTC Loss原理讲解）

文字识别也是图像领域一个常见问题。然而，对于自然场景图像，首先要定位图像中的文字位置，然后才能进行文字的识别。

• ### 【项目实践】YOLO V4万字原理详细讲解并训练自己的数据集（pytorch完整项目打包下载）

YOLOV4是YOLOV3的改进版，在YOLOV3的基础上结合了非常多的小Tricks。尽管没有目标检测上革命性的改变，但是YOLOV4依然很好...

• ### 轻松学Pytorch – 构建生成对抗网络

又好久没有继续写了，这个是我写的第21篇文章，我还在继续坚持写下去，虽然经常各种拖延症，但是我还记得，一直没有敢忘记！今天给大家分享一下Pytorch生成对抗网...

• ### Face Recognition Loss on Mnist with Pytorch

这篇文章的重点不在于讲解FR的各种Loss，因为知乎上已经有很多，搜一下就好，本文主要提供了各种Loss的Pytorch实现以及Mnist的可视化实验，一方面让...

• ### 机器学习(7)之感知机python实现

关键字全网搜索最新排名 【机器学习算法】：排名第一 【机器学习】：排名第二 【Python】：排名第三 【算法】：排名第四 感知器PLA是一种最简单，最基本的线...

• ### 数据结构笔记（二）：栈、队列

4、栈可以用数组实现，也可以用链表实现。用数组实现的栈叫做顺序栈，用链表实现的栈叫做链式栈。

• ### 设计了一个简易的Python GUI界面

设计一个支持多个招聘网站的检索，可以通过指定目标城市、检索职业和查询数量，个性化输出检索结果，尔后将结果显示在界面并保存于文档中。

• ### python模块:win32com用法详解

import win32com from win32com.client import Dispatch, constants

• ### python GUI库图形界面开发之PyQt5信号与槽的高级使用技巧装饰器信号与槽详细使用方法与实例

在上面的代码中，‘发送者对象名称’就是使用setObjectName函数设置的名称，因此自定义槽函数的命名规则也可以看做：on+使用setObjectName设...

• ### 数据结构之——Python实现循环队列

栈是先入后出，与之相反的是队列，队列是先进先出的线性结构。队列是只允许在一端进行插入操作，而在另一端进行删除操作的线性表。允许插入的一端称为队尾，允许删除的一端...

### OpenCV学堂

OpenCV开发专家计算机视觉开发