Аверсальные изображения в TensorFlow
Я читаю статью, которая объясняет, как обмануть нейронные сети, чтобы предсказать любое изображение, которое вы хотите. Я использую mnist
набор данных.
В статье дается сравнительно подробное описание, но человек, который написал ее, использует Caffe
,
В любом случае, моим первым шагом было создание функции логистической регрессии с использованием TensorFlow, которая обучается на mnist
набор данных. Итак, если бы я 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
1 ответ
Я собираюсь показать вам, как сделать основы создания изображения соперника в TF, чтобы применить его к уже изученной модели, вам могут потребоваться некоторые адаптации.
Блоки кода работают хорошо как блоки в блокноте Jupyter, если вы хотите попробовать это в интерактивном режиме. Если вы не используете записную книжку, вам нужно добавить вызовы plt.show() для отображения графиков и удалить встроенный оператор matplotlib. Код в основном простой учебник MNIST из документации TF, я укажу на важные различия.
Первый блок просто настройка, ничего особенного...
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 только для визуализации результатов, функция потерь принимает простые логиты.
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 воссоздать сеть с входной переменной изображения в качестве входа. Пока все имена переменных остаются одинаковыми (что они должны делать, если вы используете одни и те же функции для построения сети), вы можете восстановить с помощью своей сетевой контрольной точки, а затем применить шаги из этого поста, чтобы получить состязательное изображение. Возможно, вам придется поиграть с темпами обучения и тому подобное.