Fix Inconsistent NER Grouping (Pipeline)
Author: enzoampil
Pull request: yes

### This PR solves issue #4816 by:

1. Applying entity grouping to similar entity types with different prefixes (i.e. `B` and `I`)
2. Ensuring that separate entities at the last filtered index are no longer excluded from grouping.


Running the sample script below (based on reference issue #4816) returns the expected results. Do note that the `entity_group` is based on the `entity_type` of the first entity in the group.

```
from transformers import pipeline
NER_MODEL = "mrm8488/bert-spanish-cased-finetuned-ner"
nlp_ner = pipeline("ner", model=NER_MODEL,
                   grouped_entities=True,
                   tokenizer=(NER_MODEL, {"use_fast": False}))

t = """Consuelo Araújo Noguera, ministra de cultura del presidente Andrés Pastrana (1998.2002) fue asesinada por las Farc luego de haber permanecido secuestrada por algunos meses."""
nlp_ner(t)

[{'entity_group': 'B-PER', 'score': 0.9710702640669686, 'word': 'Consuelo Araújo Noguera'},
 {'entity_group': 'B-PER', 'score': 0.9997273534536362, 'word': 'Andrés Pastrana'},
 {'entity_group': 'B-ORG', 'score': 0.8589080572128296, 'word': 'Farc'}]
```

I also ran another test to ensure that number 2 (separate entity at the last index) is working properly. I confirmed that it is working properly now.

```
nlp = pipeline('ner', grouped_entities=False)
nlp("Enzo works at the the UN")
[{'entity': 'I-PER', 'index': 1, 'score': 0.9968166351318359, 'word': 'En'},
 {'entity': 'I-PER', 'index': 2, 'score': 0.9957635998725891, 'word': '##zo'},
 {'entity': 'I-ORG', 'index': 7, 'score': 0.9986497163772583, 'word': 'UN'}]

nlp2 = pipeline('ner', grouped_entities=True)
nlp2("Enzo works at the the UN")
[{'entity_group': 'I-PER', 'score': 0.9962901175022125, 'word': 'Enzo'},
 {'entity_group': 'I-ORG', 'score': 0.9986497163772583, 'word': 'UN'}]

```

You can test these out yourself in this colab [notebook](https://colab.research.google.com/drive/1D0xK7MSOQcxOCAe8hpnpFVqdoKrmejSS?usp=sharing).

cc @dav009 @mfuntowicz

Comments:
# [Codecov](https://codecov.io/gh/huggingface/transformers/pull/4987?src=pr&el=h1) Report
> Merging [#4987](https://codecov.io/gh/huggingface/transformers/pull/4987?src=pr&el=desc) into [master](https://codecov.io/gh/huggingface/transformers/commit/58cca47c16149e43d1b516623d59e3c5d97f695e&el=desc) will **decrease** coverage by `1.40%`.
> The diff coverage is `91.66%`.

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

```diff
@@            Coverage Diff             @@
##           master    #4987      +/-   ##
==========================================
- Coverage   77.83%   76.43%   -1.41%     
==========================================
  Files         141      141              
  Lines       24634    24638       +4     
==========================================
- Hits        19175    18832     -343     
- Misses       5459     5806     +347     
```


| [Impacted Files](https://codecov.io/gh/huggingface/transformers/pull/4987?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/transformers/pipelines.py](https://codecov.io/gh/huggingface/transformers/pull/4987/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9waXBlbGluZXMucHk=) | `76.16% <91.66%> (+0.16%)` | :arrow_up: |
| [src/transformers/modeling\_tf\_mobilebert.py](https://codecov.io/gh/huggingface/transformers/pull/4987/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9tb2RlbGluZ190Zl9tb2JpbGViZXJ0LnB5) | `23.62% <0.00%> (-73.11%)` | :arrow_down: |
| [src/transformers/modeling\_tf\_electra.py](https://codecov.io/gh/huggingface/transformers/pull/4987/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9tb2RlbGluZ190Zl9lbGVjdHJhLnB5) | `26.92% <0.00%> (-68.47%)` | :arrow_down: |
| [src/transformers/tokenization\_roberta.py](https://codecov.io/gh/huggingface/transformers/pull/4987/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90b2tlbml6YXRpb25fcm9iZXJ0YS5weQ==) | `76.71% <0.00%> (-21.92%)` | :arrow_down: |
| [src/transformers/tokenization\_utils\_base.py](https://codecov.io/gh/huggingface/transformers/pull/4987/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90b2tlbml6YXRpb25fdXRpbHNfYmFzZS5weQ==) | `85.75% <0.00%> (-7.85%)` | :arrow_down: |
| [src/transformers/tokenization\_transfo\_xl.py](https://codecov.io/gh/huggingface/transformers/pull/4987/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90b2tlbml6YXRpb25fdHJhbnNmb194bC5weQ==) | `38.73% <0.00%> (-3.76%)` | :arrow_down: |
| [src/transformers/tokenization\_utils\_fast.py](https://codecov.io/gh/huggingface/transformers/pull/4987/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90b2tlbml6YXRpb25fdXRpbHNfZmFzdC5weQ==) | `92.02% <0.00%> (-2.18%)` | :arrow_down: |
| [src/transformers/tokenization\_openai.py](https://codecov.io/gh/huggingface/transformers/pull/4987/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90b2tlbml6YXRpb25fb3BlbmFpLnB5) | `82.30% <0.00%> (-1.54%)` | :arrow_down: |
| [src/transformers/tokenization\_bert.py](https://codecov.io/gh/huggingface/transformers/pull/4987/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90b2tlbml6YXRpb25fYmVydC5weQ==) | `90.86% <0.00%> (-0.46%)` | :arrow_down: |
| [src/transformers/tokenization\_utils.py](https://codecov.io/gh/huggingface/transformers/pull/4987/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90b2tlbml6YXRpb25fdXRpbHMucHk=) | `89.55% <0.00%> (-0.41%)` | :arrow_down: |
| ... and [4 more](https://codecov.io/gh/huggingface/transformers/pull/4987/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/huggingface/transformers/pull/4987?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/4987?src=pr&el=footer). Last update [58cca47...05f50d9](https://codecov.io/gh/huggingface/transformers/pull/4987?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Looks great, but this sort of code/feature also looks like a perfect candidate for more unit-testing coverage.

What do you think?

Agree, can add these as test cases in [test_pipelines](https://github.com/huggingface/transformers/blob/master/tests/test_pipelines.py).

That would be great @enzoampil!

@julien-c I've added the original issue bug as a test case (Number 1 in the original post). Do note that I only included it in the `torch` version because `mrm8488/bert-spanish-cased-finetuned-ner` seems to only work for torch. Please let me know if this is enough for this PR.

For future PRs to add new test cases coming from issues found on top of this (e.g. those from issue #5077), I was hoping to get some guidance on how we'd include them to the test coverage without making it too heavy. For context, different cases are typically based on different models, which means we'll have to run separate models to add them as test cases.

I think we should try to make the tests more unitary, meaning that for instance you would feed them fixed model outputs (no actual forward pass) and check that the actual formatted output is correct.

This might require splitting the call method in smaller more testable functions, which is totally fine IMO.

I see what you mean. Yes, that makes more sense than running different models. Will work on this.

@julien-c @LysandreJik I've performed the following adjustments to the PR:

1. **I've separated the `group_entities` function from the raw NER forward pass altogether so that it's easy to run tests that feed fixed model outputs and check that the actual formatted output is correct.**

`group_entities` now takes as an argument a list[dict] of raw NER model outputs, and converts them to the *grouped* equivalent.

2. **I've added a new `NerPipelineTests` class in `test_pipelines` which contains all the NER related tests, and includes new tests for the `group_entities` function.**

The test simply confirms if the expected formatted output (grouped) is equivalent to the actual formatted output given the raw model outputs. For the test cases, I used the two samples from the original PR post. It should be straight forward to continue adding test cases moving forward.

Please do let me know what you guys think! :smile:

Yes, looks good. I would add some typings to (at least) the `group_entities` and `group_sub_entities` but we can do that in a subsequent PR.

@LysandreJik @julien-c Thanks for the feedback. I've added typings for the `group_entities` and `group_sub_entities` functions :smile: