作者选择了 Dev Color以作为 Write for Donations计划的一部分获得捐款。
动物分类的神经网络是否可以被欺骗?欺骗动物分类器可能有很少的后果,但如果我们的面部身份验证器可以被欺骗? 或者我们的自动驾驶汽车原型软件? 幸运的是,许多工程师和研究人员站在我们移动设备或汽车上的原型计算机视觉模型和生产质量模型之间。
在本教程中,您将尝试欺骗
或欺骗动物分类器. 当您通过教程工作时,您将使用 OpenCV
,计算机视觉库,以及 PyTorch
,深度学习库。
- 创建一个 targeted adversarial 示例. 选择一个图像,比如说,一只狗. 选择一个 target 类,比如说,一只猫. 你的目标是欺骗神经网络相信所描绘的狗是猫.
- 创建一个 _adversarial 防御。
到本教程结束时,您将拥有欺骗神经网络的工具,并了解如何防御伎俩。
前提条件
要完成本教程,您将需要以下内容:
您可以遵循 如何安装和设置Python 的本地编程环境 3来配置您所需的一切。
- 建议您查看 构建基于情绪的狗过滤器;本教程没有被明确使用,但引入了分类的概念。
步骤 1 – 创建您的项目和安装依赖
让我们为这个项目创建一个工作区,并安装您需要的依赖,您将把工作区称为AdversarialML
:
1mkdir ~/AdversarialML
导航到AdversarialML
目录:
1cd ~/AdversarialML
创建一个目录,存储您的所有资产:
1mkdir ~/AdversarialML/assets
然后为项目创建一个新的虚拟环境:
1python3 -m venv adversarialml
激活你的环境:
1source adversarialml/bin/activate
然后安装 PyTorch,您将在本教程中使用的Python深度学习框架。
在 macOS 上,使用以下命令安装 Pytorch:
1python -m pip install torch==1.2.0 torchvision==0.4.0
在 Linux 和 Windows 上,使用以下命令为仅 CPU 构建:
1pip install torch==1.2.0+cpu torchvision==0.4.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
2pip install torchvision
现在安装OpenCV
和numpy
的预装二进制,分别是计算机视觉和线性 algebra的库。
1python -m pip install opencv-python==3.4.3.18 numpy==1.14.5
在Linux发行版中,您需要安装libSM.so
:
1sudo apt-get install libsm6 libxext6 libxrender-dev
随着依赖的安装,让我们运行一个名为ResNet18的动物分类器,我们将在下面描述。
步骤2 - 运行预训练动物分类器
该图书馆,PyTorch的官方计算机视觉图书馆,包含常用的计算机视觉神经网络的预训练版本. 这些神经网络都是在 ImageNet 2012上训练的,该数据集有120万个培训图像,1000个类。 这些类包括车辆,场所和最重要的是动物。 在这个步骤中,你将运行这些预训练的神经网络之一,称为ResNet18。 我们将指在ImageNet上训练的ResNet18作为动物分类器
。
ResNet18 是名为 残留神经网络的神经网络家族中最小的神经网络,由 MSR(He et al.)开发。简而言之,他发现,神经网络(称为函数 f
,输入 x
,输出 f(x)
)会更好地与残留连接
x + f(x)
。
点击下载(https://www.pexels.com/photo/photographer-animal-photography-dog-58997/)以下命令:
1wget -O assets/dog.jpg assets/trick_neural_network/step2a.png
然后,下载一个 JSON 文件,将神经网络输出转换为人读类名称:
1wget -O assets/imagenet_idx_to_label.json https://raw.githubusercontent.com/do-community/tricking-neural-networks/master/utils/imagenet_idx_to_label.json
接下来,创建一个脚本,在狗的图像上运行你的预训练模型. 创建一个名为 `step_2_pretrained.py'的新文件:
1nano step_2_pretrained.py
首先,通过导入必要的包并声明一个主要
函数来添加 Python 锅炉板:
1[label step_2_pretrained.py]
2from PIL import Image
3import json
4import torchvision.models as models
5import torchvision.transforms as transforms
6import torch
7import sys
8
9def main():
10 pass
11
12if __name__ == '__main__':
13 main()
接下来,从神经网络输出加载到人类可读的类名称。
1[label step_2_pretrained.py]
2. . .
3def get_idx_to_label():
4 with open("assets/imagenet_idx_to_label.json") as f:
5 return json.load(f)
6. . .
创建一个图像转换函数,以确保您的输入图像首先具有正确的尺寸,其次正常化正确。
1[label step_2_pretrained.py]
2. . .
3def get_image_transform():
4 transform = transforms.Compose([
5 transforms.Resize(224),
6 transforms.CenterCrop(224),
7 transforms.ToTensor(),
8 transforms.Normalize(mean=[0.485, 0.456, 0.406],
9 std=[0.229, 0.224, 0.225])
10 ])
11 return transform
12. . .
在get_image_transform中,您定义了一些不同的转换,以便应用于传输到神经网络的图像:
transforms.Resize(224)
:将图像的较小侧重调整为224×672,例如,如果图像是448×672,则此操作将图像变成224×336。transforms.CenterCrop(224)
:从图像的中心取出224×224的面积。transforms.ToTensor()
:将图像转换为PyTorch紧张器。 所有PyTorch模型都需要PyTorch紧张器作为输入。transforms.Normalize(mean=..., std=...)
:通过减去平均值,然后通过标准偏差来标准化您的输入。
添加一个实用程序来预测动物类别,根据图像. 此方法使用前两种实用程序来执行动物分类:
1[label step_2_pretrained.py]
2. . .
3def predict(image):
4 model = models.resnet18(pretrained=True)
5 model.eval()
6
7 out = model(image)
8
9 _, pred = torch.max(out, 1)
10 idx_to_label = get_idx_to_label()
11 cls = idx_to_label[str(int(pred))]
12 return cls
13. . .
在这里,预测
函数使用预训练的神经网络来分类提供的图像:
models.resnet18(pretrained=True)
: 在本教程中加载一个名为ResNet18的预训练神经网络。model.eval()
: 在评估
模式下运行的模型进行修改。唯一的其他模式是训练
模式,但不需要训练模式,因为您在本教程中没有训练模型(即更新模型的参数)。out = model(image)
:在提供的转换图像上运行神经网络。_, pred = torch.max(out, 1)
:神经网络为每个可能的类别输出一个概率。 此步骤计算了具有最高概率的类索引。 例如,如果 `out = [0.4, 0.1, 0.2]
接下来,按照最后一个函数,添加一个工具来加载图像:
1[label step_2_pretrained.py]
2. . .
3def load_image():
4 assert len(sys.argv) > 1, 'Need to pass path to image'
5 image = Image.open(sys.argv[1])
6
7 transform = get_image_transform()
8 image = transform(image)[None]
9 return image
10. . .
这将从第一个参数中提供的路径加载到脚本中的图像。 transform(image)[None]
适用于前几行中定义的图像转换序列。
最后,将你的主要
函数填充到以下,来加载你的图像并分类图像中的动物:
1[label step_2_pretrained.py]
2def main():
3 x = load_image()
4 print(f'Prediction: {predict(x)}')
重复检查您的文件是否符合我们的最后步骤 2 脚本在 step_2_pretrained.py
在 GitHub 上。
1python step_2_pretrained.py assets/dog.jpg
这将产生以下输出,显示您的动物分类器按预期工作:
1[secondary_label Output]
2Prediction: Pembroke, Pembroke Welsh corgi
接下来,你会看到一个反对的例子在行动中通过欺骗一个神经网络,在图像中有不可察觉的差异。
步骤3 – 尝试一个对手的例子
现在,你将合成一个对抗的例子,并在这个例子上测试神经网络。对于本教程,你将构建对抗的例子形式x + r
,其中x
是原始图像和r
是某种干扰
。你最终将自己创建r
干扰,但在这个步骤中,你将下载我们之前为你创建的一种。
1wget -O assets/adversarial_r.npy https://github.com/do-community/tricking-neural-networks/blob/master/outputs/adversarial_r.npy?raw=true
创建一个名为「step_3_adversarial.py」的新文件:
1nano step_3_adversarial.py
在此文件中,您将执行以下三步进程,以产生对手示例:
- 转换一个图像
2 应用干扰
r
3 反转干扰的图像
在步骤 3 结束时,您将看到一个对手图像. 首先,导入必要的包,并声明一个主要
函数:
1[label step_3_adversarial.py]
2from PIL import Image
3import torchvision.transforms as transforms
4import torch
5import numpy as np
6import os
7import sys
8
9from step_2_pretrained import get_idx_to_label, get_image_transform, predict, load_image
10
11def main():
12 pass
13
14if __name__ == '__main__':
15 main()
接下来,创建一个图像转换
,将以前的图像转换逆转。
1[label step_3_adversarial.py]
2. . .
3def get_inverse_transform():
4 return transforms.Normalize(
5 mean=[-0.485/0.229, -0.456/0.224, -0.406/0.255], # INVERSE normalize images, according to https://pytorch.org/docs/stable/torchvision/models.html
6 std=[1/0.229, 1/0.224, 1/0.255])
7. . .
和以前一样,transforms.Normalize
操作抽取了平均值,并通过标准偏差进行划分(即对于原始图像x
,y
= transforms.Normalize(mean=u, std=o) = (x - u) / o))。
作为反向转换的一部分,添加一种方法,将 PyTorch 压缩器转换回 PIL 图像。
1[label step_3_adversarial.py]
2. . .
3def tensor_to_image(tensor):
4 x = tensor.data.numpy().transpose(1, 2, 0) * 255.
5 x = np.clip(x, 0, 255)
6 return Image.fromarray(x.astype(np.uint8))
7. . .
tensor.data.numpy()
将 PyTorch 压缩器转换为 NumPy 数组..transpose(1, 2, 0)
将(频道,宽度,高度)
重新排列为(高度,宽度,频道)
. 这个 NumPy 数组大约在(0, 1)' 范围内。 最后,以 255 倍来确保图像现在处于
(0, 255)' 范围内。np.clip
确保图像中的所有值都在 `(0, 255)' 之间。x.astype(np.uint8)
确保所有图像值都是整数。 最后, `Image.fromarray(...)' 会从 NumPy 数组创
然后,使用这些实用程序以创建以下对手示例:
1[label step_3_adversarial.py]
2. . .
3def get_adversarial_example(x, r):
4 y = x + r
5 y = get_inverse_transform()(y[0])
6 image = tensor_to_image(y)
7 return image
8. . .
此函数生成如下部分开头所描述的对立示例:
y = x + r
. 取你的干扰r
,并将其添加到原始图像x
.get_inverse_transform
: 获取并应用你之前定义的几行反向图像转换。
最后,修改你的主要
函数来加载图像,加载对抗干扰r
,应用干扰,将对抗示例保存到磁盘上,并在对抗示例上运行预测:
1[label step_3_adversarial.py]
2def main():
3 x = load_image()
4 r = torch.Tensor(np.load('assets/adversarial_r.npy'))
5
6 # save perturbed image
7 os.makedirs('outputs', exist_ok=True)
8 adversarial = get_adversarial_example(x, r)
9 adversarial.save('outputs/adversarial.png')
10
11 # check prediction is new class
12 print(f'Old prediction: {predict(x)}')
13 print(f'New prediction: {predict(x + r)}')
您的完成的文件应该匹配 GitHub 的 step_3_adversarial.py
。
1python step_3_adversarial.py assets/dog.jpg
你会看到这个输出:
1[secondary_label Output]
2Old prediction: Pembroke, Pembroke Welsh corgi
3New prediction: goldfish, Carassius auratus
您现在已经创建了一个对抗性的例子:欺骗神经网络以认为科吉是金鱼. 在下一步,您实际上将创建您在这里使用的r
干扰。
步骤4:了解对手的例子
关于分类的原理,请参阅 如何构建一个以情绪为基础的狗过滤器
。
退一步,请记住,您的分类模型为每个类输出一个概率。在推断过程中,模型预测了具有最高概率的类。在训练过程中,您更新了模型参数 t
,以最大限度地提高正确的类 y
的概率,根据您的数据 x
。
1argmax_y P(y|x,t)
但是,要生成对立的例子,你现在可以修改你的目标,而不是找到一个类,你的目标现在是找到一个新的图像,‘x’。
1argmax_x P(w|x)
请注意,神经网络重量t
在上述表达式中缺少,这是因为你现在承担了对手的角色:其他人已经训练并部署了一个模型。你只能创建对手输入,并且不允许修改部署的模型。
作为一个提醒,对于本教程,你假设对抗的例子是x
的亲属转换,换句话说,你的对抗的例子以某些r
的形式x + r
。
步骤五:创建一个对手的例子
在此步骤中,您将学习一个r
的干扰,因此您的corgi被错误分类为金鱼。创建一个名为step_5_perturb.py
的新文件:
1nano step_5_perturb.py
导入必要的包,并声明一个主要
函数:
1[label step_5_perturb.py]
2from torch.autograd import Variable
3import torchvision.models as models
4import torch.nn as nn
5import torch.optim as optim
6import numpy as np
7import torch
8import os
9
10from step_2_pretrained import get_idx_to_label, get_image_transform, predict, load_image
11from step_3_adversarial import get_adversarial_example
12
13def main():
14 pass
15
16if __name__ == '__main__':
17 main()
直接在导入之后,并在主要
函数之前,定义两个常数:
1[label step_5_perturb.py]
2. . .
3TARGET_LABEL = 1
4EPSILON = 10 / 255.
5. . .
第一个常数 TARGET_LABEL
是错误分类 corgi 的类别. 在这种情况下,索引 1
与金鱼
相符。第二个常数 EPSILON
是每个图像值所允许的最大干扰量。
按照你的两个常数,添加一个辅助函数来定义神经网络和干扰参数 r
:
1[label step_5_perturb.py]
2. . .
3def get_model():
4 net = models.resnet18(pretrained=True).eval()
5 r = nn.Parameter(data=torch.zeros(1, 3, 224, 224), requires_grad=True)
6 return net, r
7. . .
model.resnet18(pretrained=True)
像以前一样加载了名为 ResNet18 的预训练神经网络. 您也像以前一样使用.eval
将模型设置为评估模式。nn.Parameter(...)
定义了一个新的干扰r
,输入图像的大小。 输入图像也大小(1, 3, 224, 224)
。requires_grad=True
关键字参数确保您可以在此文件中更新此干扰r
。
接下来,开始修改你的主
函数. 开始通过加载模型净
,加载输入x
,并定义标签标签
:
1[label step_5_perturb.py]
2. . .
3def main():
4 print(f'Target class: {get_idx_to_label()[str(TARGET_LABEL)]}')
5 net, r = get_model()
6 x = load_image()
7 labels = Variable(torch.Tensor([TARGET_LABEL])).long()
8 . . .
接下来,在你的主要
函数中定义标准和优化器,前者告诉PyTorch目标是什么,即要最大限度地减少损失。
1[label step_5_perturb.py]
2. . .
3 criterion = nn.CrossEntropyLoss()
4 optimizer = optim.SGD([r], lr=0.1, momentum=0.1)
5. . .
直接接下来,为参数 r
添加主训练循环:
1[label step_5_perturb.py]
2. . .
3 for i in range(30):
4 r.data.clamp_(-EPSILON, EPSILON)
5 optimizer.zero_grad()
6
7 outputs = net(x + r)
8 loss = criterion(outputs, labels)
9 loss.backward()
10 optimizer.step()
11
12 _, pred = torch.max(outputs, 1)
13 if i % 5 == 0:
14 print(f'Loss: {loss.item():.2f} / Class: {get_idx_to_label()[str(int(pred))]}')
15. . .
在这个培训循环的每个迭代中,你:
r.data.clamp-(...)
: 确保参数r
是小,在EPSILON
内为 0.optimizer.zero_grad()
: 清除您在上次迭代中计算过的任何梯度。model(x + r)
: 运行修改图像x + r
的推断。- 计算
loss
. - 计算
loss.backward
的梯度。
然后保存最后的干扰 r
:
1[label step_5_perturb.py]
2def main():
3 . . .
4 for i in range(30):
5 . . .
6 . . .
7 np.save('outputs/adversarial_r.npy', r.data.numpy())
直接接下来,仍然在主
函数中,保存被干扰的图像:
1[label step_5_perturb.py]
2. . .
3 os.makedirs('outputs', exist_ok=True)
4 adversarial = get_adversarial_example(x, r)
最后,对原始图像和对手示例进行预测:
1[label step_5_perturb.py]
2 print(f'Old prediction: {predict(x)}')
3 print(f'New prediction: {predict(x + r)}')
双重检查你的脚本匹配 step_5_perturb.py
在GitHub上。
1python step_5_perturb.py assets/dog.jpg
你的脚本将产生如下。
1[secondary_label Output]
2Target class: goldfish, Carassius auratus
3Loss: 17.03 / Class: Pembroke, Pembroke Welsh corgi
4Loss: 8.19 / Class: Pembroke, Pembroke Welsh corgi
5Loss: 5.56 / Class: Pembroke, Pembroke Welsh corgi
6Loss: 3.53 / Class: Pembroke, Pembroke Welsh corgi
7Loss: 1.99 / Class: Pembroke, Pembroke Welsh corgi
8Loss: 1.00 / Class: goldfish, Carassius auratus
9Old prediction: Pembroke, Pembroke Welsh corgi
10New prediction: goldfish, Carassius auratus
最后两行表明你现在已经从头开始完成了对手示例的构建,你的神经网络现在将一个完全合理的科吉图像归类为金鱼。
您现在已经表明,神经网络可以很容易被欺骗 - 更重要的是,对对抗的例子缺乏坚韧性会产生重大后果。 一个自然的下一个问题是:你如何对抗对抗的例子? 各种组织进行了大量的研究,包括 OpenAI。
步骤6:防范对立的例子
在此步骤中,您将实施对对抗示例的防御。想法如下:您现在是用于生产的动物分类器的所有者.您不知道可能生成哪些对抗示例,但您可以修改图像或模型以防止攻击。
在你辩护之前,你应该自己看到图像操纵是多么不可察觉。
- 资产/狗.jpg`
- 输出/adversarial.png`
在这里,你将两者一边显示. 你的原始图像将具有不同的视角比例. 你能告诉我哪个是对手的例子?
请注意,新图像看起来与原始相同. 正如它所示,左图像是你的对手图像. 要确定,下载图像并运行您的评估脚本:
1wget -O assets/adversarial.png https://github.com/alvinwan/fooling-neural-network/blob/master/outputs/adversarial.png?raw=true
2python step_2_pretrained.py assets/adversarial.png
这将产生金鱼类,以证明其敌对性质:
1[secondary_label Output]
2Prediction: goldfish, Carassius auratus
您将运行一个相当天真但有效的防御:将图像压缩到损失的JPEG格式。
1python
然后,将对手图像加载为PNG,并将其保存为JPEG。
1from PIL import Image
2image = Image.open('assets/adversarial.png')
3image.save('outputs/adversarial.jpg')
输入CTRL + D
来留下Python交互提示,然后在压缩对手示例上使用您的模型进行推断:
1python step_2_pretrained.py outputs/adversarial.jpg
这将现在输出科吉类,证明你的天真防御的有效性。
1[secondary_label Output]
2Prediction: Pembroke, Pembroke Welsh corgi
现在你已经完成了你的第一个对抗防御,请注意,这种防御不需要知道对抗的例子是如何产生的,这就是使一个有效的防御。还有许多其他形式的防御,其中许多涉及重新训练神经网络。然而,这些重新训练程序是他们自己的话题,超出本教程的范围。
结论
要了解本教程中您的工作含义,请重新审视两张图像:原始图像和反对图像。
尽管这两个图像看起来与人眼相同,但第一个已经被操纵来欺骗你的模型。 两个图像都清楚地显示出一个corgi,但该模型完全有信心第二个模型包含一个金鱼。 这应该关心你,当你包装这个教程时,记住你的模型的脆弱性。 仅仅通过应用简单的转换,你可以欺骗它。 这些是真实的,可信的危险,即使是尖端的研究也逃避了。 机器学习安全之外的研究同样容易受到这些缺陷的影响,并且作为一名实践者,你必须安全地应用机器学习。 有关更多阅读,请查看以下链接:
- Adversarial Machine Learning从NeurIPS 2018会议的教程.
- 来自OpenAI的相关博客帖子: 攻击机器学习与对手示例, 测试对看不见的对手的强度,和 Robust Adversarial Inputs。
有关更多机器学习内容和教程,您可以访问我们的 机器学习主题页面。