def gap(self, x: Instance, weight): """ :param x: Instance object :param weight: a vector specifying linear weights :return: real value of gap(x) """ x_prime = x.get_csr_matrix().toarray().T return math.fabs(weight * x_prime - self.threshold)
def binary_gradient_descent(self, attack_instance: Instance): # sparse attack with binary features index_lst = [] iter_time = 0 attacker_score = self.get_score( attack_instance.get_csr_matrix().toarray()) while iter_time < self.max_iter: grad = self.gradient(attack_instance.get_csr_matrix().toarray()) if index_lst is not []: # eliminate the index we have already modified for i in index_lst: grad[i] = 0 change_index = np.argmax(np.absolute(grad)) new_attack_instance = deepcopy(attack_instance) new_attack_instance.get_feature_vector().flip_bit(change_index) index_lst.append(change_index) new_attacker_score = self.get_score( new_attack_instance.get_csr_matrix().toarray()) if new_attacker_score < attacker_score: attack_instance = new_attack_instance attacker_score = new_attacker_score iter_time += 1 return attack_instance
def random_start_coordinate_greedy(self, instance: Instance): """ implement the n random start algorithm by performing CG for n times. The minimized Q and x is used as new attack instance. :param instance: :return: """ instance_lst = [] q_value_lst = [] old_x = instance.get_csr_matrix().toarray()[0] for i in range(self.random_start): new_attacked_instance = self.coordinate_greedy(instance) x = new_attacked_instance.get_csr_matrix().toarray()[0] q = self.transform_cost(x, old_x) instance_lst.append(new_attacked_instance) q_value_lst.append(q) return min(zip(instance_lst, q_value_lst), key=lambda x: x[1])[0]
def coordinate_greedy(self, instance: Instance) -> Instance: """ Greddily update the feature to incrementally improve the attackers utility. run CS from L random starting points in the feature space. We repeat the alternation until differences of instances are small or max_change is reached. no_improve_count: number of points Q: transofrm cost(we use quodratic distance) GreedyImprove: using the coordinate descent algorithm. :param instance: :return: if the result is still classified as +1, we return origin instance else we return the improved. """ indices = [i for i in range(0, self.num_features)] x = xk = instance.get_csr_matrix().toarray()[0] no_improve_count = 0 shuffle(indices) count = 0 for i in indices: xkplus1 = self.minimize_transform(xk, x, i) oldQ = self.transform_cost(xk, x) newQ = self.transform_cost(xkplus1, x) # step_change = np.log(newQ) / np.log(oldQ) # using difference instead of log ratio for convergence check xk = xkplus1 no_improve_count += 1 if newQ - oldQ > 0 and oldQ != 0: step_change = np.log(newQ - oldQ) if step_change <= self.epsilon: break if no_improve_count > self.max_change: break count += 1 mat_indices = [x for x in range(0, self.num_features) if xk[x] != 0] mat_data = [xk[x] for x in range(0, self.num_features) if xk[x] != 0] new_instance = Instance( -1, RealFeatureVector(self.num_features, mat_indices, mat_data)) return new_instance
def coordinate_greedy(self, instance: Instance) -> Instance: indices = [i for i in range(0, self.num_features)] x = xk = instance.get_csr_matrix().toarray()[0] # Q = [self.transform_cost(xk,x)] # f = [self.learn_model.model.learner.predict(xk.reshape(1,-1))] # p = [self.learn_model.model.learner.coef_.dot(xk)+ # self.learn_model.model.learner.intercept_] # c = [self.quadratic_cost(xk,x)] no_improve_count = 0 shuffle(indices) for i in indices: xkplus1 = self.minimize_transform(xk, i) oldQ = self.transform_cost(xk, x) newQ = self.transform_cost(xkplus1, x) # step_change = np.log(newQ) / np.log(oldQ) # using difference instead of log ratio for convergence check step_change = newQ - oldQ # print('oldQ= '+str(oldQ) + ' newQ= '+str(newQ)+ # ' step_change= '+str(step_change)) # print('xk[i]= ' + str(xk[i]) + ' xk+1[i]= ' + # str(xkplus1[i]) + ' x[i]= ' + str(x[i])) if step_change >= 0: no_improve_count += 1 if no_improve_count >= self.max_change: break else: xk = xkplus1 # Q.append(self.transform_cost(xk,x)) # f.append( # self.learn_model.model.learner.predict(xk.reshape(1, -1))) # c.append(self.quadratic_cost(xk,x)) # p.append(self.learn_model.model.learner.coef_.dot(xk) + # self.learn_model.model.learner.intercept_) # print('xk shape: '+str(xk.shape)) # Q = np.array(Q) # f = np.array(f) # c = np.array(c) # p = np.array(p).reshape((-1,)) # pnc = p+c # print(p.shape) # print(c.shape) # print(pnc.shape) # t = np.array([i for i in range(len(Q))]) # plt.plot(t,Q,'r', label='Q(x)') # plt.plot(t, f, 'b', label='sign(f(x))') # plt.plot( t,c ,'g', label='||x-xi||^2') # plt.plot(t, p, 'b--',label='w.T*x+b') # plt.plot(t, pnc, 'r--', # label='w.T*x+b + ||x-xi||^2') # plt.legend() # plt.show() # ('mod succeeded') mat_indices = [x for x in range(0, self.num_features) if xk[x] != 0] new_instance = Instance( -1, BinaryFeatureVector(self.num_features, mat_indices)) if self.learn_model.predict( new_instance) == self.learn_model.positive_classification: return instance else: return new_instance
def coordinate_greedy(self, instance: Instance): """ Greedily update the feature to incrementally improve the attackers utility. run CS from L random starting points in the feature space. We repeat the alternation until differences of instances are small or max_change is reached. no_improve_count: number of points Q: transofrm cost(we use quodratic distance) GreedyImprove: using the coordinate descent algorithm. :param instance: :return: if the result is still classified as +1, we return origin instance else we return the improved. """ instance_len = instance.get_feature_count() if DEBUG: iteration_list = [] Q_value_list = [] x = xk = instance.get_csr_matrix().toarray()[0] # converge is used for checking convergance conditions # if the last convergence_time iterations all satisfy <= eplison condition # ,the attack successfully finds a optimum converge = 0 for iteration_time in range(self.max_iteration): i = randint(0, instance_len - 1) #calcualte cost function and greediy improve from a random feature i xkplus1 = self.minimize_transform(xk, x, i) old_q = self.transform_cost(xk, x) new_q = self.transform_cost(xkplus1, x) # check whether Q_value actually descends and converges to a minimum # plot the iteration and Q_values using matplotlib #if DEBUG: # iteration_list.append(iteration_time) # Q_value_list.append(new_q) # if new_q < 0: # print("Attack finishes because Q is less than 0") # break if new_q - old_q <= 0: xk = xkplus1 step_change = old_q - new_q # the np.log() may not converge in special cases # makes sure the cost function actually converges # alternative implementation? #step_change = np.log(new_q) / np.log(old_q) #step_change = np.log(old_q - new_q) if step_change <= self.epsilon: converge += 1 if converge >= self.convergence_time: #print("Attack finishes because of convergence!") break #if DEBUG: # plt.plot(iteration_list,Q_value_list) mat_indices = [x for x in range(0, self.num_features) if xk[x] != 0] mat_data = [xk[x] for x in range(0, self.num_features) if xk[x] != 0] new_instance = Instance( -1, RealFeatureVector(self.num_features, mat_indices, mat_data)) return new_instance
def gradient_descent(self, instance: Instance, neg_instances): #store iteration and objective values for plotting.... #iteration_lst = [] #objective_lst = [] # attack_intance-> np array attack_instance = instance.get_csr_matrix().toarray() root_instance = attack_instance obj_function_value_list = [] # store the modified gradient descent attack instances # find a list of potential neg_instances, the closest distance, and updated gradients candidate_attack_instances = [attack_instance] attacker_score = self.get_score(attack_instance) closer_neg_instances, dist, grad_update = self.compute_gradient( attack_instance, neg_instances) obj_func_value = attacker_score + self.lambda_val * dist obj_function_value_list.append(obj_func_value) for iter in range(self.max_iter): # no d(x,x_prime) is set to limit the boundary of attacks # compute the obj_func_value of the last satisfied instance # append to the value list #iteration_lst.append(iter) #objective_lst.append(obj_func_value) past_instance = candidate_attack_instances[-1] new_instance = self.update_within_boundary(past_instance, root_instance, grad_update) # compute the gradient and objective function value of the new instance closer_neg_instances, dist, new_grad_update = \ self.compute_gradient(new_instance, closer_neg_instances) new_attacker_score = self.get_score(new_instance) obj_func_value = new_attacker_score + self.lambda_val * dist # check convergence information # we may reach a local min if the function value does not change # if obj_func_value == obj_function_value_list[-1]: # print("Local min is reached. Iteration: %d, Obj value %d" %(iter,obj_func_value)) # mat_indices = [x for x in range(0, self.num_features) if new_instance[0][x] != 0] # mat_data = [new_instance[0][x] for x in range(0, self.num_features) if new_instance[0][x] != 0] # return Instance(-1, RealFeatureVector(self.num_features, mat_indices, mat_data)) # check a small epsilon(difference is a small value after # several iterations) if self.check_convergence_info(obj_func_value, obj_function_value_list): #print("Goes to Convergence here.... Iteration: %d, Obj value %.4f" % (iter,obj_func_value)) mat_indices = [ x for x in range(0, self.num_features) if new_instance[0][x] != 0 ] mat_data = [ new_instance[0][x] for x in range(0, self.num_features) if new_instance[0][x] != 0 ] #plt.plot(iteration_lst,objective_lst) return Instance( -1, RealFeatureVector(self.num_features, mat_indices, mat_data)) # does not satisfy convergence requirement # store onto the list elif obj_func_value < obj_function_value_list[-1]: obj_function_value_list.append(obj_func_value) if not (new_instance == candidate_attack_instances[-1]).all(): candidate_attack_instances.append(new_instance) attacker_score = new_attacker_score grad_update = new_grad_update #print("Convergence has not been found..") #plt.plot(iteration_lst, objective_lst) mat_indices = [ x for x in range(0, self.num_features) if candidate_attack_instances[-1][0][x] != 0 ] mat_data = [ candidate_attack_instances[-1][0][x] for x in range(0, self.num_features) if candidate_attack_instances[-1][0][x] != 0 ] return Instance( -1, RealFeatureVector(self.num_features, mat_indices, mat_data))