硬刚 ArcFace | ECCV 2022 人脸识别新方法 BoundaryFace:一种基于噪声标签自校正框架(附源码实现)

简介: 硬刚 ArcFace | ECCV 2022 人脸识别新方法 BoundaryFace:一种基于噪声标签自校正框架(附源码实现)

引言


得益于数据集规模的大规模增长以及算法的不断演进,基于深度学习的人脸识别算法取得了巨大的进步。今年来也涌现出了许多基于 margin 改进的人脸识别损失函数,例如 ArcFace、CosFace、AdaFace 等等,这些基于边距的损失无疑成为了人脸识别中 softmax 损失的首先替代方案。


本文主要考虑两个问题:


困难样本挖掘对判别学习的重要性

如何解决大规模数据中普通存在的标签噪声问题

因此,作者提出从决策边界的角度出发,提出了一种新颖的挖掘框架,该框架侧重于样本的真实类中心与其最近的负类中心之间的关系。具体来说,提出了闭集噪声标签自校正模块,使得该框架在包含大量标签噪声的数据集上表现良好。所提出的方法在各种人脸识别基准测试中始终优于 SOTA 方法如 ArcFace,值得大家踊跃尝试。


背景


人脸识别是计算机视觉界研究最广泛的课题之一。 大规模数据集、网络架构和损失函数推动了深度卷积神经网络 (DCNN) 在人脸识别方面的成功。 特别是,为了提取判别特征,最新的工作提出了一些直观合理的损失函数。


对于人脸识别,目前存在的损失可以分为两种方法:一种将人脸识别任务视为一般分类问题,因此使用 softmax 进行分类;另一个使用度量学习来解决问题并直接学习嵌入,例如 ArcFace 等。由于度量学习损失通常受到样本批次组合爆炸和半难样本挖掘的影响,因此第二个问题需要通过更复杂的采样策略来解决。


有人指出,经典的分类损失函数(即 Softmax 损失)无法获得判别特征。 基于目前的测试协议,测试集通常与训练图像没有重叠,因此提取具有高判别能力的特征尤为重要。为此,先后提出了 Center loss 和 NormFace 以获得判别性特征。Center loss 开发了一个中心损失来学习每个主题的中心。为了确保训练过程与测试一致,Normface 使网络提取的特征和最后一个全连接层的权重向量位于单位超球面上。最近,还提出了一些基于 margin 的 softmax 损失函数来增强类内紧凑性,同时扩大类间差异,从而产生更具辨别力的特征。


以上方法都取得了比较满意的效果。 然而,有两个非常重要的问题仍然必须解决:


1)以前的研究忽视了困难样本挖掘对判别学习的重要性。相关研究表明,困难样本挖掘是提高性能的关键步骤。 因此,出现了一些基于挖掘的 softmax 损失。 最近,提出了 MV-Arc-Softmax 和 CurricularFace。两者都考虑了样本 ground truth 类与所有负类之间的关系,这可能会使决策边界的优化复杂化。


2)margin-based softmax loss 和 mining-based softmax loss 都忽略了标签噪声的影响。人脸识别数据集中的噪声由两种类型组成:封闭集噪声,其中一些样本被错误地赋予了同一数据集中其他身份的标签,以及开放集噪声,其中样本的子集不属于任何类别,都被错误地分配了一个标签,或者包含一些非面孔。对于噪声,尤其是闭集噪声,会严重影响模型的性能。不幸的是,去除噪声的成本很高,而且在许多情况下是不切实际的。直观地说,如果训练集有噪声,基于挖掘的 softmax 损失函数会对模型产生负面影响。也就是说,基于挖掘的 softmax 在具有严重噪声问题的数据集上的表现可能不如基线方法。因此,设计一个可以同时执行困难样本挖掘和容忍噪声的损失函数仍然是一个悬而未决的问题。


837aad21a7afe53eac80dd97e96d08d6.png


