[WIP] Create a Trainer class to handle TF2 model training
Author: jplu
Pull request: yes

**EDIT**
Close this PR to create a cleaner, and more on purpose one.

Hello,

I'm opening the pull request I was talking about in the issue #2783. Here the proposed features in this PR:

- [x] add checkpoint manager in order to make a training fault-tolerant
- [x] add custom fit method to take into account the specific training steps in distributed mode or not
- [x] add optimizer creation method depending of its name
- [ ] add loss method in order to be able to custom the loss computation
- [x] add a Tensorboard summary writer to make the logs available in Tensorboard

For now I have created the definition of the methods with their documentation but with a `raise NotImplementedError` as first I would like to have your opinion on the signatures of these methods. Also I know that @julien-c you have recently worked on a `TFModelUtilsMixin` class. Do you think that some of these methods should go into it instead of directly in `TFPreTrainedModel`?

ping also @sshleifer 



Comments:
I'm not 100% sure which ones of those methods need to live on the model vs. in the training framework

For instance, over in #2816, @srush is implementing support for `pytorch-lightning`, which over in PyTorch world, handles a lot of those tasks. In PyTorch we wouldn't want to implement these in to the model themselves.

Thoughts?

This is a really good point indeed, because it is something I don't know myself and also why I wanted your opinion on this.

How I see the whole picture is like the following. Having a class that will handle the training, let's name it `Trainer` for example, we can imagine this class implementing:
- An LR finder
- A cyclic training
- And maybe other things
Then looks like:
```
class Trainer(object):
  def __init__(self, model_path, training_data, eval_data, **kwargs):
   #kwargs will contain the the parameters of the TFPretrainedModel class
   # such as distributed=True, optimizer="adam", etc...
    self.model = Automodel.from_pretrained(model_path, kwargs)
    self.tokenizer = AutoTokenizer.from_pretrained(model_path)
    self.training_data = training_data
    self.eval_data = eval_data

  def preprocess_data():
    # preprocessing the data with the tokenizer
  
  def lr_finder():
     #blabla implementation
     return best_lr_over_training_data

  def train(epochs):
    lr = self.lr_finder()
    self.model.create_optimizer(lr) # Certainly need to modify the signature of this method in the file above
    self.create_checkpoint_manager("save")
    self.create_summary_writer("logs")
    self.model.fit(training_data, epochs) # implementing the cycling training here instead of just model.fit()
```

Then the code of the external user would be maybe something like:
```
parameters = {....}
trainer = Trainer("bert-base-uncased", [training, data], [eval, data],  parameters)
trainer.preprocess_data()
trainer.train(4) # We can even imagine not giving the number of epochs, and use an EarlyStop callback and give a default number of epochs.
trainer.model.save_pretrained()
```

Of course this is just the first draft that comes threw my mind. There will be certainly several changes.

I have checked what is `pytorch-lightning` and it blows my mind, this is really awesome! So convenient and group a lot of the things I want to add here indeed. Unfortunately, I don't know such lib over TF2. I will take some time to check if it exists in parallel of what I'm doing here :)

Ok, I finally moved everything into a `Trainer` class. I think it was a bad idea to mix the pretrained model and the training features. I think it is much better now.

Also instead of the long list of keys in the `**kwargs` parameter we can imagine a config file specifically made for training, and one could custom the training just by updating the JSON file and not the code itself.

You now have a working example in `examples/run_tf_glue_with_trainer.py`. You can now see how simple it becomes to train a model, if we put apart the config dictionary, training a model takes 4 lines of code.

Of course there is still a lot of work to do, but now you can have a much better idea of where I wanna go. The next main focuses will be:
- how to select such or such data processor in order to have a trainer more generic for the dataprocessing part
- include metrics
- run an evaluation

This looks really great @jplu!

Thanks!

Close this PR to create a cleaner, and more on purpose one.