用噪声欺骗神经网络





我一直着迷于系统故障及其行为的怪异之处,尤其是当它们在正常条件下运行时。最近,我在Ian Goodfellow的演示文稿中看到了一张幻灯片,觉得很有趣。随机的视觉噪声被馈送到训练有素的神经网络,她将其识别为她知道的物体之一。这里立即出现许多问题。不同训练有素的神经网络会看到相同的物体吗?神经网络对该随机噪声确实是可识别对象的最大置信度是多少?神经网络实际上在那里看到了什么?



由于我对此的好奇心,此条目诞生了。幸运的是,使用PyTorch可以轻松进行此类实验... 为了可视化为什么神经网络以某种方式对对象进行分类,我使用了Captum模型可解释性框架可以从Github下载该代码



问题的重要性



您可能会问为什么这些问题很重要。在许多情况下,开发人员不会从头开始构建模型。他们从模型动物园中选择平台和预先训练的网络作为起点。这样可以节省时间-您无需收集数据并进行神经网络的初始训练。但是,这也意味着在意想不到的地方可能会出现意想不到的问题。根据使用此模型的方式,过程中可能会出现安全问题。



预训练模型



预训练的模型易于入门,可以快速提交数据进行分类。在这种情况下,您无需定义模型并进行训练-所有这些都已经在您完成之前完成,并且可以在部署后立即使用。从Torchvision库预训练的模型被训练从一组图像Imagenet数据库,分为1000...重要的是要记住,该培训涉及识别图片中的单个对象,而不是解析包含各种对象的复杂图像。在第二种情况下,您也可以获得有趣的结果,但这是一个完全不同的主题。从Torchvision库下载经过预训练的模型非常容易。您只需要通过将预训练参数设置为True来导入所选模型。我还在模型中包括了评估模式,因为测试过程中没有学习曲线。



首先,我有一行代码选择使用cuda或cpu,这取决于GPU是否可用。对于这种简单的测试,不需要GPU,但是由于我有一个,所以我使用它。



device = "cuda" if torch.cuda.is_available() else "cpu"
	
import torchvision.models as models
vgg16 = models.vgg16(pretrained=True)
vgg16.eval()
vgg16.to(device)


可在此处找到Torchvision的预训练模型列表我不想使用所有预先训练的神经网络,这已经太多了。我选择了以下五个:



  • vgg16
  • 网路18
  • 亚历克斯网
  • 密集网
  • 起始


我没有使用任何特殊的方法来选择神经网络。例如,Vgg16和Inception通常在不同的示例中使用,并且都不同。



如何创建带有噪点的图像



我们将需要一种自动生成包含噪声的图像的方法,该图像可以馈入神经网络。为此,我结合使用了Numpy库和PIL库,并编写了一个小函数来返回充满随机噪声的图像。



import numpy as np
from PIL import Image
 
def gen_image():
    image = (np.random.standard_normal([256, 256, 3]) * 255).astype(np.uint8)
    im = Image.fromarray(image)
     
    return im  


您最终会得到如下内容:







转换影像



之后,我们需要将图像转换为张量并将其标准化。以下代码不仅可以用于随机噪声,还可以用于我们想要馈入预训练的神经网络的任何图像(这就是代码使用Resize和CenterCrop值的原因)。



def xform_image(image):
     
    transform = transforms.Compose([transforms.Resize(256),
                                    transforms.CenterCrop(224),
                                    transforms.ToTensor(),
                                    transforms.Normalize([0.485, 0.456, 0.406],
                                                         [0.229, 0.224, 0.225])])
     
    new_image = transform(image).to(device)
    new_image = new_image.unsqueeze_(0)
     
    return new_image


我们得到预测



准备好转换后的图像后,很容易从展开的模型中获得预测。在这种情况下,假定xform_image函数返回image_xform。在我用于测试的代码中,我将工作分解为这两个功能,但是在这里我将它们放在一起以方便参考。我们本质上需要将转换后的图像馈送到网络,运行softmax函数,使用topk函数获得分数,并获得最佳结果的预测标签ID。



with torch.no_grad():
    vgg16_res = vgg16(image_xform)
    vgg16_output = F.softmax(vgg16_res, dim=1)
    vgg16score, pred_label_idx = torch.topk(vgg16_output, 1)


结果



好了,现在我们看看如何生成嘈杂的图像并将其馈送到预先训练的网络。那么结果如何呢?对于此测试,我决定生成1000张带噪图像,通过5个选定的经过预训练的网络运行它们,并将其填充到Pandas数据框中以进行快速分析。结果很有趣,有些出乎意料。

vgg16 网路18 亚历克斯网 密集网 起始
计数 1000.000000 1000.000000 1000.000000 1000.000000 1000.000000
意思 0.226978 0.328249 0.147289 0.409413 0.020204
性病 0.067972 0.071808 0.038628 0.148315 0.016490
0.074922 0.127953 0.061019 0.139161 0.005963
25% 0.178240 0.278830 0.120568 0.291042 0.011641
50% 0.223623 0.324111 0.143090 0.387705 0.015880
75% 0.270547 0.373325 0.171139 0.511357 0.022519
最高 0.438011 0.580559 0.328568 0.868025 0.198698


