如何一招“骗过”五种神经网络?

2020-06-05 09:56:19 浏览数 (1)

【飞桨开发者说】张鑫,西安电子科技大学研二在读,飞桨AI安全对抗赛决赛第二名,软件设计师

图像识别作为人工智能最成熟的应用领域,已经大规模落地并服务于人们的日常生活。但在大规模商业化的同时,也面临更多方面的威胁。对抗样本通过对图像做微小的改动,在用户无感知的情况下,会导致AI系统被入侵、错误命令被执行。欺骗AI系统做出错误的决断,将会给社会造成重大的损失。通过研究如何欺骗AI系统,对现有的薄弱点进行修补,使得AI系统更健壮。

本文提供了一个强劲的攻击方案,该方案可以对ResNeXt50白盒模型、人工加固的灰盒模型、AutoDL模型均有显著的效果,并在飞桨AI安全对抗赛中取得较好成绩。在该比赛中,选手需要尽量小的扰动下骗过更多的模型,才能拿到更好的得分,一起来看看他是如何实现的吧。

01

方案部分结果展示

02

解题思路

相比初赛,决赛的难点在于多了一个人工加固的模型(灰盒),黑盒模型增加为三个,包括由AutoDL技术训练的模型,因此需要针对人工加固模型训练自己加固的模型。针对黑盒模型,我主要集成多样化的模型来逼近;针对AutoDL技术训练的模型,我主要集成AutoDL搜索出的网络结构来迁移攻击。

此外相比初赛,我在方案中添加了多样的越过局部最优的策略和限定变动像素点的限制,同时对生成的图片进行了小扰动截断,保证在提升迁移性能的同时降低MSE

03

攻击方法

攻击所用算法主体为MomentumIteratorAttack,代码实现参照了advbox。在此基础之上,尝试了不同的目标函数,集成了不同的模型,添加了多样的越过局部最优的策略。

3.1目标函数

损失函数1:

其中,m为模型个数,

为标签y的独热编码,

为第i个模型的参数。此损失函数引用于[1]

代码如下:

代码语言:javascript复制
loss_logp = -1*fluid.layers.log(1-fluid.layers.matmul(out1,one_hot_label[0],transpose_y=True))
            -1*fluid.layers.log(1-fluid.layers.matmul(out2,one_hot_label[0],transpose_y=True))
            -1*fluid.layers.log(1-fluid.layers.matmul(out3,one_hot_label2[0],transpose_y=True))
            -1*fluid.layers.log(1-fluid.layers.matmul(out4,one_hot_label2[0],transpose_y=True))
            -1*fluid.layers.log(1-fluid.layers.matmul(out5,one_hot_label2[0],transpose_y=True))
            -1*fluid.layers.log(1-fluid.layers.matmul(out6,one_hot_label2[0],transpose_y=True))
            -1*fluid.layers.log(1-fluid.layers.matmul(out8,one_hot_label2[0],transpose_y=True))
            -1*fluid.layers.log(1-fluid.layers.matmul(out9,one_hot_label2[0],transpose_y=True))

损失函数2:

其中,N为类别数量,M为模型数量,

为当输入为X时,模型j判定类别i的概率。

代码如下:

代码语言:javascript复制
out_total1 = fluid.layers.softmax(out_logits1[0] out_logits2[0])
out_total2 = fluid.layers.softmax(out_logits3[0] out_logits4[0] out_logits5[0] out_logits6[0] out_logits7[0] out_logits8[0] out_logits9[0])
loss2 = fluid.layers.matmul(ze, fluid.layers.cross_entropy(input=out_total1, label=label[0]))
      fluid.layers.matmul(ze, fluid.layers.cross_entropy(input=out_total2, label=label2[0]))

损失函数3:

符号含义同损失函数2,不再赘述。

代码如下:

