Пример #1
0
    def _input_top_k2(pipeline, pretokenized=True):
        raw_text = ["this", "is", "a", "test"] if pretokenized else "this is a test"
        tag_idx_sequence = [
            vocabulary.index_for_label(pipeline.backbone.vocab, tag)
            for tag in ["O", "O", "O", "U-NER"]
        ]
        tag_idx_sequence2 = [
            vocabulary.index_for_label(pipeline.backbone.vocab, tag)
            for tag in ["O", "B-NER", "I-NER", "L-NER"]
        ]
        viterbi_paths = [(tag_idx_sequence, 2), (tag_idx_sequence2, 1)]
        single_forward_output = dict(
            viterbi_paths=viterbi_paths,
            raw_text=raw_text,
        )

        return pipeline.head._make_task_prediction(single_forward_output, None)
Пример #2
0
    def _input_top_k2(pipeline, pretokenized=True):
        raw_text = [["this", "is", "a", "test"]
                    ] if pretokenized else ["this is a test"]
        tag_idx_sequence = [
            vocabulary.index_for_label(pipeline.backbone.vocab, tag)
            for tag in ["O", "O", "O", "U-NER"]
        ]
        tag_idx_sequence2 = [
            vocabulary.index_for_label(pipeline.backbone.vocab, tag)
            for tag in ["O", "B-NER", "I-NER", "L-NER"]
        ]
        viterbi_paths = [[(tag_idx_sequence, 2), (tag_idx_sequence2, 1)]]
        task_output = TaskOutput(viterbi_paths=viterbi_paths,
                                 raw_text=raw_text,
                                 mask=None,
                                 probs=None)

        return pipeline.head.decode(task_output).as_dict()
    def explain_prediction(
        self, prediction: Dict[str, numpy.array], instance: Instance, n_steps: int
    ) -> Dict[str, Any]:
        """Here, we must apply transformations for manage ListFields tensors shapes"""

        dataset = Batch([instance])
        input_tokens_ids = dataset.as_tensor_dict()
        ig = IntegratedGradients(self._explain_embeddings)

        num_wrapping_dims = 1

        document_tokens = [
            [token.text for token in cast(TextField, text_field).tokens]
            for text_field in cast(ListField, instance.get(self.forward_arg_name))
        ]
        document_tensors = input_tokens_ids.get(self.forward_arg_name)
        mask = get_text_field_mask(
            document_tensors, num_wrapping_dims=num_wrapping_dims
        )
        text_embeddings = self.backbone.embedder.forward(
            document_tensors, num_wrapping_dims=num_wrapping_dims
        )

        label_id = vocabulary.index_for_label(
            self.backbone.vocab, prediction.get(self.label_name)
        )
        attributions, delta = ig.attribute(
            text_embeddings,
            target=label_id,
            additional_forward_args=mask,
            return_convergence_delta=True,
            n_steps=n_steps,
        )
        attributions = attributions.sum(dim=3).squeeze(0)
        attributions = attributions / torch.norm(attributions)
        attributions = attributions.detach().numpy()

        return {
            **prediction,
            "explain": {
                self.forward_arg_name: [
                    [
                        {"token": token, "attribution": attribution}
                        for token, attribution in zip(
                            sentence_tokens, sentence_attribution
                        )
                    ]
                    for sentence_tokens, sentence_attribution in zip(
                        document_tokens, attributions
                    )
                ]
            },
        }
Пример #4
0
    def _mixed_input_top_k1(pipeline):
        raw_text = [["this", "is", "a", "test"], "this is a test"]
        tag_idx_sequence = [
            vocabulary.index_for_label(pipeline.backbone.vocab, tag)
            for tag in ["O", "O", "O", "U-NER"]
        ]
        viterbi_paths = [[(tag_idx_sequence, 1)]] * 2
        task_output = TaskOutput(viterbi_paths=viterbi_paths,
                                 raw_text=raw_text,
                                 mask=None,
                                 probs=None)

        return pipeline.head.decode(task_output).as_dict()
Пример #5
0
    def explain_prediction(
        self, prediction: Dict[str, numpy.array], instance: Instance, n_steps: int
    ) -> Dict[str, Any]:

        dataset = Batch([instance])
        input_tokens_ids = dataset.as_tensor_dict()
        ig = IntegratedGradients(self._explain_embeddings)

        num_wrapping_dims = 0

        text_tokens = [
            token.text
            for token in cast(TextField, instance.get(self.forward_arg_name)).tokens
        ]
        text_tensor = input_tokens_ids.get(self.forward_arg_name)
        mask = get_text_field_mask(text_tensor, num_wrapping_dims=num_wrapping_dims)
        text_embeddings = self.backbone.embedder.forward(
            text_tensor, num_wrapping_dims=num_wrapping_dims
        )

        label_id = vocabulary.index_for_label(
            self.backbone.vocab, prediction["labels"][0]
        )
        attributions, delta = ig.attribute(
            text_embeddings,
            n_steps=n_steps,
            target=label_id,
            additional_forward_args=mask,
            return_convergence_delta=True,
        )
        attributions = attributions.sum(dim=2).squeeze(0)
        attributions = attributions / torch.norm(attributions)
        attributions = attributions.detach().numpy()

        return {
            **prediction,
            "explain": {
                self.forward_arg_name: [
                    {"token": token, "attribution": attribution}
                    for token, attribution in zip(text_tokens, attributions)
                ]
            },
        }