在本文中,作者从决策边界的角度出发,提出了一种新的容忍闭集噪声的挖掘框架。上图描述了该方法提出的动机。具体来说,该框架基于闭集噪声标签校正的前提,直接强调介于 GT 类别中心和最近的负样本类别中心之间的困难样本特征。如果一个样本是闭集噪声,则该样本很有可能分布在最近的负类的决策边界内,而最近的负类很可能是噪声样本的真实类。基于这一发现,作者提出了一个模块,可以在训练期间自动发现闭集噪声并动态更正其标签。基于此模块,挖掘框架可以在严重噪声影响下的大规模数据集上正常工作。

前情铺垫

在正式讲解本方法前,笔者有义务先为大家回顾下当下几类主流的人脸识别损失函数。

Margin-based softmax

由于基本的 softmax 损失不能保证具有足够判别力的面部特征,因此提出了一些基于 Margin 的 softmax 损失,旨在增强类内紧凑性同时扩大类间差异 . 如 《Large-margin softmax loss for convolutional neural networks》 在人脸识别中引入乘法边际以产生判别特征。《Deep hypersphere embedding for face recognition》 在 GT 类和其他类之间引入了一个角度余量 A-Softmax,以鼓励更大的类间差异。由于乘法边际可能会遇到优化问题,Cosface 提出了一个附加边际来稳定优化过程。Arcface 则改变了附加边际的形式,产生了具有明显几何意义的损失。此外,有的研究了传统的基于边缘的 softmax 损失的两个关键超参数的影响,并通过分析它们如何调制预测的分类概率提出了 AdaCos。 即使这些基于边际的 softmax 损失也取得了相对较好的性能,但都没有考虑到 hard sample mining 和 label noise 的影响。


Mining-based softmax


困难样本挖掘方法有两种经典的做法,即 Focal loss 和 Online Hard Sample Mining (OHEM)。最近的一些工作,MV-Arc-Softmax 和 CurricularFace 的灵感来自于将 margin 和 mining 集成到一个框架中。 MV-ArcSoftmax 明确地将误分类样本定义为困难样本,并通过增加相应的负余弦相似度的权重自适应地加强它们,最终在 GT 类和相应的负目标类之间产生更大的特征间隔。 CurricularFace 则将 curriculum learning 应用于人脸识别,前期关注易样本,后期关注难样本。 但是,一方面,两者都考虑了样本 ground truth 类与所有负类之间的关系,这可能会使决策边界的优化复杂化; 另一方面,标签噪声对 mining 造成了一些不利影响。


众所周知,如今人脸识别的成功得益于大规模的训练数据。在这些百万规模的数据集中不可避免地存在噪声。 不幸的是,构建一个“足够干净”的人脸数据集既昂贵又困难。 MV-Arc-Softmax 和 CurricularFace 都假设数据集是干净的(即几乎没有噪音),但在许多情况下这种假设并不成立。直观上,数据集包含的噪声越多,基于挖掘的 softmax 损失的性能就越差。 与开放集噪声不同,一旦我们纠正了它们的标签,封闭集噪声就可以成为干净数据的一部分。 总的来说,本文方法不同于目前流行的基于挖掘的 softmax,因为该方法可以进行困难样本挖掘,同时很好地处理闭集噪声,而当前的方法做不到这一点。


Metric learning loss


Triplet loss 是一种经典的度量学习算法。尽管组合爆炸问题已经让很多研究者将注意力转向了传统 oftmax 适配上,但仍然有一些研究者在探索 metric loss的优化。目前将 proxy 的思想引入到 metric learning 中是目前主流的选择。 proxy-triplet 用正负代理替换标准三元组损失中的正负样本。 最近,NPT-Loss 改进了三元组损失,其将代理三元组中的负代理替换为最近邻负代理,最终形式不包含任何超参数。NPT-Loss 还具有隐式 hardnegative 挖掘的效果。


本文提出的 BoundaryFace 的动机也部分受到了 NPTLoss 的启发。其中,BoundaryFace 与 NPT-Loss 之间的主要区别是:


NPT-Loss 本质上是一种度量学习损失,而 BoundaryFace 本质上是一种基于 softmax 的损失。

NPT-Loss 仍然受到标签噪声的影响。从直观上看,虽然度量学习算法可以更直接地实现类内紧致和类间差异,但噪声问题可能更为突出。

