Ejemplo n.º 1
0
    def process(self,
                surrogate,
                train_nodes,
                unlabeled_nodes=None,
                reset=True):
        assert isinstance(surrogate, gg.gallery.nodeclas.DenseGCN), surrogate

        # poisoning attack in DeepRobust
        if unlabeled_nodes is None:
            victim_nodes = gf.asarray(train_nodes)
            victim_labels = self.graph.node_label[victim_nodes]
        else:  # Evasion attack in original paper
            self_training_labels = self.estimate_self_training_labels(surrogate, unlabeled_nodes)
            victim_nodes = np.hstack([train_nodes, unlabeled_nodes])
            victim_labels = np.hstack([self.graph.node_label[train_nodes], self_training_labels])

        with tf.device(self.device):
            adj_tensor = gf.astensor(self.graph.adj_matrix.A)
            self.victim_nodes = gf.astensor(victim_nodes)
            self.victim_labels = gf.astensor(victim_labels)
            self.adj_tensor = adj_tensor
            self.x_tensor = gf.astensor(self.graph.node_attr)
            self.complementary = tf.ones_like(adj_tensor) - tf.eye(self.num_nodes) - 2. * adj_tensor
            self.loss_fn = sparse_categorical_crossentropy
            self.adj_changes = tf.Variable(tf.zeros_like(adj_tensor))
            self.surrogate = surrogate.model

            # used for `CW_loss=True`
            self.label_matrix = tf.gather(tf.eye(self.num_classes), self.victim_labels)
            self.range_idx = tf.range(victim_nodes.size, dtype=self.intx)
            self.indices_real = tf.stack([self.range_idx, self.victim_labels],
                                         axis=1)
        if reset:
            self.reset()
        return self
Ejemplo n.º 2
0
    def train_loader(self, index):

        labels = self.graph.node_label[index]
        sequence = NullSequence(
            x=[self.cache.X, self.cache.A, self.cache.adjacency],
            y=gf.astensor(labels, device=self.data_device),
            out_weight=gf.astensor(index, device=self.data_device),
            device=self.data_device)
        return sequence
Ejemplo n.º 3
0
    def config_train_data(self, index):

        labels = self.graph.label[index]
        sequence = NullSequence(
            [self.cache.feat, self.cache.adj, self.cache.adjacency],
            gf.astensor(labels, device=self.data_device),
            gf.astensor(index, device=self.data_device),
            device=self.data_device)
        return sequence
Ejemplo n.º 4
0
    def construct_mask(self):
        adj_mask = np.ones(self.graph.adj_matrix.shape, dtype=self.floatx)
        x_mask = np.ones(self.graph.node_attr.shape, dtype=self.floatx)
        adj_mask[:, self.target] = 0.
        adj_mask[self.target, :] = 0.
        x_mask[self.target, :] = 0

        adj_mask = gf.astensor(adj_mask)
        x_mask = gf.astensor(x_mask)

        return adj_mask, x_mask
Ejemplo n.º 5
0
    def data_step(self, adj_transform="add_self_loop", feat_transform=None):

        graph = self.graph
        adj_matrix = gf.get(adj_transform)(graph.adj_matrix)
        attr_matrix = gf.get(feat_transform)(graph.attr_matrix)

        feat = gf.astensor(attr_matrix, device=self.data_device)
        # without considering `edge_weight`
        edges = gf.astensor(adj_matrix, device=self.data_device)[0]

        # ``edges`` and ``feat`` are cached for later use
        self.register_cache(feat=feat, edges=edges)
Ejemplo n.º 6
0
    def process(self, surrogate, victim_nodes, victim_labels=None, reset=True):
        if isinstance(surrogate, gg.gallery.nodeclas.Trainer):
            surrogate = surrogate.model

        if victim_labels is None:
            victim_labels = self.graph.node_label[victim_nodes]

        with tf.device(self.device):
            self.surrogate = surrogate
            self.loss_fn = sparse_categorical_crossentropy
            self.victim_nodes = gf.astensor(victim_nodes)
            self.victim_labels = gf.astensor(victim_labels)
        if reset:
            self.reset()
        return self
