初学者的神经网络技巧

图片


作为一年一度的ZeroNights HackQuest 2018竞赛的一部分,参与者被邀请尝试参加一些不重要的任务和竞赛。 其中一部分与神经网络的对抗示例的生成有关。 在我们的文章中,我们已经关注了攻击方法和机器学习算法的防御 。 在本出版物的框架中,我们将分析一个示例,说明如何使用foolbox库通过ZeroNights Hackquest解决任务。


在此任务中,攻击者应该能够访问服务器。 成功之后,他在主目录中看到以下文件结构:


| Home --| KerasModel.h5 --| Task.txt --| ZeroSource.bmp 

Task.txt文件中包含以下信息:


 Now it is time for a final boss! http://51.15.100.188:36491/predict You have a mode and an image. To get a ticket, you need to change an image so that it is identified as "1". curl -X POST -F image=@ZeroSource.bmp 'http://51.15.100.188:36491/predict'. (don't forget about normalization (/255) ^_^) 

为了获得令人垂涎的票证,要求攻击者转换ZeroSource.bmp:


图片


这样,在将神经网络发送到服务器后,该图像将被解释为1。如果尝试不处理就发送该图像,则神经网络的结果将为0。


而且,当然,此任务的主要提示是模型文件KerasModel.h5(此文件可帮助攻击者将攻击转移到WhiteBox平面,因为他可以访问神经网络及其相关的所有数据)。 文件名立即包含提示-在其上实现神经网络的框架的名称。


正是这些介绍性注释使参与者着手解决任务:


  • 用Keras编写的神经网络模型。
  • 使用curl将图像发送到服务器的能力。
  • 需要更改的原始图像。

在服务器端,检查尽可能简单:


  1. 图片尺寸应正确-28x28像素。
  2. 在此图中,模型应返回1。
  3. ZeroSource.bmp的初始映像与发送到服务器的初始映像之间的差应小于MSE指标的阈值k(标准错误)。

因此,让我们开始吧。


首先,参与者需要找到有关如何欺骗神经网络的信息。 在Google上待了一会儿之后,他得到了关键字“广告专家榜样”和“广告专家攻击”。 接下来,他需要寻找进行对抗攻击的工具。 如果您进入Google查询“对Keras神经网络的Adersarial攻击”,则第一个链接将指向FoolBox项目的GitHub-一个用于生成对抗示例的python库。 当然,还有其他库(我们在以前的文章中讨论了其中的一些库)。 而且,正如他们所说,攻击可以从头开始。 但是我们仍然专注于最受欢迎的库,以前没有遇到过对抗性攻击的人可以在Google的第一个链接上找到它。


现在,您需要编写一个Python脚本,该脚本将生成一个对抗示例。
当然,我们将从进口开始。


 import keras import numpy as np from PIL import Image import foolbox 

我们在这里看到什么?


  1. Keras是编写神经网络的框架,我们会欺骗它。
  2. NumPy是一个库,它将使我们能够有效地处理向量。
  3. PIL是用于处理图像的工具。
  4. FoolBox是用于生成对抗性示例的库。

当然,首先要做的是将神经网络模型加载到程序的内存中,并查看模型信息。


 model = keras.models.load_model("KerasModel.h5") #   model.summary() #     model.input #    ,        

在输出中,我们得到以下内容:


 Layer (type) Output Shape Param # ================================================================= conv2d_1 (Conv2D) (None, 26, 26, 32) 320 _________________________________________________________________ conv2d_2 (Conv2D) (None, 26, 26, 64) 18496 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 13, 13, 64) 0 _________________________________________________________________ dropout_1 (Dropout) (None, 13, 13, 64) 0 _________________________________________________________________ conv2d_3 (Conv2D) (None, 13, 13, 64) 36928 _________________________________________________________________ conv2d_4 (Conv2D) (None, 13, 13, 128) 73856 _________________________________________________________________ max_pooling2d_2 (MaxPooling2 (None, 6, 6, 128) 0 _________________________________________________________________ flatten_1 (Flatten) (None, 4608) 0 _________________________________________________________________ dense_1 (Dense) (None, 256) 1179904 _________________________________________________________________ dense_2 (Dense) (None, 10) 2570 ================================================================= Total params: 1,312,074 Trainable params: 1,312,074 Non-trainable params: 0 _________________________________________________________________ <tf.Tensor 'conv2d_1_input_1:0' shape=(?, 28, 28, 1) dtype=float32> 