即使具有隐式挖掘效果,NPT-Loss 也不会显式语义化硬样本。就最终公式而言,NPT-Loss 公平地对待所有样本。相比之下,BoundaryFace 从决策边界的角度出发,基于闭集噪声标签校正的前提,直接强调位于边缘区域的 hard sample 特征。


方法

损失函数推导

下面带大家一步步推导公式。先来看下 Margin-based softmax,原始的 softmax 公式如下所示:


b120fd3c8bdf999f51ddbe2cc641215f.png


image.png

e88c8798c5e37eb3510e058d16a803be.png

image.png

image.png

image.png


下面再来看下基于 Mining 的 softmax 损失是如何实现的。Hard sample mining 是让网络更加关注有价值的、难以学习的样本。现有的适用于人脸识别的挖掘方法主要有两种方式:1)从损失的角度关注损失值较大的样本。2)从样本 GT 类和 negative 类的关系上关注被网络误分类的样本。这两者可以用一个公式统一表示:

78227fcba7df5abb0f16014a5b5db62c.png

image.png

b08cc0ebada65459f7d13dadd3ad7d3a.png

同样的,CurricularFace 可以表述为:


image.png

image.png

标签自纠正

下面正式为大家介绍下本文所提及的挖掘框架下的噪声容忍度模块。与开集噪声不同,如果可以适当校正其标签,则闭集噪声样本将转换为干净样本。现有的基于挖掘的 softmax 损失作为其先决条件,假设训练集是干净的数据集。假设对真实噪声数据集中大多数闭集噪声的标签进行了校正;在这种情况下,可以充分缓解噪声数据集上困难样本挖掘方法的不良结果。更具体地说,作者发现当训练适当的情况下,网络具有基本的分类能力;并且该封闭集噪声很可能分布在最近的负类决策边界内。此外,负类很可能是该样本的真实类。


c33b034db5568baa8b8c5ed27edacae0.png

如上图所示,红框包含一个封闭集噪声样本,标记为 Class 1,但ground truth标签为 Class 2。封闭集噪声将分布在 Class 2 的决策边界内。同时,作者动态更改样本的标签,以便样本在正确的方向上得到优化。即在这个闭集噪声的标签被修正之前,在 C1 方向(即“before”箭头)进行优化;校正后,它在 C2 的方向(即“after”箭头)进行优化。标签自校正公式,文中命名为 BoundaryF1,定义如下:


38b7e053560f0848f39ec65c308e81d6.png

这意味着,在每次计算该公式之前,我们可以根据样本是否分布在最近负类的决策边界内来决定是否纠正标签。

BoundaryFace

与基于挖掘的 softmax 分配困难样本的语义不同,我本文方法只考虑位于真实类和最近的负类之间边缘区域的样本。换句话说,由于高维特征空间中的每个样本都有一个最近的负类中心,如果样本特征位于其 ground truth 类中心和最近的负类中心之间的边缘区域,那么我们将其标记为 hard 样本。


5e4f6df000409f471fcf892a1ed4b5eb.png


如上图左边所示,每个类的样本最近的负类可能不同(例如,属于 C1 类的两个样本的最近负类分别是 C2 和 C3)。而在图右手边从决策边界的角度呈现了左子图中的两个类。由于样本 x1 和 x2 位于它们的真实类别和最近的负类之间的边缘区域,我们将它们视为 hard 样本。作者在此处添加了一个额外的正则化项 f 以允许网络直接加强它们。此外,为了确保其对嘈杂数据集的有效性,本文方法将闭集噪声标签校正模块嵌入到挖掘框架中。


ba7aaede515a89aeb67c712005351c00.png

image.png


与 BoundaryF1 一样,在每次计算最终损失之前,我们可以根据样本是否分布在最近负类的决策边界内来决定是否纠正标签。

下面是该方法的伪代码实现:


fa605a04dabb60b2ad66a3c4ad4fd969.png

最后,我们来讨论下本文方法与其它 SOTA 损失函数的区别。首先,对于 ArcFace、CosFace 这类损失函数,它们是从正余弦相似度的角度引入了 margin ,即这些方法对所有样本一视同仁。而本文方法则是通过引入正则化项来挖掘困难样本,使网络更加关注困难样本。


