"); //-->
[ 摘要 ]在这篇教程中,你将会学到如何用基于图像的对抗攻击来破坏深度学习模型。我们将会利用Keras和TensorFlow深度学习库来实现自己的对抗攻击。
试想下从现在起的二十年后。现在路上所有的车辆都是利用人工智能、深度学习和计算机视觉来驱动的无人驾驶交通工具。每一次转弯、车道转换、加速和刹车都是由深度神经网络来提供技术支持。现在,想象自己在高速路上,你正坐在“驾驶室(当车自主行驶时,驾驶室还能被称为‘驾驶室’吗?)”里,你的妻子坐在副驾上,你的孩子们坐在后座。
朝前看,你看到一张巨大的贴纸正在车辆所行驶的车道上,这看上去对车的前进毫无影响。看上去这像是一张受欢迎的大幅涂鸦艺术版画,可能是几个高中生开玩笑把它放在这里,或者他们只是为了完成某个大冒险游戏。
图一:执行一次对抗攻击需要一张输入图像(左),故意用一个噪声向量来扰乱它(中),迫使神经网络对输入图像进行错误分类,最后得到一个错误的分类结果,很可能会出现一次严重的后果(右)。
一瞬间,你的汽车突然急刹车,然后立即变道,因为放在路上的那张贴画可能印的是行人、动物或者另一辆汽车。你在车里被猛推了一下,感到颈部似乎受了伤。你的妻子尖叫着,孩子们的零食在后座上蹦了起来,弹在挡风玻璃上后洒在中控台上。你和你的家人都很安全,但所经历的这一切看上去不能再糟糕了。
发生了什么?为什么你的自动驾驶车辆会做出这样的反应?是不是车内的某段代码或者某个软件存在奇怪的“bug”?
答案就是,为车辆视觉组件提供技术支持的深度神经网络看到了一张对抗图像。
对抗图像是指:含有蓄意或故意干扰像素来混淆或欺骗模型的图像。
但与此同时,这张图像对于人类来说看上去是无害且无恶意的。
这些图像导致深度神经网络故意做出错误的预测。对抗图像在某种方式上去干扰模型,导致它们无法做出正确的分类。
事实上,人类通过视觉可能无法区分正常图片和对抗攻击的图片——本质上,这两张图片对于肉眼来说是相同的。
这或许不是一个准确(或者说正确)的类比,但我喜欢在图像密码学的背景下来解释对抗进攻。利用密码学算法,我们可以把数据(例如纯文本信息)嵌入到图像中,且不改变图像本身的表象。这幅图像可以纯粹地传送到接收者那里,然后接收者再去提取图像中所隐藏的信息。
同样的,对抗攻击在输入图像中嵌入一条信息,但是这条信息并非是人们可以理解的纯文本信息,因为对抗攻击所嵌入到输入图像的是一个噪声向量。这个噪声向量是用于戏弄或混淆深度学习模型而故意构建的。
对抗攻击是如何起作用的?我们应该如何去进行防御呢?
在这篇教程中,以及这个系列的其他推文中,我们将会准确地去回答这些问题。
想要了解如何用Keras/TensorFlow在对抗攻击和图像中来破坏深度学习模型,请继续往下读。
利用Keras和TensorFlow来实现对抗图像和攻击
在这篇教程中的第一部分,会讨论下对抗攻击是什么以及它们是如何影响深度学习模型的。
在那之后,我们会实现三段独立的Python脚本:
1、第一段Python脚本是加载ImageNet数据集并解析类别标签的使用助手。
2、第二段Python脚本是利用在ImageNet数据集上预训练好的ResNet模型来实现基本的图像分类(由此来演示“标准”的图像分类)。
3、最后一段Python脚本用于执行一次对抗攻击,并且组成一张故意混淆我们的ResNet模型的对抗图像,而这两张图像对于肉眼来说看上去是一样的。
让我们开始吧!
什么是对抗对象和对抗攻击呢?它们是如何影响深度学习模型的呢?
图二:当执行对抗攻击时,给予神经网络一张图像(左),然后利用梯度下降来构建一个噪声向量(中)。这个噪声向量被加入到输入图像中,生成一个误分类(右)。
在2014年,古德费洛等人发表了一篇名为《Explaining and HarnessingAdversarial Examples(解释并驯服对抗样本)》的论文,展示了深度神经网络吸引人的属性——有可能故意扰乱一张输入图像,导致神经网络对其进行错误的分类。这种扰乱就被称为对抗攻击。
经典的对抗攻击示例就像上面图二所展示的一样。在左边,我们的输入图像在神经网络中被分在“熊猫”类别,置信度为57.7%。
在中间有一个噪音向量,在人类眼中看上去是随机的,但事实上绝非如此。
恰恰相反,这一噪声向量中的像素“等于输入图像的损失函数的梯度元素符号”(Goodfellow et al.)。
把这个噪声向量嵌入到输入图像中,产出了图二中的输出(右)。对于我们来说,这副新图像看起来与输入图像完全一样,但我们的神经网络却会将这站图像分到“长臂猿”类别,置信度高达99.7%。很奇怪吧?
一段对抗攻击和对抗图像的简短历史
图三:一个关于对抗机器学习和深度神经网络安全性出版物的时间轴(图片来源:CanMachine Learning Be Secure?图8)
对抗机器学习并不是一个新兴领域,这些攻击也不是针对深度神经网络。在2006年,巴雷诺等人发表了一篇名为《CanMachine Learning Be Secure?》的论文。论文讨论了对抗攻击,包括提出了一些对抗攻击的防御方式。
回到2006年,最先进的机器学习模型包括支持向量机(SVMs)和随机森林(RFs),这两种类型的模型均易受对抗攻击的影响。
随着2012年深度神经网络的普及度开始升高,曾寄希望于这些非线性的模型不会轻易受到这种攻击的影响,然而古德费洛等人打破了这一幻想。
他们发现深度神经网络和“前辈们”一样,都很容易受到对抗攻击的影响。
想要了解更多关于对抗攻击的历史,我建议你们去看下比格奥(Biggio)和罗利(Roli)在2017年发表的论文《Wild Patterns: Ten Years Afterthe Rise of Adversarial Machine Learning.》
为什么对抗攻击和对抗图像会成为一个麻烦呢?
图四:为什么对抗攻击会是一种麻烦?为什么我们应该关注它?(图片来源imagesource)
本篇教程顶部所谈到的示例中概述了为什么对抗攻击会给我们的健康、生活和财产带来严重损失。
另外一些后果没有那么严重的示例,例如一群黑客识别了一个谷歌用于给Gmail过滤垃圾邮件的模型,或者识别了一个Facebook自动检测****内容的NSFW(不适合上班时间浏览)过滤器模型。
如果这些黑客想要绕过Gmail垃圾邮件过滤器来让用户受到资源过载攻击,或者绕过Facebook的NSFW过滤器来上传大量的****内容,理论上讲他们是可以做到的。
这些都是对抗攻击中造成轻微后果的示例。
关于到对抗攻击带有严重后果的剧本可以包括黑客恐怖分子识别了全世界自动驾驶汽车所使用的深度神经网络(试想一下如果特斯拉垄断了市场,成为世界唯一自动驾驶汽车生产商)。
对抗图片有可能被有战略性地摆放在车道或公路上,造成连环车祸、财产损失或车内乘客的受伤甚至死亡。
只有你的想象力,对已知模型的了解程度以及对模型的使用程度是对抗攻击的天花板。
我们能够抵御对抗攻击吗?
好消息是我们可以降低对抗攻击所造成的影响(但无法将它们完全消除)。
这方面的话题将不会在今天的教程中展开,可能会在未来的教程中进行讨论。
配置你的开发环境
在这篇教程中配置你的系统,我建议你跟随这两篇教程:
如何在Ubuntu系统下配置TensorFlow2.0 ?(How to installTensorFlow 2.0 on Ubuntu)
如何在macOS系统下配置TensorFlow2.0 ?How to install TensorFlow 2.0 on macOS
这两篇教程都会协助你在便捷的Python虚拟环境中利用必要的软件来配置系统。
项目结构:
$ tree --dirsfirst
.
├── pyimagesearch
│ ├── __init__.py
│ ├── imagenet_class_index.json
│ └── utils.py
├── adversarial.png
├── generate_basic_adversary.py
├── pig.jpg
└── predict_normal.py
1 directory, 7 files
在pyimagesearch模块中,有两个文件:
1、imagenet_class_index.json: 一个JSON文件,将ImageNet类别标签标记为可读的字符串。我们将会利用这个JSON文件来决定这样一组特殊标签的整数值索引,在构建对抗图像攻击时,这个索引将会给予我们帮助。
2、utils.py: 包含简单的Python辅助函数,用于载入和解析imagenet_class_index.json
这里面还有两个我们今天需要检验的Python脚本:
1、predict_normal.py: 接收一张输入图像(pig.jpg),载入ResNet50模型,对输入图像进行分类。这个脚本的输出会是预测类别标签在ImageNet的类别标签索引。
2、generate_basic_adversary.py:利用predict_normal.py脚本中的输出,我们将构建一次对抗攻击来欺骗ResNet,这个脚本的输出(adversarial.png)将会存储在硬盘中。
做好准备来实现你的第一个Keras和TensorFlow中的对抗攻击了吗?让我们开始吧。
ImageNet 类别标签/索引帮助实用程序
在我们开始执行正常的图像分类或者对抗攻击使用的混淆图像分类之前,首先需要创建一个Python辅助函数来载入和解析ImageNet数据集的类别标签。
我们提供了一个JSON文件包含所有的类别标签索引、标识符和可读的字符串,全都包含在项目目录结构中pyimagesearch模块的imagenet_class_index.json文件里。
在这里展示JSON文件的前几行:
{
"0": [
"n01440764",
"tench"
],
"1": [
"n01443537",
"goldfish"
],
"2": [
"n01484850",
"great_white_shark"
],
"3": [
"n01491361",
"tiger_shark"
],
...
"106": [
"n01883070",
"wombat"
],
...
在这里可以看到这个文件是一个字典格式的。字典的关键字是类别标签的整数值索引,而值由二元元组组成:
1、ImageNet标签的唯一标识符;
2、有可读性的类别标签。
我们的目标是实现一个Python函数可以解析JSON文件:
接收一个输入标签;
转化成其对应标签的类别标签整数值索引。
实际上,我们就是转换imagenet_class_index.json文件中的键与值(key/value)的关系。
让我们来实现这个辅助函数吧。
打开pyimagesearch模块的utils.py文件,插入下列代码:
# importnecessary packages
import json
import os
defget_class_idx(label):
# build the path to theImageNet class label mappings file
labelPath = os.path.join(os.path.dirname(__file__),
"imagenet_class_index.json")
第2行和第3行引入我们所需要的Python包。我们要用到json Python模块来载入JSON文件,而os包用于构建文件路径,这与你使用的是什么操作系统并无关系。
接下来我们去定义get_class_idx辅助函数。这个函数的目标是接收一个输入类别标签,然后获得预测(即在ImageNet上所训练出的模型在1000个标签类别中做出的预测)的标签类别的整数值索引。
第7行是组成能够载入 pyimagesearch模块中的imagenet_class_index.json的路径。
现在载入JSON文件中的内容:
# open theImageNet class mappings file and load the mappings as
# a dictionary with the human-readable class label as the keyand
# the integerindex as the value
withopen(labelPath)as f:
imageNetClasses = {labels[1]: int(idx)for(idx, labels)in
json.load(f).items()}
# check to see if the inputclass label has a corresponding
# integer index value, and if so return it; otherwise return
# a None-type value
return imageNetClasses.get(label, None)
第4-6行,打开labelPath文件,然后将键值对的关系进行转换,这样键才是可读的标签字符串,而值是那个标签的整数值索引。
为了获得输入标签的整数值索引,可以调用imageNetClasses字典中的.get方法(最后一行),会返回:
· 如果在字典中存在改标签的话,则返回该标签的整数值索引;
· 否则返回None。
这个值返回到调用函数。
让我们在下一章节来构建get_class_idx辅助函数。
利用Keras和TensorFlow在没有对抗攻击的情况下进行图片分类
在实现了ImageNet 类标签/索引辅助函数后,让我们构造一个图像分类脚本,在没有对抗攻击的情况下实现基本分类的图像分类脚本。
这个脚本可以证明我们的ResNet模型表现正常(做出正确地预测)。在这篇教程的后半部分,你们将会发现如何构建一张对抗图像用于混淆ResNet。
让我们开始基本的图像分类脚本——在你的工程文件结构中打开predict_normal.py文件,并插入以下代码:
# import necessarypackages
from pyimagesearch.utils import get_class_idx
from tensorflow.keras.applications import ResNet50
from tensorflow.keras.applications.resnet50 import decode_predictions
from tensorflow.keras.applications.resnet50 import preprocess_input
import numpy as np
import argparse
import imutils
import cv2
在第2-9行,我们引入了所需的Python包。如果你之前使用过Keras,TensorFlow和OpenCV的话,这些对你来说都是常规操作。
如果你是Keras和TensorFlow的新手,我强烈建议你去看一下我的这篇《KerasTutorial: How to get started with Keras, Deep Learning, and Python》教程。另外,你或许想要读一读我的书《DeepLearning for Computer Vision with Python》加深你对训练自定义神经网络的理解。
在第2行,我们引入了在上一章节中定义的get_class_idx函数,这个函数可以获得ResNet50模型中最高预测标签的整数索引值。
让我们定义下 preprocess_image辅助函数:
defpreprocess_image(image):
# swap color channels,preprocess the image, and add in a batch
# dimension
image = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
image = preprocess_input(image)
image = cv2.resize(image, (224, 224))
image = np.expand_dims(image, axis=0)
# return the preprocessed image
return image
preprocess_image 方法接收一个单一的必需参数,那就是我们想要预处理的图片。
对这张图片进行预处理有以下几步:
1、将图片的BGR通道组合转化为RGB;
2、执行preprocess_input函数,用于完成ResNet50中特别的预处理和比例缩放过程;
3、将图片大小调整为224×224;
4、增加一个批次维度。
这张预处理好的图像会被返回到调用函数中。
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。