我将向您展示如何在 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 使用输入图像变量作为输入重新创建网络。只要所有变量名称保持不变(如果您使用相同的函数来构建网络,它们应该保持不变),您就可以使用您的网络检查点进行恢复,然后应用本文中的步骤来获取对抗性图像。您可能需要调整学习率等。