image.png


977e18247b9bf5d42f0d8172b3dd9715.png


实验


3d446ed07b9e2568aec5f5c488bbaeea.png

7034021e0ca3e800e619722ed6686bb0.png

作者首先在干净数据集和仅包含闭集噪声的数据集上训练 BoundaryFace。注意,此处使用 BoundaryF1 作为参考来说明困难样本挖掘的效果。可以看出,与干净数据集上的 SOTA 竞争对手相比,BoundaryFace 优于基线并取得了可比的结果,本文方法在封闭集噪声数据集上展示了优于基线和 SOTA 方法的优越性。 此外,我们可以很容易地得出以下结论:


随着闭集噪声比的增加,每个比较基线方法的性能都迅速下降;而使用本文方法时没有发生这种现象。

Mining-based softmax 在遇到闭集噪声数据时效果相反,方法在干净数据集上表现越好,结果往往越差。

此外,给定 20% 的闭集噪声,下图展示了 BoundaryFace 在训练过程中对闭集噪声的检测,并将其与 ArcFace 进行了比较。可以明显的看出,当检测到闭集噪声后,BoundaryFace 会动态更正其标签。正确的标签会导致封闭集噪声的方向从错误到正确被优化,并且还会导致更准确的类中心。此外,更准确的类中心反过来允许该方法在每次迭代中检测到更多的闭集噪声,最终达到饱和。


9d289b27ec94e9e2262ea45344456531.png


代码实现也很简单,如下所示:

class BoundaryMargin(nn.Module):
    def __init__(self, in_feature=128, out_feature=10575, s=32.0, m=0.50, easy_margin=False, epoch_start=7):
        super(BoundaryMargin, self).__init__()
        self.in_feature = in_feature
        self.out_feature = out_feature
        self.s = s
        self.m = m
        self.weight = Parameter(torch.Tensor(out_feature, in_feature))
        nn.init.xavier_uniform_(self.weight)
        self.easy_margin = easy_margin
        self.cos_m = math.cos(m)
        self.sin_m = math.sin(m)
        # make the function cos(theta+m) monotonic decreasing while theta in [0°,180°]
        self.th = math.cos(math.pi - m)
        self.mm = math.sin(math.pi - m) * m
        self.epoch_start = epoch_start
    def forward(self, x, label, epoch, img_path):
        # cos(theta)
        cosine = F.linear(F.normalize(x), F.normalize(self.weight))
        # cos(theta + m)
        sine = torch.sqrt(1.0 - torch.pow(cosine, 2))
        phi = cosine * self.cos_m - sine * self.sin_m
        if self.easy_margin:
            phi = torch.where(cosine > 0, phi, cosine)
        else:
            phi = torch.where((cosine - self.th) > 0, phi, cosine - self.mm)
        #one_hot = torch.zeros(cosine.size(), device='cuda' if torch.cuda.is_available() else 'cpu')
        one_hot = torch.zeros_like(cosine)
        one_hot.scatter_(1, label.view(-1, 1), 1)
        if epoch > self.epoch_start:
            right2 = one_hot * cosine.detach()
            left2 = (1.0 - one_hot) * phi.detach()
            left_max2, argmax2 = torch.max(left2.detach(), dim=1)
            max_index2 = argmax2.detach()
            right_max2, _ = torch.max(right2.detach(), dim=1)
            sub2 = left_max2 - right_max2
            zero2 = torch.zeros_like(sub2)
            temp2 = torch.where(sub2 > 0, sub2, zero2)
            # non_zero_index2 = torch.nonzero(temp2.detach(), as_tuple=True)
            non_zero_index2 = torch.nonzero(temp2.detach())
            numpy_index2 = torch.squeeze(non_zero_index2, 1)
            # numpy_index2 = non_zero_index2[0]
            with open(r'/home/xun/wsj/FaceX-Zoo-main/MS1M_saved_both/savedPath_' + str(epoch) + '.txt', 'a') as f:
                for index in numpy_index2:
                    one_hot_line = torch.zeros((1, self.out_feature)).cuda()
                    one_hot_line.scatter_(1, max_index2[index].unsqueeze(-1).view(-1, 1), 1)
                    one_hot[index] = one_hot_line
                    f.write(img_path[index] + '\t' + str(max_index2.cpu().numpy()[index]) + '\n')
            rectified_label = torch.topk(one_hot, 1)[1].squeeze(1).cuda()
            right = one_hot * phi
            left = (1.0 - one_hot) * cosine
            output = left + right
            output = output * self.s
            left_max, _ = torch.max(left, dim=1)
            right_max, _ = torch.max(right, dim=1)
            sub = left_max - right_max
            zero = torch.zeros_like(sub)
            temp = torch.where(sub > 0, sub, zero)
            final = torch.mean(temp) * math.pi
        if epoch <= self.epoch_start:
            rectified_label = label
            final = 0.0
            output = (one_hot * phi) + ((1.0 - one_hot) * cosine)
            output = output * self.s
        return output, final, rectified_label