Ejemplo n.º 7
0
    def train_sequence(self, index):
        labels = self.graph.node_label[index]
        index = gf.astensor(index)

        X = tf.gather(self.cache.X, index)
        sequence = FullBatchSequence(X, labels, device=self.device)
        return sequence
Ejemplo n.º 8
0
    def construct_sub_adj(self, influence_nodes, wrong_label_nodes, sub_nodes,
                          sub_edges):
        length = len(wrong_label_nodes)
        potential_edges = np.hstack([
            np.row_stack([np.tile(infl, length), wrong_label_nodes])
            for infl in influence_nodes
        ])

        if len(influence_nodes) > 1:
            # TODO: considering self-loops
            mask = self.graph.adj_matrix[potential_edges[0],
                                         potential_edges[1]].A1 == 0
            potential_edges = potential_edges[:, mask]

        nodes = np.union1d(sub_nodes, wrong_label_nodes)
        edge_weights = np.ones(sub_edges.shape[1], dtype=self.floatx)
        non_edge_weights = np.zeros(potential_edges.shape[1],
                                    dtype=self.floatx)
        self_loop_weights = np.ones(nodes.shape[0], dtype=self.floatx)
        self_loop = np.row_stack([nodes, nodes])

        self.indices = np.hstack([
            sub_edges, sub_edges[[1, 0]], potential_edges,
            potential_edges[[1, 0]], self_loop
        ])
        self.edge_weights = tf.Variable(edge_weights, dtype=self.floatx)
        self.non_edge_weights = tf.Variable(non_edge_weights,
                                            dtype=self.floatx)
        self.self_loop_weights = gf.astensor(self_loop_weights,
                                             dtype=self.floatx)
        self.edge_index = sub_edges
        self.non_edge_index = potential_edges
        self.self_loop = self_loop
    def get_feature_importance(self, candidates, steps, disable=False):
        adj = self.adj_norm
        x = self.x_tensor
        mask = (candidates[:, 0], candidates[:, 1])
        target_index = gf.astensor([self.target])
        target_label = gf.astensor(self.target_label)
        baseline_add = x.numpy()
        baseline_add[mask] = 1.0
        baseline_add = gf.astensor(baseline_add)
        baseline_remove = x.numpy()
        baseline_remove[mask] = 0.0
        baseline_remove = gf.astensor(baseline_remove)
        feature_indicator = self.graph.node_attr[mask] > 0

        features = candidates[feature_indicator]
        non_features = candidates[~feature_indicator]

        feature_gradients = tf.zeros(features.shape[0])
        non_feature_gradients = tf.zeros(non_features.shape[0])

        for alpha in tqdm(tf.linspace(0., 1.0, steps + 1),
                          desc='Computing feature importance',
                          disable=disable):
            ###### Compute integrated gradients for removing features ######
            x_diff = x - baseline_remove
            x_step = baseline_remove + alpha * x_diff

            gradients = self.compute_feature_gradients(adj, x_step,
                                                       target_index,
                                                       target_label)
            feature_gradients += -tf.gather_nd(gradients, features)

            ###### Compute integrated gradients for adding features ######
            x_diff = baseline_add - x
            x_step = baseline_add - alpha * x_diff

            gradients = self.compute_feature_gradients(adj, x_step,
                                                       target_index,
                                                       target_label)
            non_feature_gradients += tf.gather_nd(gradients, non_features)

        integrated_grads = np.zeros(feature_indicator.size)
        integrated_grads[feature_indicator] = feature_gradients.numpy()
        integrated_grads[~feature_indicator] = non_feature_gradients.numpy()

        return integrated_grads
    def get_link_importance(self, candidates, steps, disable=False):

        adj = self.adj_tensor
        x = self.x_tensor
        mask = (candidates[:, 0], candidates[:, 1])
        target_index = gf.astensor([self.target])
        target_label = gf.astensor(self.target_label)
        baseline_add = adj.numpy()
        baseline_add[mask] = 1.0
        baseline_add = gf.astensor(baseline_add)
        baseline_remove = adj.numpy()
        baseline_remove[mask] = 0.0
        baseline_remove = gf.astensor(baseline_remove)
        edge_indicator = self.graph.adj_matrix[mask].A1 > 0

        edges = candidates[edge_indicator]
        non_edges = candidates[~edge_indicator]

        edge_gradients = tf.zeros(edges.shape[0])
        non_edge_gradients = tf.zeros(non_edges.shape[0])

        for alpha in tqdm(tf.linspace(0., 1.0, steps + 1),
                          desc='Computing link importance',
                          disable=disable):
            ###### Compute integrated gradients for removing edges ######
            adj_diff = adj - baseline_remove
            adj_step = baseline_remove + alpha * adj_diff

            gradients = self.compute_structure_gradients(
                adj_step, x, target_index, target_label)
            edge_gradients += -tf.gather_nd(gradients, edges)

            ###### Compute integrated gradients for adding edges ######
            adj_diff = baseline_add - adj
            adj_step = baseline_add - alpha * adj_diff

            gradients = self.compute_structure_gradients(
                adj_step, x, target_index, target_label)
            non_edge_gradients += tf.gather_nd(gradients, non_edges)

        integrated_grads = np.zeros(edge_indicator.size)
        integrated_grads[edge_indicator] = edge_gradients.numpy()
        integrated_grads[~edge_indicator] = non_edge_gradients.numpy()

        return integrated_grads
Ejemplo n.º 11
0
    def process_step(self):
        graph = self.graph
        adj_matrix = self.adj_transform(graph.adj_matrix)
        node_attr = self.attr_transform(graph.node_attr)

        node_attr = adj_matrix @ node_attr

        self.feature_inputs, self.structure_inputs = gf.astensor(
            node_attr, device=self.device), adj_matrix
    def process(self, surrogate, reset=True):
        if isinstance(surrogate, gg.gallery.nodeclas.Trainer):
            surrogate = surrogate.model

        adj, x = self.graph.adj_matrix, self.graph.node_attr
        self.nodes_set = set(range(self.num_nodes))
        self.features_set = np.arange(self.num_attrs)

        with tf.device(self.device):
            self.surrogate = surrogate
            self.loss_fn = SparseCategoricalCrossentropy(from_logits=True)
            self.x_tensor = gf.astensor(x)
            self.adj_tensor = gf.astensor(adj.A)
            self.adj_norm = gf.normalize_adj_tensor(self.adj_tensor)

        if reset:
            self.reset()
        return self
Ejemplo n.º 13
0
    def train_sequence(self, index):
        index = gf.astensor(index)
        labels = self.graph.node_label[index]

        feature_inputs = tf.gather(self.feature_inputs, index)
        sequence = FullBatchNodeSequence(feature_inputs,
                                         labels,
                                         device=self.device)
        return sequence
Ejemplo n.º 14
0
    def data_step(self, adj_transform=None, feat_transform=None):

        graph = self.graph
        adj_matrix = gf.get(adj_transform)(graph.adj_matrix)
        attr_matrix = gf.get(feat_transform)(graph.attr_matrix)

        feat = gf.astensor(attr_matrix, device=self.data_device)

        # ``feat`` is cached for later use
        self.register_cache(feat=feat, adj=adj_matrix)
Ejemplo n.º 15
0
 def process(self, surrogate, reset=True):
     if isinstance(surrogate, gg.gallery.Trainer):
         surrogate = surrogate.model
     with tf.device(self.device):
         self.surrogate = surrogate
         self.loss_fn = sparse_categorical_crossentropy
         self.x_tensor = gf.astensor(self.graph.node_attr)
     if reset:
         self.reset()
     return self
Ejemplo n.º 16
0
    def data_step(self, adj_transform="normalize_adj", attr_transform=None):

        graph = self.graph
        adj_matrix = gf.get(adj_transform)(graph.adj_matrix)
        node_attr = gf.get(attr_transform)(graph.node_attr)
        node_attr = adj_matrix @ node_attr

        X, A = gf.astensor(node_attr, device=self.data_device), adj_matrix

        # ``A`` and ``X`` are cached for later use
        self.register_cache(X=X, A=A)
