Exemple #1
0
class MetaApprox(BaseMeta):
    def __init__(self,
                 adj,
                 x,
                 labels,
                 idx_train,
                 idx_unlabeled,
                 lr=0.1,
                 epochs=100,
                 lambda_=0.,
                 hidden_layers=[16],
                 use_relu=True,
                 self_training_labels=None,
                 seed=None,
                 name=None,
                 device='CPU:0',
                 **kwargs):

        self.lr = lr
        self.epochs = epochs
        self.lambda_ = lambda_

        if lambda_ not in (0., 0.5, 1.):
            raise ValueError(
                'Invalid value of `lanbda_`, allowed values [0: (meta-self), 1: (meta-train), 0.5: (meta-both)].'
            )

        super().__init__(adj,
                         x,
                         labels,
                         idx_train,
                         idx_unlabeled,
                         hidden_layers=hidden_layers,
                         use_relu=use_relu,
                         self_training_labels=self_training_labels,
                         seed=seed,
                         name=name,
                         device=device,
                         **kwargs)

    def build(self, hidden_layers):

        weights = []
        zeros_initializer = zeros()

        pre_hid = self.n_attrs
        for hid in hidden_layers + [self.n_classes]:
            shape = (pre_hid, hid)
            # use zeros_initializer temporary to save time
            weight = tf.Variable(
                zeros_initializer(shape=shape, dtype=self.floatx))
            weights.append(weight)
            pre_hid = hid

        self.weights = weights
        self.adj_grad_sum = tf.Variable(tf.zeros_like(self.tf_adj))
        self.x_grad_sum = tf.Variable(tf.zeros_like(self.tf_x))
        self.optimizer = Adam(self.lr, epsilon=1e-8)

    def initialize(self):

        w_initializer = glorot_uniform()
        zeros_initializer = zeros()

        for w in self.weights:
            w.assign(w_initializer(w.shape, dtype=self.floatx))

        if self.structure_attack:
            self.adj_grad_sum.assign(
                zeros_initializer(self.adj_grad_sum.shape, dtype=self.floatx))

        if self.feature_attack:
            self.x_grad_sum.assign(
                zeros_initializer(self.x_grad_sum.shape, dtype=self.floatx))

        # reset optimizer
        for var in self.optimizer.variables():
            var.assign(tf.zeros_like(var))

    @tf.function
    def meta_grad(self):
        self.initialize()

        modified_adj, modified_x = self.tf_adj, self.tf_x
        adj_grad_sum, x_grad_sum = self.adj_grad_sum, self.x_grad_sum
        optimizer = self.optimizer

        for _ in tf.range(self.epochs):

            with tf.GradientTape(persistent=True) as tape:
                if self.structure_attack:
                    modified_adj = self.get_perturbed_adj(
                        self.tf_adj, self.adj_changes)

                if self.feature_attack:
                    modified_x = self.get_perturbed_x(self.tf_x,
                                                      self.x_changes)

                adj_norm = normalize_adj_tensor(modified_adj)
                output = self.forward(modified_x, adj_norm) / 5.0
                logit_labeled = tf.gather(output, self.idx_train)
                logit_unlabeled = tf.gather(output, self.idx_unlabeled)

                loss_labeled = self.loss_fn(self.labels_train, logit_labeled)
                loss_unlabeled = self.loss_fn(self.self_training_labels,
                                              logit_unlabeled)

                attack_loss = self.lambda_ * loss_labeled + (
                    1 - self.lambda_) * loss_unlabeled

            adj_grad, x_grad = None, None

            gradients = tape.gradient(loss_labeled, self.weights)
            optimizer.apply_gradients(zip(gradients, self.weights))

            if self.structure_attack:
                adj_grad = tape.gradient(attack_loss, self.adj_changes)
                adj_grad_sum.assign_add(adj_grad)

            if self.feature_attack:
                x_grad = tape.gradient(attack_loss, self.x_changes)
                x_grad_sum.assign_add(x_grad)

            del tape

        return adj_grad_sum, x_grad_sum

    def attack(self,
               n_perturbations=0.05,
               structure_attack=True,
               feature_attack=False,
               ll_constraint=False,
               ll_cutoff=0.004,
               disable=False):

        super().attack(n_perturbations, structure_attack, feature_attack)

        if ll_constraint:
            raise NotImplementedError(
                '`log_likelihood_constraint` has not been well tested.'
                ' Please set `ll_constraint=False` to achieve a better performance.'
            )

        if feature_attack and not is_binary(self.x):
            raise ValueError(
                "Attacks on the node features are currently only supported for binary attributes."
            )

        with tf.device(self.device):
            modified_adj, modified_x = self.tf_adj, self.tf_x
            adj_changes, x_changes = self.adj_changes, self.x_changes
            structure_flips, feature_flips = self.structure_flips, self.feature_flips

            for _ in tqdm(range(self.n_perturbations),
                          desc='Peturbing Graph',
                          disable=disable):

                adj_grad, x_grad = self.meta_grad()

                adj_meta_score = tf.constant(0.0)
                x_meta_score = tf.constant(0.0)

                if structure_attack:
                    modified_adj = self.get_perturbed_adj(
                        self.tf_adj, adj_changes)
                    adj_meta_score = self.structure_score(
                        modified_adj, adj_grad, ll_constraint, ll_cutoff)

                if feature_attack:
                    modified_x = self.get_perturbed_x(self.tf_x, x_changes)
                    x_meta_score = self.feature_score(modified_x, feature_grad)

                if tf.reduce_max(adj_meta_score) >= tf.reduce_max(
                        x_meta_score):
                    adj_meta_argmax = tf.argmax(adj_meta_score)
                    row, col = divmod(adj_meta_argmax.numpy(), self.n_nodes)
                    adj_changes[row,
                                col].assign(-2. * modified_adj[row, col] + 1.)
                    adj_changes[col,
                                row].assign(-2. * modified_adj[col, row] + 1.)
                    structure_flips.append((row, col))
                else:
                    x_meta_argmax = tf.argmax(x_meta_score)
                    row, col = divmod(x_meta_argmax.numpy(), self.n_attrs)
                    x_changes[row, col].assign(-2 * modified_x[row, col] + 1)
                    feature_flips.append((row, col))
