Unverified Commit f26a3530 authored by Rishabh Manoj's avatar Rishabh Manoj Committed by GitHub
Browse files

Update pipelines.py

Modified QA pipeline to consider all features for each example before generating topk answers. 
Current pipeline only takes one SquadExample, one SquadFeature, one start logit list, one end logit list to retrieve the answer, this is not correct as one SquadExample can produce multiple SquadFeatures.
parent 16ce15ed
...@@ -705,55 +705,65 @@ class QuestionAnsweringPipeline(Pipeline): ...@@ -705,55 +705,65 @@ class QuestionAnsweringPipeline(Pipeline):
# Convert inputs to features # Convert inputs to features
examples = self._args_parser(*texts, **kwargs) examples = self._args_parser(*texts, **kwargs)
features = squad_convert_examples_to_features( features_list = [ squad_convert_examples_to_features(
examples, self.tokenizer, kwargs["max_seq_len"], kwargs["doc_stride"], kwargs["max_question_len"], False [example],
) self.tokenizer,
fw_args = self.inputs_for_model([f.__dict__ for f in features]) kwargs["max_seq_len"],
kwargs["doc_stride"],
# Manage tensor allocation on correct device kwargs["max_question_len"],
with self.device_placement(): False
if self.framework == "tf": ) for example in examples ]
fw_args = {k: tf.constant(v) for (k, v) in fw_args.items()} all_answers = []
start, end = self.model(fw_args) for features, example in zip(features_list, examples):
start, end = start.numpy(), end.numpy() fw_args = self.inputs_for_model([f.__dict__ for f in features])
else:
with torch.no_grad(): # Manage tensor allocation on correct device
# Retrieve the score for the context tokens only (removing question tokens) with self.device_placement():
fw_args = {k: torch.tensor(v, device=self.device) for (k, v) in fw_args.items()} if self.framework == "tf":
start, end = self.model(**fw_args) fw_args = {k: tf.constant(v) for (k, v) in fw_args.items()}
start, end = start.cpu().numpy(), end.cpu().numpy() start, end = self.model(fw_args)
start, end = start.numpy(), end.numpy()
answers = [] else:
for (example, feature, start_, end_) in zip(examples, features, start, end): with torch.no_grad():
# Normalize logits and spans to retrieve the answer # Retrieve the score for the context tokens only (removing question tokens)
start_ = np.exp(start_) / np.sum(np.exp(start_)) fw_args = {k: torch.tensor(v, device=self.device) for (k, v) in fw_args.items()}
end_ = np.exp(end_) / np.sum(np.exp(end_)) start, end = self.model(**fw_args)
start, end = start.cpu().numpy(), end.cpu().numpy()
# Mask padding and question
start_, end_ = start_ * np.abs(np.array(feature.p_mask) - 1), end_ * np.abs(np.array(feature.p_mask) - 1) answers = []
for (feature, start_, end_) in zip(features, start, end):
# TODO : What happens if not possible # Normalize logits and spans to retrieve the answer
# Mask CLS start_ = np.exp(start_) / np.sum(np.exp(start_))
start_[0] = end_[0] = 0 end_ = np.exp(end_) / np.sum(np.exp(end_))
starts, ends, scores = self.decode(start_, end_, kwargs["topk"], kwargs["max_answer_len"]) # Mask padding and question
char_to_word = np.array(example.char_to_word_offset) start_, end_ = start_ * np.abs(np.array(feature.p_mask) - 1), end_ * np.abs(np.array(feature.p_mask) - 1)
# Convert the answer (tokens) back to the original text # TODO : What happens if not possible
answers += [ # Mask CLS
{ start_[0] = end_[0] = 0
"score": score.item(),
"start": np.where(char_to_word == feature.token_to_orig_map[s])[0][0].item(), starts, ends, scores = self.decode(start_, end_, kwargs["topk"], kwargs["max_answer_len"])
"end": np.where(char_to_word == feature.token_to_orig_map[e])[0][-1].item(), char_to_word = np.array(example.char_to_word_offset)
"answer": " ".join(
example.doc_tokens[feature.token_to_orig_map[s] : feature.token_to_orig_map[e] + 1] # Convert the answer (tokens) back to the original text
), answers += [
} {
for s, e, score in zip(starts, ends, scores) "score": score.item(),
] "start": np.where(char_to_word == feature.token_to_orig_map[s])[0][0].item(),
if len(answers) == 1: "end": np.where(char_to_word == feature.token_to_orig_map[e])[0][-1].item(),
return answers[0] "answer": " ".join(
return answers example.doc_tokens[feature.token_to_orig_map[s] : feature.token_to_orig_map[e] + 1]
),
}
for s, e, score in zip(starts, ends, scores)
]
answers = sorted(answers, key = lambda x:x['score'], reverse=True)[:kwargs["topk"]]
all_answers+=answers
if len(all_answers) == 1:
return all_answers[0]
return all_answers
def decode(self, start: np.ndarray, end: np.ndarray, topk: int, max_answer_len: int) -> Tuple: def decode(self, start: np.ndarray, end: np.ndarray, topk: int, max_answer_len: int) -> Tuple:
""" """
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment