def transform(self, instance: Instance): """ for the binary case, the f_attack value represents the percentage of features we change. If f_attack =1, then the result should be exactly the same as innocuous target. for the real_value case, we generate a value between c_f(x_min - xij) and c_f(x_max - xij) This value will be added to the xij for the new instance :param instance: :return: instance """ if self.binary: attack_times = int(self.f_attack * self.num_features) count = 0 for i in range(0, self.num_features): instance.get_feature_vector().flip_bit(i) count += 1 if count == attack_times: return instance else: for i in range(0, self.num_features): xij = instance.get_feature_vector().get_feature(i) if not self.manual: lower_bound = self.f_attack * (self.x_min[i] - xij) upper_bound = self.f_attack * (self.x_max[i] - xij) else: lower_bound = self.f_attack * (self.xj_min - xij) upper_bound = self.f_attack * (self.xj_max - xij) delta_ij = random.uniform(lower_bound, upper_bound) instance.flip(i, xij + delta_ij) return instance
def transform(self, instance: Instance): ''' for the real_value case, we generate a value between 0 and the bound. The bound is calculated by 1- c_delta * (abs(xt - x)/abs(x) + abs(xt)) * (xt -x) This value will be added to the xij for the new instance :param instance: :return: instance ''' if self.binary: attack_times = int(self.f_attack * self.num_features) count = 0 for i in range(0, self.num_features): delta_ij = self.innocuous_target.get_feature_vector().get_feature(i) \ - instance.get_feature_vector().get_feature(i) if delta_ij != 0: if self.binary: # when features are binary instance.get_feature_vector().flip_bit(i) count += 1 if count == attack_times: return instance else: for i in range(0, self.num_features): xij = instance.get_feature_vector().get_feature(i) target = self.innocuous_target.get_feature_vector( ).get_feature(i) if abs(xij) + abs(target) == 0: bound = 0 else: bound = self.discount_factor * (1 - self.f_attack * (abs(target - xij) / (abs(xij) + abs(target)))) \ * abs((target - xij)) delta_ij = random.uniform(0, bound) instance.flip(i, xij + delta_ij) return instance
def optimize(self, instance: Instance): """Flip features that lower the prob. of being classified adversarial. Args: instance: (scipy.sparse.csr_matrix) feature vector """ change = 0 for i in range(0, self.num_features): orig_prob = self.learn_model.predict_proba([instance])[0] new_instance = deepcopy(instance) new_instance.get_feature_vector().flip_bit(i) new_prob = self.learn_model.predict_proba([new_instance])[0] if new_prob < (orig_prob - exp(self.lambda_val)): instance.get_feature_vector().flip_bit(i) change += 1 if change > self.max_change: break return instance
def transform(self, instance: Instance): ''' for the binary case, the f_attack value represents the percentage of features we change. If f_attack =1, then the result should be exactly the same as innocuous target. for the real_value case, we generate a value between c_f(x_min - xij) and c_f(x_max - xij) This value will be added to the xij for the new instance :param instance: :return: instance ''' if self.binary: attack_times = (int)(self.f_attack * self.num_features) count = 0 for i in range(0, self.num_features): delta_ij = (self.innocuous_target.get_feature_vector().get_feature(i) - instance.get_feature_vector().get_feature(i)) if delta_ij != 0: if self.binary: # when features are binary instance.get_feature_vector().flip_bit(i) count += 1 if count == attack_times: return instance else: for i in range(0, self.num_features): xij = instance.get_feature_vector().get_feature(i) if self.xj_min == 0 and self.xj_max == 0: lower_bound = self.f_attack * ( self.x_min.get_feature(i) - xij) upper_bound = self.f_attack * ( self.x_max.get_feature(i) - xij) else: lower_bound = self.f_attack * (self.xj_min - xij) upper_bound = self.f_attack * (self.xj_max - xij) # is that ok to just assign a random number between the range??? delta_ij = random.uniform(lower_bound, upper_bound) instance.flip(i, xij + delta_ij) return instance
def get_feature_vector_array(inst: Instance): """ Turns the feature vector into an np.ndarray :param inst: the Instance :return: the feature vector (np.ndarray) """ fv = inst.get_feature_vector() tmp = [] for j in range(inst.get_feature_count()): if fv.get_feature(j) == 1: tmp.append(1) else: tmp.append(0) return np.array(tmp)
def _calc_inst_loss(self, inst: Instance): """ Calculates the logistic loss for one instance :param inst: the instance :return: the logistic loss """ fv = inst.get_feature_vector().get_csr_matrix() fv = np.array(fv.todense().tolist()).flatten() # reshape is for the decision function when inputting only one sample loss = self.learner.model.learner.decision_function(fv.reshape(1, -1)) loss *= -1 * inst.get_label() loss = math.log(1 + math.exp(loss)) return loss
def _calc_inst_loss(self, inst: Instance): """ Calculates the logistic loss for one instance :param inst: the instance :return: the logistic loss """ fv = [] for i in range(inst.get_feature_count()): if inst.get_feature_vector().get_feature(i) == 1: fv.append(1) else: fv.append(0) fv = np.array(fv) # reshape is for the decision function when inputting only one sample loss = self.learner.model.learner.decision_function(fv.reshape(1, -1)) loss *= -1 * inst.get_label() loss = math.log(1 + math.exp(loss)) return loss
def transform(self, instance: Instance): ''' for the real_value case, we generate a value between 0 and the bound. The bound is calculated by 1- c_delta * (abs(xt - x)/abs(x) + abs(xt)) * (xt -x) This value will be added to the xij for the new instance :param instance: :return: instance ''' for i in range(0, self.num_features): xij = instance.get_feature_vector().get_feature(i) target = self.innocuous_target.get_feature_vector().get_feature(i) if abs(xij) + abs(target) == 0: bound = 0 else: bound = self.discount_factor * (1 - self.f_attack * (abs(target - xij) / (abs(xij) + abs(target)))) \ * abs((target - xij)) # is that ok to just assign a random number between the range??? delta_ij = random.uniform(0, bound) instance.flip(i, xij + delta_ij) return instance