我可以从这里得到什么信息?


  1. 输入模型(conv2d_1层)接受尺寸为X28x28x1的对象,其中“?” -物品数量; 如果图像为1,则尺寸为1x28x28x1。 并且图像是一个三维数组,其中一维是1。即,该图像用作从0到255的值的表。
  2. 在模型的输出(密集_2层)上,获得尺寸为10的向量。

我们加载图像,不要忘记将其转换为浮点型(进一步,神经网络将使用实数工作)并将其标准化(将所有值除以255)。 这里需要说明的是,规范化是使用神经网络时的“强制性”技巧之一,但是攻击者可能不知道这一点,因此我们在任务描述中特别添加了一条小提示):


 img = Image.open("ZeroSource.bmp") #   img = np.array(img) #     numpy.array img = img.astype('float32') #      float img /= 255 #  

现在我们可以将图像发送到加载的模型,并查看产生的结果:


 model.predict(img.reshape(1,28,28,1)) #   predict            

在输出中,我们获得以下信息


 array([[1.0000000e+00, 4.2309660e-19, 3.1170484e-15, 6.2545637e-18, 1.4199094e-16, 6.3990816e-13, 6.9493417e-10, 2.8936278e-12, 8.9440377e-14, 1.6340098e-12]], dtype=float32) 

值得解释此向量是什么:实际上,这是一个概率分布,即每个数字代表0,1,2 ...,9类的概率。 向量中所有数字的总和为1。在这种情况下,可以看出该模型确信输入图像以100%的概率表示类别0。


如果在直方图上进行描述,则会得到以下结果:


图片


绝对的信心。


如果模型无法确定类别,则概率矢量将趋向于均匀分布,这又意味着模型将以相同的概率同时将对象分配给所有类别。 直方图如下所示:


图片


通常认为,模型的类别是由给定向量中最大数目的索引确定的。 也就是说,模型可以从理论上选择概率大于10%的类别。 但是,此逻辑可能会有所不同,具体取决于开发人员描述的决策逻辑。


现在,让我们继续进行最有趣的部分-对抗攻击。


首先,要使用FoolBox库中的模型,必须将模型转换为Foolbox表示法。 您可以这样操作:


 fmodel = foolbox.models.KerasModel(model,bounds=(0,1)) #  bounds ,       ,        255,        0-1. 

之后,您可以测试不同的攻击。 让我们从最受欢迎的FGSM开始:


Fgsm

 attack = foolbox.attacks.FGSM(fmodel) #    FGSM     adversarial = attack(img.reshape(28,28,1),0) #  ,  adversarial  probs = model.predict(adversarial.reshape(1,28,28,1)) #     print(probs) #    print(np.argmax(probs)) #      

神经网络的输出如下


 [4.8592144e-01 2.5432981e-14 5.7048566e-13 1.6787202e-14 1.6875961e-11 1.2974949e-07 5.1407838e-01 3.9819957e-12 1.9827724e-09 5.7383300e-12] 6 

以及得到的图像:


图片


因此,现在以50%以上的概率将0识别为6。已经很好。 但是,我们仍然希望得到1,并且噪声水平不是很出色。 该图像看起来确实难以置信。 稍后再详细介绍。 同时,让我们尝试阻止攻击。 突然,我们仍然得到1。


L-BFGS攻击

 attack = foolbox.attacks.LBFGSAttack(fmodel) adversarial = attack(img.reshape(28,28,1),0) probs = model.predict(adversarial.reshape(1,28,28,1)) print(probs) print(np.argmax(probs)) 

结论:


 [4.7782943e-01, 1.9682934e-10, 1.0285517e-06, 3.2558936e-10, 6.5797998e-05, 4.0495447e-06, 2.5545436e-04, 3.4730587e-02, 5.5223148e-07, 4.8711312e-01] 9 

图片:


图片


再来一次。 现在我们将0识别为9,概率约为49%。 但是,噪音要小得多。


