6

我正在阅读一篇文章,该文章解释了如何欺骗神经网络来预测你想要的任何图像。我正在使用mnist数据集。

这篇文章提供了一个相对详细的演练,但是写这篇文章的人正在使用Caffe.

无论如何,我的第一步是使用在mnist数据集上训练的 TensorFlow 创建一个逻辑回归函数。因此,如果我restore使用逻辑回归模型,我可以使用它来预测任何图像。例如,我将数字 7提供给以下模型......

with tf.Session() as sess:  
    saver.restore(sess, "/tmp/model.ckpt")
    # number 7
    x_in = np.expand_dims(mnist.test.images[0], axis=0)
    classification = sess.run(tf.argmax(pred, 1), feed_dict={x:x_in})
    print(classification) 

>>>[7]

这会打印出[7]正确的数字。

现在文章解释说,为了破坏神经网络,我们需要计算神经网络的梯度。这是神经网络的导数。

文章指出,要计算梯度,我们首先需要选择一个预期的结果来移动,并将输出概率列表设置为处处为 0,为预期结果设置 1。反向传播是一种计算梯度的算法。

然后提供了Caffe关于如何计算梯度的代码......

def compute_gradient(image, intended_outcome):
    # Put the image into the network and make the prediction
    predict(image)
    # Get an empty set of probabilities
    probs = np.zeros_like(net.blobs['prob'].data)
    # Set the probability for our intended outcome to 1
    probs[0][intended_outcome] = 1
    # Do backpropagation to calculate the gradient for that outcome
    # and the image we put in
    gradient = net.backward(prob=probs)
    return gradient['data'].copy()

现在,我的问题是,我很难理解这个函数是如何仅通过将图像和概率提供给函数来获得梯度的。因为我不完全理解这段代码,所以我很难将这个逻辑翻译成TensorFlow.

我想我对Caffe框架的工作原理感到困惑,因为我以前从未见过/使用过它。如果有人可以逐步解释这种逻辑是如何工作的,那就太好了。

我已经知道它的基础知识,Backpropagation所以你可能会认为我已经知道它是如何工作的。

这是文章本身的链接... https://codewords.recurse.com/issues/five/why-do-neural-networks-think-a-panda-is-a-vulture

4

1 回答 1

7

我将向您展示如何在 TF 中生成对抗性图像的基础知识,以将其应用于您可能需要一些适应的已经学习的模型。

如果您想以交互方式尝试,这些代码块可以作为 Jupyter 笔记本中的块很好地工作。如果您不使用笔记本,则需要添加 plt.show() 调用以显示绘图并删除 matplotlib 内联语句。该代码基本上是 TF 文档中的简单 MNIST 教程,我将指出重要的区别。

第一个块只是设置,没什么特别的......

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

# if you're not using jupyter notebooks then comment this out
%matplotlib inline

import matplotlib.pyplot as plt
import numpy as np

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf

获取 MNIST 数据(它有时会关闭,因此您可能需要手动从web.archive.org下载它并将其放入该目录)。我们没有像教程中那样使用一种热编码,因为现在 TF 有更好的函数来计算不再需要一种热编码的损失。

mnist = input_data.read_data_sets('/tmp/tensorflow/mnist/input_data')

在下一个区块中,我们正在做一些“特别”的事情。输入图像张量被定义为一个变量,因为稍后我们要针对输入图像进行优化。通常你会在这里有一个占位符。它确实限制了我们一点,因为我们需要一个明确的形状,所以我们一次只输入一个示例。不是你想在生产中做的事情,但出于教学目的它很好(你可以用更多的代码来解决它)。标签是正常的占位符。

input_images = tf.get_variable("input_image", shape=[1,784], dtype=tf.float32)
input_labels = tf.placeholder(shape=[1], name='input_label', dtype=tf.int32)

我们的模型是本教程中的标准逻辑回归模型。我们只使用 softmax 来可视化结果,损失函数采用普通的 logits。

W = tf.get_variable("weights", shape=[784, 10], dtype=tf.float32, initializer=tf.random_normal_initializer())
b = tf.get_variable("biases", shape=[1, 10], dtype=tf.float32, initializer=tf.zeros_initializer())

logits = tf.matmul(input_images, W) + b
softmax = tf.nn.softmax(logits)

损失是标准交叉熵。在训练步骤中需要注意的是,有一个明确的变量列表传入 - 我们已将输入图像定义为训练变量,但我们不想在训练逻辑回归时尝试优化图像,只是权重和偏差- 所以我们明确指出。

loss = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits,labels=input_labels,name='xentropy')

mean_loss = tf.reduce_mean(loss)

train_step = tf.train.AdamOptimizer(learning_rate=0.1).minimize(mean_loss, var_list=[W,b])

开始会话...

sess = tf.Session()
sess.run(tf.global_variables_initializer())

由于批量大小为 1,训练比应有的速度慢。就像我说的,这不是你想在生产中做的事情,但这只是为了教授基础知识......

for step in range(10000):
    batch_xs, batch_ys = mnist.train.next_batch(1)
    loss_v, _ = sess.run([mean_loss, train_step], feed_dict={input_images: batch_xs, input_labels: batch_ys})

在这一点上,我们应该有一个足够好的模型来演示如何生成对抗性图像。首先,我们得到一个带有标签“2”的图像,因为这些很容易,所以即使我们的次优分类器也应该正确(如果没有,再次运行这个单元格;)这一步是随机的,所以我不能保证它会工作)。

我们将输入图像变量设置为该示例。

sample_label = -1
while sample_label != 2:
    sample_image, sample_label = mnist.test.next_batch(1)
    sample_label
plt.imshow(sample_image.reshape(28, 28),cmap='gray')

# assign image to var
sess.run(tf.assign(input_images, sample_image));
sess.run(softmax) # now using the variable as input, no feed dict

# should show something like
# array([[ 0.,  0.,  1.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]], dtype=float32)
# With the third entry being the highest by far.

现在我们要“打破”分类。我们希望在不改变网络本身的情况下,改变图像使其在网络眼中看起来更像另一个数字。为此,代码看起来与我们之前的基本相同。我们定义了一个“假”标签,与之前的损失相同(交叉熵),并且我们得到了一个优化器来最小化假损失,但这次 var_list 只包含输入图像 - 所以我们不会改变逻辑回归权重:

fake_label = tf.placeholder(tf.int32, shape=[1])
fake_loss = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits,labels=fake_label)
adversarial_step = tf.train.GradientDescentOptimizer(learning_rate=1e-3).minimize(fake_loss, var_list=[input_images])

下一个块旨在以交互方式运行多次,同时您会看到图像和分数在变化(这里向标签 8 移动):

sess.run(adversarial_step, feed_dict={fake_label:np.array([8])})
plt.imshow(sess.run(input_images).reshape(28,28),cmap='gray')
sess.run(softmax)

第一次运行此块时,分数可能仍会严重指向 2,但它会随着时间的推移而变化,经过几次运行后,您应该会看到类似下图的内容 - 请注意,该图像仍然看起来像 2,带有一些噪音在后台,但“2”的得分在 3% 左右,而“8”的得分超过 96%。

请注意,我们实际上从未明确计算过梯度——我们不需要,TF 优化器负责计算梯度并将更新应用于变量。如果要获取渐变,可以使用tf.gradients (fake_loss, input_images) 来实现。

结果

相同的模式适用于更复杂的模型,但您要做的是像往常一样训练模型 - 使用具有更大批量的占位符,或使用带有 TF 阅读器的管道,当您想要制作对抗性图像时d 使用输入图像变量作为输入重新创建网络。只要所有变量名称保持不变(如果您使用相同的函数来构建网络,它们应该保持不变),您就可以使用您的网络检查点进行恢复,然后应用本文中的步骤来获取对抗性图像。您可能需要调整学习率等。

于 2017-03-21T18:03:37.580 回答