Exemple #2
0
class MinMax(PGD):
    '''MinMax cannot ensure that there is not singleton after attack.'''
    def __init__(self,
                 adj,
                 x,
                 labels,
                 idx_train,
                 idx_unlabeled=None,
                 surrogate=None,
                 surrogate_args={},
                 surrogate_lr=5e-3,
                 seed=None,
                 name=None,
                 device='CPU:0',
                 **kwargs):

        super().__init__(adj,
                         x,
                         labels,
                         idx_train=idx_train,
                         idx_unlabeled=idx_unlabeled,
                         surrogate=surrogate,
                         surrogate_args=surrogate_args,
                         seed=seed,
                         device=device,
                         **kwargs)

        with tf.device(self.device):
            self.stored_weights = tf.identity_n(self.surrogate.weights)
            self.optimizer = Adam(surrogate_lr)

    def reset(self):
        super().reset()
        weights = self.surrogate.weights

        # restore surrogate weights
        for w1, w2 in zip(weights, self.stored_weights):
            w1.assign(w2)

        # reset optimizer
        for var in self.optimizer.variables():
            var.assign(tf.zeros_like(var))

    def attack(self,
               n_perturbations=0.05,
               sample_epochs=20,
               CW_loss=True,
               epochs=100,
               update_per_epoch=20,
               structure_attack=True,
               feature_attack=False,
               disable=False):

        super(PGD, self).attack(n_perturbations, structure_attack,
                                feature_attack)

        self.CW_loss = CW_loss

        if CW_loss:
            C = 0.1
        else:
            C = 200

        with tf.device(self.device):

            trainable_variables = self.surrogate.trainable_variables

            for epoch in tqdm(range(epochs),
                              desc='MinMax Training',
                              disable=disable):
                if (epoch + 1) % update_per_epoch == 0:
                    self.update_surrogate(trainable_variables, self.idx_attack)
                gradients = self.compute_gradients(self.idx_attack)
                lr = C / np.sqrt(epoch + 1)
                self.adj_changes.assign_add(lr * gradients)
                self.projection()

            best_s = self.random_sample(sample_epochs)
            self.structure_flips = np.transpose(np.where(best_s > 0.))

    @tf.function
    def update_surrogate(self, trainable_variables, idx):
        with tf.GradientTape() as tape:
            adj = self.get_perturbed_adj()
            adj_norm = normalize_adj_tensor(adj)
            logit = self.surrogate([self.tf_x, adj_norm, idx])
            logit = softmax(logit)
            loss = self.compute_loss(logit)

        gradients = tape.gradient(loss, trainable_variables)
        self.optimizer.apply_gradients(zip(gradients, trainable_variables))
Exemple #3
0
class MinMax(PGD):
    """MinMax cannot ensure that there is not singleton after attack."""

    def process(self,
                surrogate,
                train_nodes,
                unlabeled_nodes=None,
                lr=5e-3,
                reset=True):
        super().process(surrogate, train_nodes, unlabeled_nodes, reset=False)
        with tf.device(self.device):
            self.stored_weights = tf.identity_n(self.surrogate.weights)
            self.optimizer = Adam(lr)
        if reset:
            self.reset()
        return self

    def reset(self):
        super().reset()
        weights = self.surrogate.weights

        # restore surrogate weights
        for w1, w2 in zip(weights, self.stored_weights):
            w1.assign(w2)

        # reset optimizer
        for var in self.optimizer.variables():
            var.assign(tf.zeros_like(var))
        return self

    def attack(self,
               num_budgets=0.05,
               sample_epochs=20,
               C=None,
               CW_loss=False,
               epochs=100,
               update_per_epoch=20,
               structure_attack=True,
               feature_attack=False,
               disable=False):

        super(PGD, self).attack(num_budgets, structure_attack, feature_attack)

        self.CW_loss = CW_loss

        if not C:
            if CW_loss:
                C = 0.1
            else:
                C = 200

        with tf.device(self.device):

            for epoch in tqdm(range(epochs),
                              desc='MinMax Training',
                              disable=disable):
                if (epoch + 1) % update_per_epoch == 0:
                    self.update_surrogate(self.victim_nodes)
                gradients = self.compute_gradients(self.victim_nodes)
                lr = C / np.sqrt(epoch + 1)
                self.adj_changes.assign_add(lr * gradients)
                self.projection()

            best_s = self.random_sample(sample_epochs)
            self.adj_flips = np.transpose(np.where(best_s > 0.))
        return self

    @tf.function
    def update_surrogate(self, victim_nodes):
        trainable_variables = self.surrogate.trainable_variables
        with tf.GradientTape() as tape:
            adj = self.get_perturbed_adj()
            loss = self.compute_loss(victim_nodes)

        gradients = tape.gradient(loss, trainable_variables)
        self.optimizer.apply_gradients(zip(gradients, trainable_variables))