代码语言:javascript复制
ze = fluid.layers.fill_constant(shape=[1], value=-1, dtype='float32')
loss = 1.2*fluid.layers.matmul(ze, fluid.layers.cross_entropy(input=out1, label=label[0]))
      0.2*fluid.layers.matmul(ze, fluid.layers.cross_entropy(input=out2, label=label[0]))
      fluid.layers.matmul(ze, fluid.layers.cross_entropy(input=out3, label=label2[0]))
      fluid.layers.matmul(ze, fluid.layers.cross_entropy(input=out4, label=label2[0]))
      fluid.layers.matmul(ze, fluid.layers.cross_entropy(input=out5, label=label2[0]))
      fluid.layers.matmul(ze, fluid.layers.cross_entropy(input=out6, label=label2[0]))
      fluid.layers.matmul(ze, fluid.layers.cross_entropy(input=out7, label=label2[0]))
      fluid.layers.matmul(ze, fluid.layers.cross_entropy(input=out8, label=label2[0]))
      fluid.layers.matmul(ze, fluid.layers.cross_entropy(input=out9, label=label2[0]))

经过实测,发现三者效果接近,损失函数3效果最好。损失函数1的特点是程序运行快,猜测可能是求梯度简单。使用损失函数2时,攻击成功后的分类会接近一致。

3.2模型集成

集成模型选取思路为多元,尽可能多的不同模型,才可能逼近赛题背后的黑盒模型,所用模型总体描述如图3-1所示。

图3-1 模型集成细节

  • 橙色框中mnasnet1_0,nas_mobile_net为特意选取逼近黑盒AutoDL技术训练的黑盒模型,人工加固ResNeXt50_32x4d模型训练细节接下来会详细阐述。
  • 橙色和蓝色框中模型均采用pytorch进行迁移训练,训练集测试集为原始Stanford Dogs数据集划分,迭代次数均为25,学习率均为0.001。
  • 虚线框中为效果最好的9个模型的组合。

3.3灰盒模型攻击思路

决赛中的灰盒模型为人工加固的模型,结构为ResNeXt50。为了攻击黑盒模型,我在本地训练了一个加固模型,作为灰盒模型的逼近。训练加固模型涉及到训练集的选取和训练方法的选取,训练集的构成主要包含如下两部分:

  • 第一部分采用不同的方法攻击初赛的白盒模型(此白盒模型与灰盒模型具有相同的网络结构),将生成的n个样本集作为训练集的一部分,设计思路如图3-2所示(T代表对抗样本能被真实灰盒模型正确分类(猜测);F代表对抗样本不能被真实灰盒模型正确分类(猜测))。由于不同的攻击方法生成的对抗样本在真实的灰盒模型表现上会有差异,有些图片依然能被灰盒模型正确识别。将n个样本集集合,就可以构建出完全将灰盒攻击成功的图片集。

图3-2 对抗训练部分样本集构建思路

  • 第二部分是从Stanford Dogs数据集中随机选取的8000张图片和原始的121张图片,这些图片的选取是为了保持模型的泛化能力。

对抗模型的训练方法与一般模型训练无异。为了探索更优的对抗模型,我们进行了几组不同训练集的对比(其他参数固定),如下表所示。

其中,86.45等分数是决赛评测分数,经过实验对比第一组实验设置效果最好。

3.4 越过山丘

下面主要介绍为了解决陷入局部最优采取的一系列策略。

3.4.1 图片粒度梯度反向

此方法受启发于[2],论文中采取双路寻优,一路采用常规方法梯度上升,如图3-3中绿线所示。另一路先采取梯度下降到达这一分类局部最优再进行梯度上升,以期找到更快的上升路径,如图3-3中蓝线所示。我在实现过程中对其进行简化,仅在迭代的第一步进行梯度下降。

图3-3 梯度策略示意图(图片来自论文[2])

核心代码如下:

代码语言:javascript复制
if i==0:#第一次迭代梯度反向
        adv=adv epsilon*norm_m_m 
else:
        adv=adv-epsilon*norm_m_m 

3.4.2 像素粒度梯度反向

此方法承接于3.4.1,可将3.4.1视为整个图片粒度的梯度反向。同样是为了越过局部最优,采用3.4.2方法,随机选取梯度中5%进行取反,可视为像素粒度的梯度反向,反转比例为一超参数。同样为了在迭代后期趋于稳定,反向的梯度的比例会随着迭代次数增加而减少。比例为一需要调节的超参数。

核心代码如下:

代码语言:javascript复制
if i<50: #前50步,2%的梯度反响,随着i递减   试试5%
       dir_mask = np.random.rand(3,224,224)
        dir_mask = dir_mask>(0.15-i/900)  
        dir_mask[dir_mask==0] = -1
        norm_m_m = np.multiply(norm_m_m,dir_mask)