Ejemplo n.º 17
0
    def process_step(self):
        graph = self.transform.graph_transform(self.graph)
        adj_matrix = self.transform.adj_transform(graph.adj_matrix)
        node_attr = self.transform.attr_transform(graph.node_attr)
        node_attr = adj_matrix @ node_attr

        X, A = gf.astensor(node_attr, device=self.device), adj_matrix

        # ``A`` and ``X`` are cached for later use
        self.register_cache("X", X)
        self.register_cache("A", A)
Ejemplo n.º 18
0
    def data_step(
            self,
            adj_transform="normalize_adj",  # it is required
            feat_transform=None):

        graph = self.graph
        attr_matrix = gf.get(feat_transform)(graph.attr_matrix)

        feat = gf.astensor(attr_matrix, device=self.data_device)

        # ``feat`` is cached for later use
        self.register_cache(feat=feat)
Ejemplo n.º 19
0
    def process(self,
                train_nodes,
                unlabeled_nodes,
                self_training_labels,
                hids,
                use_relu,
                reset=True):

        self.ll_ratio = None

        with tf.device(self.device):
            self.train_nodes = gf.astensor(train_nodes, dtype=self.intx)
            self.unlabeled_nodes = gf.astensor(unlabeled_nodes, dtype=self.intx)
            self.labels_train = gf.astensor(self.graph.node_label[train_nodes], dtype=self.intx)
            self.self_training_labels = gf.astensor(self_training_labels, dtype=self.intx)
            self.adj_tensor = gf.astensor(self.graph.adj_matrix.A, dtype=self.floatx)
            self.x_tensor = gf.astensor(self.graph.node_attr, dtype=self.floatx)
            self.build(hids=hids)
            self.use_relu = use_relu
            self.loss_fn = SparseCategoricalCrossentropy(from_logits=True)

            self.adj_changes = tf.Variable(tf.zeros_like(self.adj_tensor))
            self.x_changes = tf.Variable(tf.zeros_like(self.x_tensor))

        if reset:
            self.reset()
        return self
Ejemplo n.º 20
0
    def config_train_data(self, edge_index):
        if isinstance(edge_index, (list, tuple)):
            train_edges = edge_index[0]  # postive edge index
        else:
            train_edges = edge_index

        train_edges = gf.astensor(train_edges, device=self.data_device)

        self.register_cache(edges=train_edges)
        sequence = FullBatchSequence([self.cache.feat, train_edges],
                                     out_index=edge_index,
                                     device=self.data_device)
        return sequence
Ejemplo n.º 21
0
    def process(self,
                surrogate,
                train_nodes,
                unlabeled_nodes=None,
                reset=True):
        assert isinstance(surrogate, gg.gallery.GCN), surrogate

        # poisoning attack in DeepRobust
        if unlabeled_nodes is None:
            victim_nodes = gf.asarray(train_nodes)
            victim_labels = self.graph.node_label[victim_nodes]
        else:  # Evasion attack in original paper
            self_training_labels = self.estimate_self_training_labels(
                surrogate, unlabeled_nodes)
            victim_nodes = np.hstack([train_nodes, unlabeled_nodes])
            victim_labels = np.hstack(
                [self.graph.node_label[train_nodes], self_training_labels])

        adj_tensor = gf.astensor(self.graph.adj_matrix.A, device=self.device)
        self.victim_nodes = gf.astensor(victim_nodes, device=self.device)
        self.victim_labels = gf.astensor(victim_labels, device=self.device)
        self.adj_tensor = adj_tensor
        self.x_tensor = gf.astensor(self.graph.node_attr, device=self.device)
        self.complementary = (torch.ones_like(adj_tensor) -
                              torch.eye(self.num_nodes).to(self.device) -
                              2. * adj_tensor)
        self.loss_fn = nn.CrossEntropyLoss()
        self.adj_changes = nn.Parameter(torch.zeros_like(self.adj_tensor))
        self.surrogate = surrogate.model.to(self.device)
        self.surrogate.eval()

        # # used for `CW_loss=True`
        self.label_matrix = torch.eye(self.num_classes)[self.victim_labels].to(
            self.device)
        self.range_idx = torch.arange(victim_nodes.size).to(self.device)
        self.indices_real = torch.stack([self.range_idx, self.victim_labels])
        if reset:
            self.reset()
        return self
