Adversarial exapmles (Fast Gradient Sign Method) の実装に関するメモ

##Sample code
```python
# cnn classifier

class CNN_Classifier():
  def __init__(self):
    pass

  def weight_variable(self, name, shape):
    initializer = tf.truncated_normal_initializer(mean = 0.0, stddev = 0.01, dtype = tf.float32)
    return tf.get_variable(name, shape, initializer = initializer)

  def bias_variable(self, name, shape):
    initializer = tf.constant_initializer(value = 0.0, dtype = tf.float32)
    return tf.get_variable(name, shape, initializer = initializer)

  def inference(self, x, filter_size, n_filters_1, n_filters_2, n_fc, keep_prob, reuse = False):
    x_reshaped = tf.reshape(x, [-1, 28, 28, 1])

    with tf.variable_scope('conv_1', reuse = reuse):
      w = self.weight_variable('w', [filter_size, filter_size, 1, n_filters_1])
      b = self.bias_variable('b', [n_filters_1])

      # no max_pooling
      conv_1 = tf.nn.relu(tf.nn.conv2d(x_reshaped, w, strides = [1, 2, 2, 1], padding = 'SAME') + b)

      # with max_pooling
      #conv_1 = tf.nn.relu(tf.nn.conv2d(x_reshaped, w, strides = [1, 1, 1, 1], padding = 'SAME') + b)
      #conv_1 = tf.nn.max_pool(conv_1, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = 'SAME')

    with tf.variable_scope('conv_2', reuse = reuse):
      w = self.weight_variable('w', [filter_size, filter_size, n_filters_1, n_filters_2])
      b = self.bias_variable('b', [n_filters_2])

      # no max_pooling
      conv_2 = tf.nn.relu(tf.nn.conv2d(conv_1, w, strides = [1, 2, 2, 1], padding = 'SAME') + b)

      # with max_pooling
      #conv_2 = tf.nn.relu(tf.nn.conv2d(conv_1, w, strides = [1, 1, 1, 1], padding = 'SAME') + b)
      #conv_2 = tf.nn.max_pool(conv_2, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = 'SAME')

    conv_2_flat = tf.reshape(conv_2, [-1, 7 * 7 * n_filters_2])

    with tf.variable_scope('fc_1', reuse = reuse):
      w = self.weight_variable('w', [7 * 7 * n_filters_2, n_fc])
      b = self.bias_variable('b', [n_fc])

      fc_1 = tf.nn.relu(tf.matmul(conv_2_flat, w) + b)

    fc_1_dropout = tf.nn.dropout(fc_1, keep_prob)

    with tf.variable_scope('fc_2', reuse = reuse):
      w = self.weight_variable('w', [n_fc, 10])
      b = self.bias_variable('b', [10])

      fc_2 = tf.matmul(fc_1_dropout, w) + b

    output = tf.nn.softmax(fc_2, axis = 1)

    return output

  def loss(self, y, t):
    cross_entropy = - tf.reduce_mean(tf.reduce_sum(t * tf.log(tf.clip_by_value(y, 1e-10, 1.0)), axis = 1))
    return cross_entropy

  def accuracy(self, y, t):
    correct_preds = tf.equal(tf.argmax(y, axis = 1), tf.argmax(t, axis = 1))
    accuracy = tf.reduce_mean(tf.cast(correct_preds, tf.float32))
    return accuracy

  def training(self, loss, learning_rate):
    optimizer = tf.train.AdamOptimizer(learning_rate = learning_rate)
    train_step = optimizer.minimize(loss)
    return train_step

  def training_clipped(self, loss, learning_rate, clip_norm):
    optimizer = tf.train.AdamOptimizer(learning_rate = learning_rate)

    grads_and_vars = optimizer.compute_gradients(loss)
    clipped_grads_and_vars = [(tf.clip_by_norm(grad, clip_norm = clip_norm), \
                             var) for grad, var in grads_and_vars]
    train_step = optimizer.apply_gradients(clipped_grads_and_vars)

    return train_step

  def fit(self, images_train, labels_train, images_test, labels_test, \
          filter_size, n_filters_1, n_filters_2, n_fc, \
          learning_rate, n_iter, batch_size, show_step, is_saving, model_path):

    tf.reset_default_graph()

    x = tf.placeholder(shape = [None, 28 * 28], dtype = tf.float32)
    t = tf.placeholder(shape = [None, 10], dtype = tf.float32)
    keep_prob = tf.placeholder(shape = (), dtype = tf.float32)

    y = self.inference(x, filter_size, n_filters_1, n_filters_2, n_fc, keep_prob)

    loss = self.loss(y, t)

    # Without Gradient Clipping
    train_step = self.training(loss, learning_rate)
    # With Gradient Clipping
    #train_step = self.training_clipped(loss, learning_rate, 0.1)

    acc =  self.accuracy(y, t)

    init = tf.global_variables_initializer()
    saver = tf.train.Saver()

    with tf.Session() as sess:

      sess.run(init)
      
      history_loss_train = []
      history_acc_train = []
      history_loss_test = []
      history_acc_test = []
      
      for i in range(n_iter):
        # Train
        rand_index = np.random.choice(len(images_train), size = batch_size)
        x_batch = images_train[rand_index]
        y_batch = labels_train[rand_index]

        feed_dict = {x: x_batch, t: y_batch, keep_prob: 0.7}

        sess.run(train_step, feed_dict = feed_dict)

        temp_loss = sess.run(loss, feed_dict = feed_dict)
        temp_acc = sess.run(acc, feed_dict = feed_dict)

        history_loss_train.append(temp_loss)
        history_acc_train.append(temp_acc)

        if (i + 1) % show_step == 0:
          print ('--------------------')
          print ('Iteration: ' + str(i + 1) + '  Loss: ' + str(temp_loss) + \
                '  Accuracy: ' + str(temp_acc))

        # Test
        rand_index = np.random.choice(len(images_test), size = batch_size)
        x_batch = images_test[rand_index]
        y_batch = labels_test[rand_index]

        feed_dict = {x: x_batch, t: y_batch, keep_prob: 1.0}

        temp_loss = sess.run(loss, feed_dict = feed_dict)
        temp_acc = sess.run(acc, feed_dict = feed_dict)

        history_loss_test.append(temp_loss)
        history_acc_test.append(temp_acc)

      if is_saving:
        model_path = saver.save(sess, model_path)
        print ('done saving at ', model_path)

    fig = plt.figure(figsize = (10, 3))
    ax1 = fig.add_subplot(1, 2, 1)
    ax1.plot(range(n_iter), history_loss_train, 'b-', label = 'Train')
    ax1.plot(range(n_iter), history_loss_test, 'r--', label = 'Test')
    ax1.set_title('Loss')
    ax1.legend(loc = 'upper right')

    ax2 = fig.add_subplot(1, 2, 2)
    ax2.plot(range(n_iter), history_acc_train, 'b-', label = 'Train')
    ax2.plot(range(n_iter), history_acc_test, 'r--', label = 'Test')
    ax2.set_title('Accuracy')
    ax2.legend(loc = 'lower right')

    plt.show()

  def predict(self, images, filter_size, n_filters_1, n_filters_2, n_fc, model_path):
    
    x = tf.placeholder(shape = [None, 28 * 28], dtype = tf.float32)
    t = tf.placeholder(shape = [None, 10], dtype = tf.float32)
    keep_prob = tf.placeholder(shape = (), dtype = tf.float32)

    y = self.inference(x, filter_size, n_filters_1, n_filters_2, n_fc, keep_prob, reuse = True)
   
    saver = tf.train.Saver()

    with tf.Session() as sess:
      
      saver.restore(sess, model_path)
      
      feed_dict = {x: images, keep_prob: 1.0}
      
      return sess.run(y, feed_dict = feed_dict)
  
  def generate_adversarial_perturbation(self, images, labels, \
                                        filter_size, n_filters_1, n_filters_2, n_fc, \
                                        eps, model_path):
    
    x = tf.placeholder(shape = [None, 28 * 28], dtype = tf.float32)
    t = tf.placeholder(shape = [None, 10], dtype = tf.float32)
    keep_prob = tf.placeholder(shape = (), dtype = tf.float32)

    y = self.inference(x, filter_size, n_filters_1, n_filters_2, n_fc, keep_prob, reuse = True)
    loss = -tf.reduce_sum(t * tf.log(tf.clip_by_value(y, 1e-10, 1.0)), axis = 1)
    
    grad = tf.gradients(loss, [x])[0]
    grad_norm = grad / (tf.reshape(tf.sqrt(tf.reduce_sum(tf.pow(grad, 2), axis = 1)), [-1, 1]) + 1e-8)
    r_adv = eps * grad_norm
    
    saver = tf.train.Saver()

    with tf.Session() as sess:
      
      saver.restore(sess, model_path)
      
      feed_dict = {x: images, t: labels, keep_prob: 1.0}

      return sess.run(r_adv, feed_dict = feed_dict)      
```

