[Documentation Example] Task Summary - Start/End of Span in QA Example
Author: RobMcH
Pull request: no

## Environment info
This bug pertains the [extractive QA example given in the documentation](https://huggingface.co/transformers/task_summary.html#extractive-question-answering) and will be reproducible across operating systems and library versions. I provide a fix for the PyTorch code below, the same can be applied to the TensorFlow example.

### Who can help
Probably @sgugger and/or @patrickvonplaten.

## Information

The way that the start and end of the extracted span are calculated means that it is possible that `end <= start`, which will lead to an empty answer. Instead, a joint probability distribution over start/end positions should be used with the probability of selecting (start, end) s.t. `end <= start` = 0.

## To reproduce
For reference, the original code:
```
for question in questions:
...     inputs = tokenizer(question, text, add_special_tokens=True, return_tensors="pt")
...     input_ids = inputs["input_ids"].tolist()[0]
...
...     outputs = model(**inputs)
...     answer_start_scores = outputs.start_logits
...     answer_end_scores = outputs.end_logits
...
...     answer_start = torch.argmax(
...         answer_start_scores
...     )  # Get the most likely beginning of answer with the argmax of the score
...     answer_end = torch.argmax(answer_end_scores) + 1  # Get the most likely end of answer with the argmax of the score
...
...     answer = tokenizer.convert_tokens_to_string(tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end]))
...
...     print(f"Question: {question}")
...     print(f"Answer: {answer}")
```

I suggest modifying the code something akin to the below. I also modified the answer decoding to exclude special tokens.

```
for question in questions:
...     inputs = tokenizer(question, text, add_special_tokens=True, return_tensors="pt")
...     input_ids = inputs["input_ids"].tolist()[0]
...
...     outputs = model(**inputs)
...     # Get probabilities for start and end of the answer span.
...     start_probs = torch.softmax(outputs.start_logits.squeeze(), 0)
...     end_probs = torch.softmax(outputs.end_logits.squeeze(), 0)
...     # Calculate joint probabilities.
...     answer_span = torch.outer(start_probs, end_probs)
...     # Mask out any pair (i, j) where j <= i.
...     mask = torch.ones(answer_span.shape).tril()
...     mask[mask == 1] = float("-inf")
...     # Select span based on max joint probability.
...     answer_start, answer_end = torch.where(answer_span == (answer_span + mask).max())
...     answer_start, answer_end = answer_start[0], answer_end[0] + 1
...     # Decode IDs within the span, ignoring special tokens.
...     answer = tokenizer.decode(input_ids[answer_start:answer_end], skip_special_tokens=True)
...
...     print(f"Question: {question}")
...     print(f"Answer: {answer}")
```

Running this code gives the same answers for the example input (c.f. documentation), but avoids the issue of extracting spans with `end <= start` for other inputs. One such input would for instance be:
```
text = 'stairs down eventually lead to the invocation level. Performing the invocation ritual at the vibrating square opens the stairs to the Sanctum.\n With the Amulet of Yendor in hand, the adventurer may ascend from level 1 into the Plane of Earth; thence s/he may proceed through magic portals to the planes of Air, Fire, and Water, and thence to the Astral Plane. Offering the Amulet of Yendor on the correct high altar wins the game.\n Along the way, one will encounter these branches and special levels'
questions = ["how do i descend the dungeon"]
```
for which the output of the original, unmodified code will be (because it selects a span end that is before the selected start):
```
Question: how do i descend the dungeon
Answer:
```
and for the fixed code proposed above:

```
Question: how do i descend the dungeon
Answer: stairs down eventually lead to the invocation level
```

I'm happy to port the code to TensorFlow and submit a pull request with the updated code.

Comments:
The example is just a quick demo on Question Answering and there are many, many things it doesn't do. I'd rather keep it simple since this page covers a lot of things already, and since we have the two [question answering](https://github.com/huggingface/transformers/tree/master/examples/pytorch/question-answering) scripts that are more complete.

I understand that it's a simple example but the point raised is not about things it doesn't do, but rather that it does this specific thing wrong. The code snippet assumes conditional independence which then leads to it potentially producing empty outputs. The example is placed very prominently within the documentation so I'd argue that it should be corrected. The question answering scripts seem to [make a similar assumption](https://github.com/huggingface/transformers/blob/master/examples/pytorch/question-answering/utils_qa.py#L128) but then correct for it by ignoring invalid predictions.

This issue has been automatically marked as stale because it has not had recent activity. If you think this still needs to be addressed please comment on this thread.

Please note that issues that do not follow the [contributing guidelines](https://github.com/huggingface/transformers/blob/master/CONTRIBUTING.md) are likely to be ignored.