Ejemplo n.º 22
0
    def data_step(self,
                  adj_transform="normalize_adj",
                  feat_transform=None):

        graph = self.graph
        adj_matrix = gf.get(adj_transform)(graph.adj_matrix)
        attr_matrix = gf.get(feat_transform)(graph.attr_matrix)
        attr_matrix = adj_matrix @ attr_matrix

        feat, adj = gf.astensor(attr_matrix, device=self.data_device), adj_matrix

        # ``adj`` and ``feat`` are cached for later use
        self.register_cache(feat=feat, adj=adj)
Ejemplo n.º 23
0
    def process_step(self):
        graph = self.transform.graph_transform(self.graph)
        # Dense matrix, shape [num_nodes, max_degree]
        adj_matrix = self.transform.adj_transform(graph.adj_matrix)
        node_attr = self.transform.attr_transform(graph.node_attr)
        # pad with a dummy zero vector
        node_attr = np.vstack(
            [node_attr,
             np.zeros(node_attr.shape[1], dtype=self.floatx)])

        X, A = gf.astensor(node_attr, device=self.device), adj_matrix

        # ``A`` and ``X`` are cached for later use
        self.register_cache("X", X)
        self.register_cache("A", A)
Ejemplo n.º 24
0
    def config_train_data(self, edge_index):
        if isinstance(edge_index, (list, tuple)):
            train_edges = edge_index[0]  # postive edge index
        else:
            train_edges = edge_index

        full_adj = self.graph.adj_matrix
        edge_weight = full_adj[train_edges[0], train_edges[1]].A1
        adj_matrix = gf.edge_to_sparse_adj(train_edges, edge_weight)
        train_adj = self.transform.adj_transform(adj_matrix)

        train_adj = gf.astensor(train_adj, device=self.data_device)

        self.register_cache(adj=train_adj)
        sequence = FullBatchSequence([self.cache.feat, train_adj],
                                     out_index=edge_index,
                                     device=self.data_device)
        return sequence
Ejemplo n.º 25
0
    def config_train_data(self, index):
        labels = self.graph.label[index]

        # ==========================================================
        # initial weight_y is obtained by linear regression
        feat = self.cache.feat.to(self.device)
        labels = gf.astensor(labels, device=self.device)
        A = torch.mm(feat.t(), feat) + 1e-05 * torch.eye(feat.size(1),
                                                         device=feat.device)
        labels_one_hot = feat.new_zeros(feat.size(0), self.graph.num_classes)
        labels_one_hot[torch.arange(labels.size(0)), labels] = 1
        self.model.init_weight_y = torch.mm(
            torch.mm(torch.cholesky_inverse(A), feat.t()), labels_one_hot)
        # ==========================================================

        sequence = FullBatchSequence([self.cache.feat, self.cache.g],
                                     labels,
                                     out_index=index,
                                     device=self.data_device,
                                     escape=type(self.cache.g))
        return sequence
Ejemplo n.º 26
0
    def process(self, surrogate, reset=True):
        assert isinstance(surrogate, gg.gallery.SGC), surrogate

        hops = surrogate.cfg.process.K  # NOTE: Be compatible with graphgallery
        # nodes with the same class labels
        self.similar_nodes = [
            np.where(self.graph.node_label == c)[0]
            for c in range(self.num_classes)
        ]

        with tf.device(self.device):
            W, b = surrogate.model.weights
            X = gf.astensor(self.graph.node_attr)
            self.b = b
            self.XW = X @ W
            self.SGC = SGConvolution(hops)
            self.hops = hops
            self.loss_fn = sparse_categorical_crossentropy
            self.surrogate = surrogate
        if reset:
            self.reset()
        return self
