近几年,机器学习的安全问题逐渐被研究者们所重视。而人脸识别也展现了弱人工智能向强人工智能的转化。
人脸识别是基于人的脸部特征信息进行身份识别的一种生物识别技术。人脸与人体的其它生物特征(指纹、虹膜等)一样与生俱来,它的唯一性和不易被复制的良好特性为身份鉴别提供了必要的前提,与其它类型的生物识别比较,人脸识别具非强制性、非接触性、及并发性,除此之外,还符合视觉特性。但这项技术也并非牢不可破。
近日,biendata平台邀请科大讯飞金牌讲师阿水,共同打造一门针对人脸识别的 AI 对抗攻防系列课程,主题囊括对抗样本介绍、人脸识别对抗样本、对抗防御、视频人脸检测与对抗防御等。
除对抗攻防的知识讲解外,课程还设有实战项目演练:以时下两个热门的AI对抗主题的竞赛为案例,详细讲解赛题及baseline模型代码,让参与学习的同学能够学以致用,快速上手实战。
本文为第三讲课程演讲实录整理,主要针对的赛题和baseline进行讲解,尤其是细节处理的讲解。本篇baseline利用开源的人脸识别模型生成对抗样本进行黑盒攻击。建议读者在前两讲的基础上进行第三讲的阅读。
第一讲:
Biendata,公众号:数据实战派
它为什么是人脸识别中的 “不定时炸弹”?丨独家公开课实录
第二讲:
Biendata,公众号:数据实战派
60万元奖金 “人脸攻防战”,怎么打?丨独家公开课实录(附baseline)
点击文末“阅读原文”可获取视频,“数据实战派”后台按指示获取ppt。获取代码、加入课程及竞赛讨论群可添加小助手。
一、进阶思路有哪些?
首先来看一看具体有哪些进阶思路。
在本次比赛中,一个关键点是人脸图片的尺寸问题,具体来讲就是人脸图片尺寸其实和神经网络输入的尺寸相差比较大。由于生成的对抗样本尺寸是112112,如果把一张112112尺寸的图片缩放还原成原始尺寸(可能是几百像素*几百像素),图片会变得非常模糊。
在这种情况下,如何处理超大尺寸的人脸图片问题就会成为赛题的一个关键点。
一种方法是尽可能地缩小输入的尺寸,比如只让图片中对应的人脸区域进行正向传播或反向传播,这是一种非常好的思路。
本次比赛官方提供的baseline其实就是基于这种思路,可以利用Dlib以及MTCNN先检测得到人脸的区域,然后生成人脸的Mask。
在生成人脸Mask之后,产生对抗样本的区域就主要集中在 Mask内,而其他的非Mask区域,就不需要再进行正向传播或反向传播。关于这部分细节下文展开讲解。
比如下图就是提前生成的一张图片对应的人脸Mask(黑色区域为非Mask区域),尽管这个Mask可能并不是和人脸的实际骨骼所吻合。
在实际操作中,可以选取其中关键位置通过一些方法得到一个关键的区域,作为神经网络重点关注的一个像素区域。我们把这部分区域保留下来即得到人脸Mask,在之后进行攻击或还原图像的时候就可以只针对这部分进行处理。这样处理后的区域会变得更小,也就更接近112*112的范围。
所以,总结来看,第一种方法就是基于人脸的关键点,产生人脸的Mask,我们后续就可以针对这个Mask做一些正向传播或赋值的相关操作。
第二种方法是做人脸缩放。在第二讲的baseline代码中,利用OpenCV的Resize函数来完成尺寸缩放,将图片尺寸缩放至112*112。
在这种情况下,能否用池化来完成?
比如利用卷积改变图片的维度,或者利用池化来代替Resize的操作。这样就可以让图片以原始的尺寸输入到模型中,完成更加精细的端到端的建模。
现在请各位再回忆下在之前课程中讲到的对抗样本的概念——生成一个对抗样本的过程中,实际上对抗样本与原始样本的差异性非常小,但足以让一个模型对其误分类。
那么,在此种情况下,其实可以不仅仅是从分类的损失来进行度量,也可以从其他的损失,比如 TV loss,或者L2 loss,主要是为了保证噪音是更加平滑的。
哪种方法效果比较好?
在对抗攻击比赛中,可能用FGSM或者PCD就比较好了,更复杂的一些方法可能不太实用,在很多时候不是方法的差异,而是针对细节处理的差异。
在这种情况下,原始图片是有RGB三个通道的,而我们所产生的噪音相当于是对它的扰动,也是有三个通道的。
我们需要使扰动更加平滑,可以对扰动做一个高速滤波,或者说让这些扰动在不同的channel层,用TV loss来度量通道数之间的差异性。在使扰动更加平滑之后,我们得到的样本和原始的样本差异性会更小, L2 loss也是差不多的原理。
Cosine loss是从feature map的维度衡量目标样本和原始样本之间的差异性。在这种情况下,可以从多个模型得到它的feature map,然后整体做加权求和。在进行训练的过程中,其实loss function可以不仅仅是由Cosine loss作为backward,也可以用其他的一些loss项加到backward里。
如果对刚才所讲的“关键区域”不太了解,下文将进行阐释。
具体比赛的建模过程中,如果对线上的评分模型有更清晰的理解,接下来就更知道如何去攻击它。因此,要更多地去了解一下黑盒模型的具体判断流程。
这种情况下,可以做一些对比,比如只在人脸区域增加扰动,和在人脸区域及非人脸区域都添加扰动,通过对比可以发现,只在人脸区域增加扰动的得分会更高一些。
这说明线上的具体的评分的模型,可能是基于MTCN的这种关键的检测。除关键点识别外,也可以通过attention map计算出关键区域。
Attention map是指,在进行计算过程中,可以通过正向传播计算得到的feature map和具体的标签,算一算它的反向传播的 gradience,或者也可以用camera gradience、classification gradience map、或者类别激活图CAM的方法,去将原始图片里面非常关键的一些像素区域识别出来,来代替人脸关键点。
因为在复赛里面可能有一些新的人脸图片,而且baseline里也有一些图片是识别不出来关键点的,在这种情况下,可能需要用attention map来识别出关键区域。
一般情况下attention map和人脸关键点识别区域都是集中在眼睛和鼻子以及嘴巴这些区域,所以说最终攻击的得到的一些扰动可能也是主要集中在这些区域。
这里我们为大家调研了一些具体的方法,在进行建模的过程中,以下图左边的这张图片为例,它是有背景的原始的人脸图片,在这种情况下,可以识别出这个人的人脸关键点,数量接近于20-30个。
根据这些关键点,可以得到的这个人的面部轮廓的一些三角形区域。如今,一些学术论文或学术尝试正是基于人脸的这些区域来进行更加细腻的攻击。
也就是说,在进行攻击的过程中,直接将人脸划分成很小的一些三角形区域,然后再从三角形区域来实现进一步的攻击。
以上图右图为例,我们可以找到原始图片和目标图片相似的三角形区域。
这里其实涉及到两个步骤:第一是计算得到原始图片和目标攻击图片之间的每个三角形区域做形变的角度,这在OpenCV里面可以得到。其次是找到每个三角形与在数据库里面需要攻击的三角形最近似的一些贴图,最终合成得到一个目标的图片。
这种基于人脸骨骼的三角形的分区域方法,结果并不是特别好,因为相当于是把一些不同人的面部轮廓的区域贴出来了,导致这些区域是不规整的,而且存在像素差异,有很多极具变化的边缘信息。
所以,这种方法也是可以尝试的,但在本次比赛中并不是一种很好的方法。
本次比赛,其实可以参考这种方法得到原始人脸和目标人脸的骨骼关键点的三角形区域,针对每个三角形的区域进行具体的形变,然后做一个贴图,可以加入一些对样本阐释的过程。
在之前比如CVPR的会议上,也有人脸攻击相关的比赛,建议参赛者也可以去看一下历史比赛的分享。
根据材料可以理解,TV loss相当于是正则项,来保证离散的噪音是尽可能平滑的。在建模的过程中,也可以对于噪音加入高速滤波,实现对噪音的处理,可以直接用PyTorch或openCV来进行实现,也是对图片进行了平滑的操作。
攻击的过程中,也可以加入动量项。
训练深度学习模型的时候,可以通过随机梯度下降、Adam或者其他优化方法来进行学习率的调整。很多优化器都有动量向,就是在进行一次参数更新的时候,可以将迭代过程不断地延续下去。相当于是在进行一个梯度更新的时候,加入一个具体的gradience;或者说对扰动进行更新的时候,记录一下之前的扰动,然后进行相乘,再加到当前的扰动项上面,也是非常好的做法。这些会带来分数上的差异。
比赛的时候,其实不仅仅是需要知道具体的一些攻击方法,更为重要是,在本地构建一个验证的模型。
因为比赛中的提交并不是无限制的,所以在本地构建一个验证的模型,是非常推荐的做法。
验证的模型能够模拟现场的得分,而且能够实时快速地对具体的操作进行反馈,这需要仔细了解具体的评分规则。
此外,线上的模型可能是有一定的防御性的,所以,在本地构建验证模型时,可以尝试用一些攻击方法去攻击它,然后再将这些攻击的样本进行对抗训练,提高本地模型的防御性,再用防御模型来评价最终得分。
在进行本地验证集的构建的时候,其实可以不仅仅用比赛图片,也可以用外部的数据集,比如用现在公开的一些人脸的图片来做验证集,将它单独视为一类
在本次比赛里面,数据集是按照实体分类的。瑟吉欧扣上在判断是否攻击成功时,往往并不是根据单张图片来评价。在上一讲课程中,我们的思路是让source_index的距离更接近aim_index。但是这并不是一种非常完整的做法,除此之外我们还需要模拟线上评测的规则,线上评测主要考察我们是否将2000张图片分类正确。假设将2,000张图片平均分为500个人,则每个人拥有4张人脸图片,此时aim_index的feature map 将会作为一个整体聚合为500*512。这样一来准确度将会得到提升,因为最终评价不是根据图片的维度而是实体的维度。
鉴于之前可能有的同学没有观看前面的课程,此处提供比赛官方提供的baseline。
丨baseline开源地址https://github.com/guidao20/OPPO_ADVERSARIAL_ATTACK/blob/master/crop_image.py
丨baseline发布:OPPO 安全AI挑战赛,人脸识别对抗攻击赛题详解
官方提供的baseline和我们上节课所讲的操作思路大体类似,只是在基础上增加入了crop_image.py人脸关键点检测的模型。通过人脸关键点检测模型,可以得到具体的人脸关键点区域从而生成人脸Mask。
官方baseline在生成人脸Mask这一步需要的时间比较长,在RTX_2080 Ti中程序大概需要耗时一天左右。
二、代码解析
如下图,可以看到官方baseline中这部分代码相对复杂,需要定义具体轮廓信息和像素信息。图中的函数是用来判断x,y坐标是否在region定义的区域内。
在后续的操作中需要迭代每一个具体的像素,使用inside()函数判断其是否在区域内。如果是则保留这个区域,如果不是则赋值为0。这样就产生了一个Mask,其作用是在后续生成对抗样本的时候,可以利用这个Mask进行还原。
可以看到,下图中的函数会自动传入一个Mask的信息,这个Mask是提前保存好的一张具体的图片。
在产生对抗样本的过程中,可以利用Mask做对抗的还原。将产生的噪音与Mask相乘,也就是并非关注的人脸区域最终就变为0。
各位选手可以具体查看官方提供的baseline,这部分就是baseline最核心的攻击模型,这里也是使用到了多个模型并进行迭代。
官方baseline中的使用到的tv_loss()函数,也就是刚才所讲到的loss function,是为了保留噪音,让它尽可能地平滑。
这里最终的损失函数是由三部分组成,TV_Loss,Cosine_Loss以及L2_loss,进行剃度反弹得到扰动后再进行clip操作。这个过程实际上想表达的就是,可以在模型中增加一些loss function使噪音更加平滑。
在产生得到Mask之后,攻击的过程就会变得很简单了,只需要基于人脸Mask产生对抗样本,在根据Mask还原回原图片就可以了。
在这种情况下,具体的计算过程其实也是有一定的trick的。官方baseline中加入了这样一个判断的逻辑:对抗样本如果与原始样本相似度足够小,或者与目标样本相似度足够大的情况下进行break,或者将相似度加入loss的计算中。
相当于在此时我们不仅仅是希望与原始样本的距离尽可能小,此时优化目标有两部分其中一个是source,另一个是target,此时希望样本距离source更远而距离target更近。也就是说,Cosine_loss是由adversarial example与source的相似度以及与target的相似度这样两部分组成。所以这里Cosine_loss可以更加细化。
此处可能有人疑问:target的构建是不是可以使用上一节课讲到的一个做法?
当然可以,但是其实也有一些其他的更好的做法。下面进行具体讲解。
由于线上的评价模型是黑盒的,我们在之前课程中也提到过的,如果用健康的样本或者说正常的样本让模型去做评价的话,一个正确的类别的source往往在Top 1的位置,至少大部分情况下是这样。
如果产生了一个adv_example之后,则可能source的样本会变为Top 2的位置,因为此时target的人脸的类别就变成了Top 1。
但是这样是不是就足够了呢?并不是。
这种情况下我非常建议大家去做一个考量,就是在这种情况下我们攻击的成功率类似于一个Top K的任务。希望对抗样本识别出来的最终概率尽可能在Top 20里面出现或者Top 50,也就是说只让本地模型误分类有时并不足以让线上模型也能够分类错误。
所以,可以做这样一个操作。假设有500张人脸图片,此时可以使生成的adv_example与所有500个人都不相似,相当于加入一些外部数据集。
比如从 LWF中筛选得到1000个人的人脸照片,相当于最终产生的对抗样本在1500个人里的排名尽可能靠后。
在这种情况下可以理解为让Top K尽可能高,因为我们本地在进行攻击的时候,本地模型可能过于简单而线上的模型往往会更复杂。所以我们需要尽可能让评价指标更难,也就是我们可以用Top 1 来评价对抗样本。
进行评价的时候,可以让模型A产生对抗样本,模型B来进行评价。
baseline中我们并没有尝试去做这一步,但实际上Top K的error可能不会特别高,因为一个人脸的迁移性也并不一定是100%。在进行验证级的构造时,模型A和模型B应当是不同的模型,这里可能有人疑惑,怎么找到这么多模型?
在这里分享一些经验,再找模型的时候,比如insightface的一些github。在每个git里面我们尽可能找比如Pytorch、 Tensorflow或者MXNet等不同类型的模型,其中一个仓库的模型作为模型A,另一个仓库作为模型B,尽可能使模型A和模型B不一致,这样我们得到的评价分数相对也会更可信。
所以,如果想提升比赛的分数,就可以利用上面所讲到的这些思路来进行展开。
关于对抗攻防的一些方法,下图是我整理的思维导图,涵盖了常见的任务、数据集、评价指标、攻击和防御的方法等。
由于本次比赛的模型是有一定防御性的,所以如果有额外精力以及GPU资源的话,非常建议选手对模型做一些防御性的操作,比如说网络蒸馏对抗训练、对抗识别、输入重建等一些方法,增加评价模型B的防御性。
接下来的部分会讲解上图中的一些细节。
在之前的课程中,我们讲到FGSM单步的攻击可以转变为多步,这相当于是一种迭代式的攻击。进行攻击的时候使其重复多次,那么在这节课中,我们也讲到在攻击的过程中我们还可以加入动量的操作,尽可能地使产生的噪音具备更好的性能。
加入动量的操作非常简单,只需保存上一次产生的扰动,然后将其加入到当前扰动的过程中,直接相乘即可,同时我们需要设置一个具体的权重。
此外,还有一个知识点叫做Diverse Input Method,这也是在本次比赛过程中用到非常多次的一个操作。
在攻击的过程中,可以使样本加入一些多样性,那么这个多样性是怎么来的呢?
可以使样本随机resize到一些不同的尺寸,然后再对其进行随机padding。
举例来说,如果样本resize之后尺寸变小,我们就会对它周边的像素padding到112尺寸,之后再作为输入重复之前的产生对抗样本的操作。这样输入的图片就会更加多样化。这种情况下其实需要注意,由于进行了resize和padding的操作,那么,在还原的过程中也需要将产生的扰动减去padding并resize回去。
有了多样性的输入之后,就可以得到多个扰动,根据每个对抗样本带来的损失作为权重,将多个扰动进行加权求和,即得到最终所产生的噪音。当然,在得到我们的扰动之后,我们在进行迭代过的程中可以加入一个高斯核。此前的课程中我们也有讲到,加入高斯核之后相当于会使扰动的低频信息被过滤掉。
以上这些便是本节课分享的的一些提分trick,如果把这些技巧都进行尝试,选手肯定会得到更好的分数。在迭代的过程中,我们的代码需要尽可能地高效,最好可以提前将人脸关键点计算出来,并且生成人脸Mask,尽可能快速迭代计算对抗的过程。