3.4.3 每步迭代前,对原始图片添加高斯噪声

此方法受启发于[6],论文作者认为攻击模型的梯度具有噪声,损害了迁移能力。论文作者用一组原始图片加噪声后的梯度的平均代替原来的梯度,效果得到提升,形式化表述如下:

而我与论文作者理解不同,添加噪声意在增加梯度的噪声,以越过局部最优,再者多次计算梯度非常耗时,因此我选用了只加一次噪声,均值为0,方差为超参数,形式化表述如下:

同时,为在迭代后期趋于稳定,在添加噪声时,噪声的方差会随着迭代次数的增加而减小。

核心代码如下:

代码语言:javascript复制
if i<50:
        adv_noise = (adv np.random.normal(loc=0.0, scale=0.8 epsilon/90,size = (3,224,224))).astype('float32')
else:
        adv_noise = (adv np.random.normal(loc=0.0, scale=0.2,size = (3,224,224))).astype('float32')

3.4.4 将图片变动的像素点限定在梯度最大的5%

该方法受启发于[3], 随机在图像中选择max_pixels个点。而我采用的方法是计算所有像素的梯度,选取梯度最大的5%进行变动,此举可以有效降低MSE。

3.4.5 三通道梯度平均

该方法受启发于[3],文中,作者随机在图像中选择max_pixels个点 在多个信道中同时进行修改。因此我尝试了两种方法,一是只计算R或G或B通道的梯度,三个通道减去相同的梯度。二是将三通道的梯度进行平均。从结果来看,后者更有效,提分明显。

3.4.6 攻击后进行目标攻击

此方法受启发于[2],作者在成功越过分界线后进行消除无用噪声操作,作者认为此举可以加强对抗样本的迁移能力。

我的做法与此不同,我认为不仅要越过边界,还要走向这个错误分类的低谷。此举依据的假设是:尽管不同模型的分界线存在差异,模型学到的特征应是相似的。思路如图3-4中红色箭头所示,带有圆圈的数字表示迭代步数。

因此在成功攻击之后,我又添加了两步定向攻击,目标为攻击成功时被错分的类别。在集成攻击时,目标为被错分类别的众数。

图3-4 目标攻击示意图(修改自论文[2])

核心代码如下:

代码语言:javascript复制
for i in range(2):#迭代两次进行目标攻击
        adv_noise = (adv np.random.normal(loc=0.0, scale=0.1,size = (3,224,224))).astype('float32')
        target_label=np.array([t_label]).astype('int64')
        target_label=np.expand_dims(target_label, axis=0)
        target_label2=np.array([origdict[t_label]]).astype('int64')
        target_label2=np.expand_dims(target_label2, axis=0)
        g,resul1,resul2,resul3,resul4,resul5,resul6,resul7,resul8,resul9 = exe.run(adv_program,
                         fetch_list=[gradients,out1,out2,out3,out4,out5,out6,out7,out8,out9],
                         feed={'label2':target_label2,'adv_image':adv_noise,'label': target_label }
                          )
        g = (g[0][0] g[0][1] g[0][2])/3 #三通道梯度平均
        velocity = g / (np.linalg.norm(g.flatten(),ord=1)   1e-10)
        momentum = decay_factor * momentum   velocity
        norm_m = momentum / (np.linalg.norm(momentum.flatten(),ord=2)   1e-10)
        _max = np.max(abs(norm_m))
        tmp = np.percentile(abs(norm_m), [25, 99.45, 99.5])#将图片变动的像素点限定在0.5%
        thres = tmp[2]
        mask = abs(norm_m)>thres
        norm_m_m = np.multiply(norm_m,mask)
        adv=adv epsilon*norm_m_m
        #实施linf约束
        adv=linf_img_tenosr(img,adv,epsilon)

3.5 临门一脚

然而这不够,作为一个竞赛,人人都虎视眈眈盯着奖金的时候还需要不断的提升,因此还需要临门一脚,一种后处理方法。

小扰动截断:

使用上述方法后,我的结果在95-96分之间波动,为进一步提升成绩,我选用最高分96.53分图片进行后处理。后处理方法为:将攻击后的图片与原图片进行对比,对一定阈值以下的扰动进行截断。经过不断上探阈值,发现阈值为17(图片的像素范围为0-255)的时候效果最好。此方法提分0.3左右。

