Memory leak when using DistilBert for inference to extract [CLS] hidden state
Author: lucaguarro
Pull request: no

## Environment info
<!-- You can run the command `transformers-cli env` and copy-and-paste its output below.
     Don't forget to fill out the missing fields in that output! -->

- `transformers` version: 4.7.0
- Platform: Linux-5.4.104+-x86_64-with-Ubuntu-18.04-bionic
- Python version: 3.7.10
- PyTorch version (GPU?): 1.9.0+cu102 (True)
- Tensorflow version (GPU?): 2.5.0 (True)
- Using GPU in script?: Yes
- Using distributed or parallel set-up in script?: No

### Who can help
@patrickvonplaten @drjosephliu 
<!-- Your issue will be replied to more quickly if you can figure out the right person to tag with @
 If you know how to use git blame, that is the easiest way, otherwise, here is a rough guide of **who to tag**.
 Please tag fewer than 3 people.

Models:
- albert, bert, xlm: @LysandreJik
- blenderbot, bart, marian, pegasus, encoderdecoder,  t5: @patrickvonplaten, @patil-suraj
- longformer, reformer, transfoxl, xlnet: @patrickvonplaten
- fsmt: @stas00
- funnel: @sgugger
- gpt2: @patrickvonplaten, @LysandreJik
- rag: @patrickvonplaten, @lhoestq
- tensorflow: @Rocketknight1

Library:

- benchmarks: @patrickvonplaten
- deepspeed: @stas00
- ray/raytune: @richardliaw, @amogkam
- text generation: @patrickvonplaten
- tokenizers: @LysandreJik
- trainer: @sgugger
- pipelines: @LysandreJik

Documentation: @sgugger

Model hub:

- for issues with a model report at https://discuss.huggingface.co/ and tag the model's creator.

HF projects:

- datasets: [different repo](https://github.com/huggingface/datasets)
- rust tokenizers: [different repo](https://github.com/huggingface/tokenizers)

Examples:

- maintained examples (not research project or legacy): @sgugger, @patil-suraj
- research_projects/bert-loses-patience: @JetRunner
- research_projects/distillation: @VictorSanh

 -->

## Information

Model I am using (Bert, XLNet ...): DistilBert

The problem arises when using:
* [ ] the official example scripts: (give details below)
* [X] my own modified scripts: (give details below)

The tasks I am working on is:
* [ ] an official GLUE/SQUaD task: (give the name)
* [X] my own task or dataset: (give details below)

I am attempting to extract all of the pooled outputs for each row in my dataset and return them as an array. My dataset consists of 14000 rows and the size of a single pooled output is (1,768). Therefore, I would expect my RAM usage to be ~(14000 * 768 * 4) bytes --> 43 MBs.
However, I notice that my RAM usage seems to increase exponentially as more and more iterations are executed. This occurs when using both the CPU and the GPU. When running on CPU, the google colab environment shows a huge jump in RAM-usage about 75% through my dataset.
Here is a screenshot of the RAM usage that illustrates this problem:
![RAM](https://user-images.githubusercontent.com/22605313/122801156-a28d2680-d278-11eb-8250-146689a205aa.PNG)
## To reproduce

Steps to reproduce the behavior:

1. Encode a dataset (sufficiently large; mine has 14k samples of 512 tokens)
2. Run it through my function (provided below) to extract the pooled output of each sample

`

      def getPooledOutputs(model, encoded_dataset, batch_size = 32):
        model.eval()
        pooled_outputs = []
        print("total number of iters ", len(encoded_dataset['input_ids'])//batch_size + 1)
        
        for i in range(len(encoded_dataset['input_ids'])//batch_size + 1):
          print(i)
          up_to = i*batch_size + batch_size
          if len(encoded_dataset['input_ids']) < up_to:
            up_to = len(encoded_dataset['input_ids'])
          input_ids = th.LongTensor(encoded_dataset['input_ids'][i*batch_size:up_to]).cuda()
          attention_mask = th.LongTensor(encoded_dataset['attention_mask'][i*batch_size:up_to]).cuda()
      
          with torch.no_grad():
            embeddings = model.forward(input_ids=input_ids, attention_mask=attention_mask, output_hidden_states=True)['hidden_states'][-1][:,0] # Pooled output
            pooled_outputs.extend(embeddings)
            th.cuda.empty_cache()
      
        return pooled_outputs
`

This is the error message (GPU):
> RuntimeError: CUDA out of memory. Tried to allocate 384.00 MiB (GPU 0; 15.78 GiB total capacity; 13.75 GiB already allocated; 260.75 MiB free; 14.21 GiB reserved in total by PyTorch)

On CPU my runtime just crashes.

<!-- If you have code snippets, error messages, stack traces please provide them here as well.
     Important! Use code tags to correctly format your code. See https://help.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks#syntax-highlighting
     Do not use screenshots, as they are hard to read and (more importantly) don't allow others to copy-and-paste your code.-->

## Expected behavior

<!-- A clear and concise description of what you would expect to happen. -->
To have my function return an array of the [CLS] pooled output for every row in my dataset and to have my GPU ram usage roughly constant during the entirety of the function call.

Comments:
Hi! You're keeping the results of your model forward call in memory when extending your `pooled_output` list so your memory is bound to take a hit as you iterate through your dataset

@LysandreJik Sorry I should have clarified that my dataset consists of 14000 rows and the size out of the output I am trying to extract for each one of them is (1,768). This thus corresponds to (14000 * 768 * 4) Bytes --> 43 megabytes. Unless there are undesired artifacts from the forward calls that are being stored?

Seems like I have fixed my problem by making `pooled_outputs` a pytorch tensor and not a list.
So that my function now looks like this:

```
def getPooledOutputs(model, encoded_dataset, batch_size = 32):
  model.eval()

  # pooled_outputs = []
  pooled_outputs = torch.empty([0,768]).cuda()
  print("total number of iters ", len(encoded_dataset['input_ids'])//batch_size + 1)
  
  for i in range(len(encoded_dataset['input_ids'])//batch_size + 1):
    print(i)
    up_to = i*batch_size + batch_size
    if len(encoded_dataset['input_ids']) < up_to:
      up_to = len(encoded_dataset['input_ids'])
    input_ids = th.LongTensor(encoded_dataset['input_ids'][i*batch_size:up_to]).cuda()
    attention_mask = th.LongTensor(encoded_dataset['attention_mask'][i*batch_size:up_to]).cuda()

    with torch.no_grad():
      embeddings = model.forward(input_ids=input_ids, attention_mask=attention_mask, output_hidden_states=True)['hidden_states'][-1][:,0] # Pooled output
      pooled_outputs = th.cat([pooled_outputs, embeddings],0)
      th.cuda.empty_cache()

  return pooled_outputs
```

Still do not know why having a list of tensors is a problem but I suppose that this does not concern Huggingface