Make loss function an init parameter
Author: Querela
Pull request: no

# 🚀 Feature request

<!-- A clear and concise description of the feature proposal.
     Please provide a link to the paper and code in case they exist. -->

I would like to request an optional init parameter of `*Model` (e. g. BertModel) that allows the user to provide an own loss function for training. If it is None, it will fall back to the default implementations.

## Motivation

<!-- Please outline the motivation for the proposal. Is your feature request
     related to a problem? e.g., I'm always frustrated when [...]. If this is related
     to another GitHub issue, please link here too. -->

Main motivation is ease of use for the user.
Say for example, I want to change the default `CrossEntropyLoss` in a non-binary classification with `BertForSequenceClassification`, I have to override `forward` in a subclass or write my own class.
Now I will have to do this for all model types (RoBERTa, XLNet ...) I want to also check out. Because of subclassing I won't be able to use `AutoModelForSequenceClassification.from_pretrained`.

Suppose, this is an init parameter with default `None`. Any model can be instanciated, and in the `forward` method the default loss functions will be used if not explicitely a custom loss function (factory) is being provided.

```python
def forward(...):
    # ...
    loss = None
        if labels is not None:
            if self.num_labels == 1:
                #  We are doing regression
                loss_fct = MSELoss()
                loss = loss_fct(logits.view(-1), labels.view(-1))
            else:
                loss_fct = CrossEntropyLoss()
                loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
```
to
```python
def __init__(..., loss_fct_cls =None):
    # ....
    self.loss_fct_cls = loss_fct_cls 

def forward(...):
    # ...
    loss = None
        if labels is not None:
            if self.num_labels == 1:
                #  We are doing regression
                loss_fct = MSELoss() if not self.loss_fct_cls else self.loss_fct_cls()
                loss = loss_fct(logits.view(-1), labels.view(-1))
            else:
                loss_fct = CrossEntropyLoss() if not self.loss_fct_cls else self.loss_fct_cls()
                loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))

# ....

# usage
config = AutoConfig.from_pretrained("bert-base-uncased", num_labels=2)
model = AutoModelForSequenceClassification.from_pretrained("bert-base-uncased", config=config, loss_fct_cls=torch.nn.BCELossWithLogitsLoss)
```

The user still has to be careful to use the correct loss function to match the number of labels for example but he can easier transition between models. The the overhead in performance and adapting a optional custom loss to each model should not be that high. It is just another hyperparameter that further allows for customization in using the models.

This might also allow for easier multi-class multi-label classifications, as it now is more for multi-class single-label, isn't it?

## Your contribution

<!-- Is there any way that you could help, e.g. by submitting a PR?
     Make sure to read the CONTRIBUTING.MD readme:
     https://github.com/huggingface/transformers/blob/master/CONTRIBUTING.md -->

In case the feature request will not immediately be denied because of reasons (not sure which?), I can start extending the existing models to allow for using an optional loss function.
I'm just not sure what the final parameter name should be because changing it can probably be done with `sed`/search-replace but doing it right the first time is just being efficient. I'm also not sure whether to store it in the config object or in the model as an attribute. (For performance reasons, it would still cache it as a model property for slightly faster access but I did not design the whole library, so I might be wrong on my thoughts.)


Comments:
While I understand where you're coming from, why not define the loss function outside of the model, like it's generally done for PyTorch models? If you do not pass the labels, you get the logits from the model. You can use these logits with your labels to compute any loss you want.

Ok. Good I asked first because I did not know this general practice. 😄 

So, if I want to use the `Trainer`, I will then be required to only override:
https://github.com/huggingface/transformers/blob/9d7d0005b046a95d9d59354714bb6c3547a612fe/src/transformers/trainer.py#L1114-L1120
&rarr; I would need to split the labels from the input, feed it into the model as usual and then compute the loss afterwards manually. Or just ignore the default computed loss and compute my own loss myself and override it.

My own loss computation can then still be like this:
https://github.com/huggingface/transformers/blob/9d7d0005b046a95d9d59354714bb6c3547a612fe/src/transformers/models/bert/modeling_bert.py#L1383-L1391

I think this is even easier. Thank you. Not sure why I did not see this ...

Yes, the trainer has a `compute_loss` method that is simple to override for that exact purpose. Glad you're satisfied with the outcome, and thanks for opening such a detailed issue.

I stumbled over this exact method when the `Trainer` was introduced but did not realize that the models still return the raw logits that I then can use for custom loss computation ...
Well, I try to research before opening issues, and looking through the source code often helps understand some details but the code base keeps growing and changing, so it's sometimes hard to keep up and not miss some obvious things.
😄