最后以随机拍子结束。 选择一个示例的方式使得很难随机获得结果。 现在我们还没有指出要获得1的任何位置。因此,我们进行了非针对性的攻击,并认为我们仍然会获得1级,但这并未发生。 因此,值得继续进行针对性攻击。 让我们使用Foolbox文档并在其中找到条件模块


在此模块中,您可以选择攻击的条件(如果支持的话)。 具体来说,我们对两个条件感兴趣:


  1. TargetClass-使得在概率分布向量中,编号为k的元素具有最大概率。
  2. TargetClassProbability-使得在概率分布的向量中,编号为k的元素的概率至少为p。

让我们同时尝试:


L-BFGS +目标类别

TargetClass标准中的主要内容是获得类别k的概率,该概率高于任何其他类别的概率。 然后,仅通过查看最大概率来做出决策的网络将被误认为。


 attack = foolbox.attacks.LBFGSAttack(fmodel,foolbox.criteria.TargetClass(1))#    ,     ,    TargetClass,  ,      ,      adversarial = attack(img.reshape(28,28,1),0) probs = model.predict(adversarial.reshape(1,28,28,1)) print(probs) print(np.argmax(probs)) 

结论:


 [3.2620126e-01 3.2813528e-01 8.5446298e-02 8.1292394e-04 1.1273423e-03 2.4886258e-02 3.3904776e-02 1.9947644e-01 8.2347924e-07 8.5878673e-06] 1 

图片:


图片


从结论中可以看出,现在我们的神经网络声称它为1,概率为32.8%,而0的概率尽可能接近此值,为32.6%。 我们做到了! 原则上,这已经足以完成任务。 但是,我们将走得更远,尝试使概率1大于0.5。


L-BFGS + TargetClassProbability

现在,我们使用标准TargetClassProbability,它使您能够获得对象中一个类的概率不低于p。 它只有两个参数:
1)对象的类号。
2)在对抗性例子中,该类别的概率。
同时,如果不可能达到这样的概率,或者找到这样一个物体的时间花费太多时间,那么对抗性物体将等于无。 您可以自己尝试通过使概率为0.99来进行验证。 然后,该方法可能不会收敛。


 attack = foolbox.attacks.LBFGSAttack(fmodel,foolbox.criteria.TargetClassProbability(1,0.5)) adversarial = attack(img.reshape(28,28,1),0) probs = model.predict(adversarial.reshape(1,28,28,1)) print(probs) print(np.argmax(probs)) 

结论:


 [4.2620126e-01 5.0013528e-01 9.5413298e-02 8.1292394e-04 1.1273423e-03 2.4886258e-02 3.3904776e-02 1.9947644e-01 8.2347924e-07 8.5878673e-06] 

万岁! 我们设法得到一个对抗示例,其中神经网络的概率1高于50%! 太好了! 现在,让我们进行非规范化(将图像恢复为0-255格式)并保存。


最终脚本如下:


 import keras from PIL import Image import numpy as np import foolbox from foolbox.criteria import TargetClassProbability import scipy.misc model = keras.models.load_model("KerasModel.h5") img = Image.open("ZeroSource.bmp") img = np.array(img.getdata()) img = img.astype('float32') img = img /255. img = img.reshape(28,28,1) fmodel = foolbox.models.KerasModel(model,bounds=(0,1)) attack = foolbox.attacks.LBFGSAttack(fmodel,criterion=TargetClassProbability(1 ,p=.5)) adversarial = attack(img[:,:,::-1], 0) adversarial = adversarial * 255 adversarial = adversarial.astype('int') scipy.misc.toimage(adversarial.reshape(28,28)).save('AdversarialExampleZero.bmp') 

最终的图像如下:


图片


结论

因此,从上述示例中我们可以看出,欺骗神经网络非常简单。 还有许多方法可以做到这一点。 只需在foolbox中打开可用攻击列表,然后尝试应用它们即可。 我们建议您尝试以相同的神经网络和相同的图像为基础,自己曲柄,以供参考 。 您可以在评论中留下您的问题。 我们将回答他们!


永远记住,无论算法和模型多么有用,它们对于极小的变动都是极不稳定的,这会导致严重的错误。 因此,我们建议您测试模型,在其中可以使用python和诸如foolbox之类的工具提供帮助。


感谢您的关注!

Source: https://habr.com/ru/post/zh-CN443164/


All Articles