Ejemplo n.º 27
0
    def predict(self, predict_data=None,
                transform=torch.nn.Softmax(dim=-1)):

        indices = gf.astensor(predict_data).view(-1)
        mapper = torch.zeros(self.graph.num_nodes).long()
        mapper[indices] = torch.arange(indices.size(0))

        if not self.model:
            raise RuntimeError(
                'You must compile your model before training/testing/predicting. Use `trainer.build()`.'
            )

        if not isinstance(predict_data, (DataLoader, Dataset)):
            predict_data = self.config_predict_data(predict_data)

        out, node_ids = self.predict_step(predict_data)
        out[mapper[node_ids.cpu()]] = out.clone()

        out = out.squeeze()
        if transform is not None:
            out = transform(out)
        return out.cpu()
Ejemplo n.º 28
0
    def process(self,
                train_nodes,
                unlabeled_nodes,
                self_training_labels,
                hids,
                use_relu,
                reset=True):
        self.ll_ratio = None

        self.train_nodes = gf.astensor(train_nodes,
                                       dtype=self.intx,
                                       device=self.device)
        self.unlabeled_nodes = gf.astensor(unlabeled_nodes,
                                           dtype=self.intx,
                                           device=self.device)
        self.labels_train = gf.astensor(self.graph.node_label[train_nodes],
                                        dtype=self.intx,
                                        device=self.device)
        self.self_training_labels = gf.astensor(self_training_labels,
                                                dtype=self.intx,
                                                device=self.device)
        self.adj_tensor = gf.astensor(self.graph.adj_matrix.A,
                                      dtype=self.floatx,
                                      device=self.device)
        self.x_tensor = gf.astensor(self.graph.node_attr,
                                    dtype=self.floatx,
                                    device=self.device)
        self.build(hids=hids)
        self.use_relu = use_relu

        self.loss_fn = torch.nn.CrossEntropyLoss()
        self.adj_changes = None
        self.x_changes = None

        if reset:
            self.reset()
        return self
Ejemplo n.º 29
0
    def attack(self,
               target,
               num_budgets=None,
               symmetric=True,
               direct_attack=True,
               structure_attack=True,
               feature_attack=False,
               disable=False):

        super().attack(target, num_budgets, direct_attack, structure_attack,
                       feature_attack)

        if feature_attack and not self.graph.is_binary():
            raise RuntimeError(
                "Currently only attack binary node attributes are supported")

        with tf.device(self.device):
            target_index = gf.astensor([self.target])
            target_labels = gf.astensor(self.target_label)

            modified_adj, modified_nx = self.modified_adj, self.modified_nx

            if not direct_attack:
                adj_mask, x_mask = self.construct_mask()
            else:
                adj_mask, x_mask = None, None

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

                adj_grad, x_grad = self.compute_gradients(
                    modified_adj, modified_nx, target_index, target_labels)

                adj_grad_score = tf.constant(0.0)
                x_grad_score = tf.constant(0.0)

                if structure_attack:

                    if symmetric:
                        adj_grad = (adj_grad + tf.transpose(adj_grad)) / 2.

                    adj_grad_score = self.structure_score(
                        modified_adj, adj_grad, adj_mask)

                if feature_attack:
                    x_grad_score = self.feature_score(modified_nx, x_grad,
                                                      x_mask)

                if tf.reduce_max(adj_grad_score) >= tf.reduce_max(
                        x_grad_score):
                    adj_grad_argmax = tf.argmax(adj_grad_score)
                    row, col = divmod(adj_grad_argmax.numpy(), self.num_nodes)
                    modified_adj[row, col].assign(1. - modified_adj[row, col])
                    modified_adj[col, row].assign(1. - modified_adj[col, row])
                    self.adj_flips.append((row, col))
                else:
                    x_grad_argmax = tf.argmax(x_grad_score)
                    row, col = divmod(x_grad_argmax.numpy(), self.num_attrs)
                    modified_nx[row, col].assign(1. - modified_nx[row, col])
                    self.nattr_flips.append((row, col))
        return self