总结


本文主要提出了一种新的挖掘框架,即 BoundaryFace,它可以容忍用于人脸识别的闭集噪声。 BoundaryFace 在很大程度上缓解了基于挖掘的 softmax 在具有严重噪声问题的数据集上的不良性能。BoundaryFace 易于实现且收敛性强。此外,作者研究了可能被优化为困难样本的噪声样本的影响,最终取得了极具竞争力的结果。


image.png


如果您也对人工智能和计算机视觉全栈领域感兴趣,强烈推荐您关注有料、有趣、有爱的公众号『CVHub』,每日为大家带来精品原创、多领域、有深度的前沿科技论文解读及工业成熟解决方案!欢迎添加小编微信号: cv_huber,备注"CSDN",加入 CVHub 官方学术&技术交流群,一起探讨更多有趣的话题!


目录
相关文章
|
2月前
|
监控 安全 数据可视化
java智慧工地人脸识别终端源码
微服务+Java+Spring Cloud +Vue+UniApp +MySql
40 7
|
1月前
|
Java 计算机视觉
java实现人脸识别源码【含测试效果图】——前台页面层(login.jsp)
java实现人脸识别源码【含测试效果图】——前台页面层(login.jsp)
13 0
|
1月前
|
Java 计算机视觉
java实现人脸识别源码【含测试效果图】——前台显示层(index.jsp)
java实现人脸识别源码【含测试效果图】——前台显示层(index.jsp)
15 1
|
1月前
|
Java 计算机视觉
java实现人脸识别源码【含测试效果图】——Servlet层(FaceServlet)
java实现人脸识别源码【含测试效果图】——Servlet层(FaceServlet)
12 1
|
1月前
|
Java 计算机视觉
java实现人脸识别源码【含测试效果图】——ServiceImpl层(UserServiceImpl)
java实现人脸识别源码【含测试效果图】——ServiceImpl层(UserServiceImpl)
19 1
|
1月前
|
Java 计算机视觉
java实现人脸识别源码【含测试效果图】——Service层(IUserService)
java实现人脸识别源码【含测试效果图】——Service层(IUserService)
13 1
|
1月前
|
Java 计算机视觉
java实现人脸识别源码【含测试效果图】——DaoImpl层(UserDaoImpl)
java实现人脸识别源码【含测试效果图】——DaoImpl层(UserDaoImpl)
9 1
|
1月前
|
Java 计算机视觉
java实现人脸识别源码【含测试效果图】——DaoImpl层(BaseDaoUtilImpl)
java实现人脸识别源码【含测试效果图】——DaoImpl层(BaseDaoUtilImpl)
10 0
|
1月前
|
Java 计算机视觉
java实现人脸识别源码【含测试效果图】——Dao层(IBaseDaoUtil)
java实现人脸识别源码【含测试效果图】——Dao层(IBaseDaoUtil)
14 0
|
1月前
|
Java 计算机视觉
java实现人脸识别源码【含测试效果图】——Dao层(BaseDao)
java实现人脸识别源码【含测试效果图】——Dao层(BaseDao)
14 1

热门文章

最新文章