```python
# import mnist data
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets('datalab/mnist', one_hot = True)

images_x_train = mnist.train.images
labels_x_train = mnist.train.labels
images_x_test = mnist.test.images
labels_x_test = mnist.test.labels
```

```python
cnn = CNN_Classifier()

filter_size = 3
n_filters_1 = 16
n_filters_2 = 16
n_fc = 128
learning_rate = 0.01
n_iter = 500
batch_size = 64
show_step = 100
model_path = 'datalab/model'

images_train = images_x_train
labels_train = labels_x_train
#images_train = images_x_train[:100]   # small number of sample data
#labels_train = labels_x_train[:100]   # small number of sample data

images_test = images_x_test
#images_test = images_flip_test   # flipped images 
labels_test = labels_x_test

is_saving = False

cnn.fit(images_train, labels_train, images_test, labels_test, \
         filter_size, n_filters_1, n_filters_2, n_fc, learning_rate, \
         n_iter, batch_size, show_step, is_saving, model_path)
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/254604/dc5582b5-773d-c34a-8444-8d23dd32c033.png)

```python
u, c = np.unique(np.argmax(labels_train, axis = 1), return_counts = True)

print (dict(zip(u, c)))
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/254604/0f9f68b8-401f-b825-b585-cc7c5cc89fda.png)


```python
indices = np.random.choice(1000, 10)
images = images_test[indices]
labels = labels_test[indices]
eps = 1.0

r_adv = cnn.generate_adversarial_perturbation(images, labels, \
                                          filter_size, n_filters_1, n_filters_2, n_fc, eps, model_path)

original = np.reshape(images[0], (28, 28))
perturbation = np.reshape(r_adv[0], (28, 28))
adversarial = np.reshape(images[0] + r_adv[0], (28, 28))


fig = plt.figure(figsize = (7, 5))

ax = fig.add_subplot(1, 3, 1)
ax.imshow(original, cmap = 'gray')
ax.set_title('original')
ax.set_axis_off()

ax = fig.add_subplot(1, 3, 2)
ax.imshow(perturbation, cmap = 'gray')
ax.set_title('perturbation')
ax.set_axis_off()

ax = fig.add_subplot(1, 3, 3)
ax.imshow(adversarial, cmap = 'gray')
ax.set_title('adversarial')
ax.set_axis_off()

plt.show()

images_adversarial = images + r_adv
y_hat = cnn.predict(images_adversarial, filter_size, n_filters_1, n_filters_2, n_fc, model_path)

print ('prediction')
print (np.argmax(y_hat, axis = 1))
print ('actual')
print (np.argmax(labels, axis = 1))
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/254604/578dc7fe-4e4b-e6db-bdd5-d034ff728426.png)