Exemplo n.º 1
0
    def __attack_graph(
        self,
        model: NeuralModelBase,
        batch: MiniBatch,
        mask: torch.Tensor,
        num_samples,
        shuffle: ShuffleStrategy,
        mode: AdversarialMode,
    ):
        tree_sizes = batch.lengths
        offsets = (np.cumsum(tree_sizes) - np.array(tree_sizes)).tolist()

        if mask is not None:
            tree_masks = torch.split(mask, tree_sizes)
            tree_nodes = [
                np.flatnonzero(tree_mask.cpu().numpy())
                for tree_mask in tree_masks
            ]
        else:
            tree_nodes = [None] * len(tree_sizes)

        g = batch.X
        assert g.number_of_nodes() == sum(tree_sizes)
        original_values = g.ndata["values"]

        tree_rules, tree_num_samples = self._initialize_adversarial_rules(
            num_samples, tree_nodes, model, batch, shuffle, mode)
        adversarial_mask = None

        try:
            for idx in range(max(tree_num_samples)):
                values = g.ndata["values"].cpu().numpy()

                if mode == AdversarialMode.INDIVIDUAL_GRADIENT:
                    # obtain gradients w.r.t. idx-th classifiable position in each input
                    shuffle.for_next_position()
                    for rules in tree_rules:
                        for rule in rules:
                            shuffle.shuffle_candidates(rule)

                if mode == AdversarialMode.BATCH_GRADIENT_BOOSTING:
                    # mask: compute gradient only for correctly predicted positions in all previous iterations
                    g.ndata["values"] = original_values
                    adversarial_mask = model.get_adversarial_mask(
                        batch,
                        mask_field="mask_valid",
                        previous_mask=adversarial_mask)

                if mode in [
                        AdversarialMode.BATCH_GRADIENT_ASCENT,
                        AdversarialMode.BATCH_GRADIENT_BOOSTING,
                ]:
                    shuffle.initialize(model,
                                       batch,
                                       position_mask=adversarial_mask)
                    for rules in tree_rules:
                        for rule in rules:
                            shuffle.shuffle_candidates(rule)

                for ith_tree, (rules,
                               offset) in enumerate(zip(tree_rules, offsets)):
                    # one node with constant assign => one rule
                    for rule in rules:
                        # apply each rule with the batch offset of its example
                        idx_to_use = idx
                        if mode in [
                                AdversarialMode.BATCH_GRADIENT_ASCENT,
                                AdversarialMode.BATCH_GRADIENT_BOOSTING,
                        ]:
                            # values have been shuffled again, we can just use the argmax
                            idx_to_use = 0
                        rule.apply_first_valid(idx_to_use,
                                               values,
                                               usage_offset=offset)

                g.ndata["values"] = torch.tensor(values,
                                                 dtype=torch.long,
                                                 device=original_values.device)
                yield batch
        except GeneratorExit:
            return
        finally:
            g.ndata["values"] = original_values
Exemplo n.º 2
0
    def __attack_seq(
        self,
        model: NeuralModelBase,
        batch: MiniBatch,
        mask: torch.Tensor,
        num_samples,
        shuffle: ShuffleStrategy,
        mode: AdversarialMode,
    ):

        tree_sizes = batch.lengths
        if mask is not None:
            raise NotImplementedError
        else:
            tree_nodes = [None] * len(tree_sizes)

        tree_rules, tree_num_samples = self._initialize_adversarial_rules(
            num_samples, tree_nodes, model, batch, shuffle=shuffle, mode=mode)

        # the inputs should contain two tensors for types and values
        # assert len(batch.inputs) == 2
        # only values are being changed
        original_values = batch.X[-1]
        adversarial_masks = None

        try:
            for idx in range(max(tree_num_samples)):
                batch_values = batch.X[-1].cpu().t().numpy()
                idx_to_use = idx

                if mode == AdversarialMode.INDIVIDUAL_GRADIENT:
                    # obtain gradients w.r.t. idx-th classifiable position in each input
                    shuffle.for_next_position()
                    for rules in tree_rules:
                        for rule in rules:
                            shuffle.shuffle_candidates(rule)

                if mode == AdversarialMode.BATCH_GRADIENT_BOOSTING:
                    # mask: compute gradient only for correctly predicted positions in all previous iterations
                    batch.X[-1] = original_values
                    adversarial_masks = [
                        model.get_adversarial_mask(
                            minibatch,
                            mask_field="mask_valid",
                            previous_mask=adversarial_masks,
                        ) for minibatch in batch
                    ]

                if mode in [
                        AdversarialMode.BATCH_GRADIENT_ASCENT,
                        AdversarialMode.BATCH_GRADIENT_BOOSTING,
                ]:
                    shuffle.initialize(model,
                                       batch,
                                       None,
                                       position_mask=adversarial_masks)
                    for rules in tree_rules:
                        for rule in rules:
                            shuffle.shuffle_candidates(rule)
                    # values have been shuffled again, we can just use the argmax
                    idx_to_use = 0

                assert len(batch_values) == len(tree_rules)
                # for every example in batch ...
                for rules, values in zip(tree_rules, batch_values):
                    # one node with constant assign => one rule
                    for rule in rules:
                        rule.apply_first_valid(idx_to_use, values)

                batch.X[-1] = torch.tensor(
                    batch_values.transpose(),
                    dtype=torch.long,
                    device=original_values.device,
                )
                yield batch
        except GeneratorExit:
            return
        finally:
            batch.X[-1] = original_values

            for rules in tree_rules:
                for rule in rules:
                    rule.reset()