bat.attacks.simba_attack
This module implements the black-box attack SimBA
.
1""" 2This module implements the black-box attack `SimBA`. 3""" 4 5import os 6import gc 7import numpy as np 8from tqdm import tqdm 9import concurrent.futures 10 11from bat.attacks.base_attack import BaseAttack 12 13SCALE = 255 14PREPROCESS = lambda x: x 15 16def proj_lp(v, xi=0.1, p=2): 17 """ 18 SUPPORTS only p = 2 and p = Inf for now 19 """ 20 if p == 2: 21 v = v * min(1, xi/np.linalg.norm(v.flatten('C'))) 22 # v = v / np.linalg.norm(v.flatten(1)) * xi 23 elif p == np.inf: 24 v = np.sign(v) * np.minimum(abs(v), xi) 25 else: 26 raise ValueError('Values of p different from 2 and Inf are currently not supported...') 27 28 return v 29 30class SimBA(BaseAttack): 31 """ 32 Implementation of the `SimBA` attack. Paper link: https://arxiv.org/abs/1905.07121 33 """ 34 35 def __init__(self, classifier): 36 """ 37 Create a class: `SimBA` instance. 38 - classifier: model to attack 39 """ 40 super().__init__(classifier) 41 42 def init(self, x, max_it): 43 """ 44 Initialize the attack. 45 """ 46 47 x_adv = x.copy() 48 y_pred = self.classifier.predict(PREPROCESS(x.copy())) 49 50 perm = [] 51 for xi in x: 52 perm.append(np.random.permutation(xi.reshape(-1).shape[0])) 53 assert len(perm[-1]) > max_it, 'The maxinum number of iteration should be smaller than the image dimension.' 54 55 return x_adv, y_pred, perm 56 57 def step(self, x_adv, y_pred, perm, index, epsilon): 58 """ 59 Single step for non-distributed attack. 60 """ 61 62 x_adv_plus = [] 63 x_adv_minus = [] 64 x_adv_diff = [] 65 for i in range(0, len(x_adv)): 66 diff = np.zeros(x_adv[i].reshape(-1).shape[0]) 67 diff[perm[i][index]] = epsilon 68 diff = diff.reshape(x_adv[i].shape) 69 x_adv_plus.append(np.clip(x_adv[i] + diff, 0, 1 * SCALE)) 70 x_adv_minus.append(np.clip(x_adv[i] - diff, 0, 1 * SCALE)) 71 x_adv_diff.append(diff) 72 73 plus = self.classifier.predict(PREPROCESS(x_adv_plus.copy())) 74 minus = self.classifier.predict(PREPROCESS(x_adv_minus.copy())) 75 76 for i in range(0, len(x_adv)): 77 if plus[i][np.argmax(y_pred[i])] < y_pred[i][np.argmax(y_pred[i])]: 78 x_adv[i] = x_adv[i] + x_adv_diff[i] 79 y_pred[i] = plus[i] 80 elif minus[i][np.argmax(y_pred[i])] < y_pred[i][np.argmax(y_pred[i])]: 81 x_adv[i] = x_adv[i] - x_adv_diff[i] 82 y_pred[i] = minus[i] 83 else: 84 pass 85 86 return x_adv, y_pred 87 88 def batch(self, x_adv, y_pred, perm, index, epsilon, concurrency): 89 """ 90 Single step for distributed attack. 91 """ 92 noises = [] 93 for i in range(0, len(x_adv)): 94 noises.append(np.zeros(x_adv[i].shape)) 95 96 with concurrent.futures.ThreadPoolExecutor() as executor: 97 future_to_url = {executor.submit(self.step, x_adv, y_pred, perm, index+j, epsilon): j for j in range(0, concurrency)} 98 for future in concurrent.futures.as_completed(future_to_url): 99 j = future_to_url[future] 100 try: 101 x_adv_new, _ = future.result() 102 for i in range(0, len(x_adv)): 103 noises[i] = noises[i] + x_adv_new[i] - x_adv[i] 104 except Exception as exc: 105 print('Task %r generated an exception: %s' % (j, exc)) 106 else: 107 pass 108 109 for i in range(0, len(x_adv)): 110 if(np.sum(noises[i]) != 0): 111 noises = proj_lp(noises[i], xi = 10) 112 x_adv[i] = np.clip(x_adv[i] + noises[i], 0, 1 * SCALE) 113 114 y_adv = self.classifier.predict(PREPROCESS(x_adv.copy())) 115 116 return x_adv, y_adv 117 118 def attack(self, x, y, epsilon=0.05, max_it=1000, concurrency=1): 119 """ 120 Initiate the attack. 121 122 - x: input data 123 - y: input labels 124 - epsilon: perturbation on each pixel 125 - max_it: number of iterations 126 - concurrency: number of concurrent threads 127 """ 128 129 n_targets = 0 130 if type(x) == list: 131 n_targets = len(x) 132 elif type(x) == np.ndarray: 133 n_targets = x.shape[0] 134 else: 135 raise ValueError('Input type not supported...') 136 137 assert n_targets > 0 138 139 # Initialize attack 140 x_adv, y_pred, perm = self.init(x, max_it) 141 142 # Compute number of images correctly classified 143 y_pred_classes = np.argmax(y_pred, axis=1) 144 correct_classified_mask = (y_pred_classes == y) 145 correct_classified = [i for i, v in enumerate(correct_classified_mask) if v] 146 147 # Images to attack 148 not_dones_mask = correct_classified_mask.copy() 149 150 print('Clean accuracy: {:.2%}'.format(np.mean(correct_classified_mask))) 151 152 if np.mean(correct_classified_mask) == 0: 153 print('No clean examples classified correctly. Aborting...') 154 n_queries = np.ones(len(x)) # ones because we have already used 1 query 155 156 mean_nq, mean_nq_ae = np.mean(n_queries), np.mean(n_queries) 157 158 return x 159 160 else: 161 if n_targets > 1: 162 # Horizontally Distributed Attack 163 pbar = tqdm(range(0, max_it), desc="Distributed SimBA Attack (Horizontal)") 164 else: 165 # Vertically Distributed Attack 166 pbar = tqdm(range(0, max_it, concurrency), desc="Distributed SimBA Attack (Vertical)") 167 168 total_queries = np.zeros(len(x)) 169 170 for i_iter in pbar: 171 172 not_dones = [i for i, v in enumerate(not_dones_mask) if v] 173 174 x_adv_curr = [x_adv[idx] for idx in not_dones] 175 y_curr = [y_pred[idx] for idx in not_dones] 176 perm_curr = [perm[idx] for idx in not_dones] 177 178 y_curr = np.array(y_curr) 179 180 if n_targets > 1: 181 # Horizontally Distributed Attack 182 x_adv_curr, y_curr = self.step(x_adv_curr, y_curr, perm_curr, i_iter, epsilon*SCALE) 183 else: 184 # Vertically Distributed Attack 185 x_adv_curr, y_curr = self.batch(x_adv_curr, y_curr, perm_curr, i_iter, epsilon*SCALE, concurrency) 186 187 for i in range(len(not_dones)): 188 x_adv[not_dones[i]] = x_adv_curr[i] 189 y_pred[not_dones[i]] = y_curr[i] 190 191 # Logging stuff 192 if n_targets > 1: 193 # Horizontally Distributed Attack 194 total_queries += 2 * not_dones_mask 195 else: 196 # Vertically Distributed Attack 197 total_queries += 2 * concurrency * not_dones_mask + 1 198 199 y_pred_classes = np.argmax(y_pred, axis=1) 200 not_dones_mask = not_dones_mask * (y_pred_classes == y) 201 202 success_mask = correct_classified_mask * (1 - not_dones_mask) 203 num_success = success_mask.sum() 204 current_success_rate = (num_success / correct_classified_mask.sum()) 205 206 if num_success == 0: 207 success_queries = -1 208 else: 209 success_queries = ((success_mask * total_queries).sum() / num_success) 210 211 pbar.set_postfix({'Total Queries': total_queries.sum(), 'Mean Higest Prediction': y_pred[correct_classified].max(axis=1).mean(), 'Attack Success Rate': current_success_rate, 'Avg Queries': success_queries}) 212 213 acc = not_dones_mask.sum() / correct_classified_mask.sum() 214 mean_nq, mean_nq_ae = np.mean(total_queries), np.mean(total_queries *success_mask) 215 216 # Early break 217 if current_success_rate == 1.0: 218 break 219 220 gc.collect() 221 222 return x_adv
SCALE =
255
def
PREPROCESS(x):
15PREPROCESS = lambda x: x
def
proj_lp(v, xi=0.1, p=2):
17def proj_lp(v, xi=0.1, p=2): 18 """ 19 SUPPORTS only p = 2 and p = Inf for now 20 """ 21 if p == 2: 22 v = v * min(1, xi/np.linalg.norm(v.flatten('C'))) 23 # v = v / np.linalg.norm(v.flatten(1)) * xi 24 elif p == np.inf: 25 v = np.sign(v) * np.minimum(abs(v), xi) 26 else: 27 raise ValueError('Values of p different from 2 and Inf are currently not supported...') 28 29 return v
SUPPORTS only p = 2 and p = Inf for now
31class SimBA(BaseAttack): 32 """ 33 Implementation of the `SimBA` attack. Paper link: https://arxiv.org/abs/1905.07121 34 """ 35 36 def __init__(self, classifier): 37 """ 38 Create a class: `SimBA` instance. 39 - classifier: model to attack 40 """ 41 super().__init__(classifier) 42 43 def init(self, x, max_it): 44 """ 45 Initialize the attack. 46 """ 47 48 x_adv = x.copy() 49 y_pred = self.classifier.predict(PREPROCESS(x.copy())) 50 51 perm = [] 52 for xi in x: 53 perm.append(np.random.permutation(xi.reshape(-1).shape[0])) 54 assert len(perm[-1]) > max_it, 'The maxinum number of iteration should be smaller than the image dimension.' 55 56 return x_adv, y_pred, perm 57 58 def step(self, x_adv, y_pred, perm, index, epsilon): 59 """ 60 Single step for non-distributed attack. 61 """ 62 63 x_adv_plus = [] 64 x_adv_minus = [] 65 x_adv_diff = [] 66 for i in range(0, len(x_adv)): 67 diff = np.zeros(x_adv[i].reshape(-1).shape[0]) 68 diff[perm[i][index]] = epsilon 69 diff = diff.reshape(x_adv[i].shape) 70 x_adv_plus.append(np.clip(x_adv[i] + diff, 0, 1 * SCALE)) 71 x_adv_minus.append(np.clip(x_adv[i] - diff, 0, 1 * SCALE)) 72 x_adv_diff.append(diff) 73 74 plus = self.classifier.predict(PREPROCESS(x_adv_plus.copy())) 75 minus = self.classifier.predict(PREPROCESS(x_adv_minus.copy())) 76 77 for i in range(0, len(x_adv)): 78 if plus[i][np.argmax(y_pred[i])] < y_pred[i][np.argmax(y_pred[i])]: 79 x_adv[i] = x_adv[i] + x_adv_diff[i] 80 y_pred[i] = plus[i] 81 elif minus[i][np.argmax(y_pred[i])] < y_pred[i][np.argmax(y_pred[i])]: 82 x_adv[i] = x_adv[i] - x_adv_diff[i] 83 y_pred[i] = minus[i] 84 else: 85 pass 86 87 return x_adv, y_pred 88 89 def batch(self, x_adv, y_pred, perm, index, epsilon, concurrency): 90 """ 91 Single step for distributed attack. 92 """ 93 noises = [] 94 for i in range(0, len(x_adv)): 95 noises.append(np.zeros(x_adv[i].shape)) 96 97 with concurrent.futures.ThreadPoolExecutor() as executor: 98 future_to_url = {executor.submit(self.step, x_adv, y_pred, perm, index+j, epsilon): j for j in range(0, concurrency)} 99 for future in concurrent.futures.as_completed(future_to_url): 100 j = future_to_url[future] 101 try: 102 x_adv_new, _ = future.result() 103 for i in range(0, len(x_adv)): 104 noises[i] = noises[i] + x_adv_new[i] - x_adv[i] 105 except Exception as exc: 106 print('Task %r generated an exception: %s' % (j, exc)) 107 else: 108 pass 109 110 for i in range(0, len(x_adv)): 111 if(np.sum(noises[i]) != 0): 112 noises = proj_lp(noises[i], xi = 10) 113 x_adv[i] = np.clip(x_adv[i] + noises[i], 0, 1 * SCALE) 114 115 y_adv = self.classifier.predict(PREPROCESS(x_adv.copy())) 116 117 return x_adv, y_adv 118 119 def attack(self, x, y, epsilon=0.05, max_it=1000, concurrency=1): 120 """ 121 Initiate the attack. 122 123 - x: input data 124 - y: input labels 125 - epsilon: perturbation on each pixel 126 - max_it: number of iterations 127 - concurrency: number of concurrent threads 128 """ 129 130 n_targets = 0 131 if type(x) == list: 132 n_targets = len(x) 133 elif type(x) == np.ndarray: 134 n_targets = x.shape[0] 135 else: 136 raise ValueError('Input type not supported...') 137 138 assert n_targets > 0 139 140 # Initialize attack 141 x_adv, y_pred, perm = self.init(x, max_it) 142 143 # Compute number of images correctly classified 144 y_pred_classes = np.argmax(y_pred, axis=1) 145 correct_classified_mask = (y_pred_classes == y) 146 correct_classified = [i for i, v in enumerate(correct_classified_mask) if v] 147 148 # Images to attack 149 not_dones_mask = correct_classified_mask.copy() 150 151 print('Clean accuracy: {:.2%}'.format(np.mean(correct_classified_mask))) 152 153 if np.mean(correct_classified_mask) == 0: 154 print('No clean examples classified correctly. Aborting...') 155 n_queries = np.ones(len(x)) # ones because we have already used 1 query 156 157 mean_nq, mean_nq_ae = np.mean(n_queries), np.mean(n_queries) 158 159 return x 160 161 else: 162 if n_targets > 1: 163 # Horizontally Distributed Attack 164 pbar = tqdm(range(0, max_it), desc="Distributed SimBA Attack (Horizontal)") 165 else: 166 # Vertically Distributed Attack 167 pbar = tqdm(range(0, max_it, concurrency), desc="Distributed SimBA Attack (Vertical)") 168 169 total_queries = np.zeros(len(x)) 170 171 for i_iter in pbar: 172 173 not_dones = [i for i, v in enumerate(not_dones_mask) if v] 174 175 x_adv_curr = [x_adv[idx] for idx in not_dones] 176 y_curr = [y_pred[idx] for idx in not_dones] 177 perm_curr = [perm[idx] for idx in not_dones] 178 179 y_curr = np.array(y_curr) 180 181 if n_targets > 1: 182 # Horizontally Distributed Attack 183 x_adv_curr, y_curr = self.step(x_adv_curr, y_curr, perm_curr, i_iter, epsilon*SCALE) 184 else: 185 # Vertically Distributed Attack 186 x_adv_curr, y_curr = self.batch(x_adv_curr, y_curr, perm_curr, i_iter, epsilon*SCALE, concurrency) 187 188 for i in range(len(not_dones)): 189 x_adv[not_dones[i]] = x_adv_curr[i] 190 y_pred[not_dones[i]] = y_curr[i] 191 192 # Logging stuff 193 if n_targets > 1: 194 # Horizontally Distributed Attack 195 total_queries += 2 * not_dones_mask 196 else: 197 # Vertically Distributed Attack 198 total_queries += 2 * concurrency * not_dones_mask + 1 199 200 y_pred_classes = np.argmax(y_pred, axis=1) 201 not_dones_mask = not_dones_mask * (y_pred_classes == y) 202 203 success_mask = correct_classified_mask * (1 - not_dones_mask) 204 num_success = success_mask.sum() 205 current_success_rate = (num_success / correct_classified_mask.sum()) 206 207 if num_success == 0: 208 success_queries = -1 209 else: 210 success_queries = ((success_mask * total_queries).sum() / num_success) 211 212 pbar.set_postfix({'Total Queries': total_queries.sum(), 'Mean Higest Prediction': y_pred[correct_classified].max(axis=1).mean(), 'Attack Success Rate': current_success_rate, 'Avg Queries': success_queries}) 213 214 acc = not_dones_mask.sum() / correct_classified_mask.sum() 215 mean_nq, mean_nq_ae = np.mean(total_queries), np.mean(total_queries *success_mask) 216 217 # Early break 218 if current_success_rate == 1.0: 219 break 220 221 gc.collect() 222 223 return x_adv
Implementation of the SimBA
attack. Paper link: https://arxiv.org/abs/1905.07121
SimBA(classifier)
36 def __init__(self, classifier): 37 """ 38 Create a class: `SimBA` instance. 39 - classifier: model to attack 40 """ 41 super().__init__(classifier)
Create a class: SimBA
instance.
- classifier: model to attack
def
init(self, x, max_it):
43 def init(self, x, max_it): 44 """ 45 Initialize the attack. 46 """ 47 48 x_adv = x.copy() 49 y_pred = self.classifier.predict(PREPROCESS(x.copy())) 50 51 perm = [] 52 for xi in x: 53 perm.append(np.random.permutation(xi.reshape(-1).shape[0])) 54 assert len(perm[-1]) > max_it, 'The maxinum number of iteration should be smaller than the image dimension.' 55 56 return x_adv, y_pred, perm
Initialize the attack.
def
step(self, x_adv, y_pred, perm, index, epsilon):
58 def step(self, x_adv, y_pred, perm, index, epsilon): 59 """ 60 Single step for non-distributed attack. 61 """ 62 63 x_adv_plus = [] 64 x_adv_minus = [] 65 x_adv_diff = [] 66 for i in range(0, len(x_adv)): 67 diff = np.zeros(x_adv[i].reshape(-1).shape[0]) 68 diff[perm[i][index]] = epsilon 69 diff = diff.reshape(x_adv[i].shape) 70 x_adv_plus.append(np.clip(x_adv[i] + diff, 0, 1 * SCALE)) 71 x_adv_minus.append(np.clip(x_adv[i] - diff, 0, 1 * SCALE)) 72 x_adv_diff.append(diff) 73 74 plus = self.classifier.predict(PREPROCESS(x_adv_plus.copy())) 75 minus = self.classifier.predict(PREPROCESS(x_adv_minus.copy())) 76 77 for i in range(0, len(x_adv)): 78 if plus[i][np.argmax(y_pred[i])] < y_pred[i][np.argmax(y_pred[i])]: 79 x_adv[i] = x_adv[i] + x_adv_diff[i] 80 y_pred[i] = plus[i] 81 elif minus[i][np.argmax(y_pred[i])] < y_pred[i][np.argmax(y_pred[i])]: 82 x_adv[i] = x_adv[i] - x_adv_diff[i] 83 y_pred[i] = minus[i] 84 else: 85 pass 86 87 return x_adv, y_pred
Single step for non-distributed attack.
def
batch(self, x_adv, y_pred, perm, index, epsilon, concurrency):
89 def batch(self, x_adv, y_pred, perm, index, epsilon, concurrency): 90 """ 91 Single step for distributed attack. 92 """ 93 noises = [] 94 for i in range(0, len(x_adv)): 95 noises.append(np.zeros(x_adv[i].shape)) 96 97 with concurrent.futures.ThreadPoolExecutor() as executor: 98 future_to_url = {executor.submit(self.step, x_adv, y_pred, perm, index+j, epsilon): j for j in range(0, concurrency)} 99 for future in concurrent.futures.as_completed(future_to_url): 100 j = future_to_url[future] 101 try: 102 x_adv_new, _ = future.result() 103 for i in range(0, len(x_adv)): 104 noises[i] = noises[i] + x_adv_new[i] - x_adv[i] 105 except Exception as exc: 106 print('Task %r generated an exception: %s' % (j, exc)) 107 else: 108 pass 109 110 for i in range(0, len(x_adv)): 111 if(np.sum(noises[i]) != 0): 112 noises = proj_lp(noises[i], xi = 10) 113 x_adv[i] = np.clip(x_adv[i] + noises[i], 0, 1 * SCALE) 114 115 y_adv = self.classifier.predict(PREPROCESS(x_adv.copy())) 116 117 return x_adv, y_adv
Single step for distributed attack.
def
attack(self, x, y, epsilon=0.05, max_it=1000, concurrency=1):
119 def attack(self, x, y, epsilon=0.05, max_it=1000, concurrency=1): 120 """ 121 Initiate the attack. 122 123 - x: input data 124 - y: input labels 125 - epsilon: perturbation on each pixel 126 - max_it: number of iterations 127 - concurrency: number of concurrent threads 128 """ 129 130 n_targets = 0 131 if type(x) == list: 132 n_targets = len(x) 133 elif type(x) == np.ndarray: 134 n_targets = x.shape[0] 135 else: 136 raise ValueError('Input type not supported...') 137 138 assert n_targets > 0 139 140 # Initialize attack 141 x_adv, y_pred, perm = self.init(x, max_it) 142 143 # Compute number of images correctly classified 144 y_pred_classes = np.argmax(y_pred, axis=1) 145 correct_classified_mask = (y_pred_classes == y) 146 correct_classified = [i for i, v in enumerate(correct_classified_mask) if v] 147 148 # Images to attack 149 not_dones_mask = correct_classified_mask.copy() 150 151 print('Clean accuracy: {:.2%}'.format(np.mean(correct_classified_mask))) 152 153 if np.mean(correct_classified_mask) == 0: 154 print('No clean examples classified correctly. Aborting...') 155 n_queries = np.ones(len(x)) # ones because we have already used 1 query 156 157 mean_nq, mean_nq_ae = np.mean(n_queries), np.mean(n_queries) 158 159 return x 160 161 else: 162 if n_targets > 1: 163 # Horizontally Distributed Attack 164 pbar = tqdm(range(0, max_it), desc="Distributed SimBA Attack (Horizontal)") 165 else: 166 # Vertically Distributed Attack 167 pbar = tqdm(range(0, max_it, concurrency), desc="Distributed SimBA Attack (Vertical)") 168 169 total_queries = np.zeros(len(x)) 170 171 for i_iter in pbar: 172 173 not_dones = [i for i, v in enumerate(not_dones_mask) if v] 174 175 x_adv_curr = [x_adv[idx] for idx in not_dones] 176 y_curr = [y_pred[idx] for idx in not_dones] 177 perm_curr = [perm[idx] for idx in not_dones] 178 179 y_curr = np.array(y_curr) 180 181 if n_targets > 1: 182 # Horizontally Distributed Attack 183 x_adv_curr, y_curr = self.step(x_adv_curr, y_curr, perm_curr, i_iter, epsilon*SCALE) 184 else: 185 # Vertically Distributed Attack 186 x_adv_curr, y_curr = self.batch(x_adv_curr, y_curr, perm_curr, i_iter, epsilon*SCALE, concurrency) 187 188 for i in range(len(not_dones)): 189 x_adv[not_dones[i]] = x_adv_curr[i] 190 y_pred[not_dones[i]] = y_curr[i] 191 192 # Logging stuff 193 if n_targets > 1: 194 # Horizontally Distributed Attack 195 total_queries += 2 * not_dones_mask 196 else: 197 # Vertically Distributed Attack 198 total_queries += 2 * concurrency * not_dones_mask + 1 199 200 y_pred_classes = np.argmax(y_pred, axis=1) 201 not_dones_mask = not_dones_mask * (y_pred_classes == y) 202 203 success_mask = correct_classified_mask * (1 - not_dones_mask) 204 num_success = success_mask.sum() 205 current_success_rate = (num_success / correct_classified_mask.sum()) 206 207 if num_success == 0: 208 success_queries = -1 209 else: 210 success_queries = ((success_mask * total_queries).sum() / num_success) 211 212 pbar.set_postfix({'Total Queries': total_queries.sum(), 'Mean Higest Prediction': y_pred[correct_classified].max(axis=1).mean(), 'Attack Success Rate': current_success_rate, 'Avg Queries': success_queries}) 213 214 acc = not_dones_mask.sum() / correct_classified_mask.sum() 215 mean_nq, mean_nq_ae = np.mean(total_queries), np.mean(total_queries *success_mask) 216 217 # Early break 218 if current_success_rate == 1.0: 219 break 220 221 gc.collect() 222 223 return x_adv
Initiate the attack.
- x: input data
- y: input labels
- epsilon: perturbation on each pixel
- max_it: number of iterations
- concurrency: number of concurrent threads