Пример #6
0
    def explain_prediction(self, prediction: Dict[str, np.array],
                           instance: Instance, n_steps: int) -> Dict[str, Any]:
        """Calculates attributions for each data field in the record by integrating the gradients.

        IMPORTANT: The calculated attributions only make sense for a duplicate/not_duplicate binary classification task
        of the two records.

        Parameters
        ----------
        prediction
        instance
        n_steps

        Returns
        -------
        prediction_dict
            The prediction dictionary with a newly added "explain" key
        """
        batch = Batch([instance])
        tokens_ids = batch.as_tensor_dict()

        # 1. Get field encodings
        # TODO(dcfidalgo): optimize: for the prediction we already embedded and field encoded the records.
        #     Also, the forward passes here are always done on cpu!
        field_encoded_record1, record_mask_record1 = self._field_encoding(
            tokens_ids.get(self._RECORD1_ARG_NAME_IN_FORWARD))
        field_encoded_record2, record_mask_record2 = self._field_encoding(
            tokens_ids.get(self._RECORD2_ARG_NAME_IN_FORWARD))
        if not field_encoded_record1.size() == field_encoded_record2.size():
            raise RuntimeError(
                "Both records must have the same number of data fields!")

        # 2. Get attributes
        ig = IntegratedGradients(self._bimpm_forward)

        prediction_target = int(
            vocabulary.index_for_label(self.backbone.vocab,
                                       prediction["labels"][0]))
        ig_attribute_record1 = ig.attribute(
            inputs=(field_encoded_record1, field_encoded_record2),
            baselines=(field_encoded_record2, field_encoded_record2),
            additional_forward_args=(record_mask_record1, record_mask_record2),
            target=prediction_target,
            return_convergence_delta=True,
            n_steps=n_steps,
        )

        ig_attribute_record2 = ig.attribute(
            inputs=(field_encoded_record1, field_encoded_record2),
            baselines=(field_encoded_record1, field_encoded_record1),
            additional_forward_args=(record_mask_record1, record_mask_record2),
            target=prediction_target,
            return_convergence_delta=True,
            n_steps=n_steps,
        )
        # The code below was an attempt to make attributions for the "duplicate case" more meaningful ... did not work
        # # duplicate case:
        # # Here we integrate each record along the path from the null vector -> record1/2
        # # assuming that the null vector provides the highest "not duplicate" score.
        # if prediction_target == 0:
        #     ig_attribute_record1 = ig.attribute(
        #         inputs=(field_encoded_record1, field_encoded_record2),
        #         baselines=(torch.zeros_like(field_encoded_record1), field_encoded_record2),
        #         additional_forward_args=(record_mask_record1, record_mask_record2),
        #         # we fix the target since we want negative integrals always to be associated
        #         # to the "not_duplicate" case and positive ones to the "duplicate" case
        #         target=0,
        #         return_convergence_delta=True,
        #     )
        #
        #     ig_attribute_record2 = ig.attribute(
        #         inputs=(field_encoded_record1, field_encoded_record2),
        #         baselines=(field_encoded_record1, torch.zeros_like(field_encoded_record2)),
        #         additional_forward_args=(record_mask_record1, record_mask_record2),
        #         # we fix the target since we want negative integrals always to be associated
        #         # to the "not_duplicate" case and positive ones to the "duplicate" case
        #         target=0,
        #         return_convergence_delta=True,
        #     )
        #
        # # not duplicate case:
        # # Here we integrate each record along the path from record2/1 -> record1/2
        # # assuming that the same record provides the highest "duplicate" score.
        # elif prediction_target == 1:
        #     ...
        # else:
        #     raise RuntimeError("The `explain` method is only implemented for a binary classification task: "
        #                        "[duplicate, not_duplicate]")

        attributions_record1, delta_record1 = self._get_attributions_and_delta(
            ig_attribute_record1, 0)
        attributions_record2, delta_record2 = self._get_attributions_and_delta(
            ig_attribute_record2, 1)

        # 3. Get tokens corresponding to the attributions
        field_tokens_record1 = []
        for textfield in instance.get(self._RECORD1_ARG_NAME_IN_FORWARD):
            field_tokens_record1.append(" ".join(
                [token.text for token in textfield.tokens]))
        field_tokens_record2 = []
        for textfield in instance.get(self._RECORD2_ARG_NAME_IN_FORWARD):
            field_tokens_record2.append(" ".join(
                [token.text for token in textfield.tokens]))

        return {
            **prediction,
            "explain": {
                self._RECORD1_ARG_NAME_IN_FORWARD: [{
                    "token": token,
                    "attribution": attribution
                } for token, attribution in zip(field_tokens_record1,
                                                attributions_record1)],
                self._RECORD2_ARG_NAME_IN_FORWARD: [{
                    "token": token,
                    "attribution": attribution
                } for token, attribution in zip(field_tokens_record2,
                                                attributions_record2)],
            },
        }