TensorFlow 中的对抗性图像

6
我正在阅读一篇文章,它讲解如何欺骗神经网络以预测任何你想要的图像。我正在使用mnist数据集。
这篇文章提供了相对详细的步骤,但作者使用了Caffe。
无论如何,我的第一步是使用TensorFlow创建一个逻辑回归函数,该函数在mnist数据集上进行了训练。因此,如果我恢复逻辑回归模型,就可以使用它来预测任何图像。例如,我将数字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。反向传播是一种用于计算梯度的算法。
然后提供了代码来说明如何在中计算梯度...
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框架的工作原理感到困惑,因为我之前从未见过/使用过它。如果有人能够逐步解释这个逻辑是如何工作的,那就太好了。
我已经知道反向传播的基础知识,所以您可以假设我已经知道它的工作原理。
以下是文章链接:https://codewords.recurse.com/issues/five/why-do-neural-networks-think-a-panda-is-a-vulture
1个回答

7
我将向您展示如何在TF中生成对抗性图像的基础知识,如果要将其应用于已学习的模型,则可能需要进行一些适应。如果您想要交互式地尝试此操作,代码块在Jupyter笔记本中可以很好地作为块工作。如果您不使用笔记本电脑,则需要添加plt.show()调用以显示绘图并删除matplotlib inline语句。该代码基本上是来自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)这样做。

Result

同样的模式适用于更复杂的模型,但您需要像平常一样训练模型 - 使用具有更大批次的占位符或使用TF读取器的管道,并且当您想要进行对抗性图像时,您将重新创建具有输入图像变量作为输入的网络。只要所有变量名称保持相同(如果您使用相同的函数构建网络,则应该如此),您可以使用网络检查点进行还原,然后应用此帖子中的步骤以获得对抗性图像。您可能需要尝试不同的学习率等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接