如您所见,某些神经网络已确定该噪声实际上代表着相当高的置信度。 Resnet18和densitynet均达到50%的峰值。一切都很好,但是这些网络在噪声中究竟能“看到”什么?有趣的是,不同的网络在那里“找到”了不同的对象。 每个网络看到的都是不同的东西。 Resnet18 100%地确定它是水母,相反,Inception对预测的信心很小,尽管与此同时,她看到的物体比其他任何网络都要多。



Vgg16:

978

14

7

1



Resnet18:

1000



Alexnet:

942

58



Densenet:

893

37

33

20

16

1



Inception:

155

123

102

85

83

81

69

32

26

25

24

18

16

16

12

12

11

9

9

8

7

5

5

5

- 5

4

4

4

4

3

3

3

3

3

2

2

2

2

2

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1

1








只是为了好玩,我决定看看微软将在噪点图像下添加什么样的签名,我将它带到了本文的开头。对于测试,我决定采用最简单的方法,并从Office 365中使用PowerPoint。结果很有趣,因为不同于试图识别单个对象的imagenet模型,PowerPoint试图识别多个对象以创建图像的准确描述。



该图显示了大象,人,大的球。


结果并没有让我失望。从我的角度来看,噪音的图像被认为是马戏团。



观点



这引出了另一个问题-神经网络看到了什么,使其认为噪声是物体?在寻找答案时,我们可以使用模型解释工具,该工具将使我们能够大致了解网络“所见”。 Captum是PyTorch的模型解释框架。我在这里没有做任何特别的事情,我只是使用了他们网站上教程中代码。我刚刚添加了internal_batch_size参数,其值为50,因为没有它,我的GPU很快就会耗尽内存。



对于可视化,我使用了两个基于梯度的归因和一个基于遮挡的归因。通过这些可视化,我们尝试了解对分类器而言重要的内容,因此可以“看到”网络看到的内容。我还使用了预先训练的resnet模型,但是您可以更改代码并使用任何其他预先训练的模型。



在进入噪声之前,我以洋甘菊的图像作为渲染过程的演示,因为它的标志很容易识别。



result = resnet18(image_xform)
result = F.softmax(result, dim=1)
score, pred_label_idx = torch.topk(result, 1)
 
integrated_gradients = IntegratedGradients(resnet18)
attributions_ig = integrated_gradients.attribute(image_xform, target=pred_label_idx, 
                                                 internal_batch_size=50, n_steps=200)
 
default_cmap = LinearSegmentedColormap.from_list('custom blue', 
                                                 [(0, '#ffffff'),
                                                  (0.25, '#000000'),
                                                  (1, '#000000')], N=256)
 
_ = viz.visualize_image_attr(np.transpose(attributions_ig.squeeze().cpu().detach().numpy(), (1,2,0)),
                             np.transpose(image_xform.squeeze().cpu().detach().numpy(), (1,2,0)),
                             method='heat_map',
                             cmap=default_cmap,
                             show_colorbar=True,
                             sign='positive',
                             outlier_perc=1)






noise_tunnel = NoiseTunnel(integrated_gradients)
 
attributions_ig_nt = noise_tunnel.attribute(image_xform, n_samples=10, nt_type='smoothgrad_sq', target=pred_label_idx, internal_batch_size=50)
_ = viz.visualize_image_attr_multiple(np.transpose(attributions_ig_nt.squeeze().cpu().detach().numpy(), (1,2,0)),
                                      np.transpose(image_xform.squeeze().cpu().detach().numpy(), (1,2,0)),
                                      ["original_image", "heat_map"],
                                      ["all", "positive"],
                                      cmap=default_cmap,
                                      show_colorbar=True)






occlusion = Occlusion(resnet18)
 
attributions_occ = occlusion.attribute(image_xform,
                                       strides = (3, 8, 8),
                                       target=pred_label_idx,
                                       sliding_window_shapes=(3,15, 15),
                                       baselines=0)
 
_ = viz.visualize_image_attr_multiple(np.transpose(attributions_occ.squeeze().cpu().detach().numpy(), (1,2,0)),
                                      np.transpose(image_xform.squeeze().cpu().detach().numpy(), (1,2,0)),
                                      ["original_image", "heat_map"],
                                      ["all", "positive"],
                                      show_colorbar=True,
                                      outlier_perc=2,
                                     )






噪声可视化



我们基于洋甘菊生成了以前的图像,现在是时候看看事物如何在随机噪声下工作。



我正在使用经过预训练的resnet18网络,使用此图像,她可以确定自己看到水母40%。我不会重复该代码,用于渲染的代码与上面给出的代码相同。



















从可视化中可以明显看出,我们人类永远无法理解为什么网络在这里看到水母。图像的某些区域被标记为更重要,但它们根本不像我们在洋甘菊示例中看到的那样定义。与洋甘菊不同,水母是无定形的,透明度不同。



您可能想知道处理水母真实图像的渲染效果如何?我的代码发布在Github上,借助它的帮助,很容易找到该问题的答案。



结论



根据此记录,很容易看到通过向神经网络提供意外的输入来使其变得愚蠢。值得称赞的是,我们会说他们做了自己的工作,并尽了最大的努力。从工作结果还可以看出,在这种情况下仅靠低置信度过滤掉选项是不够的,因为某些选项具有很高的置信度。我们需要警惕现实世界中的系统如此容易失效的情况。进入系统的意外数据不应令我们感到惊讶-这是安全专家已经进行了相当长的时间了。



All Articles