From f3acd2f9831223c68989f963586851336fae631a Mon Sep 17 00:00:00 2001 From: ZhidanLiu Date: Tue, 15 Mar 2022 17:46:30 +0800 Subject: [PATCH] fix cmetrics problem --- .jenkins/check/config/filter_pylint.txt | 1 + .jenkins/test/config/dependent_packages.yaml | 2 +- mindarmour/__init__.py | 2 +- mindarmour/adv_robustness/attacks/__init__.py | 6 +- mindarmour/adv_robustness/attacks/attack.py | 11 +- .../adv_robustness/attacks/carlini_wagner.py | 128 +++++++-------- mindarmour/adv_robustness/attacks/deep_fool.py | 35 ++--- .../detectors/black/similarity_detector.py | 4 +- mindarmour/fuzz_testing/fuzzing.py | 1 - .../transform/image/natural_perturb.py | 10 +- .../privacy/diff_privacy/optimizer/optimizer.py | 13 +- mindarmour/privacy/diff_privacy/train/model.py | 16 +- mindarmour/privacy/evaluation/_check_config.py | 9 +- mindarmour/privacy/sup_privacy/sup_ctrl/conctrl.py | 171 ++++++++++++--------- mindarmour/privacy/sup_privacy/train/model.py | 1 - .../concept_drift/concept_drift_check_images.py | 4 +- .../concept_drift_check_time_series.py | 2 - .../reliability/model_fault_injection/__init__.py | 1 - .../model_fault_injection/fault_injection.py | 2 - .../model_fault_injection/fault_type.py | 2 - setup.py | 6 +- 21 files changed, 206 insertions(+), 221 deletions(-) diff --git a/.jenkins/check/config/filter_pylint.txt b/.jenkins/check/config/filter_pylint.txt index 44146c8..ca195e4 100644 --- a/.jenkins/check/config/filter_pylint.txt +++ b/.jenkins/check/config/filter_pylint.txt @@ -6,6 +6,7 @@ "mindarmour/setup.py" "missing-docstring" "mindarmour/setup.py" "invalid-name" "mindarmour/mindarmour/reliability/model_fault_injection/fault_injection.py" "protected-access" +"mindarmour/setup.py" "unused-argument" # Tests "mindarmour/tests/st" "missing-docstring" diff --git a/.jenkins/test/config/dependent_packages.yaml b/.jenkins/test/config/dependent_packages.yaml index 8076a06..dc434a6 100644 --- a/.jenkins/test/config/dependent_packages.yaml +++ b/.jenkins/test/config/dependent_packages.yaml @@ -1,2 +1,2 @@ mindspore: - 'mindspore/mindspore/version/202202/20220226/master_20220226002452_09f114e52ef6ebdefb1de4477e035b771b61f5b6/' + 'mindspore/mindspore/daily/202203/20220320/master_20220320041531_3e442945369de2d9dd20e9e2e9d3c7524a128ee7_newest/' diff --git a/mindarmour/__init__.py b/mindarmour/__init__.py index 71bbbe0..ec295cd 100644 --- a/mindarmour/__init__.py +++ b/mindarmour/__init__.py @@ -15,7 +15,7 @@ MindArmour, a tool box of MindSpore to enhance model trustworthiness and achieve privacy-preserving machine learning. """ -from .adv_robustness.attacks import Attack +from .adv_robustness.attacks.attack import Attack from .adv_robustness.attacks.black.black_model import BlackModel from .adv_robustness.defenses.defense import Defense from .adv_robustness.detectors.detector import Detector diff --git a/mindarmour/adv_robustness/attacks/__init__.py b/mindarmour/adv_robustness/attacks/__init__.py index 377c2ce..b8ed133 100644 --- a/mindarmour/adv_robustness/attacks/__init__.py +++ b/mindarmour/adv_robustness/attacks/__init__.py @@ -15,8 +15,10 @@ This module includes classical black-box and white-box attack algorithms in making adversarial examples. """ -from .gradient_method import * -from .iterative_gradient_method import * +from .gradient_method import FastGradientMethod, FastGradientSignMethod, RandomFastGradientMethod, \ + RandomFastGradientSignMethod, LeastLikelyClassMethod, RandomLeastLikelyClassMethod +from .iterative_gradient_method import IterativeGradientMethod, BasicIterativeMethod, MomentumIterativeMethod, \ + ProjectedGradientDescent, DiverseInputIterativeMethod, MomentumDiverseInputIterativeMethod from .deep_fool import DeepFool from .jsma import JSMAAttack from .carlini_wagner import CarliniWagnerL2Attack diff --git a/mindarmour/adv_robustness/attacks/attack.py b/mindarmour/adv_robustness/attacks/attack.py index 8a05bf1..4035d04 100644 --- a/mindarmour/adv_robustness/attacks/attack.py +++ b/mindarmour/adv_robustness/attacks/attack.py @@ -178,13 +178,12 @@ class Attack: best_position = check_numpy_param('best_position', best_position) x_ori, best_position = check_equal_shape('x_ori', x_ori, 'best_position', best_position) _, original_num = self._detection_scores((best_position,) + auxiliary_inputs, gt_boxes, gt_labels, model) - # pylint: disable=invalid-name - REDUCTION_ITERS = 6 # recover 10% difference each time and recover 60% totally. - for _ in range(REDUCTION_ITERS): - BLOCK_NUM = 30 # divide the image into 30 segments - block_width = best_position.shape[0] // BLOCK_NUM + reduction_iters = 6 # recover 10% difference each time and recover 60% totally. + for _ in range(reduction_iters): + block_num = 30 # divide the image into 30 segments + block_width = best_position.shape[0] // block_num if block_width > 0: - for i in range(BLOCK_NUM): + for i in range(block_num): diff = x_ori[i*block_width: (i+1)*block_width, :, :]\ - best_position[i*block_width:(i+1)*block_width, :, :] if np.max(np.abs(diff)) >= 0.1*(self._bounds[1] - self._bounds[0]): diff --git a/mindarmour/adv_robustness/attacks/carlini_wagner.py b/mindarmour/adv_robustness/attacks/carlini_wagner.py index 9843505..e63df14 100644 --- a/mindarmour/adv_robustness/attacks/carlini_wagner.py +++ b/mindarmour/adv_robustness/attacks/carlini_wagner.py @@ -125,20 +125,14 @@ class CarliniWagnerL2Attack(Attack): self._num_classes = check_int_positive('num_classes', num_classes) self._min = check_param_type('box_min', box_min, float) self._max = check_param_type('box_max', box_max, float) - self._bin_search_steps = check_int_positive('search_steps', - bin_search_steps) - self._max_iterations = check_int_positive('max_iterations', - max_iterations) - self._confidence = check_param_multi_types('confidence', confidence, - [int, float]) - self._learning_rate = check_value_positive('learning_rate', - learning_rate) - self._initial_const = check_value_positive('initial_const', - initial_const) + self._bin_search_steps = check_int_positive('search_steps', bin_search_steps) + self._max_iterations = check_int_positive('max_iterations', max_iterations) + self._confidence = check_param_multi_types('confidence', confidence, [int, float]) + self._learning_rate = check_value_positive('learning_rate', learning_rate) + self._initial_const = check_value_positive('initial_const', initial_const) self._abort_early = check_param_type('abort_early', abort_early, bool) self._fast = check_param_type('fast', fast, bool) - self._abort_early_check_ratio = check_value_positive('abort_early_check_ratio', - abort_early_check_ratio) + self._abort_early_check_ratio = check_value_positive('abort_early_check_ratio', abort_early_check_ratio) self._targeted = check_param_type('targeted', targeted, bool) self._net_grad = GradWrap(self._network) self._sparse = check_param_type('sparse', sparse, bool) @@ -154,10 +148,8 @@ class CarliniWagnerL2Attack(Attack): new_x (numpy.ndarray): Adversarial examples. org_x (numpy.ndarray): Original benign input samples. org_or_target_class (numpy.ndarray): Original/target labels. - constant (float): A trade-off constant to use to balance loss - and perturbation norm. - confidence (float): Confidence level of the output of adversarial - examples. + constant (float): A trade-off constant to use to balance loss and perturbation norm. + confidence (float): Confidence level of the output of adversarial examples. Returns: numpy.ndarray, norm of perturbation, sum of the loss and the @@ -183,7 +175,7 @@ class CarliniWagnerL2Attack(Attack): other_class_index = _best_logits_of_other_class( logits, org_or_target_class, value=np.inf) - loss1 = np.sum((new_x - org_x)**2, + loss1 = np.sum((new_x - org_x) ** 2, axis=tuple(range(len(new_x.shape))[1:])) loss2 = np.zeros_like(loss1, dtype=self._dtype) loss2_grade = np.zeros_like(new_x, dtype=self._dtype) @@ -193,16 +185,16 @@ class CarliniWagnerL2Attack(Attack): loss2[i] = max(0, logits[i][other_class_index[i]] - logits[i][org_or_target_class[i]] + confidence) - loss2_grade[i] = constant[i]*(jaco_grad[other_class_index[ + loss2_grade[i] = constant[i] * (jaco_grad[other_class_index[ i]][i] - jaco_grad[org_or_target_class[i]][i]) else: for i in range(org_or_target_class.shape[0]): loss2[i] = max(0, logits[i][org_or_target_class[i]] - logits[i][other_class_index[i]] + confidence) - loss2_grade[i] = constant[i]*(jaco_grad[org_or_target_class[ + loss2_grade[i] = constant[i] * (jaco_grad[org_or_target_class[ i]][i] - jaco_grad[other_class_index[i]][i]) - total_loss = loss1 + constant*loss2 - loss1_grade = 2*(new_x - org_x) + total_loss = loss1 + constant * loss2 + loss1_grade = 2 * (new_x - org_x) for i in range(org_or_target_class.shape[0]): if loss2[i] < 0: msg = 'loss value should greater than or equal to 0, ' \ @@ -233,7 +225,7 @@ class CarliniWagnerL2Attack(Attack): mean = (self._min + self._max) / 2 diff = (self._max - self._min) / 2 inputs = (inputs - mean) / diff - inputs = inputs*0.999999 + inputs = inputs * 0.999999 return np.arctanh(inputs) def _to_model_space(self, inputs): @@ -257,8 +249,8 @@ class CarliniWagnerL2Attack(Attack): the_grad = 1 - np.square(inputs) mean = (self._min + self._max) / 2 diff = (self._max - self._min) / 2 - inputs = inputs*diff + mean - the_grad = the_grad*diff + inputs = inputs * diff + mean + the_grad = the_grad * diff return inputs, the_grad def _check_success(self, logits, labels): @@ -292,35 +284,30 @@ class CarliniWagnerL2Attack(Attack): reconstructed_original, _ = self._to_model_space(att_original) # find an adversarial sample - const = np.ones_like(labels, dtype=self._dtype)*self._initial_const + const = np.ones_like(labels, dtype=self._dtype) * self._initial_const lower_bound = np.zeros_like(labels, dtype=self._dtype) - upper_bound = np.ones_like(labels, dtype=self._dtype)*np.inf + upper_bound = np.ones_like(labels, dtype=self._dtype) * np.inf adversarial_res = inputs.copy() - adversarial_loss = np.ones_like(labels, dtype=self._dtype)*np.inf + adversarial_loss = np.ones_like(labels, dtype=self._dtype) * np.inf samples_num = labels.shape[0] adv_flag = np.zeros_like(labels) for binary_search_step in range(self._bin_search_steps): - if (binary_search_step == self._bin_search_steps - 1) and \ - (self._bin_search_steps >= 10): + if (binary_search_step == self._bin_search_steps - 1) and (self._bin_search_steps >= 10): const = min(1e10, upper_bound) - LOGGER.debug(TAG, - 'starting optimization with const = %s', - str(const)) + LOGGER.debug(TAG, 'starting optimization with const = %s', str(const)) att_perturbation = np.zeros_like(att_original, dtype=self._dtype) - loss_at_previous_check = np.ones_like(labels, dtype=self._dtype)*np.inf + loss_at_previous_check = np.ones_like(labels, dtype=self._dtype) * np.inf # create a new optimizer to minimize the perturbation optimizer = _AdamOptimizer(att_perturbation.shape) for iteration in range(self._max_iterations): - x_input, dxdp = self._to_model_space( - att_original + att_perturbation) + x_input, dxdp = self._to_model_space(att_original + att_perturbation) logits = self._network(Tensor(x_input)).asnumpy() - current_l2_loss, current_loss, dldx = self._loss_function( - logits, x_input, reconstructed_original, - labels, const, self._confidence) + current_l2_loss, current_loss, dldx = self._loss_function(logits, x_input, reconstructed_original, + labels, const, self._confidence) is_adv = self._check_success(logits, labels) @@ -334,58 +321,51 @@ class CarliniWagnerL2Attack(Attack): if np.all(adv_flag): if self._fast: LOGGER.debug(TAG, "succeed find adversarial examples.") - msg = 'iteration: {}, logits_att: {}, ' \ - 'loss: {}, l2_dist: {}' \ - .format(iteration, - np.argmax(logits, axis=1), - current_loss, current_l2_loss) + msg = 'iteration: {}, logits_att: {}, loss: {}, l2_dist: {}' \ + .format(iteration, np.argmax(logits, axis=1), current_loss, current_l2_loss) LOGGER.debug(TAG, msg) return adversarial_res dldx, inputs = check_equal_shape('dldx', dldx, 'inputs', inputs) - gradient = dldx*dxdp - att_perturbation += \ - optimizer(gradient, self._learning_rate) + gradient = dldx * dxdp + att_perturbation += optimizer(gradient, self._learning_rate) # check if should stop iteration early flag = True iter_check = iteration % (np.ceil( - self._max_iterations*self._abort_early_check_ratio)) + self._max_iterations * self._abort_early_check_ratio)) if self._abort_early and iter_check == 0: # check progress for i in range(inputs.shape[0]): - if current_loss[i] <= .9999*loss_at_previous_check[i]: + if current_loss[i] <= .9999 * loss_at_previous_check[i]: flag = False # stop Adam if all samples has no progress if flag: - LOGGER.debug(TAG, - 'step:%d, no progress yet, stop iteration', - binary_search_step) + LOGGER.debug(TAG, 'step:%d, no progress yet, stop iteration', binary_search_step) break loss_at_previous_check = current_loss - - for i in range(samples_num): - # update bound based on search result - if adv_flag[i]: - LOGGER.debug(TAG, - 'example %d, found adversarial with const=%f', - i, const[i]) - upper_bound[i] = const[i] - else: - LOGGER.debug(TAG, - 'example %d, failed to find adversarial' - ' with const=%f', - i, const[i]) - lower_bound[i] = const[i] - - if upper_bound[i] == np.inf: - const[i] *= 10 - else: - const[i] = (lower_bound[i] + upper_bound[i]) / 2 + upper_bound, lower_bound, const = self._update_bounds(samples_num, adv_flag, const, upper_bound, + lower_bound) return adversarial_res + def _update_bounds(self, samples_num, adv_flag, const, upper_bound, lower_bound): + """update bound based on search result""" + for i in range(samples_num): + if adv_flag[i]: + LOGGER.debug(TAG, 'example %d, found adversarial with const=%f', i, const[i]) + upper_bound[i] = const[i] + else: + LOGGER.debug(TAG, 'example %d, failed to find adversarial with const=%f', i, const[i]) + lower_bound[i] = const[i] + + if upper_bound[i] == np.inf: + const[i] *= 10 + else: + const[i] = (lower_bound[i] + upper_bound[i]) / 2 + return upper_bound, lower_bound, const + class _AdamOptimizer: """ @@ -428,8 +408,8 @@ class _AdamOptimizer: """ gradient = check_numpy_param('gradient', gradient) self._t += 1 - self._m = beta1*self._m + (1 - beta1)*gradient - self._v = beta2*self._v + (1 - beta2)*gradient**2 - alpha = learning_rate*np.sqrt(1 - beta2**self._t) / (1 - beta1**self._t) - pertur = -alpha*self._m / (np.sqrt(self._v) + epsilon) + self._m = beta1 * self._m + (1 - beta1) * gradient + self._v = beta2 * self._v + (1 - beta2) * gradient ** 2 + alpha = learning_rate * np.sqrt(1 - beta2 ** self._t) / (1 - beta1 ** self._t) + pertur = -alpha * self._m / (np.sqrt(self._v) + epsilon) return pertur diff --git a/mindarmour/adv_robustness/attacks/deep_fool.py b/mindarmour/adv_robustness/attacks/deep_fool.py index 9de6380..60eefa2 100644 --- a/mindarmour/adv_robustness/attacks/deep_fool.py +++ b/mindarmour/adv_robustness/attacks/deep_fool.py @@ -253,16 +253,7 @@ class DeepFool(Attack): if diff_w_k < diff_w: diff_w = diff_w_k weight = w_k - if self._norm_level == 2 or self._norm_level == '2': - r_i = diff_w*weight / (np.linalg.norm(weight) + 1e-8) - elif self._norm_level == np.inf or self._norm_level == 'inf': - r_i = diff_w*np.sign(weight) \ - / (np.linalg.norm(weight, ord=1) + 1e-8) - else: - msg = 'ord {} is not available in normalization,' \ - .format(str(self._norm_level)) - LOGGER.error(TAG, msg) - raise NotImplementedError(msg) + r_i = self._normalize_r_i(diff_w, weight) r_tot[idx, ...] = r_tot[idx, ...] + r_i images = self._update_image(x_origin, r_tot) @@ -311,16 +302,7 @@ class DeepFool(Attack): diff_w = diff_w_k weight = w_k - if self._norm_level == 2 or self._norm_level == '2': - r_i = diff_w*weight / (np.linalg.norm(weight) + 1e-8) - elif self._norm_level == np.inf or self._norm_level == 'inf': - r_i = diff_w*np.sign(weight) \ - / (np.linalg.norm(weight, ord=1) + 1e-8) - else: - msg = 'ord {} is not available in normalization.' \ - .format(str(self._norm_level)) - LOGGER.error(TAG, msg) - raise NotImplementedError(msg) + r_i = self._normalize_r_i(diff_w, weight) r_tot[idx, ...] = r_tot[idx, ...] + r_i if self._bounds is not None: @@ -337,3 +319,16 @@ class DeepFool(Attack): inputs = inputs.astype(inputs_dtype) del preds, grads return inputs + + def _normalize_r_i(self, diff_w, weight): + """normalize r_i used to update r_tot""" + if self._norm_level == 2 or self._norm_level == '2': + r_i = diff_w * weight / (np.linalg.norm(weight) + 1e-8) + elif self._norm_level == np.inf or self._norm_level == 'inf': + r_i = diff_w * np.sign(weight) / (np.linalg.norm(weight, ord=1) + 1e-8) + else: + msg = 'ord {} is not available in normalization,'.format(str(self._norm_level)) + LOGGER.error(TAG, msg) + raise NotImplementedError(msg) + + return r_i diff --git a/mindarmour/adv_robustness/detectors/black/similarity_detector.py b/mindarmour/adv_robustness/detectors/black/similarity_detector.py index 4fed46f..a5cf583 100644 --- a/mindarmour/adv_robustness/detectors/black/similarity_detector.py +++ b/mindarmour/adv_robustness/detectors/black/similarity_detector.py @@ -160,9 +160,7 @@ class SimilarityDetector(Detector): distance_mat = np.sort(distance_mat, axis=-1) distances.append(distance_mat[:, :self._max_k_neighbor]) # the rest - distance_mat = _pairwise_distances(x_input=data[(data.shape[0] // - self._chunk_size)* - self._chunk_size:, :], + distance_mat = _pairwise_distances(x_input=data[(data.shape[0] // self._chunk_size) * self._chunk_size:, :], y_input=data) distance_mat = np.sort(distance_mat, axis=-1) distances.append(distance_mat[:, :self._max_k_neighbor]) diff --git a/mindarmour/fuzz_testing/fuzzing.py b/mindarmour/fuzz_testing/fuzzing.py index 253bec6..a70c57e 100644 --- a/mindarmour/fuzz_testing/fuzzing.py +++ b/mindarmour/fuzz_testing/fuzzing.py @@ -450,7 +450,6 @@ class Fuzzer: for mutate in mutate_config: method = mutate['method'] if method not in self._attacks_list: - # mutates[method] = self._strategies[method]() mutates[method] = self._strategies[method] else: network = self._target_model._network diff --git a/mindarmour/natural_robustness/transform/image/natural_perturb.py b/mindarmour/natural_robustness/transform/image/natural_perturb.py index db645dc..29848c4 100644 --- a/mindarmour/natural_robustness/transform/image/natural_perturb.py +++ b/mindarmour/natural_robustness/transform/image/natural_perturb.py @@ -120,9 +120,11 @@ class _NaturalPerturb: pass def _check(self, image): - """ Check image format. If input image is RGB and its shape + """ + Check image format. If input image is RGB and its shape is (C, H, W), it will be transposed to (H, W, C). If the value - of the image is not normalized , it will be rescaled between 0 to 255.""" + of the image is not normalized , it will be rescaled between 0 to 255. + """ rgb = _is_rgb(image) chw = False gray3dim = False @@ -131,14 +133,10 @@ class _NaturalPerturb: chw = _is_chw(image) if chw: image = _chw_to_hwc(image) - else: - image = image else: if len(np.shape(image)) == 3: gray3dim = True image = image[0] - else: - image = image if normalized: image = image * 255 return rgb, chw, normalized, gray3dim, np.uint8(image) diff --git a/mindarmour/privacy/diff_privacy/optimizer/optimizer.py b/mindarmour/privacy/diff_privacy/optimizer/optimizer.py index a2b6d39..92c5d9a 100644 --- a/mindarmour/privacy/diff_privacy/optimizer/optimizer.py +++ b/mindarmour/privacy/diff_privacy/optimizer/optimizer.py @@ -36,7 +36,7 @@ _reciprocal = P.Reciprocal() @_grad_scale.register("Tensor", "Tensor") def tensor_grad_scale(scale, grad): """ grad scaling """ - return grad*_reciprocal(scale) + return grad * _reciprocal(scale) class _TupleAdd(nn.Cell): @@ -141,12 +141,11 @@ class DPOptimizerClassFactory: self._mech_param_updater = None if self._mech is not None and self._mech._decay_policy is not None: - self._mech_param_updater = _MechanismsParamsUpdater(decay_policy=self._mech._decay_policy, - decay_rate=self._mech._noise_decay_rate, - cur_noise_multiplier= - self._mech._noise_multiplier, - init_noise_multiplier= - self._mech._initial_noise_multiplier) + self._mech_param_updater = _MechanismsParamsUpdater( + decay_policy=self._mech._decay_policy, + decay_rate=self._mech._noise_decay_rate, + cur_noise_multiplier=self._mech._noise_multiplier, + init_noise_multiplier=self._mech._initial_noise_multiplier) def construct(self, gradients): """ diff --git a/mindarmour/privacy/diff_privacy/train/model.py b/mindarmour/privacy/diff_privacy/train/model.py index f304553..faade7f 100644 --- a/mindarmour/privacy/diff_privacy/train/model.py +++ b/mindarmour/privacy/diff_privacy/train/model.py @@ -62,7 +62,7 @@ _reciprocal = P.Reciprocal() @_grad_scale.register("Tensor", "Tensor") def tensor_grad_scale(scale, grad): """ grad scaling """ - return grad*F.cast(_reciprocal(scale), F.dtype(grad)) + return grad * F.cast(_reciprocal(scale), F.dtype(grad)) class DPModel(Model): @@ -417,10 +417,8 @@ class _TrainOneStepWithLossScaleCell(Cell): self._noise_mech_param_updater = _MechanismsParamsUpdater( decay_policy=self._noise_mech._decay_policy, decay_rate=self._noise_mech._noise_decay_rate, - cur_noise_multiplier= - self._noise_mech._noise_multiplier, - init_noise_multiplier= - self._noise_mech._initial_noise_multiplier) + cur_noise_multiplier=self._noise_mech._noise_multiplier, + init_noise_multiplier=self._noise_mech._initial_noise_multiplier) def construct(self, data, label, sens=None): """ @@ -444,8 +442,8 @@ class _TrainOneStepWithLossScaleCell(Cell): record_labels = self._split(label) # first index loss = self.network(record_datas[0], record_labels[0]) - scaling_sens_filled = C.ones_like(loss)*F.cast(scaling_sens, - F.dtype(loss)) + scaling_sens_filled = C.ones_like(loss) * F.cast(scaling_sens, + F.dtype(loss)) record_grad = self.grad(self.network, weights)(record_datas[0], record_labels[0], scaling_sens_filled) @@ -465,8 +463,8 @@ class _TrainOneStepWithLossScaleCell(Cell): total_loss = loss for i in range(1, self._micro_batches): loss = self.network(record_datas[i], record_labels[i]) - scaling_sens_filled = C.ones_like(loss)*F.cast(scaling_sens, - F.dtype(loss)) + scaling_sens_filled = C.ones_like(loss) * F.cast(scaling_sens, + F.dtype(loss)) record_grad = self.grad(self.network, weights)(record_datas[i], record_labels[i], scaling_sens_filled) diff --git a/mindarmour/privacy/evaluation/_check_config.py b/mindarmour/privacy/evaluation/_check_config.py index 48bd6f8..61b11e1 100644 --- a/mindarmour/privacy/evaluation/_check_config.py +++ b/mindarmour/privacy/evaluation/_check_config.py @@ -31,6 +31,7 @@ def _is_positive_int(item): return False return item > 0 + def _is_non_negative_int(item): """Verify that the value is a non-negative integer.""" if not isinstance(item, int): @@ -51,6 +52,7 @@ def _is_non_negative_float(item): return False return item >= 0 + def _is_range_0_1_float(item): if not isinstance(item, (int, float)): return False @@ -151,7 +153,6 @@ _VALID_CONFIG_CHECKLIST = { } - def _check_config(attack_config, config_checklist): """ Verify that config_list is valid. @@ -161,7 +162,7 @@ def _check_config(attack_config, config_checklist): check_param_type("config", config, dict) if set(config.keys()) != {"params", "method"}: msg = "Keys of each config in attack_config must be {}," \ - "but got {}.".format({'method', 'params'}, set(config.keys())) + "but got {}.".format({'method', 'params'}, set(config.keys())) LOGGER.error(TAG, msg) raise KeyError(msg) @@ -175,7 +176,7 @@ def _check_config(attack_config, config_checklist): if not params.keys() <= config_checklist[method].keys(): msg = "Params in method {} is not accepted, the parameters " \ - "that can be set are {}.".format(method, set(config_checklist[method].keys())) + "that can be set are {}.".format(method, set(config_checklist[method].keys())) LOGGER.error(TAG, msg) raise KeyError(msg) @@ -199,7 +200,7 @@ def _check_config(attack_config, config_checklist): break if not flag: - msg = "Setting of parmeter {} in method {} is invalid".format(param_key, method) + msg = "Setting of parameter {} in method {} is invalid".format(param_key, method) raise ValueError(msg) diff --git a/mindarmour/privacy/sup_privacy/sup_ctrl/conctrl.py b/mindarmour/privacy/sup_privacy/sup_ctrl/conctrl.py index d5626b6..ec4acd4 100644 --- a/mindarmour/privacy/sup_privacy/sup_ctrl/conctrl.py +++ b/mindarmour/privacy/sup_privacy/sup_ctrl/conctrl.py @@ -26,11 +26,14 @@ from mindspore.nn import Cell from mindarmour.utils.logger import LogUtil from mindarmour.utils._check_param import check_int_positive, check_value_positive, \ check_value_non_negative, check_param_type + LOGGER = LogUtil.get_instance() TAG = 'Suppression training.' + class SuppressPrivacyFactory: """ Factory class of SuppressCtrl mechanisms""" + def __init__(self): pass @@ -107,6 +110,7 @@ class SuppressPrivacyFactory: LOGGER.error(TAG, msg) raise ValueError(msg) + class SuppressCtrl(Cell): """ For details, please check `Tutorial `_ @@ -122,6 +126,7 @@ class SuppressCtrl(Cell): sparse_end (float): The sparsity to reach. sparse_start (Union[float, int]): The sparsity to start. """ + def __init__(self, networks, mask_layers, end_epoch, batch_num, start_epoch, mask_times, lr, sparse_end, sparse_start): super(SuppressCtrl, self).__init__() @@ -137,7 +142,7 @@ class SuppressCtrl(Cell): self.weight_lower_bound = 0.005 # all network weight will be larger than this value self.sparse_vibra = 0.02 # the sparsity may have certain range of variations - self.sparse_valid_max_weight = 0.02 # if max network weight is less than this value, suppress operation stop temporarily + self.sparse_valid_max_weight = 0.02 # if max network weight is less than this value, operation stop temporarily self.add_noise_thd = 0.50 # if network weight is more than this value, noise is forced self.noise_volume = 0.1 # noise volume 0.1 self.base_ground_thd = 0.0000001 # if network weight is less than this value, will be considered as 0 @@ -149,72 +154,15 @@ class SuppressCtrl(Cell): self.mask_start_step = 0 # suppress operation is actually started at this step self.mask_prev_step = 0 # previous suppress operation is done at this step self.cur_sparse = 0.0 # current sparsity to which one suppress will get - self.mask_all_steps = (end_epoch - start_epoch + 1)*batch_num # the amount of step contained in all suppress operation - self.mask_step_interval = self.mask_all_steps/mask_times # the amount of step contaied in one suppress operation + self.mask_all_steps = (end_epoch - start_epoch + 1) * batch_num # the amount of step contained in all operation + self.mask_step_interval = self.mask_all_steps / mask_times # the amount of step contained in one operation self.mask_initialized = False # flag means the initialization is done self.grad_idx_map = [] - if self.lr > 0.5: - msg = "learning rate should not be greater than 0.5, but got {}".format(self.lr) - LOGGER.error(TAG, msg) - raise ValueError(msg) - - if self.mask_start_epoch > self.mask_end_epoch: - msg = "start_epoch should not be greater than end_epoch, but got start_epoch and end_epoch are: " \ - "{}, {}".format(self.mask_start_epoch, self.mask_end_epoch) - LOGGER.error(TAG, msg) - raise ValueError(msg) - - if self.mask_end_epoch > 100: - msg = "The end_epoch should be smaller than 100, but got {}".format(self.mask_end_epoch) - LOGGER.error(TAG, msg) - raise ValueError(msg) - - if self.mask_step_interval <= 0: - msg = "step_interval should be greater than 0, but got {}".format(self.mask_step_interval) - LOGGER.error(TAG, msg) - raise ValueError(msg) - - if self.mask_step_interval <= 10 or self.mask_step_interval >= 20: - msg = "mask_interval should be greater than 10, smaller than 20, but got {}".format(self.mask_step_interval) - msg += "\n Precision of trained model may be poor !!! " - msg += "\n please modify epoch_start, epoch_end and batch_num !" - msg += "\n mask_interval = (epoch_end-epoch_start+1)*batch_num/mask_times, batch_num = samples/batch_size" - LOGGER.info(TAG, msg) - - if self.sparse_end >= 1.00 or self.sparse_end <= 0: - msg = "sparse_end should be in range (0, 1), but got {}".format(self.sparse_end) - LOGGER.error(TAG, msg) - raise ValueError(msg) - - if self.sparse_start >= self.sparse_end: - msg = "sparse_start should be smaller than sparse_end, but got sparse_start and sparse_end are: " \ - "{}, {}".format(self.sparse_start, self.sparse_end) - LOGGER.error(TAG, msg) - raise ValueError(msg) + self._check_params() if mask_layers is not None: - mask_layer_id = 0 - for one_mask_layer in mask_layers: - if not isinstance(one_mask_layer, MaskLayerDes): - msg = "mask_layers should be a list of MaskLayerDes, but got a {}".format(type(one_mask_layer)) - LOGGER.error(TAG, msg) - raise ValueError(msg) - layer_name = one_mask_layer.layer_name - mask_layer_id2 = 0 - for one_mask_layer_2 in mask_layers: - if mask_layer_id != mask_layer_id2 and layer_name == one_mask_layer_2.layer_name: - msg = "Mask layer name should be unique, but got duplicate name: {} in mask_layer {} and {}".\ - format(layer_name, mask_layer_id, mask_layer_id2) - LOGGER.error(TAG, msg) - raise ValueError(msg) - if mask_layer_id != mask_layer_id2 and one_mask_layer.grad_idx == one_mask_layer_2.grad_idx: - msg = "Grad_idx should be unique, but got duplicate idx: {} in mask_layer {} and {}".\ - format(layer_name, one_mask_layer_2.layer_name, one_mask_layer.grad_idx) - LOGGER.error(TAG, msg) - raise ValueError(msg) - mask_layer_id2 = mask_layer_id2 + 1 - mask_layer_id = mask_layer_id + 1 + self._check_mask_layers() if networks is not None: for layer in networks.get_parameters(expand=True): @@ -277,6 +225,71 @@ class SuppressCtrl(Cell): msg += "\nsup_privacy only support SGD optimizer" LOGGER.warn(TAG, msg) + def _check_params(self): + """check parameters""" + if self.lr > 0.5: + msg = "learning rate should not be greater than 0.5, but got {}".format(self.lr) + LOGGER.error(TAG, msg) + raise ValueError(msg) + + if self.mask_start_epoch > self.mask_end_epoch: + msg = "start_epoch should not be greater than end_epoch, but got start_epoch and end_epoch are: " \ + "{}, {}".format(self.mask_start_epoch, self.mask_end_epoch) + LOGGER.error(TAG, msg) + raise ValueError(msg) + + if self.mask_end_epoch > 100: + msg = "The end_epoch should be smaller than 100, but got {}".format(self.mask_end_epoch) + LOGGER.error(TAG, msg) + raise ValueError(msg) + + if self.mask_step_interval <= 0: + msg = "step_interval should be greater than 0, but got {}".format(self.mask_step_interval) + LOGGER.error(TAG, msg) + raise ValueError(msg) + + if self.mask_step_interval <= 10 or self.mask_step_interval >= 20: + msg = "mask_interval should be greater than 10, smaller than 20, but got {}".format(self.mask_step_interval) + msg += "\n Precision of trained model may be poor !!! " + msg += "\n please modify epoch_start, epoch_end and batch_num !" + msg += "\n mask_interval = (epoch_end-epoch_start+1)*batch_num/mask_times, batch_num = samples/batch_size" + LOGGER.info(TAG, msg) + + if self.sparse_end >= 1.00 or self.sparse_end <= 0: + msg = "sparse_end should be in range (0, 1), but got {}".format(self.sparse_end) + LOGGER.error(TAG, msg) + raise ValueError(msg) + + if self.sparse_start >= self.sparse_end: + msg = "sparse_start should be smaller than sparse_end, but got sparse_start and sparse_end are: " \ + "{}, {}".format(self.sparse_start, self.sparse_end) + LOGGER.error(TAG, msg) + raise ValueError(msg) + + def _check_mask_layers(self): + """check mask layers""" + mask_layer_id = 0 + for one_mask_layer in self.mask_layers: + if not isinstance(one_mask_layer, MaskLayerDes): + msg = "mask_layers should be a list of MaskLayerDes, but got a {}".format(type(one_mask_layer)) + LOGGER.error(TAG, msg) + raise ValueError(msg) + layer_name = one_mask_layer.layer_name + mask_layer_id2 = 0 + for one_mask_layer_2 in self.mask_layers: + if mask_layer_id != mask_layer_id2 and layer_name == one_mask_layer_2.layer_name: + msg = "Mask layer name should be unique, but got duplicate name: {} in mask_layer {} and {}". \ + format(layer_name, mask_layer_id, mask_layer_id2) + LOGGER.error(TAG, msg) + raise ValueError(msg) + if mask_layer_id != mask_layer_id2 and one_mask_layer.grad_idx == one_mask_layer_2.grad_idx: + msg = "Grad_idx should be unique, but got duplicate idx: {} in mask_layer {} and {}". \ + format(layer_name, one_mask_layer_2.layer_name, one_mask_layer.grad_idx) + LOGGER.error(TAG, msg) + raise ValueError(msg) + mask_layer_id2 = mask_layer_id2 + 1 + mask_layer_id = mask_layer_id + 1 + def update_status(self, cur_epoch, cur_step, cur_step_in_epoch): """ Update the suppress operation status. @@ -296,7 +309,7 @@ class SuppressCtrl(Cell): self.mask_prev_step = cur_step self.to_do_mask = True # execute the last suppression operation - elif cur_epoch == self.mask_end_epoch and cur_step_in_epoch == self.batch_num-2: + elif cur_epoch == self.mask_end_epoch and cur_step_in_epoch == self.batch_num - 2: self.mask_prev_step = cur_step self.to_do_mask = True else: @@ -340,8 +353,8 @@ class SuppressCtrl(Cell): grad_mask_cell = self.grads_mask_list[grad_idx] last_sparse_pos = grad_mask_cell.sparse_pos_list[-1] if actual_stop_pos <= 0 or \ - (actual_stop_pos < last_sparse_pos + grad_mask_cell.part_num and \ - grad_mask_cell.is_approximity and m > 0): + (actual_stop_pos < last_sparse_pos + grad_mask_cell.part_num and \ + grad_mask_cell.is_approximity and m > 0): sparse_weight_thd = 0 msg = "{} len={}, sparse={}, current sparse thd={}, [idle] \n" \ .format(layer.name, len_array, actual_stop_pos / len_array, sparse_weight_thd) @@ -377,7 +390,7 @@ class SuppressCtrl(Cell): del partition msg = "{} len={}, sparse={}, current sparse thd={}, max={}, min={}, avg={}, avg_abs={} \n".format( - layer.name, len_array, actual_stop_pos/len_array, sparse_weight_thd, + layer.name, len_array, actual_stop_pos / len_array, sparse_weight_thd, weight_abs_max, weight_abs_min, weight_avg, weight_abs_avg) LOGGER.info(TAG, msg) del weight_array_flat_abs @@ -413,7 +426,7 @@ class SuppressCtrl(Cell): p = 0 q = 0 # add noise on weights if not masking or clipping. - weight_noise_bound = min(self.add_noise_thd, max(self.noise_volume*10, weight_abs_max*0.75)) + weight_noise_bound = min(self.add_noise_thd, max(self.noise_volume * 10, weight_abs_max * 0.75)) size = self.grads_mask_list[layer_index].para_num for i in range(0, size): if mul_mask_array_flat[i] <= 0.0: @@ -428,14 +441,14 @@ class SuppressCtrl(Cell): else: # not mask if weight_array_flat[i] > 0.0: - add_mask_array_flat[i] = (weight_array_flat[i] \ + add_mask_array_flat[i] = (weight_array_flat[i] - min(self.weight_lower_bound, sparse_weight_thd)) / self.lr else: add_mask_array_flat[i] = (weight_array_flat[i] + min(self.weight_lower_bound, sparse_weight_thd)) / self.lr p = p + 1 elif is_lower_clip and abs(weight_array_flat[i]) <= \ - self.weight_lower_bound and sparse_weight_thd > self.weight_lower_bound*0.5: + self.weight_lower_bound and sparse_weight_thd > self.weight_lower_bound * 0.5: # not mask mul_mask_array_flat[i] = 1.0 if weight_array_flat[i] > 0.0: @@ -463,8 +476,8 @@ class SuppressCtrl(Cell): grad_mask_cell.update() de_weight_cell.update() msg = "Dimension of mask tensor is {}D, which located in the {}-th layer of the network. \n The number of " \ - "suppressed elements, max-clip elements, min-clip elements and noised elements are {}, {}, {}, {}"\ - .format(len(grad_mask_cell.mul_mask_array_shape), layer_index, m, n, p, q) + "suppressed elements, max-clip elements, min-clip elements and noised elements are {}, {}, {}, {}" \ + .format(len(grad_mask_cell.mul_mask_array_shape), layer_index, m, n, p, q) LOGGER.info(TAG, msg) grad_mask_cell.sparse_pos_list.append(m) @@ -500,8 +513,8 @@ class SuppressCtrl(Cell): for i in range(0, part_num): if split_k_num <= 0: break - array_row_mul_mask = mul_mask_array_flat[i * part_size : (i + 1) * part_size] - array_row_flat_abs = weight_array_flat_abs[i * part_size : (i + 1) * part_size] + array_row_mul_mask = mul_mask_array_flat[i * part_size: (i + 1) * part_size] + array_row_flat_abs = weight_array_flat_abs[i * part_size: (i + 1) * part_size] if not init_batch_suppress: array_row_flat_abs_masked = np.where(array_row_mul_mask <= 0.0, -1.0, array_row_flat_abs) set_abs = set(array_row_flat_abs_masked) @@ -553,7 +566,7 @@ class SuppressCtrl(Cell): split_k_num, (actual_stop_pos - last_sparse_pos), actual_stop_pos, real_suppress_num) LOGGER.info(TAG, msg) if init_batch_suppress: - init_sparse_actual = real_suppress_num/para_num + init_sparse_actual = real_suppress_num / para_num print("init batch suppresss, actual sparse = {}".format(init_sparse_actual)) gc.collect() @@ -660,6 +673,7 @@ class SuppressCtrl(Cell): return sparse, sparse_value_1, sparse_value_2 def calc_actual_sparse_for_fc1(self, networks): + """calculate actual sparse for full connection 1 layer""" return self.calc_actual_sparse_for_layer(networks, "fc1.weight") def calc_actual_sparse_for_layer(self, networks, layer_name): @@ -716,6 +730,7 @@ class SuppressCtrl(Cell): msg += "\nsup_privacy only support SGD optimizer" LOGGER.info(TAG, msg) + def get_one_mask_layer(mask_layers, layer_name): """ Returns the layer definitions that need to be suppressed. @@ -732,6 +747,7 @@ def get_one_mask_layer(mask_layers, layer_name): return each_mask_layer return None + class MaskLayerDes: """ Describe the layer that need to be suppressed. @@ -763,6 +779,7 @@ class MaskLayerDes: >>> masklayers = [] >>> masklayers.append(MaskLayerDes("conv1.weight", 0, False, True, 10)) """ + def __init__(self, layer_name, grad_idx, is_add_noise, is_lower_clip, min_num, upper_bound=1.20): self.layer_name = check_param_type('layer_name', layer_name, str) check_param_type('grad_idx', grad_idx, int) @@ -773,6 +790,7 @@ class MaskLayerDes: self.upper_bound = check_value_positive('upper_bound', upper_bound) self.inited = False + class GradMaskInCell(Cell): """ Define the mask matrix for gradients masking. @@ -787,6 +805,7 @@ class GradMaskInCell(Cell): If min_num is smaller than (parameter num*SupperssCtrl.sparse_end), min_num has no effect. upper_bound ([float, int]): max abs value of weight in this layer, default: 1.20. """ + def __init__(self, array, is_add_noise, is_lower_clip, min_num, upper_bound=1.20): super(GradMaskInCell, self).__init__() self.mul_mask_array_shape = array.shape @@ -806,7 +825,7 @@ class GradMaskInCell(Cell): self.part_size = self.para_num self.part_num_max = 16 self.para_many_num = 10000 - self.para_huge_num = 10*10000*10000 + self.para_huge_num = 10 * 10000 * 10000 if self.para_num > self.para_many_num: self.is_approximity = True @@ -836,6 +855,7 @@ class GradMaskInCell(Cell): """ self.mul_mask_tensor = Tensor(self.mul_mask_array_flat.reshape(self.mul_mask_array_shape), mstype.float32) + class DeWeightInCell(Cell): """ Define the mask matrix for de-weight masking. @@ -843,6 +863,7 @@ class DeWeightInCell(Cell): Args: array (numpy.ndarray): The mask array. """ + def __init__(self, array): super(DeWeightInCell, self).__init__() self.add_mask_array_shape = array.shape diff --git a/mindarmour/privacy/sup_privacy/train/model.py b/mindarmour/privacy/sup_privacy/train/model.py index 92c953e..3b06a8d 100644 --- a/mindarmour/privacy/sup_privacy/train/model.py +++ b/mindarmour/privacy/sup_privacy/train/model.py @@ -196,7 +196,6 @@ class _TupleMul(nn.Cell): def construct(self, input1, input2): """Add two tuple of data.""" out = self.hyper_map(self.mul, input1, input2) - #print(out) return out # come from nn.cell_wrapper.TrainOneStepCell diff --git a/mindarmour/reliability/concept_drift/concept_drift_check_images.py b/mindarmour/reliability/concept_drift/concept_drift_check_images.py index 9aebc99..2967561 100644 --- a/mindarmour/reliability/concept_drift/concept_drift_check_images.py +++ b/mindarmour/reliability/concept_drift/concept_drift_check_images.py @@ -11,8 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# ============================================================================ - """ Out-of-Distribution detection module for images. """ @@ -33,6 +31,7 @@ class OodDetector: model (Model):The training model. ds_train (numpy.ndarray): The training dataset. """ + def __init__(self, model, ds_train): self.model = model self.ds_train = check_param_type('ds_train', ds_train, np.ndarray) @@ -66,7 +65,6 @@ class OodDetector: - float, the optimal threshold. """ - def ood_predict(self, threshold, ds_test): """ The out-of-distribution detection. diff --git a/mindarmour/reliability/concept_drift/concept_drift_check_time_series.py b/mindarmour/reliability/concept_drift/concept_drift_check_time_series.py index 0b02e18..cd2ccab 100644 --- a/mindarmour/reliability/concept_drift/concept_drift_check_time_series.py +++ b/mindarmour/reliability/concept_drift/concept_drift_check_time_series.py @@ -9,8 +9,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# ============================================================================ - """ Concpt drift module """ diff --git a/mindarmour/reliability/model_fault_injection/__init__.py b/mindarmour/reliability/model_fault_injection/__init__.py index f2c861a..655bb9a 100644 --- a/mindarmour/reliability/model_fault_injection/__init__.py +++ b/mindarmour/reliability/model_fault_injection/__init__.py @@ -8,7 +8,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# ============================================================================ """ This module provides model fault injection to evaluate the reliability of given model. """ diff --git a/mindarmour/reliability/model_fault_injection/fault_injection.py b/mindarmour/reliability/model_fault_injection/fault_injection.py index d9a3b21..122726f 100644 --- a/mindarmour/reliability/model_fault_injection/fault_injection.py +++ b/mindarmour/reliability/model_fault_injection/fault_injection.py @@ -8,8 +8,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# ============================================================================ - """ Fault injection module """ diff --git a/mindarmour/reliability/model_fault_injection/fault_type.py b/mindarmour/reliability/model_fault_injection/fault_type.py index 6e40c22..917a11b 100644 --- a/mindarmour/reliability/model_fault_injection/fault_type.py +++ b/mindarmour/reliability/model_fault_injection/fault_type.py @@ -8,8 +8,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# ============================================================================ - """ Fault type module """ diff --git a/setup.py b/setup.py index 4e54435..b253068 100644 --- a/setup.py +++ b/setup.py @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +""" +setup script +""" import os import stat import shlex @@ -27,7 +30,7 @@ pkg_dir = os.path.join(cur_dir, 'build') def clean(): - # pylint: disable=unused-argument + """clean""" def readonly_handler(func, path, execinfo): os.chmod(path, stat.S_IWRITE) func(path) @@ -38,6 +41,7 @@ def clean(): def write_version(file): + """write version""" file.write("__version__ = '{}'\n".format(version))