├── LICENSE ├── README.md ├── attack.py ├── demo.png └── relationship.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Cihang Xie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Improving Transferability of Adversarial Examples with Input Diversity 2 | 3 | This paper proposed to improve the transferability of adversarial examples by creating diverse input patterns (https://arxiv.org/abs/1803.06978). Instead of only using the original images to generate adversarial examples, the proposed method, Diverse Input Iterative Fast Gradient Sign 4 | Method (DI2-FGSM), applies random transformations to the input images at each iteration. The generated adversarial examples are much more transferable than those generated by FGSM and I-FGSM. An example is shown below: 5 | 6 | ![demo](demo.png) 7 | 8 | 9 | ## Extension 10 | To improve the transferability further, we 11 | - integrate momentum term into the attack process (https://arxiv.org/abs/1710.06081); 12 | - attack multiple networks simultaneously (https://arxiv.org/abs/1611.02770). 13 | 14 | By evaluating this enhanced attack w.r.t. the top 3 defense submissions and 3 official baselines from NIPS 2017 adversarial competition (https://www.kaggle.com/c/nips-2017-non-targeted-adversarial-attack), it reaches an average success rate of 73.0%, which outperforms the top 1 attack submission in the NIPS competition by a large margin of 6.6%. Please refer to the Table 3 in the paper for details. 15 | 16 | 17 | ## Relationships between different attacks 18 | 19 | Different attacks can be related via different parameter settings, as shown below: 20 | 21 | 22 | 23 | ## Inception_v3 model 24 | 25 | - http://download.tensorflow.org/models/inception_v3_2016_08_28.tar.gz 26 | 27 | ## Acknowledgements 28 | 29 | - For the implementations of random resizing and random padding (https://arxiv.org/abs/1711.01991), the original version is available at https://github.com/cihangxie/NIPS2017_adv_challenge_defense. We adopt a more user-friendly re-implementation https://github.com/anishathalye/obfuscated-gradients in our repo only for releasing purpose. 30 | 31 | ## Citing this work 32 | 33 | If you find this work is useful in your research, please consider citing: 34 | 35 | @inproceedings{xie2019improving, 36 | title={Improving Transferability of Adversarial Examples with Input Diversity}, 37 | author={Xie, Cihang and Zhang, Zhishuai and Zhou, Yuyin and Bai, Song and Wang, Jianyu and Ren, Zhou and Yuille, Alan}, 38 | Booktitle = {Computer Vision and Pattern Recognition}, 39 | year={2019}, 40 | organization={IEEE} 41 | } 42 | -------------------------------------------------------------------------------- /attack.py: -------------------------------------------------------------------------------- 1 | """Implementation of sample attack on Inception_v3""" 2 | 3 | from __future__ import absolute_import 4 | from __future__ import division 5 | from __future__ import print_function 6 | 7 | import os 8 | 9 | import numpy as np 10 | from PIL import Image 11 | from scipy.misc import imread, imresize, imsave 12 | from scipy.misc import imresize 13 | 14 | import tensorflow as tf 15 | 16 | from tensorflow.contrib.slim.nets import inception 17 | 18 | slim = tf.contrib.slim 19 | 20 | tf.flags.DEFINE_string( 21 | 'master', '', 'The address of the TensorFlow master to use.') 22 | 23 | tf.flags.DEFINE_string( 24 | 'checkpoint_path', '', 'Path to checkpoint for inception network.') 25 | 26 | tf.flags.DEFINE_string( 27 | 'input_dir', '', 'Input directory with images.') 28 | 29 | tf.flags.DEFINE_string( 30 | 'output_dir', '', 'Output directory with images.') 31 | 32 | tf.flags.DEFINE_integer( 33 | 'image_width', 299, 'Width of each input images.') 34 | 35 | tf.flags.DEFINE_integer( 36 | 'image_height', 299, 'Height of each input images.') 37 | 38 | tf.flags.DEFINE_integer( 39 | 'image_resize', 330, 'Height of each input images.') 40 | 41 | tf.flags.DEFINE_integer( 42 | 'batch_size', 10, 'How many images process at one time.') 43 | 44 | tf.flags.DEFINE_float( 45 | 'max_epsilon', 16.0, 'Maximum size of adversarial perturbation.') 46 | 47 | tf.flags.DEFINE_float( 48 | 'prob', 0.5, 'probability of using diverse inputs.') 49 | 50 | # if momentum = 1, this attack becomes M-DI-2-FGSM 51 | tf.flags.DEFINE_float( 52 | 'momentum', 0.0, 'Momentum.') 53 | 54 | tf.flags.DEFINE_string( 55 | 'GPU_ID', '0', 'which GPU to use.') 56 | 57 | FLAGS = tf.flags.FLAGS 58 | 59 | print("print all settings\n") 60 | print(FLAGS.master) 61 | print(FLAGS.__dict__) 62 | 63 | os.environ['CUDA_DEVICE_ORDER'] = 'PCI_BUS_ID' 64 | os.environ['CUDA_VISIBLE_DEVICES'] = FLAGS.GPU_ID 65 | 66 | 67 | def load_images(input_dir, output_dir, batch_shape): 68 | """Read png images from input directory in batches. 69 | Args: 70 | input_dir: input directory 71 | batch_shape: shape of minibatch array, i.e. [batch_size, height, width, 3] 72 | Yields: 73 | filenames: list file names without path of each image 74 | Lenght of this list could be less than batch_size, in this case only 75 | first few images of the result are elements of the minibatch. 76 | images: array with all images from this batch 77 | """ 78 | images = np.zeros(batch_shape) 79 | filenames = [] 80 | idx = 0 81 | batch_size = batch_shape[0] 82 | for filepath in tf.gfile.Glob(os.path.join(input_dir, '*.png')): 83 | temp_name = str.split(filepath, '/') 84 | output_name = output_dir + '/'+ temp_name[-1] 85 | # check if the file exist 86 | if os.path.isfile(output_name) == False: 87 | with tf.gfile.Open(filepath) as f: 88 | image = imread(f, mode='RGB').astype(np.float) / 255.0 89 | # Images for inception classifier are normalized to be in [-1, 1] interval. 90 | images[idx, :, :, :] = image * 2.0 - 1.0 91 | filenames.append(os.path.basename(filepath)) 92 | idx += 1 93 | if idx == batch_size: 94 | yield filenames, images 95 | filenames = [] 96 | images = np.zeros(batch_shape) 97 | idx = 0 98 | if idx > 0: 99 | yield filenames, images 100 | 101 | 102 | def save_images(images, filenames, output_dir): 103 | """Saves images to the output directory. 104 | Args: 105 | images: array with minibatch of images 106 | filenames: list of filenames without path 107 | If number of file names in this list less than number of images in 108 | the minibatch then only first len(filenames) images will be saved. 109 | output_dir: directory where to save images 110 | """ 111 | for i, filename in enumerate(filenames): 112 | # Images for inception classifier are normalized to be in [-1, 1] interval, 113 | # so rescale them back to [0, 1]. 114 | with tf.gfile.Open(os.path.join(output_dir, filename), 'w') as f: 115 | imsave(f, (images[i, :, :, :] + 1.0) * 0.5 * 255, format='png') 116 | 117 | 118 | def graph(x, y, i, x_max, x_min, grad): 119 | eps = 2.0 * FLAGS.max_epsilon / 255.0 120 | eps_iter = 2.0 / 255.0 121 | num_classes = 1001 122 | momentum = FLAGS.momentum 123 | 124 | with slim.arg_scope(inception.inception_v3_arg_scope()): 125 | logits, end_points = inception.inception_v3( 126 | input_diversity(x), num_classes=num_classes, is_training=False) 127 | pred = tf.argmax(end_points['Predictions'], 1) 128 | 129 | # here is the way to stable gt lables 130 | first_round = tf.cast(tf.equal(i, 0), tf.int64) 131 | y = first_round * pred + (1 - first_round) * y 132 | 133 | one_hot = tf.one_hot(y, num_classes) 134 | cross_entropy = tf.losses.softmax_cross_entropy(one_hot, logits) 135 | 136 | # compute the gradient info 137 | noise = tf.gradients(cross_entropy, x)[0] 138 | noise = noise / tf.reduce_mean(tf.abs(noise), [1,2,3], keep_dims=True) 139 | # accumulate the gradient 140 | noise = momentum * grad + noise 141 | 142 | x = x + eps_iter * tf.sign(noise) 143 | x = tf.clip_by_value(x, x_min, x_max) 144 | i = tf.add(i, 1) 145 | return x, y, i, x_max, x_min, noise 146 | 147 | 148 | def stop(x, y, i, x_max, x_min, grad): 149 | num_iter = int(min(FLAGS.max_epsilon+4, 1.25*FLAGS.max_epsilon)) 150 | return tf.less(i, num_iter) 151 | 152 | 153 | def input_diversity(input_tensor): 154 | rnd = tf.random_uniform((), FLAGS.image_width, FLAGS.image_resize, dtype=tf.int32) 155 | rescaled = tf.image.resize_images(input_tensor, [rnd, rnd], method=tf.image.ResizeMethod.NEAREST_NEIGHBOR) 156 | h_rem = FLAGS.image_resize - rnd 157 | w_rem = FLAGS.image_resize - rnd 158 | pad_top = tf.random_uniform((), 0, h_rem, dtype=tf.int32) 159 | pad_bottom = h_rem - pad_top 160 | pad_left = tf.random_uniform((), 0, w_rem, dtype=tf.int32) 161 | pad_right = w_rem - pad_left 162 | padded = tf.pad(rescaled, [[0, 0], [pad_top, pad_bottom], [pad_left, pad_right], [0, 0]], constant_values=0.) 163 | padded.set_shape((input_tensor.shape[0], FLAGS.image_resize, FLAGS.image_resize, 3)) 164 | return tf.cond(tf.random_uniform(shape=[1])[0] < tf.constant(FLAGS.prob), lambda: padded, lambda: input_tensor) 165 | 166 | 167 | def main(_): 168 | eps = 2.0 * FLAGS.max_epsilon / 255.0 169 | batch_shape = [FLAGS.batch_size, FLAGS.image_height, FLAGS.image_width, 3] 170 | 171 | with tf.Graph().as_default(): 172 | # Prepare graph 173 | x_input = tf.placeholder(tf.float32, shape=batch_shape) 174 | x_max = tf.clip_by_value(x_input + eps, -1.0, 1.0) 175 | x_min = tf.clip_by_value(x_input - eps, -1.0, 1.0) 176 | 177 | y = tf.constant(np.zeros([FLAGS.batch_size]), tf.int64) 178 | i = tf.constant(0) 179 | grad = tf.zeros(shape=batch_shape) 180 | x_adv, _, _, _, _, _ = tf.while_loop(stop, graph, [x_input, y, i, x_max, x_min, grad]) 181 | # Run computation 182 | saver = tf.train.Saver() 183 | with tf.Session() as sess: 184 | saver.restore(sess, FLAGS.checkpoint_path) 185 | for filenames, images in load_images(FLAGS.input_dir, FLAGS.output_dir, batch_shape): 186 | adv_images = sess.run(x_adv, feed_dict={x_input: images}) 187 | save_images(adv_images, filenames, FLAGS.output_dir) 188 | 189 | 190 | if __name__ == '__main__': 191 | tf.app.run() 192 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cihangxie/DI-2-FGSM/10ffd9b9e94585b6a3b9d6858a9a929dc488fc02/demo.png -------------------------------------------------------------------------------- /relationship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cihangxie/DI-2-FGSM/10ffd9b9e94585b6a3b9d6858a9a929dc488fc02/relationship.png --------------------------------------------------------------------------------