src/transformers/pipelines/question_answering.py/QuestionAnsweringPipeline/postprocess
class QuestionAnsweringPipeline:    def postprocess(
        self,
        model_outputs,
        top_k=1,
        handle_impossible_answer=False,
        max_answer_len=15,
    ):
        min_null_score = 1000000  # large and positive
        answers = []
        start_ = model_outputs["start"][0]
        end_ = model_outputs["end"][0]
        feature = model_outputs["features"][0]
        example = model_outputs["example"]
        # Ensure padded tokens & question tokens cannot belong to the set of candidate answers.
        undesired_tokens = np.abs(np.array(feature.p_mask) - 1)

        if feature.attention_mask is not None:
            undesired_tokens = undesired_tokens & feature.attention_mask

        # Generate mask
        undesired_tokens_mask = undesired_tokens == 0.0

        # Make sure non-context indexes in the tensor cannot contribute to the softmax
        start_ = np.where(undesired_tokens_mask, -10000.0, start_)
        end_ = np.where(undesired_tokens_mask, -10000.0, end_)

        # Normalize logits and spans to retrieve the answer
        start_ = np.exp(start_ - np.log(np.sum(np.exp(start_), axis=-1, keepdims=True)))
        end_ = np.exp(end_ - np.log(np.sum(np.exp(end_), axis=-1, keepdims=True)))

        if handle_impossible_answer:
            min_null_score = min(min_null_score, (start_[0] * end_[0]).item())

        # Mask CLS
        start_[0] = end_[0] = 0.0

        starts, ends, scores = self.decode(start_, end_, top_k, max_answer_len, undesired_tokens)
        if not self.tokenizer.is_fast:
            char_to_word = np.array(example.char_to_word_offset)

            # Convert the answer (tokens) back to the original text
            # Score: score from the model
            # Start: Index of the first character of the answer in the context string
            # End: Index of the character following the last character of the answer in the context string
            # Answer: Plain text of the answer
            for s, e, score in zip(starts, ends, scores):
                answers.append(
                    {
                        "score": score.item(),
                        "start": np.where(char_to_word == feature.token_to_orig_map[s])[0][0].item(),
                        "end": np.where(char_to_word == feature.token_to_orig_map[e])[0][-1].item(),
                        "answer": " ".join(
                            example.doc_tokens[feature.token_to_orig_map[s] : feature.token_to_orig_map[e] + 1]
                        ),
                    }
                )
        else:
            # Convert the answer (tokens) back to the original text
            # Score: score from the model
            # Start: Index of the first character of the answer in the context string
            # End: Index of the character following the last character of the answer in the context string
            # Answer: Plain text of the answer
            question_first = bool(self.tokenizer.padding_side == "right")
            enc = feature.encoding

            # Sometimes the max probability token is in the middle of a word so:
            # - we start by finding the right word containing the token with `token_to_word`
            # - then we convert this word in a character span with `word_to_chars`
            sequence_index = 1 if question_first else 0
            for s, e, score in zip(starts, ends, scores):
                try:
                    start_word = enc.token_to_word(s)
                    end_word = enc.token_to_word(e)
                    start_index = enc.word_to_chars(start_word, sequence_index=sequence_index)[0]
                    end_index = enc.word_to_chars(end_word, sequence_index=sequence_index)[1]
                except Exception:
                    # Some tokenizers don't really handle words. Keep to offsets then.
                    start_index = enc.offsets[s][0]
                    end_index = enc.offsets[e][1]

                answers.append(
                    {
                        "score": score.item(),
                        "start": start_index,
                        "end": end_index,
                        "answer": example.context_text[start_index:end_index],
                    }
                )

        if handle_impossible_answer:
            answers.append({"score": min_null_score, "start": 0, "end": 0, "answer": ""})

            answers = sorted(answers, key=lambda x: x["score"], reverse=True)[:top_k]
        if len(answers) == 1:
            return answers[0]
        return answers