├── model_input_size_info.json ├── FGSM_attack.py ├── README.md ├── utils_for_test.py ├── Iter_FGSM_attack.py ├── test_FGSM.py ├── test_IterFGSM.py ├── test_CW_EADL1.py ├── l1_attack.py ├── setup_imagenet.py ├── test_transferability.py └── l2_attack.py /model_input_size_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "resnet_v2_50": 299, 3 | "resnet_v2_101": 299, 4 | "resnet_v2_152": 299, 5 | "inception_v1": 224, 6 | "inception_v2": 224, 7 | "inception_v3": 299, 8 | "inception_v4": 299, 9 | "inception_resnet_v2": 299, 10 | "vgg_16": 224, 11 | "vgg_19": 224, 12 | "mobilenet_v1_025": 224, 13 | "mobilenet_v1_050": 224, 14 | "mobilenet_v1_100": 224, 15 | "nasnet_large": 331, 16 | "alexnet": 227, 17 | "densenet121_k32": 224, 18 | "densenet169_k32": 224, 19 | "densenet161_k48": 224 20 | } -------------------------------------------------------------------------------- /FGSM_attack.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import tensorflow as tf 3 | import numpy as np 4 | import time 5 | from six.moves import xrange 6 | #from inspect import signature 7 | 8 | class FGSM: 9 | def __init__(self, sess, model, eps, use_log=True, targeted=True, batch_size=1, ord=np.inf, clip_min=-0.5, clip_max=0.5): 10 | """ 11 | The implementation of Ian Goodfellow's FGSM attack. 12 | Returns adversarial examples for the supplied model. 13 | targeted: True if we should perform a targetted attack, False otherwise. 14 | default is targeted. 15 | """ 16 | 17 | image_size, num_channels, num_labels = model.image_size, model.num_channels, model.num_labels 18 | self.sess = sess 19 | self.model = model 20 | self.eps = eps 21 | self.batch_size = batch_size 22 | 23 | self.clip_min = clip_min 24 | self.clip_max = clip_max 25 | 26 | shape = (batch_size,image_size,image_size,num_channels) 27 | 28 | self.x = tf.Variable(np.zeros(shape), dtype=tf.float32) 29 | self.y = tf.Variable(np.zeros((batch_size,num_labels)), dtype=tf.float32) 30 | 31 | self.logits = self.model.predict(self.x) 32 | 33 | 34 | self.y = self.y / tf.reduce_sum(self.y, 1, keep_dims=True) 35 | #Generate the gradient of the loss function. 36 | #decide whether use logits or softmax 37 | 38 | # if use_log: 39 | # op = self.logits.op 40 | # if "softmax" in str(op).lower(): 41 | # self.logits, = op.inputs 42 | 43 | # self.adv_loss = tf.nn.softmax_cross_entropy_with_logits(logits=self.logits, labels=self.y) 44 | if use_log: 45 | self.adv_loss = tf.nn.softmax_cross_entropy_with_logits(logits=self.logits, labels=self.y) 46 | else: 47 | self.adv_loss = -tf.reduce_sum(self.logits * self.y, axis = 1) 48 | 49 | if targeted: 50 | self.adv_loss = -self.adv_loss 51 | 52 | self.grad, = tf.gradients(self.adv_loss, self.x) 53 | 54 | # signed gradient 55 | if ord == np.inf: 56 | # Take sign of gradient 57 | self.normalized_grad = tf.sign(self.grad) 58 | self.normalized_grad = tf.stop_gradient(self.normalized_grad) 59 | elif ord == 1: 60 | red_ind = list(xrange(1, len(self.x.get_shape()))) 61 | self.normalized_grad = self.grad / tf.reduce_sum(tf.abs(self.grad), 62 | reduction_indices=red_ind, 63 | keep_dims=True) 64 | elif ord == 2: 65 | red_ind = list(xrange(1, len(self.x.get_shape()))) 66 | square = tf.reduce_sum(tf.square(self.grad), 67 | reduction_indices=red_ind, 68 | keep_dims=True) 69 | self.normalized_grad = self.grad / tf.sqrt(square) 70 | else: 71 | raise NotImplementedError("Only L-inf, L1 and L2 norms are " 72 | "currently implemented.") 73 | 74 | # Multiply by constant epsilon 75 | self.scaled_grad = self.eps * self.normalized_grad 76 | 77 | # Add perturbation to original example to obtain adversarial example 78 | self.adv_x = self.x + self.scaled_grad 79 | 80 | self.adv_x = tf.clip_by_value(self.adv_x, self.clip_min, self.clip_max) 81 | 82 | return 83 | 84 | def attack(self, imgs, targets): 85 | """ 86 | Perform the one-shot FGSM attack on the given images for the given targets. 87 | If self.targeted is true, then the targets represents the target labels. 88 | If self.targeted is false, then targets are the original class labels. 89 | """ 90 | 91 | print("Perofming FGSM attack") 92 | 93 | adv_x_concrete = self.sess.run(self.adv_x, feed_dict={self.x: imgs, 94 | self.y: targets }) 95 | print("Done on the FGSM attack") 96 | 97 | return adv_x_concrete 98 | 99 | """ 100 | A dummy wrapper for the FGSM attack. Just for invoked by test_FGSM.py (based on test_all.py) 101 | """ 102 | def attack_batch(self, imgs, targets): 103 | 104 | adv_x_concrete = self.sess.run(self.adv_x, feed_dict={self.x: imgs, self.y: targets}) 105 | 106 | return adv_x_concrete 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Is Robustness the Cost of Accuracy? – A Comprehensive Study on the Robustness of 18 Deep Image Classification Models 2 | ===================================== 3 | 4 | The prediction accuracy has been the long-lasting and sole standard for comparing the performance of different image classification models, including the ImageNet competition. However, recent studies have highlighted the lack of robustness in well-trained deep neural networks to adversarial examples. Visually imperceptible perturbations to natural images can easily be crafted and mislead the image classifiers towards misclassification. To demystify the trade-offs between robustness and accuracy, in this paper we thoroughly benchmark 18 ImageNet models using multiple robustness metrics, including the distortion, success rate and transferability of adversarial examples between 306 pairs of models. Our extensive experimental results reveal several new insights: (1) linear scaling law - the empirical $\ell_2$ and $\ell_\infty$ distortion metrics scale linearly with the logarithm of classification error; (2) model architecture is a more critical factor to robustness than model size, and the disclosed accuracy-robustness Pareto frontier can be used as an evaluation criterion for ImageNet model designers; (3) for a similar network architecture, increasing network depth slightly improves robustness in $\ell_\infty$ distortion; (4) there exist models (in VGG family) that exhibit high adversarial transferability, while most adversarial examples crafted from one model can only be transferred within the same family. 5 | 6 | For more details, please see our paper: 7 | 8 | Is Robustness the Cost of Accuracy? – A Comprehensive Study on the Robustness of 18 Deep Image Classification Models. 9 | (https://arxiv.org/abs/1808.01688) by Dong Su\*, Huan Zhang\*, Hongge Chen, Jinfeng Yi, Pin-Yu Chen, Yupeng Gao. European Conference on Computer Vision (ECCV), 2018. 10 | 11 | \* Equal contribution 12 | 13 | 14 | Experiment Setup 15 | ------------------------------------- 16 | 17 | The code is tested with python 3.6.5 and TensorFlow v1.8. We suggest to use Conda to manage your Python environments. The following Conda packages are required: 18 | 19 | ``` 20 | conda install pillow numpy scipy pandas tensorflow-gpu h5py 21 | grep 'AMD' /proc/cpuinfo >/dev/null && conda install nomkl 22 | ``` 23 | Note: the second command is only needed in the linux environment. 24 | 25 | 26 | Then clone this repository: 27 | ``` 28 | git clone git@github.com:huanzhang12/Adversarial_Survey.git 29 | cd Adversarial_Survey 30 | ``` 31 | 32 | To prepare the ImageNet dataset, download and unzip the following archive: 33 | 34 | http://download.huan-zhang.com/datasets/adv/img.tar.gz 35 | 36 | create the `./imagenetdata` directory, and put the `imgs` folder under the `./imagenetdata` directory, relative to the Adversarial_Survey repository. This path can be changed in `setup_imagenet.py`. 37 | 38 | To prepare the ImageNet models: 39 | Create the `./tmp/` directory. In the Adversarial_Survey directory, run 40 | ``` 41 | python setup_imagenet.py 42 | ``` 43 | All pretrained model will be saved to `./tmp/imagenet` directory. 44 | 45 | 46 | Run the experiment 47 | -------------------------------------- 48 | The following are some examples of attacks: 49 | 50 | To run the FGSM untargeted attack on the densenet169_k32 model with epsilon=0.3, 51 | ``` 52 | python test_FGSM.py --dataset=imagenet --attack=FGSM --num_valid_test_imgs=10 --attack_batch_size=1 --model_name=densenet169_k32 --numimg=0 --firstimg=0 --save=./saved_results/FGSM/epsilon_0.3_imagenet_FGSM_targeted_densenet169_k32 --epsilon=0.3 --target_type=7 --use_zvalue --seed=1215 --untargeted 53 | ``` 54 | 55 | To run the IFGSM targeted attack on the densenet169_k32 model with optimal attack budget, 56 | ``` 57 | python test_IterFGSM.py --dataset=imagenet --attack=IterFGSM --num_valid_test_imgs=10 --attack_batch_size=1 --model_name=densenet169_k32 --numimg=0 --firstimg=0 --save=./saved_results/IterFGSM/epsilon_0.02_iterations_50_imagenet_IterFGSM_targeted_densenet169_k32 --initial_eps=0.02 --max_attempts=9 --iter_num=50 --target_type=7 --use_zvalue --seed=1215 58 | ``` 59 | 60 | One can also run IFGSM targeted attack with fixed attack budget , 61 | ``` 62 | python test_IterFGSM.py --dataset=imagenet --attack=IterFGSM --num_valid_test_imgs=10 --attack_batch_size=1 --model_name=densenet169_k32 --numimg=0 --firstimg=0 --save=./saved_results/IterFGSM/epsilon_0.2_iterations_50_imagenet_IterFGSM_targeted_densenet169_k32 --initial_eps=0.2 --max_attempts=1 --iter_num=50 --target_type=7 --use_zvalue --seed=1215 63 | ``` 64 | 65 | To run the CW targeted attack on the densenet169_k32 model 66 | ``` 67 | python test_CW_EADL1.py --dataset=imagenet --attack=CW --numimg=0 --firstimg=0 --num_valid_test_imgs=10 --save=./saved_results/CW/kappa_0_imagenet_CW_targeted_densenet161_k48 --maxiter=1000 --lr=0.001 --binary_steps=9 --init_const=0.01 --use_zvalue --target_type=7 --kappa=0 --model_name=densenet169_k32 --attack_batch_size=1 --seed=1215 68 | ``` 69 | 70 | To run the EADL1 targeted attack on the densenet169_k32 model 71 | ``` 72 | python test_CW_EADL1.py --dataset=imagenet --attack=EADL1 --numimg=0 --firstimg=0 --num_valid_test_imgs=10 --save=./saved_results/EADL1/kappa_0_imagenet_EADL1_targeted_densenet161_k48 --maxiter=1000 --lr=0.001 --binary_steps=9 --init_const=0.01 --use_zvalue --target_type=7 --kappa=0 --model_name=densenet169_k32 --attack_batch_size=1 --seed=1215 73 | ``` 74 | 75 | To run the untargeted transferability attack to the given target model, e.g. densenet161_k48, from the generated untargeted adversarial examples from the model densenet169_k32 76 | ``` 77 | python test_transferability.py --dataset=imagenet --attack=FGSM --epsilon=0.3 --src_adv_sample_path=./saved_results/FGSM/densenet169_k32_eps_0.3_untargeted/imagenet/FGSM/targeted_False --save=./saved_results/transferability/FGSM/epsilon_0.3_imagenet_FGSM_untargeted_densenet169_k32_densenet161_k48/ --use_zvalue --src_model_name=densenet169_k32 --target_model_name=densenet161_k48 --untargeted 78 | ``` 79 | 80 | Several global parameters: 81 | 82 | `--model_name`: it can be one from the supported 18 ImageNet model list: 83 | ``` 84 | ['resnet_v2_50','resnet_v2_101','resnet_v2_152','inception_v1','inception_v2','inception_v3','inception_v4', 'inception_resnet_v2','vgg_16','vgg_19','mobilenet_v1_025','mobilenet_v1_050','mobilenet_v1_100', 'densenet121_k32', 'densenet169_k32', 'densenet161_k48', 'nasnet_large', 'alexnet'] 85 | ``` 86 | 87 | `--untargeted`: For running untargeted attacks. For running targeted attacks, do not add this option in the command. 88 | 89 | `--target_type`: For running targeted attacks, you can specify the target type: least likely target (type: `0b0100`), top2 likely (type: `0b0001`) or random target (type: `0b0010`). In the above examples, we run targeted attack over all of these three target types and use `0b0111` which is 7 as the `target_type`. 90 | 91 | `--seed`: For setting the random seed. 92 | 93 | `--save`: For setting the location for storing the evaluation results 94 | 95 | `--firstimg`: For setting the starting image id in the pool. The default value is 0. 96 | 97 | `--numimg`: For setting the number of images to be examed in the attack. The default value of it is 0 which means using all loaded images. 98 | 99 | `--num_valid_test_imgs`: the number of valid images (correctly classified by the target model) to be used in the attack. 100 | 101 | `--attack_batch_size`: For setting the number of images to attack in a batch way. 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /utils_for_test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from PIL import Image 3 | import random 4 | 5 | 6 | 7 | def linf_loss(x, y): 8 | # return np.max(np.abs(x-y)) 9 | return np.linalg.norm(x.flatten() - y.flatten(), ord=np.inf) 10 | 11 | def l2_loss(x, y): 12 | # return (np.sum((x - y) ** 2) ** .5) 13 | return np.linalg.norm(x.flatten() - y.flatten(), ord=2) 14 | 15 | def l1_loss(x, y): 16 | # return np.sum(np.abs(x-y)) 17 | return np.linalg.norm(x.flatten() - y.flatten(), ord=1) 18 | 19 | def l0_loss(x, y): 20 | # return np.sum(np.abs(x - y) >= 1e-10) 21 | return np.linalg.norm(x.flatten() - y.flatten(), ord=0) 22 | 23 | def show(img, name = "output.png"): 24 | np.save('img', img) 25 | fig = (img + 0.5)*255 26 | fig = fig.astype(np.uint8).squeeze() 27 | pic = Image.fromarray(fig) 28 | pic.save(name) 29 | remap = " .*#"+"#"*100 30 | img = (img.flatten()+.5)*3 31 | if len(img) != 784: return 32 | print("START") 33 | for i in range(28): 34 | print("".join([remap[int(round(x))] for x in img[i*28:i*28+28]])) 35 | return 36 | 37 | 38 | def dump(img, path, save_png=False): 39 | #save as npy 40 | np.save(path + ".npy", img) 41 | 42 | if save_png == True: 43 | #save as png file 44 | fig = (img + 0.5)*255 45 | fig = fig.astype(np.uint8).squeeze() 46 | pic = Image.fromarray(fig) 47 | pic.save(path + ".png") 48 | 49 | return 50 | 51 | 52 | def generate_data(data, samples, targeted=True, random_and_least_likely = False, skip_wrong_label = True, start=0, ids = None, 53 | target_classes = None, target_type=0b1111, predictor = None, imagenet=False, remove_background_class=False, 54 | total_num_valid_samples=1, num_random_targets=1): 55 | 56 | inputs = [] 57 | targets = [] 58 | true_labels = [] 59 | true_ids = [] 60 | information = [] 61 | target_candidate_pool = np.eye(data.test_labels.shape[1]) 62 | target_candidate_pool_remove_background_class = np.eye(data.test_labels.shape[1] - 1) 63 | print('generating labels...') 64 | 65 | print('target_type = ', target_type) 66 | 67 | if ids is None: 68 | ids = range(samples) 69 | else: 70 | ids = ids[start:start+samples] 71 | if target_classes: 72 | target_classes = target_classes[start:start+samples] 73 | start = 0 74 | total = 0 75 | num_valid_samples = 0 76 | for i in ids: 77 | total += 1 78 | 79 | if num_valid_samples >= total_num_valid_samples: 80 | print("reaching the total_num_valid_samples, ", total_num_valid_samples) 81 | break 82 | 83 | if remove_background_class == True: 84 | true_label = np.argmax(data.test_labels[start + i][1:]) 85 | else: 86 | true_label = np.argmax(data.test_labels[start + i]) 87 | 88 | print("true_label = ", true_label) 89 | 90 | if targeted: 91 | predicted_label = -1 # unknown 92 | 93 | if random_and_least_likely: 94 | # if there is no user specified target classes 95 | if target_classes is None: 96 | original_predict = np.squeeze(predictor(np.array([data.test_data[start+i]]))) 97 | 98 | print("original_predict.shape = ", original_predict.shape) 99 | 100 | num_classes = len(original_predict) 101 | predicted_label = np.argmax(original_predict) 102 | least_likely_label = np.argmin(original_predict) 103 | top2_label = np.argsort(original_predict)[-2] 104 | start_class = 1 if (imagenet and not remove_background_class) else 0 105 | 106 | new_seq = [least_likely_label, top2_label, predicted_label] 107 | 108 | if imagenet: 109 | if remove_background_class: 110 | sample_pool = [x for x in range(0, 1000) if x != true_label] 111 | else: 112 | sample_pool = [x for x in range(1, 1001) if x != true_label] 113 | else: 114 | sample_pool = [x for x in range(data.test_labels.shape[1]) if x != true_label] 115 | 116 | random_seq = random.sample(sample_pool, num_random_targets) 117 | new_seq[2] = random_seq[0] 118 | 119 | seq = [] 120 | if true_label != predicted_label and skip_wrong_label: 121 | seq = [] 122 | else: 123 | num_valid_samples += 1 124 | 125 | if target_type & 0b0100: 126 | # least 127 | seq.append(new_seq[0]) 128 | information.append('least') 129 | if target_type & 0b0001: 130 | # top-2 131 | seq.append(new_seq[1]) 132 | information.append('top2') 133 | if target_type & 0b0010: 134 | # random 135 | seq.append(new_seq[2]) 136 | information.append('random') 137 | else: 138 | # use user specified target classes 139 | seq = target_classes[total - 1] 140 | information.extend(len(seq) * ['user']) 141 | 142 | 143 | else: 144 | if imagenet: 145 | if remove_background_class: 146 | seq = random.sample(range(0,1000), 10) 147 | else: 148 | seq = random.sample(range(1,1001), 10) 149 | information.extend(data.test_labels.shape[1] * ['random']) 150 | else: 151 | seq = range(data.test_labels.shape[1]) 152 | information.extend(data.test_labels.shape[1] * ['seq']) 153 | 154 | print("[DATAGEN][L1] no = {}, true_id = {}, true_label = {}, predicted = {}, correct = {}, seq = {}, info = {}".format(total, start + i, 155 | true_label, predicted_label, true_label == predicted_label, seq, [] if len(seq) == 0 else information[-len(seq):])) 156 | 157 | 158 | for j in seq: 159 | if(j == true_label): 160 | print("=======skip the original image label========") 161 | continue 162 | inputs.append(data.test_data[start+i]) 163 | if remove_background_class: 164 | targets.append(target_candidate_pool_remove_background_class[j]) 165 | else: 166 | targets.append(target_candidate_pool[j]) 167 | true_labels.append(data.test_labels[start+i]) 168 | if remove_background_class: 169 | true_labels[-1] = true_labels[-1][1:] 170 | true_ids.append(start+i) 171 | else: 172 | 173 | original_predict = np.squeeze(predictor(np.array([data.test_data[start + i]]))) 174 | predicted_label = np.argmax(original_predict) 175 | 176 | print("i = %d, true_label = %d, predicted_label = %d" % (i, true_label, predicted_label)) 177 | 178 | if true_label != predicted_label and skip_wrong_label: 179 | print("untargeted setting: skipping wrongly classified samples") 180 | continue 181 | else: 182 | num_valid_samples += 1 183 | 184 | inputs.append(data.test_data[start+i]) 185 | if remove_background_class: 186 | # shift target class by 1 187 | print(np.argmax(data.test_labels[start+i])) 188 | print(np.argmax(data.test_labels[start+i][1:1001])) 189 | targets.append(data.test_labels[start+i][1:1001]) 190 | else: 191 | targets.append(data.test_labels[start+i]) 192 | true_labels.append(data.test_labels[start+i]) 193 | if remove_background_class: 194 | true_labels[-1] = true_labels[-1][1:] 195 | true_ids.append(start+i) 196 | information.extend(['original']) 197 | 198 | inputs = np.array(inputs) 199 | targets = np.array(targets) 200 | true_labels = np.array(true_labels) 201 | true_ids = np.array(true_ids) 202 | 203 | 204 | print("total = ", total) 205 | print("len(inputs) = ", len(inputs)) 206 | print("num_valid_samples = ", num_valid_samples) 207 | 208 | print('labels generated') 209 | 210 | return inputs, targets, true_labels, true_ids, information 211 | 212 | -------------------------------------------------------------------------------- /Iter_FGSM_attack.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import tensorflow as tf 3 | import numpy as np 4 | from six.moves import xrange 5 | import time 6 | 7 | class Iter_FGSM: 8 | def __init__(self, sess, model): 9 | image_size, num_channels, num_labels = model.image_size, model.num_channels, model.num_labels 10 | self.sess = sess 11 | self.model = model 12 | self.x = tf.placeholder(shape=[None, image_size, image_size, num_channels], dtype=tf.float32) 13 | self.logits = self.model.predict(self.x) 14 | self.predicts = self.logits 15 | 16 | def init_attack(self, sess, model, iter_num, use_log=True, targeted=True, batch_size=1, ord=np.inf, clip_min=-0.5, clip_max=0.5): 17 | 18 | """ 19 | The implementation of Ian Goodfellow's Iterative-FGSM attack. 20 | Returns adversarial examples for the supplied model. 21 | targeted: True if we should perform a targetted attack, False otherwise. 22 | default is targeted. 23 | """ 24 | 25 | image_size, num_channels, num_labels = model.image_size, model.num_channels, model.num_labels 26 | self.sess = sess 27 | self.model = model 28 | self.batch_size = batch_size 29 | # self.eps = eps 30 | # self.eps_iter = eps_iter 31 | self.iter_num = iter_num 32 | self.ord = ord 33 | self.clip_min = clip_min 34 | self.clip_max = clip_max 35 | 36 | shape = (batch_size,image_size,image_size,num_channels) 37 | 38 | self.eps = tf.Variable(np.zeros(batch_size), dtype=tf.float32, name='eps_var') 39 | self.eps_iter = tf.Variable(np.zeros(batch_size), dtype=tf.float32,name='eps_iter_var') 40 | self.assign_eps = tf.placeholder(tf.float32,[batch_size],name='feed_eps') 41 | self.assign_eps_iter = tf.placeholder(tf.float32,[batch_size],name='feed_eps_iter') 42 | self.setup = [] 43 | self.assign_eps_op = self.eps.assign(self.assign_eps) 44 | self.setup.append(self.assign_eps_op) 45 | self.assign_eps_iter_op = self.eps_iter.assign(self.assign_eps_iter) 46 | self.setup.append(self.assign_eps_iter_op) 47 | 48 | self.imgs = tf.Variable(np.zeros(shape), dtype=tf.float32) 49 | # self.x = tf.Variable(np.zeros(shape), dtype=tf.float32) 50 | # self.x = tf.placeholder(shape=[None, image_size, image_size, num_channels], dtype=tf.float32) 51 | self.y = tf.Variable(np.zeros((batch_size,num_labels)), dtype=tf.float32) 52 | # self.logits = self.model.predict(self.x) 53 | 54 | #Generate the gradient of the loss function. 55 | #preds_max = tf.reduce_max(self.logits, 1, keep_dims=True) 56 | #self.y = tf.to_float(tf.equal(self.logits, preds_max)) 57 | self.y = self.y / tf.reduce_sum(self.y, 1, keep_dims=True) 58 | #Generate the gradient of the loss function. 59 | #decide whether use logits or softmax 60 | # self.predicts = self.logits 61 | 62 | # if use_log: 63 | # op = self.logits.op 64 | # if "softmax" in str(op).lower(): 65 | # self.logits, = op.inputs 66 | 67 | if use_log: 68 | self.adv_loss = tf.nn.softmax_cross_entropy_with_logits(logits=self.logits, labels=self.y) 69 | else: 70 | self.adv_loss = -tf.reduce_sum(self.logits * self.y, axis = 1) 71 | 72 | if targeted: 73 | self.adv_loss = -self.adv_loss 74 | 75 | self.grad, = tf.gradients(self.adv_loss, self.x) 76 | # signed gradient 77 | if self.ord == np.inf: 78 | # Take sign of gradient 79 | self.normalized_grad = tf.sign(self.grad) 80 | self.normalized_grad = tf.stop_gradient(self.normalized_grad) 81 | elif self.ord == 1: 82 | red_ind = list(xrange(1, len(self.x.get_shape()))) 83 | self.normalized_grad = self.grad / tf.reduce_sum(tf.abs(self.grad), 84 | reduction_indices=red_ind, 85 | keep_dims=True) 86 | elif self.ord == 2: 87 | red_ind = list(xrange(1, len(self.x.get_shape()))) 88 | square = tf.reduce_sum(tf.square(self.grad), 89 | reduction_indices=red_ind, 90 | keep_dims=True) 91 | self.normalized_grad = self.grad / tf.sqrt(square) 92 | else: 93 | raise NotImplementedError("Only L-inf, L1 and L2 norms are " 94 | "currently implemented.") 95 | 96 | # Multiply by constant epsilon 97 | 98 | #self.eps_iter = tf.cast(self.eps_iter, tf.float32) 99 | self.EPS_ITER = tf.reshape(tf.tile(tf.reshape(self.eps_iter,[-1,1]),[1, image_size*image_size*num_channels]),shape) 100 | # self.eps_iter = tf.reshape(self.eps_iter,[-1,1]) 101 | # self.eps_iter = tf.tile(self.eps_iter, [1, image_size*image_size*num_channels]) 102 | # self.eps_iter = tf.reshape(self.eps_iter,[batch_size,image_size,image_size,num_channels]) 103 | 104 | 105 | #self.eps = tf.cast(self.eps, tf.float32) 106 | self.EPS = tf.reshape(tf.tile(tf.reshape(self.eps,[-1,1]),[1, image_size*image_size*num_channels]),shape) 107 | # self.eps = tf.reshape(self.eps,[-1,1]) 108 | # self.eps = tf.tile(self.eps, [1, image_size*image_size*num_channels]) 109 | # self.eps = tf.reshape(self.eps,[batch_size,image_size,image_size,num_channels]) 110 | self.scaled_grad = tf.multiply(self.EPS_ITER,self.normalized_grad) 111 | print(self.scaled_grad.get_shape()) 112 | self.adv_x = self.x + self.scaled_grad 113 | 114 | self.adv_x = tf.clip_by_value(self.adv_x, self.clip_min, self.clip_max) 115 | self.eta = self.adv_x - self.imgs 116 | 117 | #self.eta = tf.clip_by_value(self.eta, -self.eps, self.eps) 118 | 119 | # Clipping perturbation eta to self.ord norm ball 120 | if self.ord == np.inf: 121 | self.eta = tf.clip_by_value(self.eta, -self.EPS, self.EPS) 122 | elif self.ord in [1, 2]: 123 | reduc_ind = list(xrange(1, len(self.eta.get_shape()))) 124 | if self.ord == 1: 125 | norm = tf.reduce_sum(tf.abs(self.eta), reduction_indices=reduc_ind, keep_dims=True) 126 | elif self.ord == 2: 127 | norm = tf.sqrt(tf.reduce_sum(tf.square(self.eta), reduction_indices=reduc_ind, keep_dims=True)) 128 | self.eta = tf.multiply(self.eta, self.EPS) / norm 129 | return 130 | 131 | def predict(self, imgs): 132 | # imgs = np.array(imgs, dtype = 'f') 133 | # self.sess.run(self.setup, {self.assign_eps:np.zeros(self.batch_size), self.assign_eps_iter:np.zeros(self.batch_size)} ) 134 | predicts = self.sess.run([self.predicts], feed_dict = {self.x : imgs}) 135 | return predicts 136 | 137 | def one_attack(self, imgs, targets, eps, eps_iter, verbose = False): 138 | #eta = np.dtype('Float64') 139 | #adv_x = np.dtype('Float64') 140 | eta = 0 141 | imgs = np.array(imgs, dtype = 'f') 142 | self.sess.run(self.setup, {self.assign_eps:eps, self.assign_eps_iter:eps_iter} ) 143 | for i in range(self.iter_num): 144 | #eta = self.sess.run(self.adv_x, feed_dict = {self.x : imgs + eta, self.y : targets}) - imgs 145 | eta, loss, predicts = self.sess.run([self.eta, self.adv_loss, self.predicts], feed_dict = {self.imgs : imgs, self.x : imgs + eta, self.y : targets}) 146 | if verbose: 147 | print('iteration:', i, 'loss is: ', loss[:10]) 148 | for idx, predict in enumerate(predicts[:10]): 149 | print("Classification {}: {}".format(idx, np.argsort(predict)[-1:-11:-1])) 150 | print("Probabilities/Logits {}: {}".format(idx, np.sort(predict)[-1:-11:-1])) 151 | print() 152 | adv_x = eta + imgs 153 | adv_x = np.clip(adv_x, self.clip_min, self.clip_max) 154 | 155 | 156 | return adv_x 157 | 158 | 159 | def attack(self, inputs, targets, targeted, initial_eps = 0.3, max_attempts = 10, verbose = False): 160 | batch_size = len(inputs) 161 | success_log = [[] for _ in range(batch_size)] 162 | eps_val = [[initial_eps] for _ in range(batch_size)] 163 | best_adv = np.full(inputs.shape,np.nan) 164 | for try_index in range(max_attempts): 165 | eps = np.array([item[try_index] for item in eps_val]) 166 | print("***********eps:***********:",eps) 167 | eps_iter = eps / self.iter_num 168 | adv = self.one_attack(inputs, targets, eps=eps, eps_iter=eps_iter) 169 | for i in range(len(adv)): 170 | print('----------------------------------------') 171 | # show(inputs[i], "original_{}.png".format(i)) 172 | original_predict = np.squeeze(self.predict(inputs[i:i+1])) 173 | print("Original Classification:", np.argsort(original_predict)[-1:-11:-1]) 174 | print("Original Probabilities/Logits:", np.sort(original_predict)[-1:-11:-1]) 175 | print("Original Classification minimum:", np.argsort(original_predict)[0:10]) 176 | print("Original Probabilities/Logits minimum:", np.sort(original_predict)[0:10]) 177 | 178 | target_label = np.argmax(targets[i]) 179 | attack_label = None 180 | success = False 181 | print("Target:", target_label) 182 | # if the array contains NaN, the solver did not return a solution 183 | if (np.any(np.isnan(adv[i:i+1]))): 184 | print('Attack failed. (solver returned NaN)') 185 | l0 = l1 = l2 = linf = np.nan 186 | continue 187 | else: 188 | # print("Adversarial:") 189 | # show(adv[i], "adversarial_{}.png".format(i)) 190 | # print("Noise:") 191 | # show(adv[i] - inputs[i], "attack_diff.png") 192 | 193 | adv_predict = np.squeeze(self.predict(adv[i:i+1])) 194 | print("Adversarial Classification:", np.argsort(adv_predict)[-1:-11:-1]) 195 | print("Adversarial Probabilities/Logits:", np.sort(adv_predict)[-1:-11:-1]) 196 | print("Adversarial Classification minimum:", np.argsort(adv_predict)[0:10]) 197 | print("Adversarial Probabilities/Logits minimum:", np.sort(adv_predict)[0:10]) 198 | attack_label = np.argmax(adv_predict) 199 | 200 | if targeted: 201 | success = np.argsort(adv_predict)[-1] == target_label 202 | else: 203 | success = np.argsort(adv_predict)[-1] != target_label 204 | success_log[i] += [success] 205 | if success: 206 | best_adv[i] = adv[i] 207 | print("Attack succeeded.") 208 | if try_index + 1 < max_attempts: 209 | if any(not _ for _ in success_log[i]): 210 | last_false = len(success_log[i]) - success_log[i][::-1].index(False) - 1 211 | eps_val[i] += [0.5 * (eps_val[i][try_index] + eps_val[i][last_false])] 212 | else: 213 | eps_val[i] += [eps_val[i][try_index] * 0.5] 214 | else: 215 | print("Attack failed.") 216 | if try_index + 1 < max_attempts: 217 | if any(_ for _ in success_log[i]): 218 | last_true = len(success_log[i]) - success_log[i][::-1].index(True) - 1 219 | eps_val[i] += [0.5 * (eps_val[i][try_index] + eps_val[i][last_true])] 220 | else: 221 | eps_val[i] += [eps_val[i][try_index] * 2.0] 222 | continue 223 | print("success log:", success_log) 224 | print("eps values:", eps_val) 225 | return best_adv, eps_val 226 | 227 | 228 | def attack_batch(self, imgs, targets, targeted): 229 | return self.attack(imgs, targets, targeted) 230 | 231 | 232 | -------------------------------------------------------------------------------- /test_FGSM.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import tensorflow as tf 4 | import numpy as np 5 | import random 6 | import time 7 | 8 | from setup_imagenet import ImageNet, ImageNetModel 9 | 10 | from FGSM_attack import FGSM 11 | 12 | from utils_for_test import linf_loss, l0_loss, l1_loss, l2_loss, dump, generate_data 13 | 14 | def main(args): 15 | 16 | with tf.Session() as sess: 17 | 18 | random.seed(args["seed"]) 19 | np.random.seed(args["seed"]) 20 | tf.set_random_seed(args["seed"]) 21 | 22 | print("seed = ", args["seed"]) 23 | 24 | overall_timestart = time.time() 25 | 26 | use_log = not args['use_zvalue'] 27 | 28 | print("use_log = ", use_log) 29 | 30 | data_map = { 31 | } 32 | 33 | model_map = { 34 | } 35 | 36 | if args['dataset'] == "imagenet": 37 | model_map[args['model_name']] = ImageNetModel(sess, use_log, args['model_name']) 38 | data_map['imagenet'] = ImageNet(model_map[args['model_name']].image_size, load_total_imgs=args['numimg_loaded']) 39 | 40 | print('Loading model', args['dataset']) 41 | data = data_map[args['dataset']] 42 | model = model_map[args['model_name']] 43 | 44 | if args['numimg'] == 0: 45 | args['numimg'] = len(data.test_labels) - args['firstimg'] 46 | print('Using', args['numimg'], 'test images') 47 | # load attack module 48 | 49 | print('args = ', args) 50 | 51 | targeted_flag = not args['untargeted'] 52 | 53 | print("targeted_flag = ", targeted_flag) 54 | 55 | random.seed(args['seed']) 56 | np.random.seed(args['seed']) 57 | tf.set_random_seed(args['seed']) 58 | 59 | print('Generate data') 60 | model_name = args['model_name'] 61 | 62 | if 'vgg' in model_name or 'densenet' in model_name or 'alexnet' in model_name: 63 | remove_background_class_flag = True 64 | else: 65 | remove_background_class_flag = False 66 | 67 | all_inputs, all_targets, all_labels, all_true_ids, img_info = generate_data(data, samples=args['numimg'], 68 | targeted=targeted_flag, random_and_least_likely = True, predictor=model.model.predict, 69 | start=args['firstimg'], imagenet=isinstance(data, ImageNet), 70 | remove_background_class=remove_background_class_flag, target_type=args['target_type'], 71 | total_num_valid_samples=args['num_valid_test_imgs']) 72 | 73 | print('len(all_inputs) = ', len(all_inputs)) 74 | 75 | attack_batch_size = args['attack_batch_size'] 76 | if attack_batch_size == 0: 77 | attack_batch_size = all_true_ids.size 78 | print("attack_batch_size = ", attack_batch_size) 79 | 80 | 81 | if args['ord'] == 'inf': 82 | ord_arg = np.inf 83 | else: 84 | ord_arg = int(args['ord']) 85 | 86 | if args['attack'] == "FGSM": 87 | attack = FGSM(sess, model, eps=args['epsilon'], targeted=targeted_flag, use_log=use_log, 88 | batch_size=attack_batch_size, ord=ord_arg, clip_min=args['clip_min'], 89 | clip_max=args['clip_max']) 90 | else: 91 | print("Invalid attack name, exit 1") 92 | return 93 | 94 | saved_path = "{}/{}/{}/targeted_{}".format(args['save'], args['dataset'], args['attack'], targeted_flag) 95 | if not os.path.exists(saved_path): 96 | os.system("mkdir -p " + saved_path) 97 | 98 | img_no = 0 99 | total_success = 0 100 | l0_list = [] 101 | l1_list = [] 102 | l2_list = [] 103 | linf_list = [] 104 | time_list = [] 105 | 106 | verbose_f = open(args['save'] + "/" + "_".join([args['dataset'], args['attack'], str(targeted_flag), 107 | str(args['epsilon']), "verbose.txt"]), "w") 108 | aggre_f = open(args['save'] + "/" + "_".join([args['dataset'], args['attack'], str(targeted_flag), 109 | str(args['epsilon']), "aggre.txt"]), "w") 110 | 111 | if targeted_flag == True: 112 | verbose_head_str = '\t'.join(['total', 'seq', 'id', 'time', 'success', 'prev_class', 'target', 113 | 'new_class', 'l0_distortion', 'l1_distortion', 'l2_distortion', 114 | 'linf_distortion']) 115 | else: 116 | verbose_head_str = '\t'.join(['total', 'seq', 'id', 'time', 'success', 'prev_class', 'new_class', 117 | 'l0_distortion', 'l1_distortion', 'l2_distortion', 'linf_distortion']) 118 | 119 | aggre_head_str = '\t'.join(['total_count', 'success_rate', 'l0_avg', 'l0_std', 'l1_avg', 'l1_std', 120 | 'l2_avg', 'l2_std', 'linf_avg', 'linf_std', 'time_avg', 'time_std']) 121 | 122 | verbose_f.write(verbose_head_str + '\n') 123 | aggre_f.write(aggre_head_str + '\n') 124 | 125 | print("all_true_ids.size = ", all_true_ids.size) 126 | sys.stdout.flush() 127 | 128 | random.seed(args['seed']) 129 | np.random.seed(args['seed']) 130 | tf.set_random_seed(args['seed']) 131 | 132 | for i in range(0, all_true_ids.size, attack_batch_size): 133 | 134 | if i + attack_batch_size > all_true_ids.size: 135 | actual_attack_batch_size = all_true_ids.size - i 136 | else: 137 | actual_attack_batch_size = attack_batch_size 138 | 139 | inputs = all_inputs[i:i+actual_attack_batch_size] 140 | targets = all_targets[i:i+actual_attack_batch_size] 141 | labels = all_labels[i:i+actual_attack_batch_size] 142 | 143 | timestart = time.time() 144 | 145 | """perform the attack""" 146 | print("perform the attack") 147 | adv = attack.attack(inputs, targets) 148 | 149 | timeend = time.time() 150 | 151 | time_used = timeend - timestart 152 | time_used_per_image = time_used / attack_batch_size 153 | 154 | for j in range(len(adv)): 155 | 156 | print("="*10, "i = ", i, "="*10, "j=", j, "="*10) 157 | 158 | original_predict = np.squeeze(model.model.predict(np.array([inputs[j]]))) 159 | original_prob = np.sort(original_predict) 160 | original_class = np.argsort(original_predict) 161 | print("Original Classification:", original_prob[-1:-11:-1]) 162 | print("Original Probabilities/Logits:", original_class[-1:-11:-1]) 163 | 164 | sys.stdout.flush() 165 | 166 | true_label = np.argmax(labels[j]) 167 | target_label = np.argmax(targets[j]) 168 | attack_label = None 169 | success = False 170 | 171 | img_no += 1 172 | 173 | print("Target:", target_label) 174 | # if the array contains NaN, the solver did not return a solution 175 | if (np.any(np.isnan(adv[j]))): 176 | print('Attack failed. (solver returned NaN)') 177 | l0_distortion = l1_distortion = l2_distortion = linf_distortion = np.nan 178 | else: 179 | 180 | l0_distortion = l0_loss(adv[j], inputs[j]) 181 | l1_distortion = l1_loss(adv[j], inputs[j]) 182 | l2_distortion = l2_loss(adv[j], inputs[j]) 183 | linf_distortion = linf_loss(adv[j], inputs[j]) 184 | 185 | adversarial_predict = np.squeeze(model.model.predict(np.array([adv[j]]))) 186 | adversarial_prob = np.sort(adversarial_predict) 187 | adversarial_class = np.argsort(adversarial_predict) 188 | attack_label = np.argmax(adversarial_predict) 189 | 190 | print("adversarial probabilities:", adversarial_prob[-1:-11:-1]) 191 | print("adversarial classification:", adversarial_class[-1:-11:-1]) 192 | sys.stdout.flush() 193 | 194 | success = False 195 | if targeted_flag: 196 | success = np.argsort(adversarial_predict)[-1] == target_label 197 | 198 | #dealing with the tie issue in the adversarial_predict vector 199 | candidates = set([i for i in range(len(adversarial_predict)-1) 200 | if abs(adversarial_predict[i] - adversarial_prob[-1]) < 0.001]) 201 | if len(candidates) > 1 and target_label in candidates: 202 | success = True 203 | 204 | else: 205 | success = np.argsort(adversarial_predict)[-1] != target_label 206 | if success: 207 | print("Attack succeeded.") 208 | else: 209 | print("Attack failed.") 210 | 211 | if success: 212 | total_success += 1 213 | l0_list.append(l0_distortion) 214 | l1_list.append(l1_distortion) 215 | l2_list.append(l2_distortion) 216 | linf_list.append(linf_distortion) 217 | time_list.append(time_used_per_image) 218 | 219 | suffix = "id={0}_seq={1}_prev={2}_adv={3}_res={4}".format(all_true_ids[i+j], i, original_class[-1], 220 | adversarial_class[-1], success) 221 | print("Saving to", suffix) 222 | sys.stdout.flush() 223 | 224 | dump(inputs[j], "{}/imgno={}_content={}_{}".format(saved_path, img_no, 'original', suffix), 225 | save_png=False) 226 | dump(adv[j], "{}/imgno={}_content={}_{}".format(saved_path, img_no, 'adversarial', suffix), 227 | save_png=False) 228 | # dump(adv[j] - inputs[j], "{}/imgno={}_content={}_{}".format(saved_path, img_no, 'noise', suffix)) 229 | np.save("{}/imgno={}_content={}_{}".format(saved_path, img_no, 'targets', suffix) + ".npy", targets[j]) 230 | np.save("{}/imgno={}_content={}_{}".format(saved_path, img_no, 'labels', suffix) + ".npy", labels[j]) 231 | 232 | L1_debug_str = "[STATS][L1] total = {}, seq = {}, id = {}, time = {:.3f}, success = {}, " \ 233 | "prev_class = {}, new_class = {}, distortion = {:.5f}, success_rate = {:.3f}, " \ 234 | "l2_avg = {:.5f}".format(img_no, i+j, all_true_ids[i+j], 235 | time_used_per_image, success, original_class[-1], adversarial_class[-1], l2_distortion, 236 | total_success / float(img_no), 0 if total_success == 0 else np.mean(l2_list)) 237 | 238 | print(L1_debug_str) 239 | sys.stdout.flush() 240 | 241 | if targeted_flag == True: 242 | verbose_str = '\t'.join( 243 | [str(img_no), str(i+j), str(all_true_ids[i+j]), str(time_used_per_image), str(success), 244 | str(original_class[-1]), str(np.argmax(targets[j])), str(adversarial_class[-1]), 245 | str(l0_distortion), str(l1_distortion), str(l2_distortion), str(linf_distortion)]) 246 | else: 247 | verbose_str = '\t'.join( 248 | [str(img_no), str(i+j), str(all_true_ids[i+j]), str(time_used_per_image), str(success), 249 | str(original_class[-1]), str(adversarial_class[-1]), str(l0_distortion), str(l1_distortion), 250 | str(l2_distortion), str(linf_distortion)]) 251 | 252 | verbose_f.write(verbose_str + "\n") 253 | verbose_f.flush() 254 | print(verbose_head_str) 255 | print(verbose_str) 256 | 257 | sys.stdout.flush() 258 | 259 | overall_timeend_sofar = time.time() 260 | 261 | overall_time_used_sofar = overall_timeend_sofar - overall_timestart 262 | 263 | print("overall_time_used_sofar = ", overall_time_used_sofar) 264 | sys.stdout.flush() 265 | 266 | verbose_f.close() 267 | 268 | if img_no == 0: 269 | success_rate = 0.0 270 | else: 271 | success_rate = total_success / float(img_no) 272 | 273 | if total_success == 0: 274 | aggre_str = "\t".join([str(img_no), str(success_rate), str(0.0), str(0.0), str(0.0), str(0.0), 275 | str(0.0), str(0.0), str(0.0), str(0.0), str(0.0), str(0.0)]) 276 | else: 277 | aggre_str = "\t".join([str(img_no), str(success_rate), str(np.mean(l0_list)), str(np.std(l0_list)), 278 | str(np.mean(l1_list)), str(np.std(l1_list)), str(np.mean(l2_list)), 279 | str(np.std(l2_list)), str(np.mean(linf_list)), str(np.std(linf_list)), 280 | str(np.mean(time_list)), str(np.std(time_list))]) 281 | 282 | aggre_f.write(aggre_str + "\n") 283 | print(aggre_head_str) 284 | print(aggre_str) 285 | sys.stdout.flush() 286 | aggre_f.close() 287 | 288 | overall_timeend = time.time() 289 | 290 | overall_time_used = overall_timeend - overall_timestart 291 | 292 | print("overall_time_used = ", overall_time_used) 293 | sys.stdout.flush() 294 | 295 | print("ALL DONE!!!") 296 | return 297 | 298 | 299 | if __name__ == "__main__": 300 | 301 | import argparse 302 | parser = argparse.ArgumentParser() 303 | parser.add_argument("-d", "--dataset", choices=["imagenet"], default="imagenet") 304 | parser.add_argument("-s", "--save", default="./saved_results") 305 | 306 | parser.add_argument("-a", "--attack", choices=["FGSM"], default="FGSM") 307 | parser.add_argument("-u", "--untargeted", action='store_true') 308 | parser.add_argument("-e", "--epsilon", type=float, default=0.3) 309 | 310 | parser.add_argument("--num_valid_test_imgs", type=int, default=1000) 311 | 312 | parser.add_argument("--attack_batch_size", type=int, default=0) 313 | 314 | parser.add_argument( "--target_type", type=int, default=0b0111) 315 | 316 | parser.add_argument("--model_name", default="resnet_v2_50") 317 | 318 | parser.add_argument("--precision_delta", type=float, default=1e-03) 319 | 320 | parser.add_argument("--clip_min", type=float, default=-0.5) 321 | parser.add_argument("--clip_max", type=float, default=0.5) 322 | 323 | parser.add_argument("-n", "--numimg", type=int, default=0, help = "number of test images to attack") 324 | parser.add_argument("-p", "--print_every", type=int, default=100, help = "print objs every PRINT_EVERY iterations") 325 | parser.add_argument("-f", "--firstimg", type=int, default=0) 326 | parser.add_argument("--numimg_loaded", type=int, default=1000, help = "number of test images to load") 327 | 328 | parser.add_argument('-l', '--loss_function', choices=['l0', 'l1', 'l2', 'linf'], default='linf') 329 | 330 | #Follow Cleverhans, Order of the norm (mimics NumPy). Possible values: np.inf, 1 or 2. 331 | parser.add_argument('--ord', choices=['inf', '1', '2'], default='inf') 332 | 333 | parser.add_argument("-z", "--use_zvalue", action='store_true') 334 | parser.add_argument("--seed", type=int, default=1216) 335 | 336 | args = vars(parser.parse_args()) 337 | 338 | # setup random seed 339 | random.seed(args['seed']) 340 | np.random.seed(args['seed']) 341 | tf.set_random_seed(args['seed']) 342 | print(args) 343 | 344 | main(args) 345 | 346 | print("Experiment Done!!!") 347 | 348 | -------------------------------------------------------------------------------- /test_IterFGSM.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import tensorflow as tf 4 | import numpy as np 5 | import random 6 | import time 7 | 8 | from setup_imagenet import ImageNet, ImageNetModel 9 | 10 | from Iter_FGSM_attack import Iter_FGSM 11 | 12 | from utils_for_test import linf_loss, l0_loss, l1_loss, l2_loss, dump, generate_data 13 | 14 | 15 | def main(args): 16 | 17 | with tf.Session() as sess: 18 | 19 | random.seed(args["seed"]) 20 | np.random.seed(args["seed"]) 21 | tf.set_random_seed(args["seed"]) 22 | 23 | print("seed = ", args["seed"]) 24 | 25 | overall_timestart = time.time() 26 | 27 | use_log = not args['use_zvalue'] 28 | 29 | print("use_log = ", use_log) 30 | 31 | data_map = { 32 | } 33 | 34 | model_map = { 35 | } 36 | 37 | if args['dataset'] == "imagenet": 38 | model_map[args['model_name']] = ImageNetModel(sess, use_log, args['model_name'], create_prediction=False) 39 | data_map['imagenet'] = ImageNet(model_map[args['model_name']].image_size, load_total_imgs=args['numimg_loaded']) 40 | 41 | print('Loading model', args['dataset']) 42 | data = data_map[args['dataset']] 43 | model = model_map[args['model_name']] 44 | 45 | if args['numimg'] == 0: 46 | args['numimg'] = len(data.test_labels) - args['firstimg'] 47 | print('Using', args['numimg'], 'test images') 48 | # load attack module 49 | 50 | print('args = ', args) 51 | 52 | targeted_flag = not args['untargeted'] 53 | 54 | print("targeted_flag = ", targeted_flag) 55 | 56 | # load attack module 57 | if args['attack'] == "IterFGSM": 58 | print("IterFGSM") 59 | attack = Iter_FGSM(sess, model) 60 | else: 61 | print("Invalid attack name, exit 1") 62 | return 63 | 64 | random.seed(args['seed']) 65 | np.random.seed(args['seed']) 66 | tf.set_random_seed(args['seed']) 67 | 68 | print('Generate data') 69 | model_name = args['model_name'] 70 | 71 | if 'vgg' in model_name or 'densenet' in model_name or 'alexnet' in model_name: 72 | remove_background_class_flag = True 73 | else: 74 | remove_background_class_flag = False 75 | 76 | sys.stdout.flush() 77 | 78 | all_inputs, all_targets, all_labels, all_true_ids, img_info = generate_data(data, samples=args['numimg'], 79 | targeted=targeted_flag, random_and_least_likely = True, predictor=attack.predict, 80 | start=args['firstimg'], imagenet=isinstance(data, ImageNet), 81 | remove_background_class=remove_background_class_flag, target_type=args['target_type'], 82 | total_num_valid_samples=args['num_valid_test_imgs']) 83 | 84 | print("all_true_ids = ", all_true_ids) 85 | print('all_labels = ', all_labels) 86 | 87 | print('len(all_inputs) = ', len(all_inputs)) 88 | 89 | attack_batch_size = args['attack_batch_size'] 90 | if attack_batch_size == 0: 91 | attack_batch_size = all_true_ids.size 92 | print("attack_batch_size = ", attack_batch_size) 93 | 94 | attack.init_attack(sess, model, iter_num=args['iter_num'], targeted=targeted_flag, batch_size=attack_batch_size, 95 | use_log = use_log) 96 | 97 | saved_path = "{}/{}/{}/targeted_{}".format(args['save'], args['dataset'], args['attack'], targeted_flag) 98 | if not os.path.exists(saved_path): 99 | os.system("mkdir -p " + saved_path) 100 | 101 | img_no = 0 102 | total_success = 0 103 | l0_list = [] 104 | l1_list = [] 105 | l2_list = [] 106 | linf_list = [] 107 | time_list = [] 108 | 109 | verbose_f = open(args['save'] + "/" + "_".join([args['dataset'], args['attack'], 110 | str(targeted_flag), "verbose.txt"]), "w") 111 | aggre_f = open(args['save'] + "/" + "_".join([args['dataset'], args['attack'], 112 | str(targeted_flag), "aggre.txt"]), "w") 113 | 114 | if targeted_flag == True: 115 | verbose_head_str = '\t'.join(['total', 'seq', 'id', 'epsilon', 'time', 'success', 'prev_class', 116 | 'target', 'new_class', 'l0_distortion', 'l1_distortion', 'l2_distortion', 117 | 'linf_distortion']) 118 | else: 119 | verbose_head_str = '\t'.join(['total', 'seq', 'id', 'epsilon', 'time', 'success', 'prev_class', 'new_class', 120 | 'l0_distortion', 'l1_distortion', 'l2_distortion', 'linf_distortion']) 121 | 122 | aggre_head_str = '\t'.join(['total_count', 'success_rate', 'l0_avg', 'l0_std', 'l1_avg', 'l1_std', 123 | 'l2_avg', 'l2_std', 'linf_avg', 'linf_std', 'time_avg', 'time_std']) 124 | 125 | verbose_f.write(verbose_head_str + '\n') 126 | aggre_f.write(aggre_head_str + '\n') 127 | 128 | print("all_true_ids.size = ", all_true_ids.size) 129 | sys.stdout.flush() 130 | 131 | 132 | random.seed(args['seed']) 133 | np.random.seed(args['seed']) 134 | tf.set_random_seed(args['seed']) 135 | 136 | for i in range(0, all_true_ids.size, attack_batch_size): 137 | 138 | if i + attack_batch_size > all_true_ids.size: 139 | break 140 | else: 141 | actual_attack_batch_size = attack_batch_size 142 | 143 | inputs = all_inputs[i:i+actual_attack_batch_size] 144 | targets = all_targets[i:i+actual_attack_batch_size] 145 | labels = all_labels[i:i+actual_attack_batch_size] 146 | 147 | timestart = time.time() 148 | 149 | """perform the attack""" 150 | print("perform the attack") 151 | adv, best_epsilon = attack.attack(inputs, targets, targeted=targeted_flag, 152 | initial_eps=args['initial_eps'], max_attempts=args['max_attempts']) 153 | 154 | timeend = time.time() 155 | 156 | time_used = timeend - timestart 157 | time_used_per_image = time_used / attack_batch_size 158 | 159 | for j in range(len(adv)): 160 | 161 | print("="*10, "i = ", i, "="*10, "j=", j, "="*10) 162 | 163 | original_predict = np.squeeze(attack.predict(np.array([inputs[j]]))) 164 | original_prob = np.sort(original_predict) 165 | original_class = np.argsort(original_predict) 166 | print("Original Classification:", original_prob[-1:-6:-1]) 167 | print("Original Probabilities/Logits:", original_class[-1:-6:-1]) 168 | sys.stdout.flush() 169 | 170 | true_label = np.argmax(labels[j]) 171 | target_label = np.argmax(targets[j]) 172 | attack_label = None 173 | success = False 174 | 175 | img_no += 1 176 | 177 | print("Target:", target_label) 178 | # if the array contains NaN, the solver did not return a solution 179 | if (np.any(np.isnan(adv[j]))): 180 | print('Attack failed. (solver returned NaN)') 181 | l0_distortion = l1_distortion = l2_distortion = linf_distortion = np.nan 182 | else: 183 | 184 | l0_distortion = l0_loss(adv[j], inputs[j]) 185 | l1_distortion = l1_loss(adv[j], inputs[j]) 186 | l2_distortion = l2_loss(adv[j], inputs[j]) 187 | linf_distortion = linf_loss(adv[j], inputs[j]) 188 | adversarial_predict = np.squeeze(attack.predict(np.array([adv[j]]))) 189 | adversarial_prob = np.sort(adversarial_predict) 190 | adversarial_class = np.argsort(adversarial_predict) 191 | attack_label = np.argmax(adversarial_predict) 192 | 193 | print("adversarial probabilities:", adversarial_prob[-1:-11:-1]) 194 | print("adversarial classification:", adversarial_class[-1:-11:-1]) 195 | sys.stdout.flush() 196 | 197 | success = False 198 | if targeted_flag: 199 | success = np.argsort(adversarial_predict)[-1] == target_label 200 | 201 | candidates = set([i for i in range(len(adversarial_predict)-1) 202 | if abs(adversarial_predict[i] - adversarial_prob[-1]) < 0.001]) 203 | if len(candidates) > 1 and target_label in candidates: 204 | success = True 205 | 206 | else: 207 | success = np.argsort(adversarial_predict)[-1] != target_label 208 | if success: 209 | print("Attack succeeded.") 210 | else: 211 | print("Attack failed.") 212 | 213 | if success: 214 | total_success += 1 215 | l0_list.append(l0_distortion) 216 | l1_list.append(l1_distortion) 217 | l2_list.append(l2_distortion) 218 | linf_list.append(linf_distortion) 219 | time_list.append(time_used_per_image) 220 | 221 | suffix = "id={0}_seq={1}_prev={2}_adv={3}_res={4}".format(all_true_ids[i+j], i, original_class[-1], 222 | adversarial_class[-1], success) 223 | print("Saving to", suffix) 224 | sys.stdout.flush() 225 | 226 | dump(inputs[j], "{}/imgno={}_content={}_{}".format(saved_path, img_no, 'original', suffix), 227 | save_png=False) 228 | dump(adv[j], "{}/imgno={}_content={}_{}".format(saved_path, img_no, 'adversarial', suffix), 229 | save_png=False) 230 | np.save("{}/imgno={}_content={}_{}".format(saved_path, img_no, 'targets', suffix) + ".npy", targets[j]) 231 | np.save("{}/imgno={}_content={}_{}".format(saved_path, img_no, 'labels', suffix) + ".npy", labels[j]) 232 | 233 | L1_debug_str = "[STATS][L1] total = {}, seq = {}, id = {}, time = {:.3f}, success = {}, " \ 234 | "prev_class = {}, new_class = {}, distortion = {:.5f}, success_rate = {:.3f}, " \ 235 | "l2_avg = {:.5f}".format(img_no, i+j, all_true_ids[i+j], 236 | time_used_per_image, success, original_class[-1], adversarial_class[-1], l2_distortion, 237 | total_success / float(img_no), 0 if total_success == 0 else np.mean(l2_list)) 238 | 239 | print(L1_debug_str) 240 | sys.stdout.flush() 241 | 242 | if targeted_flag == True: 243 | verbose_str = '\t'.join( 244 | [str(img_no), str(i+j), str(all_true_ids[i+j]), str(best_epsilon[j]), str(time_used_per_image), 245 | str(success), str(original_class[-1]), str(np.argmax(targets[j])), str(adversarial_class[-1]), 246 | str(l0_distortion), str(l1_distortion), str(l2_distortion), str(linf_distortion)]) 247 | else: 248 | verbose_str = '\t'.join( 249 | [str(img_no), str(i+j), str(all_true_ids[i+j]), str(best_epsilon[j]), str(time_used_per_image), 250 | str(success), str(original_class[-1]), str(adversarial_class[-1]), str(l0_distortion), 251 | str(l1_distortion), str(l2_distortion), str(linf_distortion)]) 252 | 253 | verbose_f.write(verbose_str + "\n") 254 | verbose_f.flush() 255 | print(verbose_head_str) 256 | print(verbose_str) 257 | 258 | sys.stdout.flush() 259 | 260 | overall_timeend_sofar = time.time() 261 | 262 | overall_time_used_sofar = overall_timeend_sofar - overall_timestart 263 | 264 | print("overall_time_used_sofar = ", overall_time_used_sofar) 265 | sys.stdout.flush() 266 | 267 | verbose_f.close() 268 | 269 | if img_no == 0: 270 | success_rate = 0.0 271 | else: 272 | success_rate = total_success / float(img_no) 273 | 274 | if total_success == 0: 275 | aggre_str = "\t".join([str(img_no), str(success_rate), str(0.0), str(0.0), str(0.0), str(0.0), 276 | str(0.0), str(0.0), str(0.0), str(0.0), str(0.0), str(0.0)]) 277 | else: 278 | aggre_str = "\t".join([str(img_no), str(success_rate), str(np.mean(l0_list)), str(np.std(l0_list)), 279 | str(np.mean(l1_list)), str(np.std(l1_list)), str(np.mean(l2_list)), 280 | str(np.std(l2_list)), str(np.mean(linf_list)), str(np.std(linf_list)), 281 | str(np.mean(time_list)), str(np.std(time_list))]) 282 | 283 | aggre_f.write(aggre_str + "\n") 284 | print(aggre_head_str) 285 | print(aggre_str) 286 | sys.stdout.flush() 287 | aggre_f.close() 288 | 289 | overall_timeend = time.time() 290 | 291 | overall_time_used = overall_timeend - overall_timestart 292 | 293 | print("overall_time_used = ", overall_time_used) 294 | sys.stdout.flush() 295 | 296 | print("ALL DONE!!!") 297 | return 298 | 299 | if __name__ == "__main__": 300 | 301 | import argparse 302 | parser = argparse.ArgumentParser() 303 | 304 | parser.add_argument("-d", "--dataset", choices=["imagenet"], default="imagenet") 305 | parser.add_argument("-s", "--save", default="./saved_results") 306 | 307 | parser.add_argument("-a", "--attack", choices=["IterFGSM"], default="IterFGSM") 308 | parser.add_argument("-u", "--untargeted", action='store_true') 309 | 310 | parser.add_argument("--initial_eps", type=float, default=0.01) 311 | 312 | parser.add_argument("--max_attempts", type=int, default=5) 313 | 314 | parser.add_argument("--iter_num", type=int, default=10) 315 | parser.add_argument("--num_valid_test_imgs", type=int, default=1000) 316 | 317 | parser.add_argument("--attack_batch_size", type=int, default=0) 318 | 319 | parser.add_argument( "--target_type", type=int, default=0b0111) 320 | 321 | parser.add_argument("--model_name", default="resnet_v2_50") 322 | 323 | parser.add_argument("--clip_min", type=float, default=-0.5) 324 | parser.add_argument("--clip_max", type=float, default=0.5) 325 | 326 | parser.add_argument("-n", "--numimg", type=int, default=0, help = "number of test images to attack") 327 | parser.add_argument("-m", "--maxiter", type=int, default=0, help = "set 0 to use default value") 328 | 329 | parser.add_argument("-p", "--print_every", type=int, default=10, help = "print objs every PRINT_EVERY iterations") 330 | parser.add_argument("-o", "--early_stop_iters", type=int, default=100, help = "print objs every EARLY_STOP_ITER iterations, 0 is maxiter//10") 331 | parser.add_argument("-f", "--firstimg", type=int, default=0) 332 | parser.add_argument("--numimg_loaded", type=int, default=1000, help = "number of test images to load") 333 | 334 | parser.add_argument('-l', '--loss_function', choices=['l0', 'l1', 'l2', 'linf'], default='linf') 335 | 336 | parser.add_argument("-z", "--use_zvalue", action='store_true', default=True) 337 | parser.add_argument("--seed", type=int, default=1216) 338 | args = vars(parser.parse_args()) 339 | 340 | # setup random seed 341 | random.seed(args['seed']) 342 | np.random.seed(args['seed']) 343 | tf.set_random_seed(args['seed']) 344 | print(args) 345 | 346 | main(args) 347 | 348 | print("Experiment Done!!!") 349 | 350 | -------------------------------------------------------------------------------- /test_CW_EADL1.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import tensorflow as tf 4 | import numpy as np 5 | import random 6 | import time 7 | 8 | from setup_imagenet import ImageNet, ImageNetModel 9 | 10 | from l2_attack import CarliniL2 11 | from l1_attack import EADL1 12 | 13 | from utils_for_test import linf_loss, l0_loss, l1_loss, l2_loss, dump, generate_data 14 | 15 | def main(args): 16 | 17 | with tf.Session() as sess: 18 | 19 | random.seed(args["seed"]) 20 | np.random.seed(args["seed"]) 21 | tf.set_random_seed(args["seed"]) 22 | 23 | print("seed = ", args["seed"]) 24 | 25 | overall_timestart = time.time() 26 | 27 | use_log = not args['use_zvalue'] 28 | 29 | print("use_log = ", use_log) 30 | 31 | data_map = { 32 | } 33 | 34 | model_map = { 35 | } 36 | 37 | if args['dataset'] == "imagenet": 38 | if args['attack'] == "CW": 39 | model_map[args['model_name']] = ImageNetModel(sess, use_log, args['model_name'], create_prediction=False) 40 | elif args['attack'] == "EADL1": 41 | model_map[args['model_name']] = ImageNetModel(sess, use_log, args['model_name'], create_prediction=True) 42 | 43 | data_map['imagenet'] = ImageNet(model_map[args['model_name']].image_size, load_total_imgs=args['numimg_loaded']) 44 | 45 | 46 | print('Loading model', args['dataset']) 47 | data = data_map[args['dataset']] 48 | model = model_map[args['model_name']] 49 | 50 | if args['numimg'] == 0: 51 | args['numimg'] = len(data.test_labels) - args['firstimg'] 52 | print('Using', args['numimg'], 'test images') 53 | # load attack module 54 | 55 | print('args = ', args) 56 | 57 | targeted_flag = not args['untargeted'] 58 | 59 | print("targeted_flag = ", targeted_flag) 60 | 61 | 62 | # load attack module 63 | if args['attack'] == "CW": 64 | 65 | attack = CarliniL2(sess, model, 100) 66 | attack_predictor = attack.predict 67 | elif args['attack'] == "EADL1": 68 | attack_predictor = model.model.predict 69 | 70 | random.seed(args['seed']) 71 | np.random.seed(args['seed']) 72 | tf.set_random_seed(args['seed']) 73 | 74 | print('Generate data') 75 | model_name = args['model_name'] 76 | 77 | if 'vgg' in model_name or 'densenet' in model_name or 'alexnet' in model_name: 78 | remove_background_class_flag = True 79 | else: 80 | remove_background_class_flag = False 81 | 82 | sys.stdout.flush() 83 | 84 | all_inputs, all_targets, all_labels, all_true_ids, img_info = generate_data(data, samples=args['numimg'], 85 | targeted=targeted_flag, random_and_least_likely = True, predictor=attack_predictor, 86 | start=args['firstimg'], imagenet=isinstance(data, ImageNet), 87 | remove_background_class=remove_background_class_flag, target_type=args['target_type'], 88 | total_num_valid_samples=args['num_valid_test_imgs']) 89 | 90 | print('len(all_inputs) = ', len(all_inputs)) 91 | print("all_inputs shape:",all_inputs.shape) 92 | print("all_targets shape:", all_targets.shape) 93 | 94 | attack_batch_size = args['attack_batch_size'] 95 | if attack_batch_size == 0: 96 | attack_batch_size = all_true_ids.size 97 | print("attack_batch_size = ", attack_batch_size) 98 | 99 | if args['attack'] == 'CW': 100 | attack.init_attack(sess, model, targeted=targeted_flag, batch_size=attack_batch_size, 101 | initial_const=args['init_const'], binary_search_steps=args['binary_steps'], 102 | max_iterations=args['maxiter'], print_every=args['print_every'], 103 | confidence=args['kappa'], use_log=use_log) 104 | 105 | elif args['attack'] == 'EADL1': 106 | print("EADL1 attack") 107 | attack = EADL1(sess, model, targeted=targeted_flag, batch_size=attack_batch_size, 108 | initial_const=args['init_const'], binary_search_steps=args['binary_steps'], 109 | max_iterations=args['maxiter'], confidence=args['kappa'], 110 | print_every=args['print_every']) 111 | 112 | else: 113 | print("Invalid attack name, exit 1") 114 | return 115 | 116 | saved_path = "{}/{}/{}/targeted_{}".format(args['save'], args['dataset'], args['attack'], targeted_flag) 117 | if not os.path.exists(saved_path): 118 | os.system("mkdir -p " + saved_path) 119 | 120 | img_no = 0 121 | total_success = 0 122 | l0_list = [] 123 | l1_list = [] 124 | l2_list = [] 125 | linf_list = [] 126 | time_list = [] 127 | 128 | verbose_f = open(args['save'] + "/" + "_".join([args['dataset'], args['attack'], str(targeted_flag), "verbose.txt"]), "w") 129 | aggre_f = open(args['save'] + "/" + "_".join([args['dataset'], args['attack'], str(targeted_flag), "aggre.txt"]), "w") 130 | 131 | if targeted_flag == True: 132 | verbose_head_str = '\t'.join(['total', 'seq', 'id', 'time', 'success', 'prev_class', 'target', 133 | 'new_class', 'l0_distortion', 'l1_distortion', 'l2_distortion', 134 | 'linf_distortion']) 135 | else: 136 | verbose_head_str = '\t'.join(['total', 'seq', 'id', 'time', 'success', 'prev_class', 'new_class', 137 | 'l0_distortion', 'l1_distortion', 'l2_distortion', 'linf_distortion']) 138 | 139 | aggre_head_str = '\t'.join(['total_count', 'success_rate', 'l0_avg', 'l0_std', 'l1_avg', 'l1_std', 140 | 'l2_avg', 'l2_std', 'linf_avg', 'linf_std', 'time_avg', 'time_std']) 141 | 142 | verbose_f.write(verbose_head_str + '\n') 143 | aggre_f.write(aggre_head_str + '\n') 144 | 145 | print("all_true_ids.size = ", all_true_ids.size) 146 | sys.stdout.flush() 147 | 148 | random.seed(args['seed']) 149 | np.random.seed(args['seed']) 150 | tf.set_random_seed(args['seed']) 151 | 152 | for i in range(0, all_true_ids.size, attack_batch_size): 153 | 154 | if i + attack_batch_size > all_true_ids.size: 155 | actual_attack_batch_size = all_true_ids.size - i 156 | else: 157 | actual_attack_batch_size = attack_batch_size 158 | 159 | inputs = all_inputs[i:i+actual_attack_batch_size] 160 | targets = all_targets[i:i+actual_attack_batch_size] 161 | labels = all_labels[i:i+actual_attack_batch_size] 162 | 163 | timestart = time.time() 164 | 165 | """perform the attack""" 166 | print("perform the attack") 167 | adv = attack.attack(inputs, targets) 168 | 169 | timeend = time.time() 170 | 171 | time_used = timeend - timestart 172 | time_used_per_image = time_used / attack_batch_size 173 | 174 | for j in range(len(adv)): 175 | 176 | print("="*10, "i = ", i, "="*10, "j=", j, "="*10) 177 | 178 | # original_predict = np.squeeze(attack.predict(np.array([inputs[j]]))) 179 | original_predict = np.squeeze(attack_predictor(np.array([inputs[j]]))) 180 | 181 | original_prob = np.sort(original_predict) 182 | original_class = np.argsort(original_predict) 183 | print("Original Classification:", original_prob[-1:-6:-1]) 184 | print("Original Probabilities/Logits:", original_class[-1:-6:-1]) 185 | sys.stdout.flush() 186 | 187 | true_label = np.argmax(labels[j]) 188 | target_label = np.argmax(targets[j]) 189 | attack_label = None 190 | success = False 191 | 192 | img_no += 1 193 | 194 | print("Target:", target_label) 195 | # if the array contains NaN, the solver did not return a solution 196 | if (np.any(np.isnan(adv[j]))): 197 | print('Attack failed. (solver returned NaN)') 198 | l0_distortion = l1_distortion = l2_distortion = linf_distortion = np.nan 199 | adversarial_class = np.zeros(original_class.shape) 200 | 201 | else: 202 | l0_distortion = l0_loss(adv[j], inputs[j]) 203 | l1_distortion = l1_loss(adv[j], inputs[j]) 204 | l2_distortion = l2_loss(adv[j], inputs[j]) 205 | linf_distortion = linf_loss(adv[j], inputs[j]) 206 | #adversarial_predict = np.squeeze(model.model.predict(np.array([adv[j]]))) 207 | # adversarial_predict = np.squeeze(attack.predict(np.array([adv[j]]))) 208 | adversarial_predict = np.squeeze(attack_predictor(np.array([adv[j]]))) 209 | 210 | adversarial_prob = np.sort(adversarial_predict) 211 | adversarial_class = np.argsort(adversarial_predict) 212 | attack_label = np.argmax(adversarial_predict) 213 | 214 | print("adversarial probabilities:", adversarial_prob[-1:-11:-1]) 215 | print("adversarial classification:", adversarial_class[-1:-11:-1]) 216 | sys.stdout.flush() 217 | 218 | success = False 219 | if targeted_flag: 220 | success = np.argsort(adversarial_predict)[-1] == target_label 221 | 222 | candidates = set([i for i in range(len(adversarial_predict)-1) 223 | if abs(adversarial_predict[i] - adversarial_prob[-1]) < 0.001]) 224 | if len(candidates) > 1 and target_label in candidates: 225 | success = True 226 | else: 227 | success = np.argsort(adversarial_predict)[-1] != target_label 228 | 229 | if success: 230 | print("Attack succeeded.") 231 | else: 232 | print("Attack failed.") 233 | 234 | if success: 235 | total_success += 1 236 | l0_list.append(l0_distortion) 237 | l1_list.append(l1_distortion) 238 | l2_list.append(l2_distortion) 239 | linf_list.append(linf_distortion) 240 | time_list.append(time_used_per_image) 241 | 242 | suffix = "id={0}_seq={1}_prev={2}_adv={3}_res={4}".format(all_true_ids[i+j], i, original_class[-1], 243 | adversarial_class[-1], success) 244 | print("Saving to", suffix) 245 | sys.stdout.flush() 246 | 247 | dump(inputs[j], "{}/imgno={}_content={}_{}".format(saved_path, img_no, 'original', suffix)) 248 | dump(adv[j], "{}/imgno={}_content={}_{}".format(saved_path, img_no, 'adversarial', suffix)) 249 | # dump(adv[j] - inputs[j], "{}/imgno={}_content={}_{}".format(saved_path, img_no, 'noise', suffix)) 250 | np.save("{}/imgno={}_content={}_{}".format(saved_path, img_no, 'targets', suffix) + ".npy", targets[j]) 251 | np.save("{}/imgno={}_content={}_{}".format(saved_path, img_no, 'labels', suffix) + ".npy", labels[j]) 252 | 253 | L1_debug_str = "[STATS][L1] total = {}, seq = {}, id = {}, time = {:.3f}, success = {}, " \ 254 | "prev_class = {}, new_class = {}, distortion = {:.5f}, success_rate = {:.3f}, " \ 255 | "l2_avg = {:.5f}".format(img_no, i+j, all_true_ids[i+j], time_used_per_image, success, 256 | original_class[-1], adversarial_class[-1], l2_distortion, 257 | total_success / float(img_no), 0 if total_success == 0 else np.mean(l2_list)) 258 | 259 | print(L1_debug_str) 260 | sys.stdout.flush() 261 | 262 | if targeted_flag == True: 263 | verbose_str = '\t'.join( 264 | [str(img_no), str(i+j), str(all_true_ids[i+j]), str(time_used_per_image), str(success), 265 | str(original_class[-1]), str(np.argmax(targets[j])), str(adversarial_class[-1]), 266 | str(l0_distortion), str(l1_distortion), str(l2_distortion), str(linf_distortion)]) 267 | else: 268 | verbose_str = '\t'.join( 269 | [str(img_no), str(i+j), str(all_true_ids[i+j]), str(time_used_per_image), str(success), 270 | str(original_class[-1]), str(adversarial_class[-1]), str(l0_distortion), str(l1_distortion), 271 | str(l2_distortion), str(linf_distortion)]) 272 | 273 | verbose_f.write(verbose_str + "\n") 274 | verbose_f.flush() 275 | print(verbose_head_str) 276 | print(verbose_str) 277 | 278 | sys.stdout.flush() 279 | 280 | overall_timeend_sofar = time.time() 281 | 282 | overall_time_used_sofar = overall_timeend_sofar - overall_timestart 283 | 284 | print("overall_time_used_sofar = ", overall_time_used_sofar) 285 | sys.stdout.flush() 286 | 287 | verbose_f.close() 288 | 289 | if img_no == 0: 290 | success_rate = 0.0 291 | else: 292 | success_rate = total_success / float(img_no) 293 | 294 | if total_success == 0: 295 | aggre_str = "\t".join([str(img_no), str(success_rate), str(0.0), str(0.0), str(0.0), str(0.0), 296 | str(0.0), str(0.0), str(0.0), str(0.0), str(0.0), str(0.0)]) 297 | else: 298 | aggre_str = "\t".join([str(img_no), str(success_rate), str(np.mean(l0_list)), str(np.std(l0_list)), 299 | str(np.mean(l1_list)), str(np.std(l1_list)), str(np.mean(l2_list)), 300 | str(np.std(l2_list)), str(np.mean(linf_list)), str(np.std(linf_list)), 301 | str(np.mean(time_list)), str(np.std(time_list))]) 302 | 303 | aggre_f.write(aggre_str + "\n") 304 | print(aggre_head_str) 305 | print(aggre_str) 306 | sys.stdout.flush() 307 | aggre_f.close() 308 | 309 | overall_timeend = time.time() 310 | 311 | overall_time_used = overall_timeend - overall_timestart 312 | 313 | print("overall_time_used = ", overall_time_used) 314 | sys.stdout.flush() 315 | 316 | print("ALL DONE!!!") 317 | return 318 | 319 | if __name__ == "__main__": 320 | 321 | import argparse 322 | parser = argparse.ArgumentParser() 323 | 324 | parser.add_argument("-d", "--dataset", choices=["imagenet"], default="imagenet") 325 | parser.add_argument("-s", "--save", default="./saved_results") 326 | 327 | parser.add_argument("-a", "--attack", choices=["CW", "EADL1"], default="CW") 328 | parser.add_argument("-u", "--untargeted", action='store_true') 329 | 330 | parser.add_argument("--num_valid_test_imgs", type=int, default=1000) 331 | 332 | parser.add_argument("--attack_batch_size", type=int, default=0) 333 | 334 | parser.add_argument( "--target_type", type=int, default=0b0111) 335 | 336 | parser.add_argument("--model_name", default="resnet_v2_50") 337 | 338 | parser.add_argument("-n", "--numimg", type=int, default=0, help = "number of test images to attack") 339 | parser.add_argument("-m", "--maxiter", type=int, default=0, help = "set 0 to use default value") 340 | 341 | parser.add_argument("-p", "--print_every", type=int, default=10, help = "print objs every PRINT_EVERY iterations") 342 | parser.add_argument("-o", "--early_stop_iters", type=int, default=100, help = "print objs every EARLY_STOP_ITER iterations, 0 is maxiter//10") 343 | parser.add_argument("-f", "--firstimg", type=int, default=0) 344 | parser.add_argument("--numimg_loaded", type=int, default=1000, help = "number of test images to load") 345 | 346 | parser.add_argument('-l', '--loss_function', choices=['l0', 'l1', 'l2', 'linf'], default='linf') 347 | 348 | parser.add_argument("-b", "--binary_steps", type=int, default=0) 349 | parser.add_argument("-c", "--init_const", type=float, default=0.0) 350 | parser.add_argument("-z", "--use_zvalue", action='store_true') 351 | 352 | parser.add_argument("-r", "--reset_adam", action='store_true', help = "reset adam after an initial solution is found") 353 | parser.add_argument("--use_resize", action='store_true', help = "resize image (only works on imagenet!)") 354 | parser.add_argument("--adam_beta1", type=float, default=0.9) 355 | parser.add_argument("--adam_beta2", type=float, default=0.999) 356 | parser.add_argument("--seed", type=int, default=1216) 357 | parser.add_argument("--solver", choices=["adam", "newton", "adam_newton", "fake_zero"], default="adam") 358 | parser.add_argument("--save_ckpts", default="", help = "path to save checkpoint file") 359 | parser.add_argument("--load_ckpt", default="", help = "path to numpy checkpoint file") 360 | parser.add_argument("--start_iter", default=0, type=int, help = "iteration number for start, useful when loading a checkpoint") 361 | parser.add_argument("--init_size", default=32, type=int, help = "starting with this size when --use_resize") 362 | parser.add_argument("--uniform", action='store_true', help = "disable importance sampling") 363 | parser.add_argument("--lr", type=float, default=1e-2, help = "learning rate") 364 | parser.add_argument("--kappa", type=int, default=0, help = "initial_constance") 365 | args = vars(parser.parse_args()) 366 | 367 | # setup random seed 368 | random.seed(args['seed']) 369 | np.random.seed(args['seed']) 370 | tf.set_random_seed(args['seed']) 371 | print(args) 372 | 373 | main(args) 374 | 375 | print("Experiment Done!!!") 376 | 377 | -------------------------------------------------------------------------------- /l1_attack.py: -------------------------------------------------------------------------------- 1 | ## l1_attack.py -- attack a network optimizing elastic-net distance with an l1 decision rule 2 | ## 3 | ## Copyright (C) 2017, Yash Sharma . 4 | ## Copyright (C) 2016, Nicholas Carlini . 5 | ## 6 | ## This program is licenced under the BSD 2-Clause licence, 7 | ## contained in the LICENCE file in this directory. 8 | 9 | import sys 10 | import tensorflow as tf 11 | import numpy as np 12 | import time 13 | 14 | BINARY_SEARCH_STEPS = 9 # number of times to adjust the constant with binary search 15 | MAX_ITERATIONS = 10000 # number of iterations to perform gradient descent 16 | ABORT_EARLY = True # if we stop improving, abort gradient descent early 17 | LEARNING_RATE = 1e-2 # larger values converge faster to less accurate results 18 | TARGETED = True # should we target one specific class? or just be wrong? 19 | CONFIDENCE = 0 # how strong the adversarial example should be 20 | INITIAL_CONST = 1e-3 # the initial constant c to pick as a first guess 21 | BETA = 1e-3 # Hyperparameter trading off L2 minimization for L1 minimization 22 | 23 | class EADL1: 24 | def __init__(self, sess, model, batch_size=1, confidence = CONFIDENCE, 25 | targeted = TARGETED, learning_rate = LEARNING_RATE, 26 | binary_search_steps = BINARY_SEARCH_STEPS, max_iterations = MAX_ITERATIONS, 27 | print_every = 100, early_stop_iters = 0, 28 | abort_early = ABORT_EARLY, 29 | initial_const = INITIAL_CONST, beta = BETA): 30 | """ 31 | EAD with L1 Decision Rule 32 | 33 | This attack is the most efficient and should be used as the primary 34 | attack to evaluate potential defenses. 35 | 36 | Returns adversarial examples for the supplied model. 37 | 38 | confidence: Confidence of adversarial examples: higher produces examples 39 | that are farther away, but more strongly classified as adversarial. 40 | batch_size: Number of attacks to run simultaneously. 41 | targeted: True if we should perform a targetted attack, False otherwise. 42 | learning_rate: The learning rate for the attack algorithm. Smaller values 43 | produce better results but are slower to converge. 44 | binary_search_steps: The number of times we perform binary search to 45 | find the optimal tradeoff-constant between distance and confidence. 46 | max_iterations: The maximum number of iterations. Larger values are more 47 | accurate; setting too small will require a large learning rate and will 48 | produce poor results. 49 | abort_early: If true, allows early aborts if gradient descent gets stuck. 50 | initial_const: The initial tradeoff-constant to use to tune the relative 51 | importance of distance and confidence. If binary_search_steps is large, 52 | the initial constant is not important. 53 | """ 54 | 55 | image_size, num_channels, num_labels = model.image_size, model.num_channels, model.num_labels 56 | self.sess = sess 57 | self.TARGETED = targeted 58 | self.LEARNING_RATE = learning_rate 59 | self.MAX_ITERATIONS = max_iterations 60 | self.print_every = print_every 61 | self.early_stop_iters = early_stop_iters if early_stop_iters != 0 else max_iterations // 10 62 | print("early stop:", self.early_stop_iters) 63 | self.BINARY_SEARCH_STEPS = binary_search_steps 64 | self.ABORT_EARLY = abort_early 65 | self.CONFIDENCE = confidence 66 | self.initial_const = initial_const 67 | self.batch_size = batch_size 68 | self.beta = beta 69 | self.beta_t = tf.cast(self.beta, tf.float32) 70 | 71 | self.repeat = binary_search_steps >= 10 72 | 73 | shape = (batch_size,image_size,image_size,num_channels) 74 | 75 | # these are variables to be more efficient in sending data to tf 76 | self.timg = tf.Variable(np.zeros(shape), dtype=tf.float32) 77 | self.newimg = tf.Variable(np.zeros(shape), dtype=tf.float32) 78 | self.slack = tf.Variable(np.zeros(shape), dtype=tf.float32) 79 | self.tlab = tf.Variable(np.zeros((batch_size,num_labels)), dtype=tf.float32) 80 | self.const = tf.Variable(np.zeros(batch_size), dtype=tf.float32) 81 | 82 | # and here's what we use to assign them 83 | self.assign_timg = tf.placeholder(tf.float32, shape) 84 | self.assign_newimg = tf.placeholder(tf.float32, shape) 85 | self.assign_slack = tf.placeholder(tf.float32, shape) 86 | self.assign_tlab = tf.placeholder(tf.float32, (batch_size,num_labels)) 87 | self.assign_const = tf.placeholder(tf.float32, [batch_size]) 88 | 89 | self.global_step = tf.Variable(0, trainable=False) 90 | self.global_step_t = tf.cast(self.global_step, tf.float32) 91 | 92 | """Fast Iterative Soft Thresholding""" 93 | """--------------------------------""" 94 | self.zt = tf.divide(self.global_step_t, self.global_step_t+tf.cast(3, tf.float32)) 95 | 96 | cond1 = tf.cast(tf.greater(tf.subtract(self.slack, self.timg),self.beta_t), tf.float32) 97 | cond2 = tf.cast(tf.less_equal(tf.abs(tf.subtract(self.slack,self.timg)),self.beta_t), tf.float32) 98 | cond3 = tf.cast(tf.less(tf.subtract(self.slack, self.timg),tf.negative(self.beta_t)), tf.float32) 99 | 100 | upper = tf.minimum(tf.subtract(self.slack,self.beta_t), tf.cast(0.5, tf.float32)) 101 | lower = tf.maximum(tf.add(self.slack,self.beta_t), tf.cast(-0.5, tf.float32)) 102 | 103 | self.assign_newimg = tf.multiply(cond1,upper)+tf.multiply(cond2,self.timg)+tf.multiply(cond3,lower) 104 | self.assign_slack = self.assign_newimg+tf.multiply(self.zt, self.assign_newimg-self.newimg) 105 | self.setter = tf.assign(self.newimg, self.assign_newimg) 106 | self.setter_y = tf.assign(self.slack, self.assign_slack) 107 | """--------------------------------""" 108 | # prediction BEFORE-SOFTMAX of the model 109 | self.output = model.predict(self.newimg) 110 | self.output_y = model.predict(self.slack) 111 | 112 | # distance to the input data 113 | self.l2dist = tf.reduce_sum(tf.square(self.newimg-self.timg),[1,2,3]) 114 | self.l2dist_y = tf.reduce_sum(tf.square(self.slack-self.timg),[1,2,3]) 115 | self.l1dist = tf.reduce_sum(tf.abs(self.newimg-self.timg),[1,2,3]) 116 | self.l1dist_y = tf.reduce_sum(tf.abs(self.slack-self.timg),[1,2,3]) 117 | self.elasticdist = self.l2dist + tf.multiply(self.l1dist, self.beta_t) 118 | self.elasticdist_y = self.l2dist_y + tf.multiply(self.l1dist_y, self.beta_t) 119 | 120 | # compute the probability of the label class versus the maximum other 121 | self.real = tf.reduce_sum((self.tlab)*self.output,1) 122 | self.real_y = tf.reduce_sum((self.tlab)*self.output_y,1) 123 | self.other = tf.reduce_max((1-self.tlab)*self.output - (self.tlab*10000),1) 124 | self.other_y = tf.reduce_max((1-self.tlab)*self.output_y - (self.tlab*10000),1) 125 | if self.TARGETED: 126 | # if targeted, optimize for making the other class most likely 127 | loss1 = tf.maximum(0.0, self.other-self.real+self.CONFIDENCE) 128 | loss1_y = tf.maximum(0.0, self.other_y-self.real_y+self.CONFIDENCE) 129 | else: 130 | # if untargeted, optimize for making this class least likely. 131 | loss1 = tf.maximum(0.0, self.real-self.other+self.CONFIDENCE) 132 | loss1_y = tf.maximum(0.0, self.real_y-self.other_y+self.CONFIDENCE) 133 | 134 | # sum up the losses 135 | # self.loss21 = tf.reduce_sum(self.l1dist) 136 | # # self.loss21_y = tf.reduce_sum(self.l1dist_y) 137 | # self.loss2 = tf.reduce_sum(self.l2dist) 138 | # self.loss2_y = tf.reduce_sum(self.l2dist_y) 139 | # self.loss1 = tf.reduce_sum(self.const*loss1) 140 | # self.loss1_y = tf.reduce_sum(self.const*loss1_y) 141 | 142 | self.loss21 = self.l1dist 143 | # self.loss21_y = tf.reduce_sum(self.l1dist_y) 144 | self.loss2 = self.l2dist 145 | self.loss2_y = self.l2dist_y 146 | self.loss1 = self.const*loss1 147 | self.loss1_y = self.const*loss1_y 148 | 149 | 150 | self.loss_opt = self.loss1_y+self.loss2_y 151 | self.loss = self.loss1+self.loss2+tf.multiply(self.beta_t,self.loss21) 152 | 153 | 154 | print("self.loss = ", self.loss) 155 | print("self.loss_opt = ", self.loss_opt) 156 | print("self.loss1_y = ", self.loss1_y) 157 | print("self.loss2_y = ", self.loss2_y) 158 | print("self.real = ", self.real) 159 | print("self.other = ", self.other) 160 | 161 | self.learning_rate = tf.train.polynomial_decay(self.LEARNING_RATE, self.global_step, self.MAX_ITERATIONS, 0, power=0.5) 162 | start_vars = set(x.name for x in tf.global_variables()) 163 | optimizer = tf.train.GradientDescentOptimizer(self.learning_rate) 164 | self.train = optimizer.minimize(self.loss_opt, var_list=[self.slack], global_step=self.global_step) 165 | end_vars = tf.global_variables() 166 | new_vars = [x for x in end_vars if x.name not in start_vars] 167 | 168 | # these are the variables to initialize when we run 169 | self.setup = [] 170 | self.setup.append(self.timg.assign(self.assign_timg)) 171 | self.setup.append(self.tlab.assign(self.assign_tlab)) 172 | self.setup.append(self.const.assign(self.assign_const)) 173 | 174 | self.init = tf.variables_initializer(var_list=[self.global_step]+[self.slack]+[self.newimg]+new_vars) 175 | 176 | def attack(self, imgs, targets): 177 | """ 178 | Perform the EAD attack on the given images for the given targets. 179 | 180 | If self.targeted is true, then the targets represents the target labels. 181 | If self.targeted is false, then targets are the original class labels. 182 | """ 183 | r = [] 184 | print('go up to',len(imgs)) 185 | for i in range(0,len(imgs),self.batch_size): 186 | print('tick',i) 187 | #r.extend(self.attack_batch(imgs[i:i+self.batch_size], targets[i:i+self.batch_size])) 188 | r.extend(self.attack_batch(imgs[i:i+self.batch_size], targets[i:i+self.batch_size])[0]) 189 | return np.array(r) 190 | 191 | def attack_batch(self, imgs, labs): 192 | """ 193 | Run the attack on a batch of images and labels. 194 | """ 195 | def compare(x,y): 196 | if not isinstance(x, (float, int, np.int64)): 197 | x = np.copy(x) 198 | if self.TARGETED: 199 | x[y] -= self.CONFIDENCE 200 | else: 201 | x[y] += self.CONFIDENCE 202 | x = np.argmax(x) 203 | if self.TARGETED: 204 | return x == y 205 | else: 206 | return x != y 207 | 208 | batch_size = self.batch_size 209 | 210 | print("="*10, "batch_size = ", batch_size, "="*10) 211 | 212 | # # convert to tanh-space 213 | # imgs = np.arctanh(imgs*1.999999) 214 | 215 | # set the lower and upper bounds accordingly 216 | lower_bound = np.zeros(batch_size) 217 | CONST = np.ones(batch_size)*self.initial_const 218 | upper_bound = np.ones(batch_size)*1e10 219 | n_success = 0 220 | 221 | # the best l2, score, and image attack 222 | o_bestl1 = [1e10]*batch_size 223 | o_bestscore = [-1]*batch_size 224 | o_bestattack = [np.zeros(imgs[0].shape)]*batch_size 225 | # fill the array as nan to indicate attack failure 226 | for b in o_bestattack: 227 | b.fill(np.nan) 228 | o_best_const = [self.initial_const]*batch_size 229 | 230 | for outer_step in range(self.BINARY_SEARCH_STEPS): 231 | print("current best l1", o_bestl1) 232 | # completely reset adam's internal state. 233 | self.sess.run(self.init) 234 | batch = imgs[:batch_size] 235 | batchlab = labs[:batch_size] 236 | 237 | bestl1 = [1e10]*batch_size 238 | bestscore = [-1]*batch_size 239 | 240 | # The last iteration (if we run many steps) repeat the search once. 241 | if self.repeat == True and outer_step == self.BINARY_SEARCH_STEPS-1: 242 | CONST = upper_bound 243 | 244 | # set the variables so that we don't have to send them over again 245 | self.sess.run(self.setup, {self.assign_timg: batch, 246 | self.assign_tlab: batchlab, 247 | self.assign_const: CONST}) 248 | self.sess.run(self.setter, feed_dict={self.assign_newimg: batch}) 249 | self.sess.run(self.setter_y, feed_dict={self.assign_slack: batch}) 250 | prev = 1e6 251 | train_timer = 0.0 252 | for iteration in range(self.MAX_ITERATIONS): 253 | # print out the losses every 10% 254 | # print("iteration = ", iteration) 255 | if iteration%(self.MAX_ITERATIONS//self.print_every) == 0: 256 | # print(iteration,self.sess.run((self.loss,self.real,self.other,self.loss1,self.loss2))) 257 | # grad = self.sess.run(self.grad_op) 258 | # old_modifier = self.sess.run(self.modifier) 259 | # np.save('white_iter_{}'.format(iteration), modifier) 260 | loss, real, other, loss1, loss2, loss21 = self.sess.run((self.loss,self.real,self.other,self.loss1,self.loss2, self.loss21)) 261 | # print("loss = ", loss) 262 | # print("real = ", real) 263 | # print("other = ", other) 264 | # print("loss1 = ", loss1) 265 | # print("loss2 = ", loss2) 266 | 267 | if self.batch_size == 1: 268 | print("[STATS][L2] iter = {}, time = {:.3f}, loss = {:.5g}, real = {:.5g}, other = {:.5g}, loss1 = {:.5g}, loss2 = {:.5g}, loss21 = {:.5g}".format(iteration, train_timer, loss[0], real[0], other[0], loss1[0], loss2[0], loss21[0])) 269 | #print("[STATS][L2] iter = {}, time = {:.3f}, real = {:.5g}, other = {:.5g}".format(iteration, train_timer, real[0], other[0])) 270 | elif self.batch_size > 10: 271 | print("[STATS][L2][SUM of {}] iter = {}, time = {:.3f}, batch_size = {}, n_success = {:.5g}, loss = {:.5g}, real = {:.5g}, other = {:.5g}, loss1 = {:.5g}, loss2 = {:.5g}, loss21 = {:.5g}".format(self.batch_size, iteration, train_timer, batch_size, n_success, sum(loss), sum(real), sum(other), sum(loss1), sum(loss2), sum(loss21))) 272 | # print("[STATS][L2][SUM of {}] iter = {}, time = {:.3f}, batch_size = {}, n_success = {:.5g}, real = {:.5g}, other = {:.5g}".format(self.batch_size, iteration, train_timer, batch_size, n_success, sum(real), sum(other))) 273 | else: 274 | print("[STATS][L2] iter = {}, time = {:.3f}".format(iteration, train_timer)) 275 | print("[STATS][L2] real =", real) 276 | print("[STATS][L2] other =", other) 277 | print("[STATS][L2] loss1 =", loss1) 278 | print("[STATS][L2] loss2 =", loss2) 279 | print("[STATS][L2] loss21 =", loss21) 280 | print("[STATS][L2] loss =", loss) 281 | sys.stdout.flush() 282 | 283 | attack_begin_time = time.time() 284 | 285 | # perform the attack 286 | self.sess.run([self.train]) 287 | self.sess.run([self.setter, self.setter_y]) 288 | l, l2s, l1s, elastic, scores, nimg = self.sess.run([self.loss, self.l2dist, self.l1dist, self.elasticdist, self.output, self.newimg]) 289 | 290 | 291 | # print out the losses every 10% 292 | """ 293 | if iteration%(self.MAX_ITERATIONS//10) == 0: 294 | print(iteration,self.sess.run((self.loss,self.loss1,self.loss2,self.loss21))) 295 | """ 296 | # check if we should abort search if we're getting nowhere. 297 | if self.ABORT_EARLY and iteration % self.early_stop_iters == 0: 298 | print("bookkeeping previous loss, iteration = ", iteration, ', l = ', l) 299 | if np.all(l > prev*.9999): 300 | #if l > prev*.9999: 301 | print("Early stopping because there is no improvement") 302 | break 303 | prev = l 304 | 305 | # adjust the best result found so far 306 | read_last_loss = False 307 | for e,(l1,sc,ii) in enumerate(zip(l1s,scores,nimg)): 308 | if l1 < bestl1[e] and compare(sc, np.argmax(batchlab[e])): 309 | bestl1[e] = l1 310 | bestscore[e] = np.argmax(sc) 311 | if l1 < o_bestl1[e] and compare(sc, np.argmax(batchlab[e])): 312 | # print a message if it is the first attack found 313 | if o_bestl1[e] == 1e10: 314 | if not read_last_loss: 315 | loss, real, other, loss1, loss2, loss21 = self.sess.run((self.loss,self.real,self.other,self.loss1,self.loss2, self.loss21)) 316 | read_last_loss = True 317 | print("[STATS][L3][First valid attack found!] iter = {}, time = {:.3f}, img = {}, loss = {:.5g}, real = {:.5g}, other = {:.5g}, loss1 = {:.5g}, loss2 = {:.5g}, loss21 = {:.5g}".format(iteration, train_timer, e, loss[e], real[e], other[e], loss1[e], loss2[e], loss21[e])) 318 | #print("[STATS][L3][First valid attack found!] iter = {}, time = {:.3f}, img = {}, real = {:.5g}, other = {:.5g}".format(iteration, train_timer, e, real[e], other[e])) 319 | 320 | n_success += 1 321 | o_bestl1[e] = l1 322 | o_bestscore[e] = np.argmax(sc) 323 | o_bestattack[e] = ii 324 | o_best_const[e] = CONST[e] 325 | 326 | train_timer += time.time() - attack_begin_time 327 | 328 | # adjust the constant as needed 329 | for e in range(batch_size): 330 | if compare(bestscore[e], np.argmax(batchlab[e])) and bestscore[e] != -1: 331 | # modifier = self.sess.run(self.modifier) 332 | # np.save("best.model", modifier) 333 | print('old constant: ', CONST[e]) 334 | # success, divide const by two 335 | upper_bound[e] = min(upper_bound[e],CONST[e]) 336 | if upper_bound[e] < 1e9: 337 | CONST[e] = (lower_bound[e] + upper_bound[e])/2 338 | print('new constant: ', CONST[e]) 339 | else: 340 | print('old constant: ', CONST[e]) 341 | # failure, either multiply by 10 if no solution found yet 342 | # or do binary search with the known upper bound 343 | lower_bound[e] = max(lower_bound[e],CONST[e]) 344 | if upper_bound[e] < 1e9: 345 | CONST[e] = (lower_bound[e] + upper_bound[e])/2 346 | else: 347 | CONST[e] *= 10 348 | print('new constant: ', CONST[e]) 349 | 350 | # return the best solution found 351 | o_bestl1 = np.array(o_bestl1) 352 | return np.array(o_bestattack), o_best_const 353 | -------------------------------------------------------------------------------- /setup_imagenet.py: -------------------------------------------------------------------------------- 1 | ## Modified by Huan Zhang for the updated Inception-v3 model (inception_v3_2016_08_28.tar.gz) 2 | ## Modified by Nicholas Carlini to match model structure for attack code. 3 | ## Original copyright license follows. 4 | 5 | 6 | # Copyright 2015 The TensorFlow Authors. All Rights Reserved. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # ============================================================================== 20 | 21 | """Simple image classification with an ImageNet Classifier. 22 | 23 | Run image classification with an ImageNet Classifier (Inception, ResNet, AlexNet, etc) trained on ImageNet 2012 Challenge data 24 | set. 25 | 26 | This program creates a graph from a saved GraphDef protocol buffer, 27 | and runs inference on an input JPEG image. It outputs human readable 28 | strings of the top 5 predictions along with their probabilities. 29 | 30 | Change the --image_file argument to any jpg image to compute a 31 | classification of that image. 32 | 33 | Please see the tutorial and website for a detailed description of how 34 | to use this script to perform image recognition. 35 | 36 | https://tensorflow.org/tutorials/image_recognition/ 37 | """ 38 | 39 | from __future__ import absolute_import 40 | from __future__ import division 41 | from __future__ import print_function 42 | 43 | import os.path 44 | import re 45 | import sys 46 | from functools import partial 47 | import random 48 | import tarfile 49 | import scipy.misc 50 | 51 | import numpy as np 52 | from six.moves import urllib 53 | import tensorflow as tf 54 | 55 | import PIL 56 | from PIL import Image 57 | 58 | 59 | model_params = {} 60 | 61 | 62 | """Add a new new entry to ImageNet models 63 | 64 | Parameters: 65 | name: name of the new model, like "resnet" 66 | url: URL to download the model 67 | image_size: image size, usually 224 or 299 68 | model_filename: model protobuf file name (.pb) 69 | label_filename: a text file contains the mapping from class ID to human readable string 70 | input_tensor: input tensor of the network defined by protobuf, like "input:0" 71 | logit: logit output tensor of the network, like "resnet_v2_50/predictions/Reshape:0" 72 | prob: probability output tensor of the network, like "resnet_v2_50/predictions/Reshape_1:0" 73 | shape: tensor for reshaping the final output, like "resnet_v2_50/predictions/Shape:0". 74 | Set to None if no reshape needed. 75 | 76 | All the tensor names can be viewed and found in TensorBoard. 77 | """ 78 | def AddModel(name, url, model_filename, image_size, label_filename, input_tensor, logit, prob, shape): 79 | global model_params 80 | param = {} 81 | param['url'] = url 82 | param['model_filename'] = model_filename 83 | param['size'] = image_size 84 | param['input'] = input_tensor 85 | param['logit'] = logit 86 | param['prob'] = prob 87 | param['shape'] = shape 88 | param['label_filename'] = label_filename 89 | param['name'] = name 90 | model_params[name] = param 91 | 92 | # pylint: disable=line-too-long 93 | AddModel('resnet_v2_50', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 94 | 'frozen_resnet_v2_50.pb', 299, 'labels.txt', 'input:0', 95 | 'resnet_v2_50/predictions/Reshape:0', 'resnet_v2_50/predictions/Reshape_1:0', 'resnet_v2_50/predictions/Shape:0') 96 | AddModel('resnet_v2_101', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 97 | 'frozen_resnet_v2_101.pb', 299, 'labels.txt', 'input:0', 98 | 'resnet_v2_101/predictions/Reshape:0', 'resnet_v2_101/predictions/Reshape_1:0', 'resnet_v2_101/predictions/Shape:0') 99 | AddModel('resnet_v2_152', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 100 | 'frozen_resnet_v2_152.pb', 299, 'labels.txt', 'input:0', 101 | 'resnet_v2_152/predictions/Reshape:0', 'resnet_v2_152/predictions/Reshape_1:0', 'resnet_v2_152/predictions/Shape:0') 102 | AddModel('inception_v1', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 103 | 'frozen_inception_v1.pb', 224, 'labels.txt', 'input:0', 104 | 'InceptionV1/Logits/Predictions/Reshape:0', 'InceptionV1/Logits/Predictions/Reshape_1:0', 'InceptionV1/Logits/Predictions/Shape:0') 105 | AddModel('inception_v2', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 106 | 'frozen_inception_v2.pb', 224, 'labels.txt', 'input:0', 107 | 'InceptionV2/Predictions/Reshape:0', 'InceptionV2/Predictions/Reshape_1:0', 'InceptionV2/Predictions/Shape:0') 108 | AddModel('inception_v3', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 109 | 'frozen_inception_v3.pb', 299, 'labels.txt', 'input:0', 110 | 'InceptionV3/Predictions/Reshape:0', 'InceptionV3/Predictions/Softmax:0', 'InceptionV3/Predictions/Shape:0') 111 | AddModel('inception_v4', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 112 | 'frozen_inception_v4.pb', 299, 'labels.txt', 'input:0', 113 | 'InceptionV4/Logits/Logits/BiasAdd:0', 'InceptionV4/Logits/Predictions:0', '') 114 | AddModel('inception_resnet_v2', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 115 | 'frozen_inception_resnet_v2.pb', 299, 'labels.txt', 'input:0', 116 | 'InceptionResnetV2/Logits/Logits/BiasAdd:0', 'InceptionResnetV2/Logits/Predictions:0', '') 117 | AddModel('vgg_16', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 118 | 'frozen_vgg_16.pb', 224, 'labels.txt', 'input:0', 119 | 'vgg_16/fc8/squeezed:0', 'vgg_16/fc8/squeezed:0', '') 120 | AddModel('vgg_19', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 121 | 'frozen_vgg_19.pb', 224, 'labels.txt', 'input:0', 122 | 'vgg_19/fc8/squeezed:0', 'vgg_19/fc8/squeezed:0', '') 123 | AddModel('mobilenet_v1_025', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 124 | 'frozen_mobilenet_v1_025.pb', 224, 'labels.txt', 'input:0', 125 | 'MobilenetV1/Predictions/Reshape:0', 'MobilenetV1/Predictions/Reshape_1:0', 'MobilenetV1/Predictions/Shape:0') 126 | AddModel('mobilenet_v1_050', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 127 | 'frozen_mobilenet_v1_050.pb', 224, 'labels.txt', 'input:0', 128 | 'MobilenetV1/Predictions/Reshape:0', 'MobilenetV1/Predictions/Reshape_1:0', 'MobilenetV1/Predictions/Shape:0') 129 | AddModel('mobilenet_v1_100', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 130 | 'frozen_mobilenet_v1_100.pb', 224, 'labels.txt', 'input:0', 131 | 'MobilenetV1/Predictions/Reshape:0', 'MobilenetV1/Predictions/Reshape_1:0', 'MobilenetV1/Predictions/Shape:0') 132 | AddModel('nasnet_large', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 133 | 'frozen_nasnet_large.pb', 331, 'labels.txt', 'input:0', 134 | 'final_layer/FC/BiasAdd:0', 'final_layer/predictions:0', '') 135 | AddModel('densenet121_k32', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 136 | 'densenet121_k32_frozen.pb', 224, 'labels.txt', 'input:0', 137 | 'densenet121/predictions/Reshape:0', 'densenet121/predictions/Reshape_1:0', 'densenet121/predictions/Shape:0') 138 | AddModel('densenet169_k32', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 139 | 'densenet169_k32_frozen.pb', 224, 'labels.txt', 'input:0', 140 | 'densenet169/predictions/Reshape:0', 'densenet169/predictions/Reshape_1:0', 'densenet169/predictions/Shape:0') 141 | AddModel('densenet161_k48', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 142 | 'densenet161_k48_frozen.pb', 224, 'labels.txt', 'input:0', 143 | 'densenet161/predictions/Reshape:0', 'densenet161/predictions/Reshape_1:0', 'densenet161/predictions/Shape:0') 144 | AddModel('alexnet', 'http://download.huan-zhang.com/models/adv/imagenet/frozen_imagenet_models_v1.1.tar.gz', 145 | 'alexnet_frozen.pb', 227, 'labels.txt', 'Placeholder:0', 146 | 'fc8/fc8:0', 'Softmax:0', '') 147 | 148 | # pylint: enable=line-too-long 149 | 150 | 151 | class NodeLookup(object): 152 | """Converts integer node ID's to human readable labels.""" 153 | 154 | def __init__(self, 155 | label_lookup_path=None): 156 | if not label_lookup_path: 157 | label_lookup_path = os.path.join( 158 | FLAGS.model_dir, 'labels.txt') 159 | self.node_lookup = self.load(label_lookup_path) 160 | 161 | def load(self, label_lookup_path): 162 | """Loads a human readable English name for each softmax node. 163 | 164 | Args: 165 | label_lookup_path: string UID to integer node ID. 166 | uid_lookup_path: string UID to human-readable string. 167 | 168 | Returns: 169 | dict from integer node ID to human-readable string. 170 | """ 171 | if not tf.gfile.Exists(label_lookup_path): 172 | tf.logging.fatal('File does not exist %s', label_lookup_path) 173 | 174 | # Loads mapping from string UID to integer node ID. 175 | node_id_to_name = {} 176 | proto_as_ascii = tf.gfile.GFile(label_lookup_path).readlines() 177 | for line in proto_as_ascii: 178 | if line: 179 | words = line.split(':') 180 | target_class = int(words[0]) 181 | name = words[1] 182 | node_id_to_name[target_class] = name 183 | 184 | return node_id_to_name 185 | 186 | def id_to_string(self, node_id): 187 | if node_id not in self.node_lookup: 188 | return '' 189 | return self.node_lookup[node_id] 190 | 191 | LOADED_GRAPH = None 192 | 193 | def create_graph(model_param): 194 | """Creates a graph from saved GraphDef file and returns a saver.""" 195 | # Creates graph from saved graph_def.pb. 196 | global LOADED_GRAPH 197 | with tf.gfile.FastGFile(os.path.join( 198 | # FLAGS.model_dir, 'classify_image_graph_def.pb'), 'rb') as f: 199 | FLAGS.model_dir, model_param['model_filename']), 'rb') as f: 200 | graph_def = tf.GraphDef() 201 | graph_def.ParseFromString(f.read()) 202 | #for line in repr(graph_def).split("\n"): 203 | # if "tensor_content" not in line: 204 | # print(line) 205 | LOADED_GRAPH = graph_def 206 | 207 | 208 | class ImageNetModelPrediction: 209 | def __init__(self, sess, use_softmax = False, model_name = "resnet_v2_50", softmax_tensor = None): 210 | self.sess = sess 211 | self.use_softmax = use_softmax 212 | model_param = model_params[model_name] 213 | self.output_name = model_param['prob'] if self.use_softmax else model_param['logit'] 214 | self.input_name = model_param['input'] 215 | self.shape_name = model_param['shape'] 216 | self.model_name = model_param['name'] 217 | self.image_size = model_param['size'] 218 | self.img = tf.placeholder(tf.float32, (None, self.image_size, self.image_size, 3)) 219 | if not softmax_tensor: 220 | # no existing graph 221 | self.softmax_tensor = tf.import_graph_def( 222 | LOADED_GRAPH, 223 | # sess.graph.as_graph_def(), 224 | input_map={self.input_name: self.img}, 225 | return_elements=[self.output_name]) 226 | if 'vgg' in self.model_name and use_softmax == True: 227 | # the pretrained VGG network output is logits, need an extra softmax 228 | self.softmax_tensor = tf.nn.softmax(self.softmax_tensor) 229 | else: 230 | # use an existing graph 231 | self.softmax_tensor = softmax_tensor 232 | print("GraphDef Size:", self.sess.graph_def.ByteSize()) 233 | 234 | def predict(self, dat): 235 | dat = np.squeeze(dat) 236 | if 'vgg' in self.model_name: 237 | # VGG uses 0 - 255 image as input 238 | dat = (0.5 + dat) * 255.0 239 | imagenet_mean = np.array([123.68, 116.78, 103.94], dtype=np.float32) 240 | dat -= imagenet_mean 241 | elif 'alexnet' in self.model_name: 242 | if dat.ndim == 3: 243 | dat = dat[:,:,::-1] 244 | else: 245 | dat = dat[:,:,:,::-1] # change RGB to BGR 246 | dat = (0.5 + dat) * 255.0 247 | imagenet_mean = np.array([104., 117., 124.], dtype=np.float32) 248 | dat -= imagenet_mean 249 | elif 'densenet' in self.model_name: 250 | dat = (0.5 + dat) * 255.0 251 | imagenet_mean = np.array([123.68, 116.78, 103.94], dtype=np.float32) 252 | dat -= imagenet_mean 253 | dat = dat * 0.017 254 | else: 255 | dat = dat * 2.0 256 | 257 | 258 | if dat.ndim == 3: 259 | scaled = dat.reshape((1,) + dat.shape) 260 | else: 261 | scaled = dat 262 | # print(scaled.shape) 263 | predictions = self.sess.run(self.softmax_tensor, 264 | {self.img: scaled}) 265 | predictions = np.squeeze(predictions) 266 | return predictions 267 | # Creates node ID --> English string lookup. 268 | node_lookup = NodeLookup() 269 | top_k = predictions.argsort()#[-FLAGS.num_top_predictions:][::-1] 270 | for node_id in top_k: 271 | print('id',node_id) 272 | human_string = node_lookup.id_to_string(node_id) 273 | score = predictions[node_id] 274 | print('%s (score = %.5f)' % (human_string, score)) 275 | return top_k[-1] 276 | 277 | 278 | CREATED_GRAPH = False 279 | class ImageNetModel: 280 | def __init__(self, sess, use_softmax = False, model_name = "resnet_v2_50", create_prediction = True): 281 | global CREATED_GRAPH 282 | self.sess = sess 283 | self.use_softmax = use_softmax 284 | model_param = model_params[model_name] 285 | maybe_download_and_extract(model_param) 286 | 287 | if not CREATED_GRAPH: 288 | create_graph(model_param) 289 | CREATED_GRAPH = True 290 | self.num_channels = 3 291 | self.output_name = model_param['prob'] if self.use_softmax else model_param['logit'] 292 | self.input_name = model_param['input'] 293 | self.shape_name = model_param['shape'] 294 | self.model_name = model_param['name'] 295 | self.num_labels = 1000 if 'vgg' in self.model_name or 'densenet' in self.model_name or 'alexnet' in self.model_name else 1001 296 | self.image_size = model_param['size'] 297 | self.use_softmax = use_softmax 298 | if create_prediction: 299 | self.model = ImageNetModelPrediction(sess, use_softmax, model_name) 300 | 301 | def predict(self, img): 302 | if 'vgg' in self.model_name: 303 | # VGG uses 0 - 255 image as input 304 | img = (0.5 + img) * 255.0 305 | imagenet_mean = np.array([123.68, 116.78, 103.94], dtype=np.float32) 306 | img -= imagenet_mean 307 | elif 'alexnet' in self.model_name: 308 | img = tf.reverse(img,axis=[-1])# change RGB to BGR 309 | img = (0.5 + img) * 255.0 310 | imagenet_mean = np.array([104., 117., 124.], dtype=np.float32) 311 | img -= imagenet_mean 312 | elif 'densenet' in self.model_name: 313 | # convert to 0 - 255 image as input 314 | img = (0.5 + img) * 255.0 315 | imagenet_mean = np.array([123.68, 116.78, 103.94], dtype=np.float32) 316 | img -= imagenet_mean 317 | img = img * 0.017 318 | else: 319 | img = img * 2.0 320 | 321 | if img.shape.is_fully_defined() and img.shape.as_list()[0] and self.shape_name: 322 | # check if a shape has been specified explicitly 323 | shape = (int(img.shape[0]), self.num_labels) 324 | self.softmax_tensor = tf.import_graph_def( 325 | LOADED_GRAPH, 326 | # self.sess.graph.as_graph_def(), 327 | input_map={self.input_name: img, self.shape_name: shape}, 328 | return_elements=[self.output_name]) 329 | if 'vgg' in self.model_name and self.use_softmax == True: 330 | # the pretrained VGG network output is logitimport_graph_defs, need an extra softmax 331 | self.softmax_tensor = tf.nn.softmax(self.softmax_tensor) 332 | else: 333 | # placeholder shape 334 | self.softmax_tensor = tf.import_graph_def( 335 | LOADED_GRAPH, 336 | # self.sess.graph.as_graph_def(), 337 | input_map={self.input_name: img}, 338 | return_elements=[self.output_name]) 339 | if 'vgg' in self.model_name and self.use_softmax == True: 340 | # the pretrained VGG network output is logits, need an extra softmax 341 | self.softmax_tensor = tf.nn.softmax(self.softmax_tensor) 342 | print("GraphDef Size:", self.sess.graph_def.ByteSize()) 343 | return self.softmax_tensor[0] 344 | 345 | 346 | def maybe_download_and_extract(model_param): 347 | """Download and extract model tar file.""" 348 | dest_directory = FLAGS.model_dir 349 | if not os.path.exists(dest_directory): 350 | os.makedirs(dest_directory) 351 | filename = model_param['url'].split('/')[-1] 352 | filepath = os.path.join(dest_directory, filename) 353 | modelname = model_param['model_filename'].split('/')[-1] 354 | modelpath = os.path.join(dest_directory, modelname) 355 | if not os.path.exists(modelpath): 356 | def _progress(count, block_size, total_size): 357 | sys.stdout.write('\r>> Downloading %s %.1f%%' % ( 358 | filename, float(count * block_size) / float(total_size) * 100.0)) 359 | sys.stdout.flush() 360 | filepath, _ = urllib.request.urlretrieve(model_param['url'], filepath, _progress) 361 | print() 362 | statinfo = os.stat(filepath) 363 | print('Succesfully downloaded', filename, statinfo.st_size, 'bytes.') 364 | if os.path.splitext(filename)[1] != '.pb': 365 | tarfile.open(filepath, 'r:gz').extractall(dest_directory) 366 | 367 | 368 | def main(_): 369 | param = model_params[FLAGS.model_name] 370 | maybe_download_and_extract(param) 371 | image = (FLAGS.image_file if FLAGS.image_file else 372 | os.path.join(FLAGS.model_dir, 'cropped_panda.jpg')) 373 | # run_inference_on_image(image) 374 | create_graph(param) 375 | image_size = param['size'] 376 | with tf.Session() as sess: 377 | dat = np.array(scipy.misc.imresize(scipy.misc.imread(image),(image_size, image_size)), dtype = np.float32) 378 | dat /= 255.0 379 | dat -= 0.5 380 | # print(dat) 381 | model = ImageNetModelPrediction(sess, True, FLAGS.model_name) 382 | predictions = model.predict(dat) 383 | # Creates node ID --> English string lookup. 384 | node_lookup = NodeLookup() 385 | top_k = predictions.argsort()#[-FLAGS.num_top_predictions:][::-1] 386 | for node_id in top_k: 387 | score = predictions[node_id] 388 | if 'vgg' in FLAGS.model_name or 'densenet' in FLAGS.model_name or 'alexnet' in FLAGS.model_name: 389 | node_id += 1 390 | print('id',node_id) 391 | human_string = node_lookup.id_to_string(node_id) 392 | print('%s (score = %.5f)' % (human_string, score)) 393 | 394 | 395 | def keep_aspect_ratio_transform(img, img_size): 396 | 397 | s_0, s_1 = img.size 398 | if s_0 < s_1: 399 | ratio = (img_size / float(s_0)) 400 | size_1 = int((float(img.size[1]) * float(ratio))) 401 | img = img.resize((img_size, size_1), PIL.Image.ANTIALIAS) 402 | else: 403 | ratio = (img_size / float(s_1)) 404 | size_0 = int((float(img.size[0]) * float(ratio))) 405 | img = img.resize((size_0, img_size), PIL.Image.ANTIALIAS) 406 | 407 | c_0 = img.size[0] // 2 408 | c_1 = img.size[1] // 2 409 | 410 | if img_size % 2 == 0: 411 | w_left = h_top = img_size // 2 412 | w_right = h_bottom = img_size // 2 413 | else: 414 | w_left = h_top = img_size // 2 415 | w_right = h_bottom = img_size // 2 + 1 416 | 417 | transformed_img = img.crop( 418 | ( 419 | c_0 - w_left, 420 | c_1 - h_top, 421 | c_0 + w_right, 422 | c_1 + h_bottom 423 | ) 424 | ) 425 | 426 | return transformed_img 427 | 428 | def readimg(ff, img_size): 429 | f = "./imagenetdata/imgs/"+ff 430 | # img = scipy.misc.imread(f) 431 | # skip small images (image should be at least img_size X img_size) 432 | 433 | # if img.shape[0] < img_size or img.shape[1] < img_size: 434 | # return None 435 | 436 | # img = np.array(scipy.misc.imresize(img,(img_size, img_size)),dtype=np.float32)/255.0-.5 437 | img = Image.open(f) 438 | transformed_img = keep_aspect_ratio_transform(img, img_size) 439 | 440 | img = np.array(transformed_img, dtype=np.float32)/255.0-.5 441 | if img.shape != (img_size, img_size, 3): 442 | return None 443 | return [img, int(ff.split(".")[0])] 444 | 445 | class ImageNet: 446 | def __init__(self, img_size, load_total_imgs = 1000): 447 | from multiprocessing import Pool, cpu_count 448 | pool = Pool(cpu_count()) 449 | file_list = sorted(os.listdir("./imagenetdata/imgs/")) 450 | random.shuffle(file_list) 451 | # for efficiency, we only load first 1000 images 452 | # You can pass load_total_imgs to load all images 453 | short_file_list = file_list[:load_total_imgs] 454 | r = pool.map(partial(readimg, img_size=img_size), short_file_list) 455 | print(short_file_list) 456 | print("Loaded imagenet", len(short_file_list), "of", len(file_list), "images") 457 | 458 | r = [x for x in r if x != None] 459 | test_data, test_labels = zip(*r) 460 | self.test_data = np.array(test_data) 461 | self.test_labels = np.zeros((len(test_labels), 1001)) 462 | self.test_labels[np.arange(len(test_labels)), test_labels] = 1 463 | 464 | pool.close() 465 | pool.join() 466 | 467 | if __name__ == '__main__': 468 | FLAGS = tf.app.flags.FLAGS 469 | # classify_image_graph_def.pb: 470 | # Binary representation of the GraphDef protocol buffer. 471 | # imagenet_synset_to_human_label_map.txt: 472 | # Map from synset ID to a human readable string. 473 | # imagenet_2012_challenge_label_map_proto.pbtxt: 474 | # Text representation of a protocol buffer mapping a label to synset ID. 475 | tf.app.flags.DEFINE_string( 476 | 'model_dir', 'tmp/imagenet', 477 | """Path to classify_image_graph_def.pb, """ 478 | """imagenet_synset_to_human_label_map.txt, and """ 479 | """imagenet_2012_challenge_label_map_proto.pbtxt.""") 480 | tf.app.flags.DEFINE_string('image_file', '', 481 | """Absolute path to image file.""") 482 | tf.app.flags.DEFINE_string('model_name', 'resnet_v2_101', 483 | """Absolute path to image file.""") 484 | tf.app.flags.DEFINE_integer('num_top_predictions', 5, 485 | """Display this many predictions.""") 486 | tf.app.run() 487 | else: 488 | # starting from TF 1.5, an parameter unkown by tf.app.flags will raise an error 489 | # so we cannot use tf.app.flags when loading this file as a module, because the 490 | # main program may define other options. 491 | from argparse import Namespace 492 | FLAGS = Namespace(model_dir="tmp/imagenet") 493 | 494 | -------------------------------------------------------------------------------- /test_transferability.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import tensorflow as tf 4 | import numpy as np 5 | import random 6 | import time 7 | import scipy as sp 8 | import json 9 | 10 | from setup_imagenet import ImageNet, ImageNetModel 11 | 12 | from PIL import Image 13 | 14 | def dump(img, path): 15 | #save as npy 16 | np.save(path + ".npy", img) 17 | 18 | #save as png file 19 | fig = (img + 0.5)*255 20 | fig = fig.astype(np.uint8).squeeze() 21 | pic = Image.fromarray(fig) 22 | # pic.resize((512,512), resample=PIL.Image.BICUBIC) 23 | pic.save(path + ".png") 24 | 25 | return 26 | 27 | #This function assumes the input image has width == height 28 | def adapt_image_size(image_fn, src_image_size, target_image_size): 29 | 30 | 31 | img = Image.open(image_fn) 32 | 33 | if src_image_size < target_image_size: 34 | 35 | print("src_image_size < target_image_size") 36 | 37 | size_diff = target_image_size - src_image_size 38 | 39 | padding_size_1 = size_diff // 2 40 | 41 | if size_diff % 2 > 0: 42 | padding_size_2 = padding_size_1 + 1 43 | else: 44 | padding_size_2 = padding_size_1 45 | 46 | cropped_image = img.crop( 47 | ( 48 | -padding_size_1, 49 | -padding_size_1, 50 | img.size[0] + padding_size_2, 51 | img.size[1] + padding_size_2 52 | ) 53 | ) 54 | 55 | print("cropped_image.size = ", cropped_image.size) 56 | 57 | cropped_image_np = np.array(cropped_image) 58 | 59 | return cropped_image_np 60 | 61 | elif src_image_size > target_image_size: 62 | 63 | print("src_image_size > target_image_size") 64 | 65 | half_the_size = src_image_size / 2 66 | 67 | crop_size_1 = target_image_size // 2 68 | 69 | if target_image_size % 2 > 0: 70 | crop_size_2 = crop_size_1 + 1 71 | else: 72 | crop_size_2 = crop_size_1 73 | 74 | cropped_image = img.crop( 75 | ( 76 | half_the_size - crop_size_1, 77 | half_the_size - crop_size_1, 78 | half_the_size + crop_size_2, 79 | half_the_size + crop_size_2 80 | ) 81 | ) 82 | 83 | cropped_image_np = np.array(cropped_image) 84 | 85 | print("cropped_image.size = ", cropped_image.size) 86 | 87 | return cropped_image_np 88 | 89 | 90 | 91 | def adapt_image_size_on_ndarray(img, src_image_size, target_image_size): 92 | 93 | print("img.shape = ", img.shape) 94 | 95 | if src_image_size < target_image_size: 96 | 97 | print("src_image_size < target_image_size") 98 | 99 | inflated_img = np.zeros((target_image_size, target_image_size, 3), dtype=img.dtype) 100 | 101 | size_diff = target_image_size - src_image_size 102 | 103 | padding_size_1 = size_diff // 2 104 | 105 | inflated_img[padding_size_1: padding_size_1 + img.shape[0], 106 | padding_size_1: padding_size_1 + img.shape[1], 107 | :] = img 108 | 109 | print("inflated_img.shape = ", inflated_img.shape) 110 | 111 | return inflated_img 112 | 113 | elif src_image_size > target_image_size: 114 | 115 | print("src_image_size > target_image_size") 116 | 117 | mid = src_image_size // 2 118 | 119 | crop_size_1 = target_image_size // 2 120 | 121 | if target_image_size % 2 > 0: 122 | crop_size_2 = crop_size_1 + 1 123 | else: 124 | crop_size_2 = crop_size_1 125 | 126 | print("mid = ", mid) 127 | print("crop_size_1 = ", crop_size_1) 128 | print("crop_size_2 = ", crop_size_2) 129 | 130 | cropped_image = img[mid - crop_size_1:mid + crop_size_2, 131 | mid - crop_size_1:mid + crop_size_2, 132 | :] 133 | 134 | print("cropped_image.shape = ", cropped_image.shape) 135 | 136 | return cropped_image 137 | 138 | 139 | 140 | def adv_sample_fn_parser(fn): 141 | # para_list = fn.split('.')[0].split('_') 142 | para_list = fn[:-4].split('_') 143 | para_dict = {} 144 | for para in para_list: 145 | if '=' in para: 146 | para_name, para_value = para.split('=') 147 | para_dict[para_name] = para_value 148 | else: 149 | para_dict[para] = para 150 | 151 | return para_dict 152 | 153 | 154 | def adv_sample_fn_assembler_and_loader(adv_sample_path, para_dict, src_input_size, target_input_size): 155 | 156 | # content_list = ['original', 'adversarial', 'noise', 'labels', 'targets'] 157 | content_list = ['original', 'adversarial', 'labels', 'targets'] 158 | 159 | adv_sample_dict = {} 160 | 161 | print("adv_sample_fn_assembler_and_loader") 162 | print("para_dict = ", para_dict) 163 | 164 | if 'target' in para_dict: 165 | para_format = "{0}/imgno={1}_content={2}_id={3}_seq={4}_prev={5}_target={6}_adv={7}_res={8}.npy" 166 | 167 | for content in content_list: 168 | fn = para_format.format(adv_sample_path, para_dict['imgno'], content, para_dict['id'], para_dict['seq'], 169 | para_dict['prev'], para_dict['target'], para_dict['adv'], para_dict['res']) 170 | 171 | obj = np.load(fn) 172 | adv_sample_dict[content] = obj 173 | 174 | if src_input_size != target_input_size: 175 | 176 | print("adv_sample_dict['original'].shape = ", adv_sample_dict['original'].shape) 177 | print("adv_sample_dict['adversarial'].shape = ", adv_sample_dict['adversarial'].shape) 178 | 179 | if len(adv_sample_dict['original'].shape) > 3: 180 | original = adv_sample_dict['original'][0] 181 | else: 182 | original = adv_sample_dict['original'] 183 | 184 | if len(adv_sample_dict['adversarial'].shape) > 3: 185 | adversarial = adv_sample_dict['adversarial'][0] 186 | else: 187 | adversarial = adv_sample_dict['adversarial'] 188 | 189 | 190 | adapted_orig_image_obj = adapt_image_size_on_ndarray(original, src_input_size, target_input_size) 191 | adapted_adv_image_obj = adapt_image_size_on_ndarray(adversarial, src_input_size, target_input_size) 192 | adv_sample_dict['original'] = adapted_orig_image_obj 193 | adv_sample_dict['adversarial'] = adapted_adv_image_obj 194 | 195 | else: 196 | para_format = "{0}/imgno={1}_content={2}_id={3}_seq={4}_prev={5}_adv={6}_res={7}.npy" 197 | 198 | for content in content_list: 199 | fn = para_format.format(adv_sample_path, para_dict['imgno'], content, para_dict['id'], para_dict['seq'], 200 | para_dict['prev'], para_dict['adv'], para_dict['res']) 201 | 202 | obj = np.load(fn) 203 | adv_sample_dict[content] = obj 204 | 205 | if src_input_size != target_input_size: 206 | 207 | if len(adv_sample_dict['original'].shape) > 3: 208 | original = adv_sample_dict['original'][0] 209 | else: 210 | original = adv_sample_dict['original'] 211 | 212 | if len(adv_sample_dict['adversarial'].shape) > 3: 213 | adversarial = adv_sample_dict['adversarial'][0] 214 | else: 215 | adversarial = adv_sample_dict['adversarial'] 216 | 217 | adapted_orig_image_obj = adapt_image_size_on_ndarray(original, src_input_size, target_input_size) 218 | adapted_adv_image_obj = adapt_image_size_on_ndarray(adversarial, src_input_size, target_input_size) 219 | adv_sample_dict['original'] = adapted_orig_image_obj 220 | adv_sample_dict['adversarial'] = adapted_adv_image_obj 221 | 222 | adv_sample_dict['config'] = para_dict 223 | 224 | return adv_sample_dict 225 | 226 | 227 | 228 | def source_model_adversarial_sample_group_generator(adv_sample_path, src_model_name, target_model_name): 229 | with open("model_input_size_info.json") as f: 230 | input_size_info = json.load(f) 231 | 232 | src_input_size = input_size_info[src_model_name] 233 | target_input_size = input_size_info[target_model_name] 234 | 235 | for entry in os.scandir(adv_sample_path): 236 | print("entry = ", entry) 237 | if 'adversarial' in entry.name and 'npy' in entry.name: 238 | print(entry.name) 239 | para_dict = adv_sample_fn_parser(entry.name) 240 | adv_sample_group = adv_sample_fn_assembler_and_loader(adv_sample_path, para_dict, src_input_size, target_input_size) 241 | 242 | yield adv_sample_group 243 | 244 | def main(args): 245 | with tf.Session() as sess: 246 | 247 | use_log = not args['use_zvalue'] 248 | 249 | print('Loading target model', args['target_model_name']) 250 | target_model = ImageNetModel(sess, use_log, args['target_model_name']) 251 | 252 | print('args = ', args) 253 | 254 | targeted_flag = not args['untargeted'] 255 | 256 | print("targeted_flag = ", targeted_flag) 257 | 258 | if args['attack'] not in ["FGSM", "IterFGSM", "EADL1", "CW"]: 259 | print("Unknown attack methods, exit 1") 260 | return 261 | 262 | random.seed(args['seed']) 263 | np.random.seed(args['seed']) 264 | tf.set_random_seed(args['seed']) 265 | 266 | #saved_path = "{}/{}/{}/targeted_{}".format(args['save'], args['dataset'], args['attack'], targeted_flag) 267 | adv_sample_path = args['src_adv_sample_path'] 268 | 269 | img_no = 0 270 | total_success = 0 271 | total_top_5_success = 0 272 | time_total = 0.0 273 | 274 | os.system("mkdir -p " + args['save']) 275 | 276 | if args['attack'] == "FGSM" or args['attack'] == "IterFGSM": 277 | verbose_f = open(args['save'] + "/" + "_".join( 278 | [args['dataset'], args['src_model_name'], args['target_model_name'], args['attack'], str(targeted_flag), str(args['epsilon']), "verbose.txt"]), "w") 279 | aggre_f = open(args['save'] + "/" + "_".join( 280 | [args['dataset'], args['src_model_name'], args['target_model_name'], args['attack'], str(targeted_flag), str(args['epsilon']), "aggre.txt"]), "w") 281 | elif args['attack'] == "AdaptiveFGSM": 282 | verbose_f = open( 283 | args['save'] + "/" + "_".join([args['dataset'], args['src_model_name'], args['target_model_name'], args['attack'], str(targeted_flag), "verbose.txt"]), 284 | "w") 285 | aggre_f = open( 286 | args['save'] + "/" + "_".join([args['dataset'], args['src_model_name'], args['target_model_name'], args['attack'], str(targeted_flag), "aggre.txt"]), "w") 287 | elif args['attack'] == 'CW' or args['attack'] == 'EADL1': 288 | verbose_f = open(args['save'] + "/" + "_".join( 289 | [args['dataset'], args['src_model_name'], args['target_model_name'], args['attack'], str(targeted_flag), str(args['kappa']), "verbose.txt"]), "w") 290 | aggre_f = open(args['save'] + "/" + "_".join( 291 | [args['dataset'], args['src_model_name'], args['target_model_name'], args['attack'], str(targeted_flag), str(args['kappa']), "aggre.txt"]), "w") 292 | 293 | if targeted_flag == True: 294 | verbose_head_str = '\t'.join( 295 | ['total', 'seq', 'id', 'time', 'success', 'success_top_5', 'src_orig_class', 'src_pred_class', 'attack_target_class', 'target_orig_class', 'target_pred_class']) 296 | else: 297 | verbose_head_str = '\t'.join( 298 | ['total', 'seq', 'id', 'time', 'success', 'src_orig_class', 'src_pred_class', 'target_orig_class', 'target_pred_class']) 299 | 300 | #top-5 success rate column is only for targeted attack 301 | #for untargeted attack, top-5 == top_1 302 | aggre_head_str = '\t'.join( 303 | ['total_count', 'success_rate', 'top_5_success_rate', 'time_avg']) 304 | 305 | verbose_f.write(verbose_head_str + '\n') 306 | aggre_f.write(aggre_head_str + '\n') 307 | 308 | if "vgg" in args['src_model_name'] or 'densenet' in args['src_model_name'] or 'alexnet' in args['src_model_name']: 309 | src_model_remove_background_class = True 310 | else: 311 | src_model_remove_background_class = False 312 | 313 | if "vgg" in args['target_model_name'] or 'densenet' in args['target_model_name'] or 'alexnet' in args['target_model_name']: 314 | target_model_remove_background_class = True 315 | else: 316 | target_model_remove_background_class = False 317 | 318 | print("src_model_remove_background_class = ", src_model_remove_background_class, ", target_model_remove_background_class = ", target_model_remove_background_class) 319 | 320 | print("adv_sample_path = ", adv_sample_path) 321 | 322 | for adv_sample_group in source_model_adversarial_sample_group_generator(adv_sample_path, args['src_model_name'], args['target_model_name']): 323 | 324 | print("true labels.shape:", np.argmax(adv_sample_group['labels'].shape)) 325 | print("true labels, argmax:", np.argmax(adv_sample_group['labels'])) 326 | print("target.shape:", np.argmax(adv_sample_group['targets'].shape)) 327 | print("target, argmax:", np.argmax(adv_sample_group['targets'])) 328 | 329 | # test if the image is correctly classified 330 | target_original_predict = target_model.model.predict(adv_sample_group['original']) 331 | target_original_predict = np.squeeze(target_original_predict) 332 | 333 | print("target_original_predict.shape", target_original_predict.shape) 334 | 335 | if target_model_remove_background_class == True: 336 | target_original_predict = np.insert(target_original_predict, 0, 0) 337 | print("vgg case, insert a leading 0 to prediction vector, shape = ", target_original_predict.shape) 338 | 339 | print("target model predict, argmax", np.argmax(target_original_predict)) 340 | 341 | target_original_prob = np.sort(target_original_predict) 342 | target_original_class = np.argsort(target_original_predict) 343 | target_original_class_label = np.argmax(target_original_predict) 344 | 345 | print("target original probabilities:", target_original_prob[-1:-6:-1]) 346 | print("target original classification:", target_original_class[-1:-6:-1]) 347 | print("target original probabilities (most unlikely):", target_original_prob[:6]) 348 | print("target original classification (most unlikely):", target_original_class[:6]) 349 | print("target_original_class_label = ", target_original_class_label) 350 | 351 | 352 | true_label = adv_sample_group['labels'] 353 | 354 | # true_label = np.insert(true_label, 0, 0) 355 | 356 | print("true_label.shape = ", true_label.shape) 357 | 358 | #The 2nd condition is for dealing with the case that using the original label in attacking vgg models 359 | if src_model_remove_background_class == True and true_label.shape == tuple([1000,]): 360 | true_label = np.insert(true_label, 0, 0) 361 | print("after expanding, true_label.shape", true_label.shape) 362 | print("true_label[:5]", true_label[:5]) 363 | 364 | print("Image no. {}, original class {}, classified as {}".format( 365 | adv_sample_group['config']['seq'], np.argmax(true_label), target_original_class_label)) 366 | if target_original_class_label != np.argmax(true_label): 367 | print("skip wrongly classified image no. {}, original class {}, classified as {}".format( 368 | adv_sample_group['config']['seq'], np.argmax(true_label), target_original_class_label)) 369 | continue 370 | 371 | img_no += 1 372 | timestart = time.time() 373 | 374 | target_adversarial_predict = target_model.model.predict(adv_sample_group['adversarial']) 375 | target_adversarial_predict = np.squeeze(target_adversarial_predict) 376 | 377 | if target_model_remove_background_class == True: 378 | target_adversarial_predict = np.insert(target_adversarial_predict, 0, 0) 379 | print("after expanding, target_adversarial_predict.shape", target_adversarial_predict.shape) 380 | print("target_adversarial_predict[:5]", target_adversarial_predict[:5]) 381 | 382 | # attack_target_class = int(adv_sample_group['config']['target']) + 1 383 | attack_target_class = np.argmax(adv_sample_group['targets']) + 1 384 | 385 | else: 386 | #attack_target_class = int(adv_sample_group['config']['target']) 387 | attack_target_class = np.argmax(adv_sample_group['targets']) 388 | 389 | 390 | target_adversarial_prob = np.sort(target_adversarial_predict) 391 | target_adversarial_class = np.argsort(target_adversarial_predict) 392 | target_adversarial_class_label = np.argmax(target_adversarial_predict) 393 | 394 | top_5_target_adversarial_class = target_adversarial_class[-1:-6:-1] 395 | 396 | 397 | 398 | print("top_5_target_adversarial_class = ", top_5_target_adversarial_class) 399 | print("set(top_5_target_adversarial_class) = ", set(top_5_target_adversarial_class)) 400 | 401 | timeend = time.time() 402 | 403 | time_used = timeend - timestart 404 | 405 | print("target adversarial probabilities:", target_adversarial_prob[-1:-6:-1]) 406 | print("target adversarial classification:", target_adversarial_class[-1:-6:-1]) 407 | print('target_adversarial_predict.shape = ', target_adversarial_predict) 408 | print("target_adversarial_class_label = ", target_adversarial_class_label) 409 | 410 | src_model_target = adv_sample_group['targets'] 411 | if src_model_remove_background_class == True: 412 | src_model_target = np.insert(src_model_target, 0, 0) 413 | src_orig_class = int(adv_sample_group['config']['prev']) + 1 414 | # src_pred_class = int(adv_sample_group['config']['adv']) + 1 415 | src_pred_class = int(float(adv_sample_group['config']['adv'])) + 1 416 | else: 417 | src_orig_class = int(adv_sample_group['config']['prev']) 418 | # src_pred_class = int(adv_sample_group['config']['adv']) 419 | src_pred_class = int(float(adv_sample_group['config']['adv'])) 420 | 421 | print('src_model_target.shape = ', src_model_target.shape) 422 | 423 | print("src_model_target label = ", np.argmax(src_model_target)) 424 | 425 | print('target_adversarial_class_label = ', target_adversarial_class_label) 426 | print('attack_target_class = ', attack_target_class) 427 | 428 | success = False 429 | top_5_success = False 430 | if targeted_flag: 431 | if target_adversarial_class_label == np.argmax(src_model_target): 432 | success = True 433 | 434 | # if attack_target_class in set(top_5_target_adversarial_class): 435 | if np.argmax(src_model_target) in set(top_5_target_adversarial_class): 436 | total_top_5_success += 1 437 | top_5_success = True 438 | 439 | else: 440 | if target_adversarial_class_label != target_original_class_label: 441 | success = True 442 | 443 | if success: 444 | total_success += 1 445 | 446 | if targeted_flag == True: 447 | verbose_str = '\t'.join( 448 | [str(img_no), str(adv_sample_group['config']['seq']), str(adv_sample_group['config']['id']), 449 | str(time_used), str(success), str(top_5_success), str(src_orig_class), str(src_pred_class), 450 | str(attack_target_class), str(target_original_class_label), str(target_adversarial_class_label)]) 451 | 452 | else: 453 | verbose_str = '\t'.join( 454 | [str(img_no), str(adv_sample_group['config']['seq']), str(adv_sample_group['config']['id']), 455 | str(time_used), str(success), str(src_orig_class), str(src_pred_class), 456 | str(target_original_class_label), str(target_adversarial_class_label)]) 457 | 458 | verbose_f.write(verbose_str + "\n") 459 | print(verbose_str) 460 | sys.stdout.flush() 461 | 462 | verbose_f.close() 463 | 464 | if img_no == 0: 465 | success_rate = 0.0 466 | top_5_success_rate = 0.0 467 | else: 468 | success_rate = total_success / float(img_no) 469 | if targeted_flag: 470 | top_5_success_rate = total_top_5_success / float(img_no) 471 | else: 472 | top_5_success_rate = success_rate 473 | 474 | if total_success == 0: 475 | time_avg = 0.0 476 | else: 477 | time_avg = time_total / total_success 478 | 479 | aggre_str = "\t".join( 480 | [str(img_no), str(success_rate), str(top_5_success_rate), str(time_avg)]) 481 | aggre_f.write(aggre_str + "\n") 482 | print(aggre_str) 483 | aggre_f.close() 484 | 485 | print("ALL DONE!!!") 486 | return 487 | 488 | if __name__ == "__main__": 489 | 490 | 491 | import argparse 492 | parser = argparse.ArgumentParser() 493 | parser.add_argument("-d", "--dataset", choices=["imagenet"], default="imagenet") 494 | parser.add_argument("-s", "--save", default="./saved_results") 495 | 496 | parser.add_argument("--src_adv_sample_path") 497 | 498 | parser.add_argument("-a", "--attack", choices=["FGSM", "IterFGSM", "CW", 'EADL1'], default="FGSM") 499 | parser.add_argument("-u", "--untargeted", action='store_true') 500 | parser.add_argument("-e", "--epsilon", type=float, default=0.3) 501 | parser.add_argument("--kappa", type=int, default=0, help = "initial_constance") 502 | 503 | 504 | parser.add_argument("--eps_iter", type=float, default=0.3) 505 | parser.add_argument("--iter_num", type=int, default=10) 506 | 507 | parser.add_argument("--src_model_name", default="resnet_v2_50") 508 | parser.add_argument("--target_model_name", default="resnet_v2_101") 509 | 510 | parser.add_argument("-z", "--use_zvalue", action='store_true') 511 | parser.add_argument("--seed", type=int, default=1216) 512 | args = vars(parser.parse_args()) 513 | 514 | # add some additional parameters 515 | # learning rate 516 | args['lr'] = 1e-2 517 | args['inception'] = False 518 | args['use_tanh'] = True 519 | 520 | # set up some parameters based on datasets 521 | if args['dataset'] == "imagenet": 522 | args['inception'] = True 523 | args['lr'] = 2e-3 524 | 525 | # setup random seed 526 | random.seed(args['seed']) 527 | np.random.seed(args['seed']) 528 | tf.set_random_seed(args['seed']) 529 | print(args) 530 | 531 | main(args) 532 | 533 | print("Experiment Done!!!") 534 | -------------------------------------------------------------------------------- /l2_attack.py: -------------------------------------------------------------------------------- 1 | ## l2_attack.py -- attack a network optimizing for l_2 distance 2 | ## 3 | ## Copyright (C) 2016, Nicholas Carlini . 4 | ## 5 | ## This program is licenced under the BSD 2-Clause licence, 6 | ## contained in the LICENCE file in this directory. 7 | 8 | import sys 9 | import tensorflow as tf 10 | import numpy as np 11 | import time 12 | 13 | BINARY_SEARCH_STEPS = 1 # number of times to adjust the constant with binary search 14 | MAX_ITERATIONS = 100 # number of iterations to perform gradient descent 15 | ABORT_EARLY = True # if we stop improving, abort gradient descent early 16 | LEARNING_RATE = 2e-3 # larger values converge faster to less accurate results 17 | TARGETED = True # should we target one specific class? or just be wrong? 18 | CONFIDENCE = 0 # how strong the adversarial example should be 19 | INITIAL_CONST = 0.1 # the initial constant c to pick as a first guess 20 | 21 | class CarliniL2: 22 | def __init__(self, sess, model,max_batch_size=3000): 23 | image_size, num_channels, num_labels = model.image_size, model.num_channels, model.num_labels 24 | self.sess = sess 25 | self.model = model 26 | self.modifier = tf.Variable(tf.zeros(shape=(max_batch_size, image_size, image_size, num_channels),dtype=np.float32), validate_shape=False) 27 | #self.modifier = tf.Variable(np.zeros(shape=(1, image_size, image_size, num_channels),dtype=np.float32), validate_shape=True) 28 | print("**************self.modifier shape:",self.modifier.get_shape()) 29 | self.assign_modifier = tf.placeholder(tf.float32, shape=[None, image_size, image_size, num_channels]) 30 | self.assign_modifier_op = tf.assign(self.modifier, self.assign_modifier, validate_shape=False) 31 | # self.assign_modifier_op = tf.assign(self.modifier, self.assign_modifier, validate_shape=True) 32 | self.timg = tf.placeholder(shape=[None, image_size, image_size, num_channels], dtype=tf.float32) 33 | self.newimg = tf.tanh(self.modifier + self.timg)/2 34 | self.predicts = self.model.predict(self.newimg) 35 | 36 | def init_attack(self, sess, model, batch_size=1, confidence = CONFIDENCE, 37 | targeted = TARGETED, learning_rate = LEARNING_RATE, 38 | binary_search_steps = BINARY_SEARCH_STEPS, max_iterations = MAX_ITERATIONS, print_every = 1, early_stop_iters = 0, 39 | abort_early = ABORT_EARLY, 40 | initial_const = INITIAL_CONST, 41 | use_log = False, adam_beta1 = 0.9, adam_beta2 = 0.999): 42 | """ 43 | The L_2 optimized attack. 44 | 45 | This attack is the most efficient and should be used as the primary 46 | attack to evaluate potential defenses. 47 | 48 | Returns adversarial examples for the supplied model. 49 | 50 | confidence: Confidence of adversarial examples: higher produces examples 51 | that are farther away, but more strongly classified as adversarial. 52 | batch_size: Number of attacks to run simultaneously. 53 | targeted: True if we should perform a targetted attack, False otherwise. 54 | learning_rate: The learning rate for the attack algorithm. Smaller values 55 | produce better results but are slower to converge. 56 | binary_search_steps: The number of times we perform binary search to 57 | find the optimal tradeoff-constant between distance and confidence. 58 | max_iterations: The maximum number of iterations. Larger values are more 59 | accurate; setting too small will require a large learning rate and will 60 | produce poor results. 61 | abort_early: If true, allows early aborts if gradient descent gets stuck. 62 | initial_const: The initial tradeoff-constant to use to tune the relative 63 | importance of distance and confidence. If binary_search_steps is large, 64 | the initial constant is not important. 65 | """ 66 | 67 | image_size, num_channels, num_labels = model.image_size, model.num_channels, model.num_labels 68 | self.sess = sess 69 | self.TARGETED = targeted 70 | self.LEARNING_RATE = learning_rate 71 | self.MAX_ITERATIONS = max_iterations 72 | self.print_every = print_every 73 | self.early_stop_iters = early_stop_iters if early_stop_iters != 0 else max_iterations // 10 74 | print("early stop:", self.early_stop_iters) 75 | self.BINARY_SEARCH_STEPS = binary_search_steps 76 | self.ABORT_EARLY = abort_early 77 | self.CONFIDENCE = confidence 78 | self.initial_const = initial_const 79 | self.batch_size = batch_size 80 | 81 | self.repeat = binary_search_steps >= 10 82 | 83 | shape = (batch_size,image_size,image_size,num_channels) 84 | self.shape = shape 85 | 86 | # the variable we're going to optimize over 87 | # self.modifier = tf.Variable(np.load('black_iter_350.npy').astype(np.float32).reshape(shape)?) 88 | 89 | # these are variables to be more efficient in sending data to tf 90 | # self.timg = tf.Variable(np.zeros(shape), dtype=tf.float32) 91 | self.tlab = tf.Variable(np.zeros((batch_size,num_labels)), dtype=tf.float32) 92 | self.const = tf.Variable(np.zeros(batch_size), dtype=tf.float32) 93 | 94 | # and here's what we use to assign them 95 | self.assign_timg = tf.placeholder(tf.float32, shape) 96 | self.assign_tlab = tf.placeholder(tf.float32, (batch_size,num_labels)) 97 | self.assign_const = tf.placeholder(tf.float32, [batch_size]) 98 | 99 | # the resulting image, tanh'd to keep bounded from -0.5 to 0.5 100 | 101 | # prediction BEFORE-SOFTMAX of the model 102 | # self.output = self.predict(self.newimg) 103 | self.output = self.predicts 104 | # self.output = self.predict(self.newimg) 105 | 106 | # distance to the input data 107 | self.l2dist = tf.reduce_sum(tf.square(self.newimg-tf.tanh(self.timg)/2),[1,2,3]) 108 | 109 | # compute the probability of the label class versus the maximum other 110 | self.real = tf.reduce_sum((self.tlab)*self.output,1) 111 | self.other = tf.reduce_max((1-self.tlab)*self.output - (self.tlab*10000),1) 112 | 113 | if self.TARGETED: 114 | if use_log: 115 | # loss1 = tf.maximum(- tf.log(self.other), - tf.log(self.real)) 116 | # loss1 = - tf.log(self.real) 117 | loss1 = tf.maximum(0.0, tf.log(self.other + 1e-30) - tf.log(self.real + 1e-30)) 118 | else: 119 | # if targetted, optimize for making the other class most likely 120 | loss1 = tf.maximum(0.0, self.other-self.real+self.CONFIDENCE) 121 | else: 122 | if use_log: 123 | # loss1 = tf.log(self.real) 124 | loss1 = tf.maximum(0.0, tf.log(self.real + 1e-30) - tf.log(self.other + 1e-30)) 125 | else: 126 | # if untargeted, optimize for making this class least likely. 127 | loss1 = tf.maximum(0.0, self.real-self.other+self.CONFIDENCE) 128 | 129 | # sum up the losses 130 | # self.loss2 = tf.reduce_sum(self.l2dist) 131 | self.loss2 = self.l2dist 132 | # self.loss1 = tf.reduce_sum(self.const*loss1) 133 | self.loss1 = self.const * loss1 134 | self.loss = self.loss1+self.loss2 135 | # self.loss = self.loss1 136 | 137 | # Setup the adam optimizer and keep track of variables we're creating 138 | start_vars = set(x.name for x in tf.global_variables()) 139 | # optimizer = tf.train.GradientDescentOptimizer(self.LEARNING_RATE) 140 | # optimizer = tf.train.MomentumOptimizer(self.LEARNING_RATE, 0.99) 141 | # optimizer = tf.train.RMSPropOptimizer(self.LEARNING_RATE) 142 | # optimizer = tf.train.AdadeltaOptimizer(self.LEARNING_RATE) 143 | # optimizer = tf.train.AdamOptimizer(self.LEARNING_RATE, adam_beta1, adam_beta2) 144 | # self.train = optimizer.minimize(self.loss, var_list=[self.modifier]) 145 | self.grad = tf.gradients(self.output,self.newimg)[0] 146 | self.train = self.adam_optimizer_tf(self.loss, self.modifier) 147 | # self.train = self.IFGSM_optimizer_tf(self.loss, self.modifier) 148 | end_vars = tf.global_variables() 149 | new_vars = [x for x in end_vars if x.name not in start_vars] 150 | # these are the variables to initialize when we run 151 | self.setup = [] 152 | # self.setup.append(self.timg.assign(self.assign_timg)) 153 | self.setup.append(self.tlab.assign(self.assign_tlab)) 154 | self.setup.append(self.const.assign(self.assign_const)) 155 | # self.grad_op = tf.gradients(self.loss, self.modifier) 156 | 157 | # self.init = tf.variables_initializer(var_list=[self.modifier]+new_vars) 158 | self.init = tf.variables_initializer(var_list=new_vars) 159 | 160 | def adam_optimizer_tf(self, loss, var): 161 | with tf.name_scope("adam_optimier"): 162 | self.grad = tf.gradients(loss, var)[0] 163 | # self.noise = tf.random_normal(self.shape, 0.0, 1.0) 164 | # self.noise = 0 165 | self.beta1 = tf.constant(0.9) 166 | self.beta2 = tf.constant(0.999) 167 | self.lr = tf.constant(self.LEARNING_RATE) 168 | self.epsilon = 1e-8 169 | self.epoch = tf.Variable(1, dtype = tf.float32) 170 | self.mt = tf.Variable(np.zeros(self.shape), dtype = tf.float32) 171 | self.vt = tf.Variable(np.zeros(self.shape), dtype = tf.float32) 172 | self.new_mt = self.beta1 * self.mt + (1 - self.beta1) * self.grad 173 | self.new_vt = self.beta2 * self.vt + (1 - self.beta2) * tf.square(self.grad) 174 | self.corr = tf.sqrt(1 - tf.pow(self.beta2, self.epoch)) / (1 - tf.pow(self.beta1, self.epoch)) 175 | self.delta = self.lr * self.corr * (self.new_mt / tf.sqrt(self.new_vt+self.epsilon)) 176 | # self.delta = self.lr * self.corr * ((self.new_mt / tf.sqrt(self.new_vt+self.epsilon)) + self.noise / tf.pow(self.epoch + 1, 0.2)) 177 | # delta = self.lr * corr * ((new_mt / tf.sqrt(new_vt + self.epsilon)) + self.noise) 178 | # delta = self.lr * (self.grad + self.noise) 179 | 180 | # assign_var = tf.assign_sub(var, delta) 181 | assign_var = tf.assign(var, var - self.delta, validate_shape=False) 182 | # assign_var = tf.assign(var, var - self.lr * self.grad, validate_shape=False) 183 | # assign_var = tf.assign(var, var - delta, validate_shape=True) 184 | assign_mt = tf.assign(self.mt, self.new_mt) 185 | assign_vt = tf.assign(self.vt, self.new_vt) 186 | assign_epoch = tf.assign_add(self.epoch, 1) 187 | return tf.group(assign_var, assign_mt, assign_vt, assign_epoch) 188 | ''' 189 | with tf.name_scope("adam_optimier"): 190 | self.grad = tf.gradients(loss, var)[0] 191 | # self.noise = tf.random_normal(self.shape, 0.0, 1.0) 192 | self.noise = 0 193 | self.beta1 = tf.constant(0.9) 194 | self.beta2 = tf.constant(0.999) 195 | self.lr = tf.constant(self.LEARNING_RATE) 196 | self.epsilon = 1e-8 197 | self.epoch = tf.Variable(1, dtype = tf.float32) 198 | self.mt = tf.Variable(np.zeros(self.shape), dtype = tf.float32) 199 | self.vt = tf.Variable(np.zeros(self.shape), dtype = tf.float32) 200 | 201 | self.new_mt = self.beta1 * self.mt + (1 - self.beta1) * self.grad 202 | new_mt = self.new_mt 203 | self.new_vt = self.beta2 * self.vt + (1 - self.beta2) * tf.square(self.grad) 204 | new_vt = self.new_vt 205 | self.pow1 = tf.pow(self.beta2, self.epoch) 206 | self.pow2 = tf.pow(self.beta1, self.epoch) 207 | self.corr_num = tf.sqrt(1 - self.pow1) 208 | self.corr_deno = 1 - self.pow2 209 | # self.corr_num = tf.sqrt(1 - tf.pow(self.beta2, self.epoch)) 210 | # self.corr_deno = 1 - tf.pow(self.beta1, self.epoch) 211 | corr = self.corr_num/self.corr_deno 212 | # delta = self.lr * corr * (new_mt / (tf.sqrt(new_vt) + self.epsilon)) 213 | self.sq = tf.sqrt(new_vt+self.epsilon) 214 | self.lr_corr = self.lr * corr 215 | self.delta = self.lr_corr * ((new_mt / self.sq))# + self.noise / tf.pow(self.epoch + 1, 0.2)) 216 | delta = self.delta 217 | # delta = self.lr * corr * ((new_mt / tf.sqrt(new_vt + self.epsilon)) + self.noise) 218 | # delta = self.lr * (self.grad + self.noise) 219 | 220 | # assign_var = tf.assign_sub(var, delta) 221 | assign_var = tf.assign(var, var - delta, validate_shape=False) 222 | # assign_var = tf.assign(var, var - self.lr * self.grad, validate_shape=False) 223 | # assign_var = tf.assign(var, var - delta, validate_shape=True) 224 | assign_mt = tf.assign(self.mt, new_mt) 225 | assign_vt = tf.assign(self.vt, new_vt) 226 | assign_epoch = tf.assign_add(self.epoch, 1) 227 | return tf.group(assign_var, assign_mt, assign_vt, assign_epoch) 228 | ''' 229 | 230 | def IFGSM_optimizer_tf(self, loss, var): 231 | with tf.name_scope("IFGSM_optimizer"): 232 | self.grad = tf.gradients(loss, var)[0] 233 | print("self.grad.shape:",self.grad.shape) 234 | y,x = np.ogrid[-149: 149+1, -149: 149+1] 235 | mask_slice = np.maximum(np.abs(x),np.abs(y))<=111 236 | mask_slice = mask_slice.astype(int) 237 | mask = np.expand_dims(np.stack((mask_slice,mask_slice,mask_slice),axis=2),axis=0) 238 | self.VGG_mask = tf.constant(mask,tf.float32) 239 | self.new_grad = tf.multiply(tf.sign(self.grad),self.VGG_mask) 240 | self.lr = tf.constant(self.LEARNING_RATE) 241 | delta = self.lr * self.new_grad 242 | assign_var = tf.assign_sub(var, delta) 243 | self.grad_norm = tf.norm(self.grad) 244 | return assign_var 245 | 246 | def predict(self, imgs): 247 | imgs = np.arctanh(imgs*1.999999) 248 | self.sess.run([self.assign_modifier_op], feed_dict = {self.assign_modifier : np.zeros_like(imgs)}) 249 | predicts = self.sess.run([self.predicts], feed_dict = {self.timg : imgs}) 250 | return predicts 251 | 252 | def attack(self, imgs, targets): 253 | """ 254 | Perform the L_2 attack on the given images for the given targets. 255 | 256 | If self.targeted is true, then the targets represents the target labels. 257 | If self.targeted is false, then targets are the original class labels. 258 | """ 259 | np.set_printoptions(precision=5) 260 | r = [] 261 | print('go up to',len(imgs)) 262 | for i in range(0,len(imgs),self.batch_size): 263 | print('tick',i) 264 | print("imgs size batch_size:",imgs[i:i+self.batch_size].shape) 265 | r.extend(self.attack_batch(imgs[i:i+self.batch_size], targets[i:i+self.batch_size])[0]) 266 | return np.array(r) 267 | 268 | def attack_batch(self, imgs, labs): 269 | """ 270 | Run the attack on a batch of images and labels. 271 | """ 272 | def compare(x,y): 273 | if not isinstance(x, (float, int, np.int64)): 274 | x = np.copy(x) 275 | if self.TARGETED: 276 | x[y] -= self.CONFIDENCE 277 | else: 278 | x[y] += self.CONFIDENCE 279 | x = np.argmax(x) 280 | if self.TARGETED: 281 | return x == y 282 | else: 283 | return x != y 284 | 285 | batch_size = self.batch_size 286 | 287 | # convert to tanh-space 288 | imgs = np.arctanh(imgs*1.999999) 289 | 290 | # set the lower and upper bounds accordingly 291 | lower_bound = np.zeros(batch_size) 292 | CONST = np.ones(batch_size)*self.initial_const 293 | upper_bound = np.ones(batch_size)*1e10 294 | n_success = 0 295 | 296 | # the best l2, score, and image attack 297 | o_bestl2 = [1e10]*batch_size 298 | o_bestscore = [-1]*batch_size 299 | o_bestattack = [np.zeros(imgs[0].shape)]*batch_size 300 | # fill the array as nan to indicate attack failure 301 | for b in o_bestattack: 302 | b.fill(np.nan) 303 | o_best_const = [self.initial_const]*batch_size 304 | modifier = self.sess.run((self.modifier),{self.timg:np.zeros_like(imgs[:batch_size])}) 305 | print("modifier before attack:",np.sum(modifier)) 306 | print("modifier shape:",modifier.shape) 307 | for outer_step in range(self.BINARY_SEARCH_STEPS): 308 | print("current best l2", o_bestl2) 309 | # completely reset adam's internal state. 310 | self.sess.run(self.init) 311 | batch = imgs[:batch_size] 312 | batchlab = labs[:batch_size] 313 | 314 | bestl2 = [1e10]*batch_size 315 | bestscore = [-1]*batch_size 316 | 317 | # The last iteration (if we run many steps) rep:eat the search once. 318 | if self.repeat == True and outer_step == self.BINARY_SEARCH_STEPS-1: 319 | CONST = upper_bound 320 | 321 | # set the variables so that we don't have to send them over again 322 | # self.sess.run(self.setup, {self.assign_timg: batch, 323 | self.sess.run(self.setup, {self.assign_tlab: batchlab, 324 | self.assign_const: CONST}) 325 | ''' 326 | print("batch shape:",batch.shape) 327 | self.sess.run([self.assign_modifier_op], feed_dict = {self.assign_modifier : np.zeros_like(batch)}) 328 | timg,modifier,newimg,loss,grad,l2dist = self.sess.run((self.timg,self.modifier,self.newimg,self.loss,self.grad,self.l2dist),feed_dict={self.timg:np.zeros_like(batch)}) 329 | # print("**************self.grad shape:",self.sess.run(tf.shape(self.grad),feed_dict={self.timg:np.zeros_like(batch)})) 330 | print("newimg:",np.sum(newimg)) 331 | print("timg",np.sum(timg)) 332 | print("self.grad",np.sum(grad)) 333 | ''' 334 | prev = 1e6 335 | train_timer = 0.0 336 | for iteration in range(self.MAX_ITERATIONS): 337 | # print out the losses every 10% 338 | if iteration%(self.MAX_ITERATIONS//self.print_every) == 0: 339 | # print(iteration,self.sess.run((self.loss,self.real,self.other,self.loss1,self.loss2))) 340 | # grad = self.sess.run(self.grad_op) 341 | # old_modifier = self.sess.run(self.modifier) 342 | # np.save('white_iter_{}'.format(iteration), modifier) 343 | loss, real, other, loss1, loss2 = self.sess.run((self.loss,self.real,self.other,self.loss1,self.loss2), {self.timg: batch}) 344 | if self.batch_size == 1: 345 | print("[STATS][L2] iter = {}, time = {:.3f}, loss = {:.5g}, real = {:.5g}, other = {:.5g}, loss1 = {:.5g}, loss2 = {:.5g}".format(iteration, train_timer, loss[0], real[0], other[0], loss1[0], loss2[0])) 346 | elif self.batch_size > 10: 347 | print("[STATS][L2][SUM of {}] iter = {}, time = {:.3f}, batch_size = {}, n_success = {:.5g}, loss = {:.5g}, real = {:.5g}, other = {:.5g}, loss1 = {:.5g}, loss2 = {:.5g}".format(self.batch_size, iteration, train_timer, batch_size, n_success, sum(loss), sum(real), sum(other), sum(loss1), sum(loss2))) 348 | else: 349 | print("[STATS][L2] iter = {}, time = {:.3f}".format(iteration, train_timer)) 350 | print("[STATS][L2] real =", real) 351 | print("[STATS][L2] other =", other) 352 | print("[STATS][L2] loss1 =", loss1) 353 | print("[STATS][L2] loss2 =", loss2) 354 | print("[STATS][L2] loss =", loss) 355 | sys.stdout.flush() 356 | 357 | attack_begin_time = time.time() 358 | # perform the attack 359 | # print("batch shape:",batch.shape) 360 | _, l, l2s, scores, nimg = self.sess.run([self.train, self.loss, 361 | self.l2dist, self.output, 362 | self.newimg],{self.timg: batch}) 363 | # new_modifier = self.sess.run(self.modifier) 364 | 365 | # print(grad[0].reshape(-1)) 366 | # print((old_modifier - new_modifier).reshape(-1)) 367 | 368 | # check if we should abort search if we're getting nowhere. 369 | if self.ABORT_EARLY and iteration % self.early_stop_iters == 0: 370 | if np.all(l > prev*.9999): 371 | print("Early stopping because there is no improvement") 372 | break 373 | prev = l 374 | 375 | # adjust the best result found so far 376 | read_last_loss = False 377 | # test_prediction = self.predict(nimg) 378 | # print("test_predction sum:",np.sum(test_prediction)) 379 | for e,(l2,sc,ii) in enumerate(zip(l2s,scores,nimg)): 380 | rank = np.argsort(sc) 381 | # test_prediction = self.predict(nimg) 382 | if l2 < bestl2[e] and compare(sc, np.argmax(batchlab[e])): 383 | bestl2[e] = l2 384 | bestscore[e] = np.argmax(sc) 385 | if l2 < o_bestl2[e] and compare(sc, np.argmax(batchlab[e])): 386 | # print a message if it is the first attack found 387 | if o_bestl2[e] == 1e10: 388 | if not read_last_loss: 389 | loss, real, other, loss1, loss2 = self.sess.run((self.loss,self.real,self.other,self.loss1,self.loss2),{self.timg: batch}) 390 | read_last_loss = True 391 | print("[STATS][L3][First valid attack found!] iter = {}, time = {:.3f}, img = {}, loss = {:.5g}, real = {:.5g}, other = {:.5g}, loss1 = {:.5g}, loss2 = {:.5g}".format(iteration, train_timer, e, loss[e], real[e], other[e], loss1[e], loss2[e])) 392 | n_success += 1 393 | o_bestl2[e] = l2 394 | o_bestscore[e] = np.argmax(sc) 395 | o_bestattack[e] = ii 396 | o_best_const[e] = CONST[e] 397 | 398 | train_timer += time.time() - attack_begin_time 399 | 400 | # adjust the constant as needed 401 | for e in range(batch_size): 402 | if compare(bestscore[e], np.argmax(batchlab[e])) and bestscore[e] != -1: 403 | # modifier = self.sess.run(self.modifier) 404 | # np.save("best.model", modifier) 405 | print('old constant: ', CONST[e]) 406 | # success, divide const by two 407 | upper_bound[e] = min(upper_bound[e],CONST[e]) 408 | if upper_bound[e] < 1e9: 409 | CONST[e] = (lower_bound[e] + upper_bound[e])/2 410 | print('new constant: ', CONST[e]) 411 | else: 412 | print('old constant: ', CONST[e]) 413 | # failure, either multiply by 10 if no solution found yet 414 | # or do binary search with the known upper bound 415 | lower_bound[e] = max(lower_bound[e],CONST[e]) 416 | if upper_bound[e] < 1e9: 417 | CONST[e] = (lower_bound[e] + upper_bound[e])/2 418 | else: 419 | CONST[e] *= 10 420 | print('new constant: ', CONST[e]) 421 | 422 | # return the best solution found 423 | o_bestl2 = np.array(o_bestl2) 424 | return np.array(o_bestattack), o_best_const 425 | --------------------------------------------------------------------------------