Preserve spaces in GPT-2 tokenizers
Author: joeddav
Pull request: yes

**The issue**: The GPT-2 and RoBERTa tokenizers are incorrectly stripping whitespace following special characters, preventing the BPE encoder from correctly encoding spaces in tokens following RoBERTa `<mask>` and `<unk>` tokens.

```
tokenizer.convert_ids_to_tokens(tokenizer.encode('She likes <mask> cats.'))
# output:    ['
', 'She', 'Ġlikes', '<mask>', 'cats', '.', '
']
# should be: ['
', 'ĠShe', 'Ġlikes', '<mask>', 'Ġcats', '.', '
']
```

This makes the model inputs (and therefore outputs) incorrect. This issue manifests itself in the `fill-mask` pipeline where the model erroneously thinks the mask is a prefix to the following word when using RoBERTa:

```
roberta_fillmask = pipeline("fill-mask")
sentence = "She likes <mask> cats."
roberta_fillmask(sentence)
# top predictions: "She likes bobcats.", "She likes pussycats."
```

This PR makes the following changes:
- Preserves trailing whitespace following special tokens
- Inserts a space after the prepended start token when `add_special_tokens` is `True` in `encode()` so that the user doesn't have to include a leading space in the string. This can be overriden with the `add_prefix_space` argument.
- Adds a `framework` argument to the `pipeline` factory function, allowing users to easily specify TF vs PyTorch

After making these changes, the top predictions from the above example become 'She likes cute cats.' and 'She likes her cats.'

Comments:
You're correct about the GPT-2 tokenizer – I failed to consider that GPT2 doesn't have a BOS token. I've pushed an alternative solution that defines a base `prepare_for_tokenization` method which children can override to make changes to the text before tokenization.

As for your second point, the changes are made where sequences are encoded in different ways and then compared. The clearest example is probably [here](https://github.com/huggingface/transformers/pull/2778/files#diff-1ca2285a5350e3d634978637356a9bdbR266-R267). The first encode is done with `add_special_tokens=False` whereas the second is done with `add_special_tokens=True`. Since adding special tokens now also adds a prefix space by default in RoBERTa, it's necessary to add `add_prefix_space=False` in the second encode so that the results are consistent.

Cool! I believe the `run_tests_tf` is failing due to a tokenization error (linked with your PR).

# [Codecov](https://codecov.io/gh/huggingface/transformers/pull/2778?src=pr&el=h1) Report
> Merging [#2778](https://codecov.io/gh/huggingface/transformers/pull/2778?src=pr&el=desc) into [master](https://codecov.io/gh/huggingface/transformers/commit/73368963b200f2d70d2267bd49a3fa794850b3ff?src=pr&el=desc) will **decrease** coverage by `1.05%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/huggingface/transformers/pull/2778/graphs/tree.svg?width=650&token=9qOlN6Hb1c&height=150&src=pr)](https://codecov.io/gh/huggingface/transformers/pull/2778?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #2778      +/-   ##
==========================================
- Coverage   75.09%   74.03%   -1.06%     
==========================================
  Files          93       93              
  Lines       15250    15263      +13     
==========================================
- Hits        11452    11300     -152     
- Misses       3798     3963     +165
```


| [Impacted Files](https://codecov.io/gh/huggingface/transformers/pull/2778?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/transformers/tokenization\_roberta.py](https://codecov.io/gh/huggingface/transformers/pull/2778/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90b2tlbml6YXRpb25fcm9iZXJ0YS5weQ==) | `100% <100%> (ø)` | :arrow_up: |
| [src/transformers/tokenization\_gpt2.py](https://codecov.io/gh/huggingface/transformers/pull/2778/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90b2tlbml6YXRpb25fZ3B0Mi5weQ==) | `96.26% <100%> (+0.05%)` | :arrow_up: |
| [src/transformers/tokenization\_utils.py](https://codecov.io/gh/huggingface/transformers/pull/2778/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90b2tlbml6YXRpb25fdXRpbHMucHk=) | `86.1% <100%> (+0.41%)` | :arrow_up: |
| [src/transformers/pipelines.py](https://codecov.io/gh/huggingface/transformers/pull/2778/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9waXBlbGluZXMucHk=) | `71.42% <100%> (-0.52%)` | :arrow_down: |
| [src/transformers/modeling\_tf\_pytorch\_utils.py](https://codecov.io/gh/huggingface/transformers/pull/2778/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9tb2RlbGluZ190Zl9weXRvcmNoX3V0aWxzLnB5) | `8.72% <0%> (-81.21%)` | :arrow_down: |
| [src/transformers/modeling\_roberta.py](https://codecov.io/gh/huggingface/transformers/pull/2778/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9tb2RlbGluZ19yb2JlcnRhLnB5) | `85.91% <0%> (-9.86%)` | :arrow_down: |
| [src/transformers/modeling\_xlnet.py](https://codecov.io/gh/huggingface/transformers/pull/2778/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9tb2RlbGluZ194bG5ldC5weQ==) | `70.94% <0%> (-2.28%)` | :arrow_down: |
| [src/transformers/modeling\_ctrl.py](https://codecov.io/gh/huggingface/transformers/pull/2778/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9tb2RlbGluZ19jdHJsLnB5) | `92.07% <0%> (-2.21%)` | :arrow_down: |
| [src/transformers/modeling\_openai.py](https://codecov.io/gh/huggingface/transformers/pull/2778/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9tb2RlbGluZ19vcGVuYWkucHk=) | `80.06% <0%> (-1.33%)` | :arrow_down: |
| ... and [1 more](https://codecov.io/gh/huggingface/transformers/pull/2778/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/huggingface/transformers/pull/2778?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/huggingface/transformers/pull/2778?src=pr&el=footer). Last update [7336896...a7bacfa](https://codecov.io/gh/huggingface/transformers/pull/2778?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Looks like it was an incorrect pipelines test which was treating each test sentence as one concat'd sequence, [here](https://github.com/huggingface/transformers/pull/2778/files#diff-ca5a8abd41d5c7bd3e6da1636c531976R97).

Yap at the moment on the Rust side, there is also an issue regarding the tokenization of Roberta, but which is slightly different from the one here.

When this PR land to master i'll rebase the tokenizers-v2 branch to run all the new tests that the branch brings and see if there is nothing breaking :).

It looks great to me 👍