注:本篇博客大部分内容并非原创,而是本人将之前收藏的资料整理,并加以自己的愚解整合成到一起,方便回顾复习,所有参考资料均已注明出处,并已点赞加收藏~
前言: 上篇我们讲完了图像分类、目标检测常用的损失函数,这篇我们继续往下讲,主要讲的是人脸识别的损失函数。
人脸识别算是CV落地最成熟的一个方向了,损失函数对于人脸模型来说实在是太重要了,常见的人脸识别框架facenet、insightface都会在论文里面花大篇幅去介绍它们的损失函数。
那么这一章,我们按照softmax→Triplet Loss→Center Loss→Sphereface→Cosface→Arcface的顺序介绍人脸识别常用的损失函数。
1. 人脸识别
1.1 Softmax Loss
softmax在激活函数篇章中已经有过详细的介绍,不太了解的同学可以翻一翻之前的内容
https://zhuanlan.zhihu.com/p/380237014
softmax既可以充当激活函数,又可以当损失函数来用(但是和之前讲的激活函数softmax又有一丢丢不一样),在经典的人脸识别框架facenet中,损失函数之一便是softmax(另外几个是tripet loss和center loss,下面再讲),具体的应用大家可以去看下官方开源的代码:
https://github.com/davidsandberg/facenet/blob/master/src/train_softmax.py
1.2 Tripet Loss(三元组损失)
三元组由三部分组成,分别是anchor, positive, negative:
- anchor是基准
- positive是针对anchor的正样本,表示与anchor来自同一个人
- negative是针对anchor的负样本
triplet loss的目标是使得: - 具有相同label的样本,它们的embedding在embedding空间尽可能接近
- 具有不同label的样本,它们的embedding距离尽可能拉远
如下图所示,图中的anchor与positive属于同一id,即
y
a
n
c
h
o
r
=
y
p
o
s
i
t
i
v
e
y_{anchor}=y_{positive}
yanchor=ypositive;而anchor与negative属于不同的id,即
y
a
n
c
h
o
r
≠
y
p
o
s
i
t
i
v
e
y_{anchor}\ne y_{positive}
yanchor=ypositive。通过不断学习后,使得anchor与positive的欧式距离变小,anchor与negative的欧式距离变大。其中,这里的anchor、Positive、negative都是图片的d维嵌入向量(我们称之为embedding)。
使用数学公式进行表达,triplet loss想达到的效果是:
其中,
d
(
)
d()
d() 表示两个向量之间的欧氏距离,
α
α
α 表示两个向量之间的 margin ,防止
d
(
x
i
a
,
x
i
p
)
=
d
(
x
i
a
,
x
i
n
)
=
0
d\left(x_{i}^{a}, x_{i}^{p}\right)=d\left(x_{i}^{a}, x_{i}^{n}\right)=0
d(xia,xip)=d(xia,xin)=0。因此,可以最小化triplet loss损失函数来达到此目的:
综上:
- triplet loss 最终的优化目标是拉近 a , p a, p a,p 的距离, 拉远 a , n a, n a,n 的距离
- easy triplets : L = 0 L = 0 L=0 即 d ( a , p ) + m a r g i n < d ( a , n ) d ( a , p ) + m a r g i n<d(a,n) d(a,p)+margin<d(a,n) ,这种情况不需要优化,大部分情况下 a , p a, p a,p 的距离很近, a , n a, n a,n 的距离远
- hard triplets : d ( a , n ) < d ( a , p ) d ( a , n ) <d ( a , p ) d(a,n)<d(a,p) , 即 a , p a, p a,p 的距离远
- semi-hard triplets : d ( a , p ) < d ( a , n ) < d ( a , p ) + m a r g i n d ( a , p ) d ( a , p ) <d ( a , n )<d ( a , p ) + m a r g i n d(a, p) d(a,p)<d(a,n)<d(a,p)+margind(a,p)
- FaceNet 中是随机选取semi-hard triplets 进行训练的, (也可以选择 hard triplets 或者两者一起进行训练)
训练方式:
- offline mining
在训练每个epoch的开始阶段,计算训练集种所有的embedding,并挑选出所有的hard triplets和semi-hard triplets,并在该epoch内训练这些triplets.
这种方式不是很高效,因为每个epoch我们都需要遍历整个数据集来生产triplets。
- online mining
这种想法就是对于每个batch的输入,动态地计算有用的triplets。给定batch size为 B B B ( B B B必须为3的倍数)的样本,我们计算其对应的 B e m b e d d i n g s B_{embeddings} Bembeddings ,此时我们最多可以找到 B 3 t r i p l e t s B^3 triplets B3triplets。当然这其中很多triplet都不是合法的(因为triplet中需要有2个是相同label,1个是不同label)
1.3 Center Loss
Center Loss函数提出于ECCV2016的一篇论文,论文链接:
http://ydwen.github.io/papers/WenECCV16.pdf
为了提高特征的区分能力,作者提出了center loss损失函数,不仅能缩小类内差异,而且能扩大类间差异。
作者首先在MNIST数据集上进行试验,将隐藏层的最后输出维度改为2,使用softmax+交叉熵作为损失函数,将其结果可视化出来,如下图所示。可以看出,交叉熵可以使每一类分开,数据分布呈射线形,但却不够区分性,即类内差异大。
左边为50K的训练集,右边为10K的测试集,也间接说明数据量足够大可以使算法的鲁棒性越强
因此,作者想要在保持数据的可分性前提下,进一步缩小类内之间的差异。为了达到这个目的,提出了Center Loss损失函数:
Center Loss的实现是通过先产生所有类别的向量,然后将这些随机的向量与该类别真正的向量求欧式距离,求得的欧氏距离便作为center loss,通过反向传播自动调整这些初始为随机的向量。
其中,
c
y
i
c_{y_i}
cyi 表示第
y
i
y_i
yi 类的中心。因此,通常将Center Loss和交叉熵进行结合,构成组合损失函数:
其中,
λ
λ
λ 表示center loss的惩罚力度。同样在MNIST中,其结果如下图所示。可以看到随着
λ
λ
λ 的增加,约束性更强,每一类会更聚集在类内中心处。
在使用Center Loss损失函数时,需要引入两个超参:
α
α
α 和
λ
λ
λ 。其中,
λ
λ
λ表示center loss的惩罚力度;而
α
α
α 控制类内中心点
c
y
i
c_{y_i}
cyi的学习率。类内中心点
c
y
i
c_{y_i}
cyi 应该随着特征的不同,会产生变化。一般会在每个mini-batch中更新类内中心点
c
y
i
c_{y_i}
cyi:
参考
[1] https://www.cnblogs.com/dengshunge/p/12252820.html
[2] https://zhuanlan.zhihu.com/p/295512971
[3] https://blog.csdn.net/u013082989/article/details/83537370