fix ZeroDivisionError and epoch counting
Author: ydshieh
Pull request: yes

@sgugger 

This PR fix 2 minor bugs in `trainer.py`.

First, the two lines
            
    num_train_epochs = (
        self.args.max_steps // (len(train_dataloader) // self.args.gradient_accumulation_steps) + 1
    )

and

    epochs_trained = self.global_step // (len(train_dataloader) // self.args.gradient_accumulation_steps)

gives `ZeroDivisionError` when `len(train_dataloader) < self.args.gradient_accumulation_steps`.

The fix takes into account the code below the comment

     # last step in epoch but step is always smaller than gradient_accumulation_steps

therefore when `len(train_dataloader) < self.args.gradient_accumulation_steps`, we still have 1 step in each epoch.

The second bug is due to the `+ 1` in the

    num_train_epochs = (
        self.args.max_steps // (len(train_dataloader) // self.args.gradient_accumulation_steps) + 1
    )

In the example below, we have

    len(train_dataloader) == 40
    self.args.gradient_accumulation_steps = 4
    self.args.max_steps = 10

However we get `num_train_epochs == 2` from the original code, which should be `1`.

    python utils/download_glue_data.py --data_dir ./examples/text-classification/glue/ --tasks all

    python3 run_glue.py \
        --task_name wnli \
        --data_dir ./glue/WNLI \
        --model_name_or_path distilbert-base-uncased \
        --output_dir ./glue/WNLI/ \
        --max_seq_length  16 \
        --num_train_epochs 1 \
        --per_device_train_batch_size 16 \
        --gradient_accumulation_steps 4 \
        --max_steps 10 \
        --logging_steps 1 \
        --save_steps 5 \
        --seed 1 \
        --do_train \
        --do_eval \
        --do_predict \
        --overwrite_output_dir


    (Pdb) len(train_dataloader)
    40
    (Pdb) self.args.gradient_accumulation_steps
    4
    (Pdb) self.args.max_steps
    10
    (Pdb) p num_train_epochs
    2

Comments:
Looks great! Would you mind adding a few tests so we don't accidentally make changes that will resurface this bug?

> 
> 
> Looks great! Would you mind adding a few tests so we don't accidentally make changes that will resurface this bug?

@sgugger 
I did a few simple tests on my side. Could you guide me how to write a test that is usually done by HF members? BTW, I don't have powerful machine, so hope the tests could be lightweight.

# [Codecov](https://codecov.io/gh/huggingface/transformers/pull/7125?src=pr&el=h1) Report
> Merging [#7125](https://codecov.io/gh/huggingface/transformers/pull/7125?src=pr&el=desc) into [master](https://codecov.io/gh/huggingface/transformers/commit/4d3914841932065f19a61ad46e178f94dedeff5a?el=desc) will **increase** coverage by `1.60%`.
> The diff coverage is `66.66%`.

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

```diff
@@            Coverage Diff             @@
##           master    #7125      +/-   ##
==========================================
+ Coverage   80.07%   81.68%   +1.60%     
==========================================
  Files         168      168              
  Lines       32257    32259       +2     
==========================================
+ Hits        25831    26351     +520     
+ Misses       6426     5908     -518     
```


| [Impacted Files](https://codecov.io/gh/huggingface/transformers/pull/7125?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/transformers/trainer.py](https://codecov.io/gh/huggingface/transformers/pull/7125/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90cmFpbmVyLnB5) | `52.90% <66.66%> (+0.29%)` | :arrow_up: |
| [src/transformers/modeling\_tf\_distilbert.py](https://codecov.io/gh/huggingface/transformers/pull/7125/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9tb2RlbGluZ190Zl9kaXN0aWxiZXJ0LnB5) | `64.55% <0.00%> (-34.28%)` | :arrow_down: |
| [src/transformers/tokenization\_albert.py](https://codecov.io/gh/huggingface/transformers/pull/7125/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90b2tlbml6YXRpb25fYWxiZXJ0LnB5) | `70.19% <0.00%> (-23.08%)` | :arrow_down: |
| [src/transformers/modeling\_mobilebert.py](https://codecov.io/gh/huggingface/transformers/pull/7125/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9tb2RlbGluZ19tb2JpbGViZXJ0LnB5) | `79.21% <0.00%> (-10.25%)` | :arrow_down: |
| [src/transformers/modeling\_xlm.py](https://codecov.io/gh/huggingface/transformers/pull/7125/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9tb2RlbGluZ194bG0ucHk=) | `88.77% <0.00%> (-2.55%)` | :arrow_down: |
| [src/transformers/file\_utils.py](https://codecov.io/gh/huggingface/transformers/pull/7125/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9maWxlX3V0aWxzLnB5) | `82.80% <0.00%> (-0.25%)` | :arrow_down: |
| [src/transformers/tokenization\_utils\_base.py](https://codecov.io/gh/huggingface/transformers/pull/7125/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy90b2tlbml6YXRpb25fdXRpbHNfYmFzZS5weQ==) | `94.04% <0.00%> (+0.13%)` | :arrow_up: |
| [src/transformers/modeling\_bert.py](https://codecov.io/gh/huggingface/transformers/pull/7125/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9tb2RlbGluZ19iZXJ0LnB5) | `88.44% <0.00%> (+0.16%)` | :arrow_up: |
| [src/transformers/data/data\_collator.py](https://codecov.io/gh/huggingface/transformers/pull/7125/diff?src=pr&el=tree#diff-c3JjL3RyYW5zZm9ybWVycy9kYXRhL2RhdGFfY29sbGF0b3IucHk=) | `93.54% <0.00%> (+0.71%)` | :arrow_up: |
| ... and [3 more](https://codecov.io/gh/huggingface/transformers/pull/7125/diff?src=pr&el=tree-more) | |

------

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


Look at the `test_trainer.py` file in the tests directory. There are utility functions to build a small `Trainer` on a simple regression problem. You can just add a few tests mimicking the ones that count the number of steps during training.

To check they pass, you can run the command:
```
pytest tests/test_trainer.py
```
which should go pretty quickly.

Thanks. I will do it and report it when ready.

I add a test `test_num_train_epochs_in_training` for the case `len(train_dataloader) < self.args.gradient_accumulation_steps`.
For the offset by 1 bug below due to the `+ 1` in 

    num_train_epochs = (
        self.args.max_steps // (len(train_dataloader) // self.args.gradient_accumulation_steps) + 1
    )

, I didn't figure a way to test, because this information is not returned in the output, and its actual usage is for this line

    logger.info("  Num Epochs = %d", num_train_epochs)

because the training loop is actually controlled by `self.args.max_steps` if it is given.

(I assume `adding a few tests` means to add in the `test_trainer.py` and push).

Yes adding the test in `test_trainer.py` is exactly what I wanted, thanks!