核心代码如下:

代码语言:javascript复制
org_img = tensor2img(img)
adv_img = tensor2img(adv)
 #17/256 以下的扰动全部截断
diff = abs(org_img-adv_img)<drop_thres   #<17的为1
diff_max = abs(org_img-adv_img)>=drop_thres  #>=17的为1
#<17的保留org_img
 tmp1 = np.multiply(org_img,diff)
#>17的保留adv_img
tmp2 = np.multiply(adv_img,diff_max)
    final_img = tmp1 tmp2

04

尝试过的方法

本小节介绍在比赛过程中尝试过但没效果过或者效果不明显的方法。

4.1 中间层攻击

依据论文[4]中所述,我用飞桨复现了文中方法,采用模型为ResNeXt50_32x4d,参数为初赛提供白盒模型参数,处理的中间层为res5a,学习率0.01,迭代次数为5次,损失函数为ILA。

图4-1 没有经过后处理生成的对抗样本与原样本对比图

图4-2 经过后处理的生成的对抗样本与原样本对比图

图4-2中Pos_Adversarial Image为图4-1中Adversarial Image图片使用中间层攻击生成的图片,使用该后处理方法后确如论文中所说图片更平滑了,但是迁移性能却下降了,因此弃用。

4.2 标签平滑

效果下降。

4.3 推后原始类别排名

以6分类问题为例说明推后原始类别排名想法,如图4-3所示。

图4-3 推后类别排名思路示意图

假设攻击前某张图片概率分布如图4-3第一行所示,属于第一类的概率最高。一旦攻击成功就停止的情况下,如图4-3第二行所示,属于第一类的概率下降为0.2,模型将原图误分类为第二类。但是第一类的类别排序仍然靠前,在其他不同分界线模型下,很可能仍然能够正确分类(猜测)。因此将终止条件改进,不但要能够攻击成功,还要原始类别的排序靠后,如图4-3第三行,原始第一类的类别概率降至0.1,排序降至第四,也许会提升迁移性能。

在实验中,这种做法效果不是很明显。与此想法类似,我还尝试了定向攻击为原始分类概率最低的类别,同样效果不明显。

05

写在赛后

1. 以上就是本人在AI安全对抗赛取得第二名的全部方案,完成运行代码可以访问AI Studio项目:https://aistudio.baidu.com/aistudio/projectdetail/296291

2. 决赛赛程中我霸榜半个月有余,绞尽脑汁尝试各种攻击方法,不断阅读论文,不断尝试效果,并且照猫画虎快速入门了飞桨,收获很多。

3. 致读者,这是一个非常好的入门对抗样本的机会,细嚼baseline和我的方案将让你入门多种这个领域的算法。

4. 感谢AI Studio让我用到了v100,基本不用担心显存不够用,我自己的台式机装的gtx 1050连n年前的vgg都跑不动。

5. 一点脑洞:受启发于[5],为人脸添加一个眼睛可以达到错分目的,类似的给狗添加一幅眼镜。

图5.1 人脸添加眼镜示意图(截取自[5])

图5.2 为狗添加眼镜示意图(左图为原始图片,右图为添加眼镜后的图片)

参考文献

[1] Liu Y , Chen X , Liu C , et al. Delving into Transferable Adversarial Examples and Black-box Attacks[J]. 2016.

[2] Shi Y , Wang S , Han Y . Curls & Whey: Boosting Black-Box Adversarial Attacks[J]. 2019.

[3] Narodytska N , Kasiviswanathan S P . Simple Black-Box Adversarial Perturbations for Deep Networks[J]. 2016.

[4] Huang Q , Katsman I , He H , et al. Enhancing Adversarial Example Transferability with an Intermediate Level Attack[J]. 2019.

[5] https://www.cs.cmu.edu/~sbhagava/papers/face-rec-ccs16.pdf

[6] Understanding and Enhancing the Transferability of Adversarial Examples

如在使用过程中有问题,可加入飞桨官方QQ群进行交流:703252161。

如果您想详细了解更多飞桨的相关内容,请参阅以下文档。

官网地址:

https://www.paddlepaddle.org.cn

飞桨开源框架项目地址:

GitHub:

https://github.com/PaddlePaddle/Paddle

Gitee:

https://gitee.com/paddlepaddle/Paddle

END

0 人点赞