def _attack_pgd_ls(self): params = { "classifier": self.classifier, "double_init_ds": self.tr, "distance": 'l1', "lb": self.lb, "ub": self.ub, "y_target": self.y_target, "attack_classes": self.attack_classes, "solver_params": { 'eta': 0.5, 'eps': 1e-2 } } attack = CAttackEvasionPGDLS(**params) attack.verbose = 1 # sec eval params param_name = 'dmax' dmax = 2 dmax_step = 0.5 param_values = CArray.arange(start=0, step=dmax_step, stop=dmax + dmax_step) return attack, param_name, param_values
def _set_evasion(self, ds, params): """Prepare the evasion attack. - discretize data if necessary - train the classifier (if not trained) - train the surrogate classifier (if not trained) - create the evasion object - choose an attack starting point Parameters ---------- ds : CDataset params : dict Parameters for the attack class. Returns ------- evas : CAttackEvasion x0 : CArray Initial attack point. y0 : CArray Label of the initial attack point. """ if not params["classifier"].is_fitted(): self.logger.info("Training classifier...") params["classifier"].fit(ds.X, ds.Y) evas = CAttackEvasionPGDLS(**params) evas.verbose = 2 # pick a malicious sample x0, y0 = self._choose_x0_2c(ds) return evas, x0, y0
def _set_evasion(self, params, x0_img_class): """Prepare the evasion attack. - train the classifier (if not trained) - train the surrogate classifier (if not trained) - create the evasion object - choose an attack starting point Parameters ---------- params : dict Parameters for the attack class. x0_img_class : int Class from which to choose the initial attack point. Returns ------- evas : CAttackEvasion x0 : CArray Initial attack point. y0 : CArray Label of the initial attack point. """ if not params["classifier"].is_fitted(): self.logger.info("Training classifier...") params["classifier"].fit(self._tr.X, self._tr.Y) evas = CAttackEvasionPGDLS(**params) evas.verbose = 2 # pick a malicious sample x0, y0 = self._choose_x0_2c(x0_img_class) return evas, x0, y0
def test_attack_pgd_ls(self): """Test SecEval using CAttackEvasionPGDLS.""" params = { "classifier": self.clf, "double_init_ds": self.tr, "distance": 'l2', "lb": -2, "ub": 2, "y_target": None, "solver_params": {'eta': 0.1, 'eps': 1e-2} } attack = CAttackEvasionPGDLS(**params) attack.verbose = 1 param_name = 'dmax' self._set_and_run(attack, param_name)
def test_attack_pgd_ls_discrete(self): """Test SecEval using CAttackEvasionPGDLS on a problematic discrete case with L1 constraint. We alter the classifier so that many weights have the same value. The optimizer should be able to evade the classifier anyway, by changing one feature each iteration. Otherwise, by changing all the feature with the same value at once, the evasion will always fail because the L1 constraint will be violated. """ self.ds = self._discretize_data(self.ds, eta=1) self.ds.X[self.ds.X > 1] = 1 self.ds.X[self.ds.X < -1] = -1 self.tr = self.ds[:self.n_tr, :] self.ts = self.ds[self.n_tr:, :] self.clf.fit(self.tr.X, self.tr.Y) # Set few features to the same max value w_new = self.clf.w.deepcopy() w_new[CArray.randint( self.clf.w.size, shape=5, random_state=0)] = self.clf.w.max() self.clf._w = w_new params = { "classifier": self.clf, "double_init": False, "distance": 'l1', "lb": -1, "ub": 1, "y_target": None, "solver_params": {'eta': 1, 'eps': 1e-2} } attack = CAttackEvasionPGDLS(**params) attack.verbose = 1 param_name = 'dmax' self._set_and_run(attack, param_name, dmax_step=1)
# Should be chosen depending on the optimization problem solver_params = { 'eta': 0.3, 'eta_min': 0.1, 'eta_max': None, 'max_iter': 100, 'eps': 1e-4 } from secml.adv.attacks.evasion import CAttackEvasionPGDLS pgd_ls_attack = CAttackEvasionPGDLS( classifier=clf, surrogate_classifier=clf, surrogate_data=tr, distance=noise_type, dmax=dmax, lb=lb, ub=ub, solver_params=solver_params, y_target=y_target) print(x0) # Run the evasion attack on x0 y_pred_pgdls, _, adv_ds_pgdls, _ = pgd_ls_attack.run(x0, y0) print("Original x0 label: ", y0.item()) print("Adversarial example label (PGD-LS): ", y_pred_pgdls.item()) print("Number of classifier gradient evaluations: {:}" "".format(pgd_ls_attack.grad_eval))
def attack_keras_model(X, Y, S, nb_attack=25, dmax=0.1): """ Generates an adversarial attack on a general model. :param X: Original inputs on which the model is trained :param Y: Original outputs on which the model is trained :param S: Original protected attributes on which the model is trained :return: Adversarial dataset (i.e. new data points + original input) """ from secml.data import CDataset from secml.array import CArray # secML wants all dimensions to be homogeneous (we had previously float and int in X) data_set_encoded_secML = CArray(X, dtype=float, copy=True) data_set_encoded_secML = CDataset(data_set_encoded_secML, Y) n_tr = round(0.66 * X.shape[0]) n_ts = X.shape[0] - n_tr logger.debug(X.shape) logger.debug(n_tr) logger.debug(n_ts) from secml.data.splitter import CTrainTestSplit splitter = CTrainTestSplit(train_size=n_tr, test_size=n_ts) # Use training set for the classifier and then pick points from an internal test set. tr_set_secML, ts_set_secML = splitter.split(data_set_encoded_secML) # tr_set_secML = CDataset(X_train,Y_train) # ts_set_secML = CDataset(X_test,Y_test) # Create a surrogate classifier # Creation of the multiclass classifier from secml.ml.classifiers import CClassifierSVM from secml.ml.classifiers.multiclass import CClassifierMulticlassOVA from secml.ml.kernel import CKernelRBF clf = CClassifierMulticlassOVA(CClassifierSVM, kernel=CKernelRBF()) # Parameters for the Cross-Validation procedure xval_params = {'C': [1e-4, 1e-3, 1e-2, 0.1, 1], 'kernel.gamma': [0.01, 0.1, 1, 10, 100, 1e3]} # Let's create a 3-Fold data splitter random_state = 999 from secml.data.splitter import CDataSplitterKFold xval_splitter = CDataSplitterKFold(num_folds=3, random_state=random_state) # Select and set the best training parameters for the classifier logger.debug("Estimating the best training parameters...") best_params = clf.estimate_parameters( dataset=tr_set_secML, parameters=xval_params, splitter=xval_splitter, metric='accuracy', perf_evaluator='xval' ) logger.debug("The best training parameters are: ", best_params) logger.debug(clf.get_params()) logger.debug(clf.num_classifiers) # Metric to use for training and performance evaluation from secml.ml.peval.metrics import CMetricAccuracy metric = CMetricAccuracy() # Train the classifier clf.fit(tr_set_secML) logger.debug(clf.num_classifiers) # Compute predictions on a test set y_pred = clf.predict(ts_set_secML.X) # Evaluate the accuracy of the classifier acc = metric.performance_score(y_true=ts_set_secML.Y, y_pred=y_pred) logger.debug("Accuracy on test set: {:.2%}".format(acc)) # Prepare attack configuration noise_type = 'l2' # Type of perturbation 'l1' or 'l2' lb, ub = 0, 1 # Bounds of the attack space. Can be set to `None` for unbounded y_target = None # None if `error-generic` or a class label for `error-specific` # Should be chosen depending on the optimization problem solver_params = { 'eta': 0.1, # grid search resolution 'eta_min': 0.1, 'eta_max': None, # None should be ok 'max_iter': 1000, 'eps': 1e-2 # Tolerance on the stopping crit. } # Run attack from secml.adv.attacks.evasion import CAttackEvasionPGDLS pgd_ls_attack = CAttackEvasionPGDLS( classifier=clf, surrogate_classifier=clf, surrogate_data=tr_set_secML, distance=noise_type, dmax=dmax, lb=lb, ub=ub, solver_params=solver_params, y_target=y_target) nb_feat = X.shape[1] result_pts = np.empty([nb_attack, nb_feat]) result_class = np.empty([nb_attack, 1]) # take a point at random being the starting point of the attack and run the attack import random for nb_iter in range(0, nb_attack): rn = random.randint(0, ts_set_secML.num_samples - 1) x0, y0 = ts_set_secML[rn, :].X, ts_set_secML[rn, :].Y, try: y_pred_pgdls, _, adv_ds_pgdls, _ = pgd_ls_attack.run(x0, y0) adv_pt = adv_ds_pgdls.X.get_data() # np.asarray([np.asarray(row, dtype=float) for row in y_tr], dtype=float) result_pts[nb_iter] = adv_pt result_class[nb_iter] = y_pred_pgdls.get_data()[0] except ValueError: logger.warning("value error on {}".format(nb_iter)) return result_pts, result_class, ts_set_secML[:nb_attack, :].Y
def _test_evasion_multiclass(self, expected_x): # EVASION self.multiclass.verbose = 2 if self.normalizer is not None: lb = self.normalizer.feature_range[0] ub = self.normalizer.feature_range[1] else: lb = None ub = None dmax = 2 self.solver_params = {'eta': 1e-1, 'eta_min': 1.0} eva = CAttackEvasionPGDLS(classifier=self.multiclass, surrogate_classifier=self.multiclass, surrogate_data=self.ds, distance='l2', dmax=dmax, lb=lb, ub=ub, solver_params=self.solver_params, y_target=self.y_target) eva.verbose = 0 # 2 # Points from class 2 region # p_idx = 0 # Points from class 1 region # p_idx = 68 # Points from class 3 region p_idx = 1 # Wrong classified point # p_idx = 53 # Evasion goes up usually # Points from class 0 region # p_idx = 49 # Wrong classified point # p_idx = 27 # Correctly classified point x0 = self.ds.X[p_idx, :] y0 = self.ds.Y[p_idx].item() x_seq = CArray.empty((0, x0.shape[1])) scores = CArray([]) f_seq = CArray([]) x = x0 for d_idx, d in enumerate(range(0, dmax + 1)): self.logger.info("Evasion at dmax: " + str(d)) eva.dmax = d x, f_opt = eva._run(x0=x0, y0=y0, x_init=x) y_pred, score = self.multiclass.predict( x, return_decision_function=True) f_seq = f_seq.append(f_opt) # not considering all iterations, just values at dmax # for all iterations, you should bring eva.x_seq and eva.f_seq x_seq = x_seq.append(x, axis=0) s = score[:, y0 if self.y_target is None else self.y_target] scores = scores.append(s) self.logger.info("Predicted label after evasion: " + str(y_pred)) self.logger.info("Score after evasion: {:}".format(s)) self.logger.info("Objective function after evasion: {:}".format(f_opt)) # Compare optimal point with expected self.assert_array_almost_equal(eva.x_opt.todense().ravel(), expected_x, decimal=4) self._make_plots(x_seq, dmax, eva, x0, scores, f_seq)
def _test_evasion_multiclass(self, expected_x): # EVASION self.multiclass.verbose = 2 if self.normalizer is not None: lb = self.normalizer.feature_range[0] ub = self.normalizer.feature_range[1] else: lb = None ub = None dmax = 3 self.solver_params = {'eta': 0.5, 'max_iter': 3} eva = CAttackEvasionPGDLS(classifier=self.multiclass, double_init_ds=self.ds, distance='l2', dmax=dmax, lb=lb, ub=ub, solver_params=self.solver_params, y_target=self.y_target) eva.verbose = 0 # 2 # Points from class 2 region # p_idx = 0 # Points from class 1 region # p_idx = 68 # Points from class 3 region p_idx = 1 # Wrong classified point # p_idx = 53 # Evasion goes up usually # Points from class 0 region # p_idx = 49 # Wrong classified point # p_idx = 27 # Correctly classified point x0 = self.ds.X[p_idx, :] y0 = self.ds.Y[p_idx].item() self.logger.info("Evasion at dmax: " + str(dmax)) eva.dmax = dmax x_opt, f_opt = eva._run(x0=x0, y0=y0, x_init=x0) y_pred, score = self.multiclass.predict(x_opt, return_decision_function=True) s = score[:, y0 if self.y_target is None else self.y_target] self.logger.info( "Number of objective function evaluations: {:}".format(eva.f_eval)) self.logger.info("Number of gradient function evaluations: {:}".format( eva.grad_eval)) self.logger.info("Predicted label after evasion: {:}".format(y_pred)) self.logger.info("Score after evasion: {:}".format(s)) self.logger.info("Objective function after evasion: {:}".format(f_opt)) # Compare optimal point with expected self.assert_array_almost_equal(eva.x_opt.todense().ravel(), expected_x, decimal=4) if self.y_target: s_ytarget_x0 = self.multiclass.decision_function(x0, self.y_target) s_ytarget_xopt = self.multiclass.decision_function( x_opt, self.y_target) self.logger.info( "Discriminat function w.r.t the target class first: {:} " "and after evasion: {:}".format(s_ytarget_x0, s_ytarget_xopt)) self.assertLess(s_ytarget_x0, s_ytarget_xopt) else: # indiscriminate attack s_ytrue_x0 = self.multiclass.decision_function(x0, y0) s_ytrue_xopt = self.multiclass.decision_function(x_opt, y0) self.logger.info( "Discriminat function w.r.t the true class first: {:} " "and after evasion: {:}".format(s_ytrue_x0, s_ytrue_xopt)) self.assertGreater(s_ytrue_x0, s_ytrue_xopt) self._make_plot(p_idx, eva, dmax)
def setUp(self): classifier = CClassifierSVM( kernel='linear', C=1.0, grad_sampling=1.0) # data parameters discrete = False lb = -2 ub = +2 n_tr = 20 n_ts = 10 n_features = 2 n_reps = 1 self.sec_eval = [] self.attack_ds = [] for rep_i in range(n_reps): self.logger.info( "Loading `random_blobs` with seed: {:}".format(rep_i)) loader = CDLRandomBlobs( n_samples=n_tr + n_ts, n_features=n_features, centers=[(-0.5, -0.5), (+0.5, +0.5)], center_box=(-0.5, 0.5), cluster_std=0.5, random_state=rep_i * 100 + 10) ds = loader.load() tr = ds[:n_tr, :] ts = ds[n_tr:, :] classifier.fit(tr) self.attack_ds.append(ts) # only manipulate positive samples, targeting negative ones self.y_target = None attack_classes = CArray([1]) params = { "classifier": classifier, "surrogate_classifier": classifier, "surrogate_data": tr, "distance": 'l1', "lb": lb, "ub": ub, "discrete": discrete, "y_target": self.y_target, "attack_classes": attack_classes, "solver_params": {'eta': 0.5, 'eps': 1e-2} } attack = CAttackEvasionPGDLS(**params) attack.verbose = 1 # sec eval params param_name = 'dmax' dmax = 2 dmax_step = 0.5 param_values = CArray.arange( start=0, step=dmax_step, stop=dmax + dmax_step) # set sec eval object self.sec_eval.append( CSecEval( attack=attack, param_name=param_name, param_values=param_values, ) )