| @@ -29,9 +29,8 @@ TAG = 'PointWiseAttack' | |||||
| class PointWiseAttack(Attack): | class PointWiseAttack(Attack): | ||||
| """ | """ | ||||
| The Pointwise Attack make sure use the minimum number of changed pixels | |||||
| to generate adversarial sample for each original sample.Those changed pixels | |||||
| will use binary seach to make sure the distance between adversarial sample | |||||
| The Pointwise Attack make sure use the minimum number of changed pixels to generate adversarial sample for each | |||||
| original sample.Those changed pixels will use binary search to make sure the distance between adversarial sample | |||||
| and original sample is as close as possible. | and original sample is as close as possible. | ||||
| References: `L. Schott, J. Rauber, M. Bethge, W. Brendel: "Towards the | References: `L. Schott, J. Rauber, M. Bethge, W. Brendel: "Towards the | ||||
| @@ -42,32 +41,23 @@ class PointWiseAttack(Attack): | |||||
| model (BlackModel): Target model. | model (BlackModel): Target model. | ||||
| max_iter (int): Max rounds of iteration to generate adversarial image. | max_iter (int): Max rounds of iteration to generate adversarial image. | ||||
| search_iter (int): Max rounds of binary search. | search_iter (int): Max rounds of binary search. | ||||
| is_targeted (bool): If True, targeted attack. If False, untargeted | |||||
| attack. Default: False. | |||||
| init_attack (Attack): Attack used to find a starting point. Default: | |||||
| None. | |||||
| sparse (bool): If True, input labels are sparse-encoded. If False, | |||||
| input labels are one-hot-encoded. Default: True. | |||||
| is_targeted (bool): If True, targeted attack. If False, untargeted attack. Default: False. | |||||
| init_attack (Attack): Attack used to find a starting point. Default: None. | |||||
| sparse (bool): If True, input labels are sparse-encoded. If False, input labels are one-hot-encoded. | |||||
| Default: True. | |||||
| Examples: | Examples: | ||||
| >>> attack = PointWiseAttack(model) | >>> attack = PointWiseAttack(model) | ||||
| """ | """ | ||||
| def __init__(self, | |||||
| model, | |||||
| max_iter=1000, | |||||
| search_iter=10, | |||||
| is_targeted=False, | |||||
| init_attack=None, | |||||
| sparse=True): | |||||
| def __init__(self, model, max_iter=1000, search_iter=10, is_targeted=False, init_attack=None, sparse=True): | |||||
| super(PointWiseAttack, self).__init__() | super(PointWiseAttack, self).__init__() | ||||
| self._model = check_model('model', model, BlackModel) | self._model = check_model('model', model, BlackModel) | ||||
| self._max_iter = check_int_positive('max_iter', max_iter) | self._max_iter = check_int_positive('max_iter', max_iter) | ||||
| self._search_iter = check_int_positive('search_iter', search_iter) | self._search_iter = check_int_positive('search_iter', search_iter) | ||||
| self._is_targeted = check_param_type('is_targeted', is_targeted, bool) | self._is_targeted = check_param_type('is_targeted', is_targeted, bool) | ||||
| if init_attack is None: | if init_attack is None: | ||||
| self._init_attack = SaltAndPepperNoiseAttack(model, | |||||
| is_targeted=self._is_targeted) | |||||
| self._init_attack = SaltAndPepperNoiseAttack(model, is_targeted=self._is_targeted) | |||||
| else: | else: | ||||
| self._init_attack = init_attack | self._init_attack = init_attack | ||||
| self._sparse = check_param_type('sparse', sparse, bool) | self._sparse = check_param_type('sparse', sparse, bool) | ||||
| @@ -77,10 +67,9 @@ class PointWiseAttack(Attack): | |||||
| Generate adversarial examples based on input samples and targeted labels. | Generate adversarial examples based on input samples and targeted labels. | ||||
| Args: | Args: | ||||
| inputs (numpy.ndarray): Benign input samples used as references to create | |||||
| adversarial examples. | |||||
| labels (numpy.ndarray): For targeted attack, labels are adversarial | |||||
| target labels. For untargeted attack, labels are ground-truth labels. | |||||
| inputs (numpy.ndarray): Benign input samples used as references to create adversarial examples. | |||||
| labels (numpy.ndarray): For targeted attack, labels are adversarial target labels. | |||||
| For untargeted attack, labels are ground-truth labels. | |||||
| Returns: | Returns: | ||||
| - numpy.ndarray, bool values for each attack result. | - numpy.ndarray, bool values for each attack result. | ||||
| @@ -90,38 +79,26 @@ class PointWiseAttack(Attack): | |||||
| - numpy.ndarray, query times for each sample. | - numpy.ndarray, query times for each sample. | ||||
| Examples: | Examples: | ||||
| >>> is_adv_list, adv_list, query_times_each_adv = attack.generate( | |||||
| >>> [[0.1, 0.2, 0.6], [0.3, 0, 0.4]], | |||||
| >>> [2, 3]) | |||||
| >>> is_adv_list, adv_list, query_times_each_adv = attack.generate([[0.1, 0.2, 0.6], [0.3, 0, 0.4]], [2, 3]) | |||||
| """ | """ | ||||
| arr_x, arr_y = check_pair_numpy_param('inputs', inputs, 'labels', | |||||
| labels) | |||||
| arr_x, arr_y = check_pair_numpy_param('inputs', inputs, 'labels', labels) | |||||
| if not self._sparse: | if not self._sparse: | ||||
| arr_y = np.argmax(arr_y, axis=1) | arr_y = np.argmax(arr_y, axis=1) | ||||
| ini_bool, ini_advs, ini_count = self._initialize_starting_point(arr_x, | |||||
| arr_y) | |||||
| ini_bool, ini_advs, ini_count = self._initialize_starting_point(arr_x, arr_y) | |||||
| is_adv_list = list() | is_adv_list = list() | ||||
| adv_list = list() | adv_list = list() | ||||
| query_times_each_adv = list() | query_times_each_adv = list() | ||||
| for sample, sample_label, start_adv, ite_bool, ite_c in zip(arr_x, | |||||
| arr_y, | |||||
| ini_advs, | |||||
| ini_bool, | |||||
| ini_count): | |||||
| for sample, sample_label, start_adv, ite_bool, ite_c in zip(arr_x, arr_y, ini_advs, ini_bool, ini_count): | |||||
| if ite_bool: | if ite_bool: | ||||
| LOGGER.info(TAG, 'Start optimizing.') | LOGGER.info(TAG, 'Start optimizing.') | ||||
| ori_label = np.argmax( | |||||
| self._model.predict(np.expand_dims(sample, axis=0))[0]) | |||||
| ori_label = np.argmax(self._model.predict(np.expand_dims(sample, axis=0))[0]) | |||||
| ini_label = np.argmax(self._model.predict(np.expand_dims(start_adv, axis=0))[0]) | ini_label = np.argmax(self._model.predict(np.expand_dims(start_adv, axis=0))[0]) | ||||
| is_adv, adv_x, query_times = self._decision_optimize(sample, | |||||
| sample_label, | |||||
| start_adv) | |||||
| adv_label = np.argmax( | |||||
| self._model.predict(np.expand_dims(adv_x, axis=0))[0]) | |||||
| LOGGER.debug(TAG, 'before ini attack label is :{}'.format(ori_label)) | |||||
| LOGGER.debug(TAG, 'after ini attack label is :{}'.format(ini_label)) | |||||
| LOGGER.debug(TAG, 'INPUT optimize label is :{}'.format(sample_label)) | |||||
| LOGGER.debug(TAG, 'after pointwise attack label is :{}'.format(adv_label)) | |||||
| is_adv, adv_x, query_times = self._decision_optimize(sample, sample_label, start_adv) | |||||
| adv_label = np.argmax(self._model.predict(np.expand_dims(adv_x, axis=0))[0]) | |||||
| LOGGER.info(TAG, 'before ini attack label is :{}'.format(ori_label)) | |||||
| LOGGER.info(TAG, 'after ini attack label is :{}'.format(ini_label)) | |||||
| LOGGER.info(TAG, 'INPUT optimize label is :{}'.format(sample_label)) | |||||
| LOGGER.info(TAG, 'after pointwise attack label is :{}'.format(adv_label)) | |||||
| is_adv_list.append(is_adv) | is_adv_list.append(is_adv) | ||||
| adv_list.append(adv_x) | adv_list.append(adv_x) | ||||
| query_times_each_adv.append(query_times + ite_c) | query_times_each_adv.append(query_times + ite_c) | ||||
| @@ -133,7 +110,7 @@ class PointWiseAttack(Attack): | |||||
| is_adv_list = np.array(is_adv_list) | is_adv_list = np.array(is_adv_list) | ||||
| adv_list = np.array(adv_list) | adv_list = np.array(adv_list) | ||||
| query_times_each_adv = np.array(query_times_each_adv) | query_times_each_adv = np.array(query_times_each_adv) | ||||
| LOGGER.debug(TAG, 'ret list is: {}'.format(adv_list)) | |||||
| LOGGER.info(TAG, 'ret list is: {}'.format(adv_list)) | |||||
| return is_adv_list, adv_list, query_times_each_adv | return is_adv_list, adv_list, query_times_each_adv | ||||
| def _decision_optimize(self, unperturbed_img, input_label, perturbed_img): | def _decision_optimize(self, unperturbed_img, input_label, perturbed_img): | ||||
| @@ -167,8 +144,8 @@ class PointWiseAttack(Attack): | |||||
| LOGGER.error(TAG, msg) | LOGGER.error(TAG, msg) | ||||
| raise ValueError(msg) | raise ValueError(msg) | ||||
| l2_dis = np.linalg.norm(perturbed_img - unperturbed_img) | l2_dis = np.linalg.norm(perturbed_img - unperturbed_img) | ||||
| LOGGER.debug(TAG, 'Before optimize, the l2 distance between original ' | |||||
| 'sample and adversarial sample is: {}'.format(l2_dis)) | |||||
| LOGGER.info(TAG, 'Before optimize, the l2 distance between original ' \ | |||||
| 'sample and adversarial sample is: {}'.format(l2_dis)) | |||||
| # recover pixel if image is adversarial | # recover pixel if image is adversarial | ||||
| for _ in range(self._max_iter): | for _ in range(self._max_iter): | ||||
| is_improve = False | is_improve = False | ||||
| @@ -180,8 +157,7 @@ class PointWiseAttack(Attack): | |||||
| if mask[ite_ind]: | if mask[ite_ind]: | ||||
| recover[ite_ind] = unperturbed_img[ite_ind] | recover[ite_ind] = unperturbed_img[ite_ind] | ||||
| query_count += 1 | query_count += 1 | ||||
| is_adv = self._model.is_adversarial( | |||||
| recover.reshape(img_shape), input_label, self._is_targeted) | |||||
| is_adv = self._model.is_adversarial(recover.reshape(img_shape), input_label, self._is_targeted) | |||||
| if is_adv: | if is_adv: | ||||
| is_improve = True | is_improve = True | ||||
| perturbed_img[ite_ind] = recover[ite_ind] | perturbed_img[ite_ind] = recover[ite_ind] | ||||
| @@ -189,8 +165,7 @@ class PointWiseAttack(Attack): | |||||
| else: | else: | ||||
| recover[ite_ind] = perturbed_img[ite_ind] | recover[ite_ind] = perturbed_img[ite_ind] | ||||
| l2_dis = np.linalg.norm(perturbed_img - unperturbed_img) | l2_dis = np.linalg.norm(perturbed_img - unperturbed_img) | ||||
| if not is_improve or (np.square(l2_dis) / np.sqrt(len(pixels_ind)) | |||||
| <= self._get_threthod()): | |||||
| if not is_improve or (np.square(l2_dis) / np.sqrt(len(pixels_ind)) <= self._get_threthod()): | |||||
| break | break | ||||
| LOGGER.debug(TAG, 'first round: Query count {}'.format(query_count)) | LOGGER.debug(TAG, 'first round: Query count {}'.format(query_count)) | ||||
| LOGGER.debug(TAG, 'Starting binary searches.') | LOGGER.debug(TAG, 'Starting binary searches.') | ||||
| @@ -205,47 +180,36 @@ class PointWiseAttack(Attack): | |||||
| continue | continue | ||||
| recover[ite_ind] = unperturbed_img[ite_ind] | recover[ite_ind] = unperturbed_img[ite_ind] | ||||
| query_count += 1 | query_count += 1 | ||||
| is_adv = self._model.is_adversarial(recover.reshape(img_shape), | |||||
| input_label, | |||||
| self._is_targeted) | |||||
| is_adv = self._model.is_adversarial(recover.reshape(img_shape), input_label, self._is_targeted) | |||||
| if is_adv: | if is_adv: | ||||
| is_improve = True | is_improve = True | ||||
| mask[ite_ind] = True | |||||
| mask[ite_ind] = False | |||||
| perturbed_img[ite_ind] = recover[ite_ind] | perturbed_img[ite_ind] = recover[ite_ind] | ||||
| l2_dis = np.linalg.norm(perturbed_img - unperturbed_img) | l2_dis = np.linalg.norm(perturbed_img - unperturbed_img) | ||||
| LOGGER.debug(TAG, | |||||
| 'Reset {}th pixel value to original, ' | |||||
| 'l2 distance: {}.'.format(ite_ind, l2_dis)) | |||||
| LOGGER.info(TAG, 'Reset {}th pixel value to original, l2 distance: {}.'.format(ite_ind, l2_dis)) | |||||
| break | break | ||||
| else: | else: | ||||
| # use binary searches | # use binary searches | ||||
| optimized_value, b_query = self._binary_search( | |||||
| perturbed_img, | |||||
| unperturbed_img, | |||||
| ite_ind, | |||||
| input_label, img_shape) | |||||
| optimized_value, b_query = self._binary_search(perturbed_img, | |||||
| unperturbed_img, | |||||
| ite_ind, | |||||
| input_label, img_shape) | |||||
| query_count += b_query | query_count += b_query | ||||
| if optimized_value != perturbed_img[ite_ind]: | if optimized_value != perturbed_img[ite_ind]: | ||||
| is_improve = True | is_improve = True | ||||
| mask[ite_ind] = True | |||||
| perturbed_img[ite_ind] = optimized_value | perturbed_img[ite_ind] = optimized_value | ||||
| l2_dis = np.linalg.norm(perturbed_img - unperturbed_img) | l2_dis = np.linalg.norm(perturbed_img - unperturbed_img) | ||||
| LOGGER.debug(TAG, | |||||
| 'Reset {}th pixel value to original, ' | |||||
| 'l2 distance: {}.'.format(ite_ind, | |||||
| l2_dis)) | |||||
| LOGGER.info(TAG, 'Reset {}th pixel value to original, l2 distance: {}.'.format(ite_ind, l2_dis)) | |||||
| break | break | ||||
| l2_dis = np.linalg.norm(perturbed_img - unperturbed_img) | l2_dis = np.linalg.norm(perturbed_img - unperturbed_img) | ||||
| if not is_improve or (np.square(l2_dis) / np.sqrt(len(pixels_ind)) | |||||
| <= self._get_threthod()): | |||||
| LOGGER.debug(TAG, 'second optimized finish.') | |||||
| if not is_improve or (np.square(l2_dis) / np.sqrt(len(pixels_ind)) <= self._get_threthod()): | |||||
| LOGGER.info(TAG, 'second optimized finish.') | |||||
| break | break | ||||
| LOGGER.info(TAG, 'Optimized finished, query count is {}'.format(query_count)) | LOGGER.info(TAG, 'Optimized finished, query count is {}'.format(query_count)) | ||||
| # this method use to optimized the adversarial sample | # this method use to optimized the adversarial sample | ||||
| return True, perturbed_img.reshape(img_shape), query_count | return True, perturbed_img.reshape(img_shape), query_count | ||||
| def _binary_search(self, perturbed_img, unperturbed_img, ite_ind, | |||||
| input_label, img_shape): | |||||
| def _binary_search(self, perturbed_img, unperturbed_img, ite_ind, input_label, img_shape): | |||||
| """ | """ | ||||
| For original pixel of inputs, use binary search to get the nearest pixel | For original pixel of inputs, use binary search to get the nearest pixel | ||||
| value with original value with adversarial feature. | value with original value with adversarial feature. | ||||